Initial commit
This commit is contained in:
544
engines/glk/agt/filename.cpp
Normal file
544
engines/glk/agt/filename.cpp
Normal file
@@ -0,0 +1,544 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/agt/agility.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace AGT {
|
||||
|
||||
#ifdef force16
|
||||
#undef int
|
||||
#define int short
|
||||
#endif
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Filetype Data */
|
||||
/*----------------------------------------------------------------------*/
|
||||
const char *extname[] = {
|
||||
"",
|
||||
DA1, DA2, DA3, DA4, DA5, DA6, DSS,
|
||||
pHNT, pOPT, pTTL,
|
||||
pSAV, pSCR, pLOG,
|
||||
pAGX, pINS, pVOC, pCFG,
|
||||
pAGT, pDAT, pMSG, pCMD, pSTD, AGTpSTD
|
||||
};
|
||||
|
||||
|
||||
#ifdef PATH_SEP
|
||||
static const char *path_sep = PATH_SEP;
|
||||
#else
|
||||
static const char *path_sep = nullptr;
|
||||
#endif
|
||||
|
||||
/* This returns the options to use when opening the given file type */
|
||||
/* rw is true if we are writing, false if we are reading. */
|
||||
const char *filetype_info(filetype ft, rbool rw) {
|
||||
if (ft < fTTL) return "rb";
|
||||
if (ft == fAGX) return rw ? "wb" : "rb";
|
||||
if (ft == fSAV) return (rw ? "wb" : "rb");
|
||||
if (ft == fTTL || ft == fINS || ft == fVOC) return "rb";
|
||||
#ifdef OPEN_AS_TEXT
|
||||
if (ft >= fCFG) return (open_as_binary ? "rb" : "r");
|
||||
#else
|
||||
if (ft >= fCFG) return "rb";
|
||||
#endif
|
||||
if (ft == fSCR) {
|
||||
if (rw)
|
||||
return (BATCH_MODE || make_test) ? "w" : "a";
|
||||
else return "r";
|
||||
}
|
||||
if (ft == fLOG) return rw ? "w" : "r";
|
||||
fatal("INTERNAL ERROR: Invalid filetype.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if ft is a possible extension in general context ft_base */
|
||||
static rbool compat_ext(filetype ft, filetype ft_base) {
|
||||
if (ft_base == fNONE || ft_base == fDA1 || ft_base == fAGX) { /* Game file */
|
||||
return (ft >= fDA1 && ft <= fDSS)
|
||||
|| ft == fOPT || ft == fTTL
|
||||
|| (ft >= fAGX && ft <= fCFG);
|
||||
}
|
||||
|
||||
if (ft_base == fSAV || ft_base == fSCR || ft_base == fLOG)
|
||||
return (ft == ft_base);
|
||||
|
||||
if (ft_base == fAGT) { /* Source code */
|
||||
return (ft >= fAGT && ft <= fCMD)
|
||||
|| ft == fTTL || ft == fCFG;
|
||||
}
|
||||
|
||||
fatal("INTERNAL ERROR: Invalid file class.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Misc. utilities */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
char *assemble_filename(const char *path, const char *root,
|
||||
const char *ext) {
|
||||
int len1, len2, len3;
|
||||
char *name;
|
||||
|
||||
len1 = len2 = len3 = 0;
|
||||
if (path != nullptr) len1 = strlen(path);
|
||||
if (root != nullptr) len2 = strlen(root);
|
||||
if (ext != nullptr) len3 = strlen(ext);
|
||||
name = (char *)rmalloc(len1 + len2 + len3 + 1);
|
||||
if (path != nullptr) memcpy(name, path, len1);
|
||||
#ifdef PREFIX_EXT
|
||||
if (ext != NULL) memcpy(name + len1, ext, len3);
|
||||
if (root != NULL) memcpy(name + len1 + len3, root, len2);
|
||||
#else
|
||||
if (root != nullptr) memcpy(name + len1, root, len2);
|
||||
if (ext != nullptr) memcpy(name + len1 + len2, ext, len3);
|
||||
#endif
|
||||
name[len1 + len2 + len3] = 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
#ifdef PATH_SEP
|
||||
/* This works for binary files; we don't care about non-binary
|
||||
files since this only used to search for game files. */
|
||||
static rbool file_exist(const char *fname) {
|
||||
return Common::File::exists(fname);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This checks to see if c matches any of the characters in matchset */
|
||||
static rbool smatch(char c, const char *matchset) {
|
||||
for (; *matchset != 0; matchset++)
|
||||
if (*matchset == c) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Taking Apart the Filename */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int find_path_sep(const char *name) {
|
||||
int i;
|
||||
|
||||
if (path_sep == nullptr)
|
||||
return -1;
|
||||
for (i = strlen(name) - 1; i >= 0; i--)
|
||||
if (smatch(name[i], path_sep)) break;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/* Checks to see if the filename (which must be path-free)
|
||||
has an extension. Returns the length of the extensions
|
||||
and writes the extension type in pft */
|
||||
static int search_for_ext(const char *name, filetype base_ft,
|
||||
filetype *pft) {
|
||||
filetype t;
|
||||
int xlen, len;
|
||||
|
||||
*pft = fNONE;
|
||||
len = strlen(name);
|
||||
if (len == 0) return 0;
|
||||
for (t = (filetype)(fNONE + 1); t <= fSTD; t = (filetype)((int)t + 1))
|
||||
if (compat_ext(t, base_ft)) {
|
||||
xlen = strlen(extname[t]);
|
||||
if (xlen == 0 || xlen > len) continue;
|
||||
#ifdef PREFIX_EXT
|
||||
if (strncasecmp(name, extname[t], xlen) == 0)
|
||||
#else
|
||||
if (fnamecmp(name + len - xlen, extname[t]) == 0)
|
||||
#endif
|
||||
{
|
||||
*pft = t;
|
||||
return xlen;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Extract root filename or extension from
|
||||
pathless name, given that the extension is of length extlen. */
|
||||
/* If isext is true, extract the extension. If isext is false,
|
||||
then extrac the root. */
|
||||
static char *extract_piece(const char *name, int extlen, rbool isext) {
|
||||
char *root;
|
||||
int len, xlen;
|
||||
rbool first; /* If true, extract from beginning; if false, extract
|
||||
from end */
|
||||
|
||||
len = strlen(name) - extlen;
|
||||
xlen = extlen;
|
||||
if (isext) {
|
||||
int tmp;
|
||||
tmp = len;
|
||||
len = xlen;
|
||||
xlen = tmp;
|
||||
}
|
||||
if (len == 0) return nullptr;
|
||||
root = (char *)rmalloc((len + 1) * sizeof(char));
|
||||
#ifdef PREFIX_EXT
|
||||
first = isext ? 1 : 0;
|
||||
#else
|
||||
first = isext ? 0 : 1;
|
||||
#endif
|
||||
if (first) {
|
||||
memcpy(root, name, len);
|
||||
root[len] = 0;
|
||||
} else {
|
||||
memcpy(root, name + xlen, len);
|
||||
root[len] = 0;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
/* This returns true if "path" is absolute, false otherwise.
|
||||
This is _very_ platform dependent. */
|
||||
static rbool absolute_path(char *path) {
|
||||
#ifdef pathtest
|
||||
return pathtest(path);
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Basic routines for dealing with file contexts */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
#define FC(x) ((file_context_rec*)(x))
|
||||
|
||||
/* formal_name is used to get filenames for diagnostic messages, etc. */
|
||||
char *formal_name(fc_type fc, filetype ft) {
|
||||
if (FC(fc)->special) return FC(fc)->gamename;
|
||||
if (ft == fNONE)
|
||||
return rstrdup(FC(fc)->shortname);
|
||||
if (ft == fAGT_STD)
|
||||
return rstrdup(AGTpSTD);
|
||||
return assemble_filename("", FC(fc)->shortname, extname[ft]);
|
||||
}
|
||||
|
||||
#ifdef PATH_SEP
|
||||
static rbool test_file(const char *path, const char *root, const char *ext) {
|
||||
char *name;
|
||||
rbool tmp;
|
||||
|
||||
name = assemble_filename(path, root, ext);
|
||||
tmp = file_exist(name);
|
||||
rfree(name);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* This does a path search for the game files. */
|
||||
static void fix_path(file_context_rec *fc) {
|
||||
char **ppath;
|
||||
|
||||
|
||||
if (gamepath == NULL) return;
|
||||
for (ppath = gamepath; *ppath != NULL; ppath++)
|
||||
if (test_file(*ppath, fc->shortname, fc->ext)
|
||||
|| test_file(*ppath, fc->shortname, pAGX)
|
||||
|| test_file(*ppath, fc->shortname, DA1)) {
|
||||
fc->path = rstrdup(*ppath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* This creates a new file context based on gamename. */
|
||||
/* ft indicates the rough use it will be put towards:
|
||||
ft=fNONE indicates it's the first pass read, before PATH has been
|
||||
read in, and so the fc shouldn't be filled out until
|
||||
fix_file_context() is called.
|
||||
ft=pDA1 indicates that name refers to the game files.
|
||||
ft=pAGX indicates the name of the AGX file to be written to.
|
||||
ft=pSAV,pLOG,pSCR all indicate that name corresponds to the
|
||||
related type of file. */
|
||||
fc_type init_file_context(const char *name, filetype ft) {
|
||||
file_context_rec *fc;
|
||||
int p, x; /* Path and extension markers */
|
||||
|
||||
fc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
|
||||
fc->special = 0;
|
||||
|
||||
fc->gamename = rstrdup(name);
|
||||
|
||||
p = find_path_sep(fc->gamename);
|
||||
if (p < 0)
|
||||
fc->path = nullptr;
|
||||
else {
|
||||
fc->path = (char *)rmalloc((p + 2) * sizeof(char));
|
||||
memcpy(fc->path, fc->gamename, p + 1);
|
||||
fc->path[p + 1] = '\0';
|
||||
}
|
||||
x = search_for_ext(fc->gamename + p + 1, ft, &fc->ft);
|
||||
fc->shortname = extract_piece(fc->gamename + p + 1, x, 0);
|
||||
fc->ext = extract_piece(fc->gamename + p + 1, x, 1);
|
||||
|
||||
#ifdef PATH_SEP
|
||||
if (fc->path == NULL && ft == fDA1)
|
||||
fix_path(fc);
|
||||
#endif
|
||||
return fc;
|
||||
}
|
||||
|
||||
|
||||
void fix_file_context(fc_type fc, filetype ft) {
|
||||
#ifdef PATH_SEP
|
||||
if (FC(fc)->path == NULL && ft == fDA1)
|
||||
fix_path(FC(fc));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* This creates new file contexts from old. */
|
||||
/* This is used to create save/log/script filenames from the game name,
|
||||
and to create include files in the same directory as the source file. */
|
||||
fc_type convert_file_context(fc_type fc, filetype ft, const char *name) {
|
||||
file_context_rec *nfc;
|
||||
rbool local_ftype; /* Indicates file should be in working directory,
|
||||
not game directory. */
|
||||
|
||||
local_ftype = (ft == fSAV || ft == fSCR || ft == fLOG);
|
||||
if (BATCH_MODE || make_test) local_ftype = 0;
|
||||
|
||||
if (name == nullptr) {
|
||||
nfc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
|
||||
nfc->gamename = nullptr;
|
||||
nfc->path = nullptr;
|
||||
nfc->shortname = rstrdup(fc->shortname);
|
||||
nfc->ext = nullptr;
|
||||
nfc->ft = fNONE;
|
||||
nfc->special = 0;
|
||||
} else {
|
||||
nfc = init_file_context(name, ft);
|
||||
}
|
||||
|
||||
/* If path already defined, then combine paths. */
|
||||
if (!local_ftype && nfc->path != nullptr && !absolute_path(nfc->path)) {
|
||||
char *newpath;
|
||||
newpath = nfc->path;
|
||||
newpath = assemble_filename(fc->path, nfc->path, "");
|
||||
rfree(nfc->path);
|
||||
nfc->path = newpath;
|
||||
}
|
||||
|
||||
/* scripts, save-games and logs should go in the working directory,
|
||||
not the game directory, so leave nfc->path equal to NULL for them. */
|
||||
if (!local_ftype && nfc->path == nullptr)
|
||||
nfc->path = rstrdup(fc->path); /* Put files in game directory */
|
||||
return nfc;
|
||||
}
|
||||
|
||||
void release_file_context(fc_type *pfc) {
|
||||
file_context_rec *fc;
|
||||
fc = FC(*pfc);
|
||||
rfree(fc->gamename);
|
||||
rfree(fc->path);
|
||||
rfree(fc->shortname);
|
||||
rfree(fc->ext);
|
||||
rfree(fc);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Routines for Finding Files */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static genfile try_open_file(const char *path, const char *root,
|
||||
const char *ext, const char *how,
|
||||
rbool nofix) {
|
||||
char *name = assemble_filename(path, root, ext);
|
||||
genfile f = fopen(name, how);
|
||||
rfree(name);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static genfile findread(file_context_rec *fc, filetype ft) {
|
||||
genfile f;
|
||||
|
||||
f = nullptr;
|
||||
|
||||
if (ft == fAGT_STD) {
|
||||
f = try_open_file(fc->path, AGTpSTD, "", filetype_info(ft, 0), 0);
|
||||
return f;
|
||||
}
|
||||
if (ft == fAGX || ft == fNONE) /* Try opening w/o added extension */
|
||||
f = try_open_file(fc->path, fc->shortname, fc->ext, filetype_info(ft, 0), 0);
|
||||
if (f == nullptr)
|
||||
f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* File IO Routines */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
genfile readopen(fc_type fc, filetype ft, const char **errstr) {
|
||||
genfile f;
|
||||
|
||||
*errstr = nullptr;
|
||||
f = findread(fc, ft);
|
||||
if (f == nullptr) {
|
||||
*errstr = "Cannot open file";
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
rbool fileexist(fc_type fc, filetype ft) {
|
||||
genfile f;
|
||||
|
||||
if (fc->special) return 0;
|
||||
f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 1);
|
||||
if (f != nullptr) { /* File already exists */
|
||||
readclose(f);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
genfile writeopen(fc_type fc, filetype ft,
|
||||
file_id_type *pfileid, const char **errstr) {
|
||||
char *name;
|
||||
genfile f;
|
||||
|
||||
*errstr = nullptr;
|
||||
name = nullptr;
|
||||
|
||||
{
|
||||
name = assemble_filename(FC(fc)->path, FC(fc)->shortname, extname[ft]);
|
||||
f = fopen(name, filetype_info(ft, 1));
|
||||
}
|
||||
if (f == nullptr) {
|
||||
*errstr = "Cannot open file";
|
||||
}
|
||||
if (pfileid == nullptr)
|
||||
rfree(name);
|
||||
else
|
||||
*pfileid = name;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
rbool filevalid(genfile f, filetype ft) {
|
||||
return (f != nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void binseek(genfile f, long offset) {
|
||||
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
|
||||
assert(rs);
|
||||
|
||||
rs->seek(offset);
|
||||
}
|
||||
|
||||
|
||||
/* This returns the number of bytes read, or 0 if there was an error. */
|
||||
long varread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
|
||||
long num;
|
||||
|
||||
*errstr = nullptr;
|
||||
assert(f != nullptr);
|
||||
|
||||
num = fread(buff, recsize, recnum, f);
|
||||
if (num != recnum)
|
||||
*errstr = "varread";
|
||||
num = num * recsize;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
rbool binread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
|
||||
long num;
|
||||
|
||||
num = varread(f, buff, recsize, recnum, errstr);
|
||||
if (num < recsize * recnum && *errstr == nullptr)
|
||||
*errstr = rstrdup("Unexpected end of file.");
|
||||
return (*errstr == nullptr);
|
||||
}
|
||||
|
||||
|
||||
rbool binwrite(genfile f, void *buff, long recsize, long recnum, rbool ferr) {
|
||||
assert(f != nullptr);
|
||||
|
||||
if (fwrite(buff, recsize, recnum, f) != (size_t)recnum) {
|
||||
if (ferr) fatal("binwrite");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void readclose(genfile f) {
|
||||
assert(f != nullptr);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void writeclose(genfile f, file_id_type fileid) {
|
||||
assert(f != nullptr);
|
||||
rfree(fileid);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
long binsize(genfile f)
|
||||
/* Returns the size of a binary file */
|
||||
{
|
||||
long pos, leng;
|
||||
|
||||
assert(f != nullptr);
|
||||
|
||||
pos = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
leng = ftell(f);
|
||||
fseek(f, pos, SEEK_SET);
|
||||
|
||||
return leng;
|
||||
}
|
||||
|
||||
rbool textrewind(genfile f) {
|
||||
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
|
||||
assert(rs);
|
||||
rs->seek(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
genfile badfile(filetype ft) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace AGT
|
||||
} // End of namespace Glk
|
||||
Reference in New Issue
Block a user