mirror of https://github.com/OpenTTD/OpenTTD
(svn r11106) -Add: added .tar support; you can pack all files in your data/ dir in how ever many .tar files you like, keeping the dir-structure equal to the unpacked version, and OpenTTD can handle them just like the files were unpacked
-Note: useful for GRF-packs and 32bpp PNGs. Don't forget to keep the dir-structure alive for 32bpp PNGs! -Note: file-loading-order: search-paths, .tar-files in the order found on disk (can be anything at all, don't depend on it.. use 'openttd -d1' to see which order they are added)release/0.6
parent
b25c661ce6
commit
5647bd5157
172
src/fileio.cpp
172
src/fileio.cpp
|
@ -204,6 +204,7 @@ const char *_subdirs[NUM_SUBDIRS] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *_searchpaths[NUM_SEARCHPATHS];
|
const char *_searchpaths[NUM_SEARCHPATHS];
|
||||||
|
std::list<const char *> _tar_list;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given file exists
|
* Check whether the given file exists
|
||||||
|
@ -302,6 +303,99 @@ FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subd
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE *FioTarFileList(const char *tar, const char *mode, size_t *filesize, FioTarFileListCallback *callback, void *userdata)
|
||||||
|
{
|
||||||
|
/* The TAR-header, repeated for every file */
|
||||||
|
typedef struct TarHeader {
|
||||||
|
char name[100]; ///< Name of the file
|
||||||
|
char mode[8];
|
||||||
|
char uid[8];
|
||||||
|
char gid[8];
|
||||||
|
char size[12]; ///< Size of the file, in ASCII
|
||||||
|
char mtime[12];
|
||||||
|
char chksum[8];
|
||||||
|
char typeflag;
|
||||||
|
char linkname[100];
|
||||||
|
char magic[6];
|
||||||
|
char version[2];
|
||||||
|
char uname[32];
|
||||||
|
char gname[32];
|
||||||
|
char devmajor[8];
|
||||||
|
char devminor[8];
|
||||||
|
char prefix[155]; ///< Path of the file
|
||||||
|
|
||||||
|
char unused[12];
|
||||||
|
} TarHeader;
|
||||||
|
|
||||||
|
assert(mode[0] == 'r'); // Only reading is supported
|
||||||
|
assert(callback != NULL); // We need a callback, else this function doens't do much
|
||||||
|
|
||||||
|
#if defined(WIN32) && defined(UNICODE)
|
||||||
|
/* fopen is implemented as a define with ellipses for
|
||||||
|
* Unicode support (prepend an L). As we are not sending
|
||||||
|
* a string, but a variable, it 'renames' the variable,
|
||||||
|
* so make that variable to makes it compile happily */
|
||||||
|
wchar_t Lmode[5];
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILE *f = fopen(tar, mode);
|
||||||
|
assert(f != NULL);
|
||||||
|
|
||||||
|
TarHeader th;
|
||||||
|
char buf[sizeof(th.name) + 1], *end;
|
||||||
|
char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
|
||||||
|
|
||||||
|
while (!feof(f)) {
|
||||||
|
/* Read the header and make sure it is a valid one */
|
||||||
|
fread(&th, 1, 512, f);
|
||||||
|
if (strncmp(th.magic, "ustar", 5) != 0) return NULL;
|
||||||
|
|
||||||
|
name[0] = '\0';
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
/* The prefix contains the directory-name */
|
||||||
|
if (th.prefix[0] != '\0') {
|
||||||
|
memcpy(name, th.prefix, sizeof(th.prefix));
|
||||||
|
name[sizeof(th.prefix)] = '\0';
|
||||||
|
len = strlen(name);
|
||||||
|
name[len] = PATHSEPCHAR;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the name of the file in a safe way at the end of 'name' */
|
||||||
|
memcpy(&name[len], th.name, sizeof(th.name));
|
||||||
|
name[len + sizeof(th.name)] = '\0';
|
||||||
|
|
||||||
|
/* Calculate the size of the file.. for some strange reason this is stored as a string */
|
||||||
|
memcpy(buf, th.size, sizeof(th.size));
|
||||||
|
buf[sizeof(th.size)] = '\0';
|
||||||
|
int skip = strtol(buf, &end, 8);
|
||||||
|
|
||||||
|
/* Check in the callback if this is the file we want */
|
||||||
|
if (callback(name, skip, userdata)) {
|
||||||
|
if (filesize != NULL) *filesize = skip;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip to the next block.. */
|
||||||
|
fseek(f, ALIGN(skip, 512), SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FioFOpenFileTarFileListCallback(const char *filename, int size, void *search_filename)
|
||||||
|
{
|
||||||
|
return strcasecmp(filename, (const char *)search_filename) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *FioFOpenFileTar(const char *filename, const char *tar_filename, size_t *filesize)
|
||||||
|
{
|
||||||
|
return FioTarFileList(tar_filename, "rb", filesize, FioFOpenFileTarFileListCallback, (void *)filename);
|
||||||
|
}
|
||||||
|
|
||||||
/** Opens OpenTTD files somewhere in a personal or global directory */
|
/** Opens OpenTTD files somewhere in a personal or global directory */
|
||||||
FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
|
FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
|
||||||
{
|
{
|
||||||
|
@ -314,6 +408,14 @@ FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir,
|
||||||
f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
|
f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
|
||||||
if (f != NULL || subdir == NO_DIRECTORY) break;
|
if (f != NULL || subdir == NO_DIRECTORY) break;
|
||||||
}
|
}
|
||||||
|
/* We can only use .tar in case of data-dir, and read-mode */
|
||||||
|
if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') {
|
||||||
|
const char *tar;
|
||||||
|
FOR_ALL_TARS(tar) {
|
||||||
|
f = FioFOpenFileTar(filename, tar, filesize);
|
||||||
|
if (f != NULL) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
@ -410,6 +512,74 @@ void ChangeWorkingDirectory(const char *exe)
|
||||||
#endif /* WITH_COCOA */
|
#endif /* WITH_COCOA */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool TarListAddFile(const char *filename)
|
||||||
|
{
|
||||||
|
/* See if we already have a tar by that name; useless to have double entries in our list */
|
||||||
|
const char *tar;
|
||||||
|
FOR_ALL_TARS(tar) {
|
||||||
|
if (strcmp(tar, filename) == 0) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG(misc, 1, "Found tar: %s", filename);
|
||||||
|
_tar_list.push_back(strdup(filename));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ScanPathForTarFiles(const char *path, int basepath_length)
|
||||||
|
{
|
||||||
|
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
|
||||||
|
|
||||||
|
uint num = 0;
|
||||||
|
struct stat sb;
|
||||||
|
struct dirent *dirent;
|
||||||
|
DIR *dir;
|
||||||
|
|
||||||
|
if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
|
||||||
|
|
||||||
|
while ((dirent = readdir(dir)) != NULL) {
|
||||||
|
const char *d_name = FS2OTTD(dirent->d_name);
|
||||||
|
char filename[MAX_PATH];
|
||||||
|
|
||||||
|
if (!FiosIsValidFile(path, dirent, &sb)) continue;
|
||||||
|
|
||||||
|
snprintf(filename, lengthof(filename), "%s%s", path, d_name);
|
||||||
|
|
||||||
|
if (sb.st_mode & S_IFDIR) {
|
||||||
|
/* Directory */
|
||||||
|
if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
|
||||||
|
AppendPathSeparator(filename, lengthof(filename));
|
||||||
|
num += ScanPathForTarFiles(filename, basepath_length);
|
||||||
|
} else if (sb.st_mode & S_IFREG) {
|
||||||
|
/* File */
|
||||||
|
char *ext = strrchr(filename, '.');
|
||||||
|
|
||||||
|
/* If no extension or extension isn't .tar, skip the file */
|
||||||
|
if (ext == NULL) continue;
|
||||||
|
if (strcasecmp(ext, ".tar") != 0) continue;
|
||||||
|
|
||||||
|
if (TarListAddFile(filename)) num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ScanForTarFiles()
|
||||||
|
{
|
||||||
|
Searchpath sp;
|
||||||
|
char path[MAX_PATH];
|
||||||
|
uint num = 0;
|
||||||
|
|
||||||
|
DEBUG(misc, 1, "Scanning for tars");
|
||||||
|
FOR_ALL_SEARCHPATHS(sp) {
|
||||||
|
FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
|
||||||
|
num += ScanPathForTarFiles(path, strlen(path));
|
||||||
|
}
|
||||||
|
DEBUG(misc, 1, "Scan complete, found %d files", num);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the base (personal dir and game data dir) paths
|
* Determine the base (personal dir and game data dir) paths
|
||||||
* @param exe the path to the executable
|
* @param exe the path to the executable
|
||||||
|
@ -461,6 +631,8 @@ extern void cocoaSetApplicationBundleDir();
|
||||||
#else
|
#else
|
||||||
_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
|
_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ScanForTarFiles();
|
||||||
}
|
}
|
||||||
#endif /* defined(WIN32) || defined(WINCE) */
|
#endif /* defined(WIN32) || defined(WINCE) */
|
||||||
|
|
||||||
|
|
10
src/fileio.h
10
src/fileio.h
|
@ -6,6 +6,7 @@
|
||||||
#define FILEIO_H
|
#define FILEIO_H
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include <list>
|
||||||
|
|
||||||
void FioSeekTo(uint32 pos, int mode);
|
void FioSeekTo(uint32 pos, int mode);
|
||||||
void FioSeekToFile(uint8 slot, uint32 pos);
|
void FioSeekToFile(uint8 slot, uint32 pos);
|
||||||
|
@ -60,6 +61,11 @@ DECLARE_POSTFIX_INCREMENT(Searchpath);
|
||||||
*/
|
*/
|
||||||
extern const char *_searchpaths[NUM_SEARCHPATHS];
|
extern const char *_searchpaths[NUM_SEARCHPATHS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the tar-files OpenTTD could search through.
|
||||||
|
*/
|
||||||
|
extern std::list<const char *>_tar_list;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given search path is a valid search path
|
* Checks whether the given search path is a valid search path
|
||||||
* @param sp the search path to check
|
* @param sp the search path to check
|
||||||
|
@ -72,6 +78,10 @@ static inline bool IsValidSearchPath(Searchpath sp)
|
||||||
|
|
||||||
/** Iterator for all the search paths */
|
/** Iterator for all the search paths */
|
||||||
#define FOR_ALL_SEARCHPATHS(sp) for (sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) if (IsValidSearchPath(sp))
|
#define FOR_ALL_SEARCHPATHS(sp) for (sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) if (IsValidSearchPath(sp))
|
||||||
|
#define FOR_ALL_TARS(tar) for (std::list<const char *>::iterator it = _tar_list.begin(); it != _tar_list.end(); it++) if (tar = *it, true)
|
||||||
|
|
||||||
|
typedef bool FioTarFileListCallback(const char *filename, int size, void *userdata);
|
||||||
|
FILE *FioTarFileList(const char *tar, const char *mode, size_t *filesize, FioTarFileListCallback *callback, void *userdata);
|
||||||
|
|
||||||
FILE *FioFOpenFile(const char *filename, const char *mode = "rb", Subdirectory subdir = DATA_DIR, size_t *filesize = NULL);
|
FILE *FioFOpenFile(const char *filename, const char *mode = "rb", Subdirectory subdir = DATA_DIR, size_t *filesize = NULL);
|
||||||
bool FioCheckFileExists(const char *filename, Subdirectory subdir = DATA_DIR);
|
bool FioCheckFileExists(const char *filename, Subdirectory subdir = DATA_DIR);
|
||||||
|
|
|
@ -356,12 +356,35 @@ static uint ScanPath(const char *path, int basepath_length)
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FioTarFileListScanNewGRFCallback(const char *filename, int size, void *userdata)
|
||||||
|
{
|
||||||
|
uint *num = (uint *)userdata;
|
||||||
|
char *ext = strrchr(filename, '.');
|
||||||
|
|
||||||
|
/* If no extension or extension isn't .grf, skip the file */
|
||||||
|
if (ext == NULL) return false;
|
||||||
|
if (strcasecmp(ext, ".grf") != 0) return false;
|
||||||
|
|
||||||
|
if (ScanPathAddGrf(filename)) (*num)++;
|
||||||
|
|
||||||
|
/* Always return false, as we don't want to stop with listing all the files */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint ScanTar(const char *filename)
|
||||||
|
{
|
||||||
|
uint num = 0;
|
||||||
|
|
||||||
|
FioTarFileList(filename, "rb", NULL, FioTarFileListScanNewGRFCallback, &num);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
/* Scan for all NewGRFs */
|
/* Scan for all NewGRFs */
|
||||||
void ScanNewGRFFiles()
|
void ScanNewGRFFiles()
|
||||||
{
|
{
|
||||||
Searchpath sp;
|
Searchpath sp;
|
||||||
char path[MAX_PATH];
|
char path[MAX_PATH];
|
||||||
|
const char *tar;
|
||||||
uint num = 0;
|
uint num = 0;
|
||||||
|
|
||||||
ClearGRFConfigList(&_all_grfs);
|
ClearGRFConfigList(&_all_grfs);
|
||||||
|
@ -371,6 +394,9 @@ void ScanNewGRFFiles()
|
||||||
FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
|
FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
|
||||||
num += ScanPath(path, strlen(path));
|
num += ScanPath(path, strlen(path));
|
||||||
}
|
}
|
||||||
|
FOR_ALL_TARS(tar) {
|
||||||
|
num += ScanTar(tar);
|
||||||
|
}
|
||||||
DEBUG(grf, 1, "Scan complete, found %d files", num);
|
DEBUG(grf, 1, "Scan complete, found %d files", num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue