src/fileio.cpp
branchNewGRF_ports
changeset 6870 ca3fd1fbe311
parent 6743 cabfaa4a0295
child 6871 5a9dc001e1ad
equal deleted inserted replaced
6869:76282d3b748d 6870:ca3fd1fbe311
    14 #ifdef WIN32
    14 #ifdef WIN32
    15 #include <windows.h>
    15 #include <windows.h>
    16 #else
    16 #else
    17 #include <pwd.h>
    17 #include <pwd.h>
    18 #include <unistd.h>
    18 #include <unistd.h>
       
    19 #endif
    19 #include <sys/stat.h>
    20 #include <sys/stat.h>
    20 #endif
       
    21 
    21 
    22 /*************************************************/
    22 /*************************************************/
    23 /* FILE IO ROUTINES ******************************/
    23 /* FILE IO ROUTINES ******************************/
    24 /*************************************************/
    24 /*************************************************/
    25 
    25 
    72 	_fio.usage_count[slot]++;
    72 	_fio.usage_count[slot]++;
    73 }
    73 }
    74 #endif /* LIMITED_FDS */
    74 #endif /* LIMITED_FDS */
    75 
    75 
    76 /* Seek to a file and a position */
    76 /* Seek to a file and a position */
    77 void FioSeekToFile(uint32 pos)
    77 void FioSeekToFile(uint8 slot, uint32 pos)
    78 {
    78 {
    79 	FILE *f;
    79 	FILE *f;
    80 #if defined(LIMITED_FDS)
    80 #if defined(LIMITED_FDS)
    81 	/* Make sure we have this file open */
    81 	/* Make sure we have this file open */
    82 	FioRestoreFile(pos >> 24);
    82 	FioRestoreFile(slot);
    83 #endif /* LIMITED_FDS */
    83 #endif /* LIMITED_FDS */
    84 	f = _fio.handles[pos >> 24];
    84 	f = _fio.handles[slot];
    85 	assert(f != NULL);
    85 	assert(f != NULL);
    86 	_fio.cur_fh = f;
    86 	_fio.cur_fh = f;
    87 	_fio.filename = _fio.filenames[pos >> 24];
    87 	_fio.filename = _fio.filenames[slot];
    88 	FioSeekTo(GB(pos, 0, 24), SEEK_SET);
    88 	FioSeekTo(pos, SEEK_SET);
    89 }
    89 }
    90 
    90 
    91 byte FioReadByte()
    91 byte FioReadByte()
    92 {
    92 {
    93 	if (_fio.buffer == _fio.buffer_end) {
    93 	if (_fio.buffer == _fio.buffer_end) {
   178 #if defined(LIMITED_FDS)
   178 #if defined(LIMITED_FDS)
   179 	FioFreeHandle();
   179 	FioFreeHandle();
   180 #endif /* LIMITED_FDS */
   180 #endif /* LIMITED_FDS */
   181 	f = FioFOpenFile(filename);
   181 	f = FioFOpenFile(filename);
   182 	if (f == NULL) error("Cannot open file '%s'", filename);
   182 	if (f == NULL) error("Cannot open file '%s'", filename);
       
   183 	uint32 pos = ftell(f);
   183 
   184 
   184 	FioCloseFile(slot); // if file was opened before, close it
   185 	FioCloseFile(slot); // if file was opened before, close it
   185 	_fio.handles[slot] = f;
   186 	_fio.handles[slot] = f;
   186 	_fio.filenames[slot] = filename;
   187 	_fio.filenames[slot] = filename;
   187 #if defined(LIMITED_FDS)
   188 #if defined(LIMITED_FDS)
   188 	_fio.usage_count[slot] = 0;
   189 	_fio.usage_count[slot] = 0;
   189 	_fio.open_handles++;
   190 	_fio.open_handles++;
   190 #endif /* LIMITED_FDS */
   191 #endif /* LIMITED_FDS */
   191 	FioSeekToFile(slot << 24);
   192 	FioSeekToFile(slot, pos);
   192 }
   193 }
   193 
   194 
   194 const char *_subdirs[NUM_SUBDIRS] = {
   195 const char *_subdirs[NUM_SUBDIRS] = {
   195 	"",
   196 	"",
   196 	"save" PATHSEP,
   197 	"save" PATHSEP,
   201 	"data" PATHSEP,
   202 	"data" PATHSEP,
   202 	"lang" PATHSEP
   203 	"lang" PATHSEP
   203 };
   204 };
   204 
   205 
   205 const char *_searchpaths[NUM_SEARCHPATHS];
   206 const char *_searchpaths[NUM_SEARCHPATHS];
       
   207 TarList _tar_list;
       
   208 TarFileList _tar_filelist;
   206 
   209 
   207 /**
   210 /**
   208  * Check whether the given file exists
   211  * Check whether the given file exists
   209  * @param filename the file to try for existance
   212  * @param filename the file to try for existance
   210  * @param subdir the subdirectory to look in
   213  * @param subdir the subdirectory to look in
   213 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
   216 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
   214 {
   217 {
   215 	FILE *f = FioFOpenFile(filename, "rb", subdir);
   218 	FILE *f = FioFOpenFile(filename, "rb", subdir);
   216 	if (f == NULL) return false;
   219 	if (f == NULL) return false;
   217 
   220 
       
   221 	FioFCloseFile(f);
       
   222 	return true;
       
   223 }
       
   224 
       
   225 /**
       
   226  * Close a file in a safe way.
       
   227  */
       
   228 void FioFCloseFile(FILE *f)
       
   229 {
   218 	fclose(f);
   230 	fclose(f);
   219 	return true;
       
   220 }
   231 }
   221 
   232 
   222 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
   233 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
   223 {
   234 {
   224 	assert(subdir < NUM_SUBDIRS);
   235 	assert(subdir < NUM_SUBDIRS);
   264 	ttd_strlcpy(buf, _personal_dir, buflen);
   275 	ttd_strlcpy(buf, _personal_dir, buflen);
   265 
   276 
   266 	return buf;
   277 	return buf;
   267 }
   278 }
   268 
   279 
   269 FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir)
   280 FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
   270 {
   281 {
   271 #if defined(WIN32) && defined(UNICODE)
   282 #if defined(WIN32) && defined(UNICODE)
   272 	/* fopen is implemented as a define with ellipses for
   283 	/* fopen is implemented as a define with ellipses for
   273 	 * Unicode support (prepend an L). As we are not sending
   284 	 * Unicode support (prepend an L). As we are not sending
   274 	 * a string, but a variable, it 'renames' the variable,
   285 	 * a string, but a variable, it 'renames' the variable,
   283 		ttd_strlcpy(buf, filename, lengthof(buf));
   294 		ttd_strlcpy(buf, filename, lengthof(buf));
   284 	} else {
   295 	} else {
   285 		snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
   296 		snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
   286 	}
   297 	}
   287 
   298 
       
   299 #if defined(WIN32)
       
   300 	if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
       
   301 #endif
       
   302 
   288 	f = fopen(buf, mode);
   303 	f = fopen(buf, mode);
   289 #if !defined(WIN32)
   304 #if !defined(WIN32)
   290 	if (f == NULL) {
   305 	if (f == NULL) {
   291 		strtolower(buf + strlen(_searchpaths[sp]) - 1);
   306 		strtolower(buf + strlen(_searchpaths[sp]) - 1);
   292 		f = fopen(buf, mode);
   307 		f = fopen(buf, mode);
   293 	}
   308 	}
   294 #endif
   309 #endif
       
   310 	if (f != NULL && filesize != NULL) {
       
   311 		/* Find the size of the file */
       
   312 		fseek(f, 0, SEEK_END);
       
   313 		*filesize = ftell(f);
       
   314 		fseek(f, 0, SEEK_SET);
       
   315 	}
   295 	return f;
   316 	return f;
   296 }
   317 }
   297 
   318 
       
   319 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
       
   320 {
       
   321 	FILE *f = fopen(entry->tar->filename, "rb");
       
   322 	assert(f != NULL);
       
   323 
       
   324 	fseek(f, entry->position, SEEK_SET);
       
   325 	if (filesize != NULL) *filesize = entry->size;
       
   326 	return f;
       
   327 }
       
   328 
   298 /** Opens OpenTTD files somewhere in a personal or global directory */
   329 /** Opens OpenTTD files somewhere in a personal or global directory */
   299 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir)
   330 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
   300 {
   331 {
   301 	FILE *f = NULL;
   332 	FILE *f = NULL;
   302 	Searchpath sp;
   333 	Searchpath sp;
   303 
   334 
   304 	assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
   335 	assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
   305 
   336 
   306 	FOR_ALL_SEARCHPATHS(sp) {
   337 	FOR_ALL_SEARCHPATHS(sp) {
   307 		f = FioFOpenFileSp(filename, mode, sp, subdir);
   338 		f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
   308 		if (f != NULL || subdir == NO_DIRECTORY) break;
   339 		if (f != NULL || subdir == NO_DIRECTORY) break;
       
   340 	}
       
   341 
       
   342 	/* We can only use .tar in case of data-dir, and read-mode */
       
   343 	if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') {
       
   344 		/* Filenames in tars are always forced to be lowercase */
       
   345 		char *lcfilename = strdup(filename);
       
   346 		strtolower(lcfilename);
       
   347 		TarFileList::iterator it = _tar_filelist.find(lcfilename);
       
   348 		free(lcfilename);
       
   349 		if (it != _tar_filelist.end()) {
       
   350 			f = FioFOpenFileTar(&((*it).second), filesize);
       
   351 		}
   309 	}
   352 	}
   310 
   353 
   311 	return f;
   354 	return f;
   312 }
   355 }
   313 
   356 
   364 		ttd_strlcat(dest, dir, MAX_PATH);
   407 		ttd_strlcat(dest, dir, MAX_PATH);
   365 	}
   408 	}
   366 	AppendPathSeparator(dest, MAX_PATH);
   409 	AppendPathSeparator(dest, MAX_PATH);
   367 
   410 
   368 	return dest;
   411 	return dest;
       
   412 }
       
   413 
       
   414 static bool TarListAddFile(const char *filename)
       
   415 {
       
   416 	/* The TAR-header, repeated for every file */
       
   417 	typedef struct TarHeader {
       
   418 		char name[100];      ///< Name of the file
       
   419 		char mode[8];
       
   420 		char uid[8];
       
   421 		char gid[8];
       
   422 		char size[12];       ///< Size of the file, in ASCII
       
   423 		char mtime[12];
       
   424 		char chksum[8];
       
   425 		char typeflag;
       
   426 		char linkname[100];
       
   427 		char magic[6];
       
   428 		char version[2];
       
   429 		char uname[32];
       
   430 		char gname[32];
       
   431 		char devmajor[8];
       
   432 		char devminor[8];
       
   433 		char prefix[155];    ///< Path of the file
       
   434 
       
   435 		char unused[12];
       
   436 	} TarHeader;
       
   437 
       
   438 	/* Check if we already seen this file */
       
   439 	TarList::iterator it = _tar_list.find(filename);
       
   440 	if (it != _tar_list.end()) return false;
       
   441 
       
   442 	FILE *f = fopen(filename, "rb");
       
   443 	assert(f != NULL);
       
   444 
       
   445 	TarListEntry *tar_entry = MallocT<TarListEntry>(1);
       
   446 	tar_entry->filename = strdup(filename);
       
   447 	_tar_list.insert(TarList::value_type(filename, tar_entry));
       
   448 
       
   449 	TarHeader th;
       
   450 	char buf[sizeof(th.name) + 1], *end;
       
   451 	char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
       
   452 	int num = 0, pos = 0;
       
   453 
       
   454 	/* Make a char of 512 empty bytes */
       
   455 	char empty[512];
       
   456 	memset(&empty[0], 0, sizeof(empty));
       
   457 
       
   458 	while (!feof(f)) {
       
   459 		fread(&th, 1, 512, f);
       
   460 		pos += 512;
       
   461 
       
   462 		/* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
       
   463 		if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
       
   464 			/* If we have only zeros in the block, it can be an end-of-file indicator */
       
   465 			if (memcmp(&th, &empty[0], 512) == 0) continue;
       
   466 
       
   467 			DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
       
   468 			return false;
       
   469 		}
       
   470 
       
   471 		name[0] = '\0';
       
   472 		int len = 0;
       
   473 
       
   474 		/* The prefix contains the directory-name */
       
   475 		if (th.prefix[0] != '\0') {
       
   476 			memcpy(name, th.prefix, sizeof(th.prefix));
       
   477 			name[sizeof(th.prefix)] = '\0';
       
   478 			len = strlen(name);
       
   479 			name[len] = PATHSEPCHAR;
       
   480 			len++;
       
   481 		}
       
   482 
       
   483 		/* Copy the name of the file in a safe way at the end of 'name' */
       
   484 		memcpy(&name[len], th.name, sizeof(th.name));
       
   485 		name[len + sizeof(th.name)] = '\0';
       
   486 
       
   487 		/* Calculate the size of the file.. for some strange reason this is stored as a string */
       
   488 		memcpy(buf, th.size, sizeof(th.size));
       
   489 		buf[sizeof(th.size)] = '\0';
       
   490 		int skip = strtol(buf, &end, 8);
       
   491 
       
   492 		/* 0 byte sized files can be skipped (dirs, symlinks, ..) */
       
   493 		if (skip == 0) continue;
       
   494 
       
   495 		/* Store this entry in the list */
       
   496 		TarFileListEntry entry;
       
   497 		entry.tar      = tar_entry;
       
   498 		entry.size     = skip;
       
   499 		entry.position = pos;
       
   500 		/* Force lowercase */
       
   501 		strtolower(name);
       
   502 
       
   503 		/* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
       
   504 #if (PATHSEPCHAR != '/')
       
   505 		for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
       
   506 #endif
       
   507 
       
   508 		DEBUG(misc, 6, "Found file in tar: %s (%d bytes, %d offset)", name, skip, pos);
       
   509 		if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
       
   510 
       
   511 		/* Skip to the next block.. */
       
   512 		skip = ALIGN(skip, 512);
       
   513 		fseek(f, skip, SEEK_CUR);
       
   514 		pos += skip;
       
   515 	}
       
   516 
       
   517 	DEBUG(misc, 1, "Found tar '%s' with %d new files", filename, num);
       
   518 	fclose(f);
       
   519 
       
   520 	return true;
       
   521 }
       
   522 
       
   523 static int ScanPathForTarFiles(const char *path, int basepath_length)
       
   524 {
       
   525 	extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
       
   526 
       
   527 	uint num = 0;
       
   528 	struct stat sb;
       
   529 	struct dirent *dirent;
       
   530 	DIR *dir;
       
   531 
       
   532 	if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
       
   533 
       
   534 	while ((dirent = readdir(dir)) != NULL) {
       
   535 		const char *d_name = FS2OTTD(dirent->d_name);
       
   536 		char filename[MAX_PATH];
       
   537 
       
   538 		if (!FiosIsValidFile(path, dirent, &sb)) continue;
       
   539 
       
   540 		snprintf(filename, lengthof(filename), "%s%s", path, d_name);
       
   541 
       
   542 		if (sb.st_mode & S_IFDIR) {
       
   543 			/* Directory */
       
   544 			if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
       
   545 			AppendPathSeparator(filename, lengthof(filename));
       
   546 			num += ScanPathForTarFiles(filename, basepath_length);
       
   547 		} else if (sb.st_mode & S_IFREG) {
       
   548 			/* File */
       
   549 			char *ext = strrchr(filename, '.');
       
   550 
       
   551 			/* If no extension or extension isn't .tar, skip the file */
       
   552 			if (ext == NULL) continue;
       
   553 			if (strcasecmp(ext, ".tar") != 0) continue;
       
   554 
       
   555 			if (TarListAddFile(filename)) num++;
       
   556 		}
       
   557 	}
       
   558 
       
   559 	closedir(dir);
       
   560 	return num;
       
   561 }
       
   562 
       
   563 void ScanForTarFiles()
       
   564 {
       
   565 	Searchpath sp;
       
   566 	char path[MAX_PATH];
       
   567 	uint num = 0;
       
   568 
       
   569 	DEBUG(misc, 1, "Scanning for tars");
       
   570 	FOR_ALL_SEARCHPATHS(sp) {
       
   571 		FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
       
   572 		num += ScanPathForTarFiles(path, strlen(path));
       
   573 	}
       
   574 	DEBUG(misc, 1, "Scan complete, found %d files", num);
   369 }
   575 }
   370 
   576 
   371 #if defined(WIN32) || defined(WINCE)
   577 #if defined(WIN32) || defined(WINCE)
   372 /**
   578 /**
   373  * Determine the base (personal dir and game data dir) paths
   579  * Determine the base (personal dir and game data dir) paths
   452 extern void cocoaSetApplicationBundleDir();
   658 extern void cocoaSetApplicationBundleDir();
   453 	cocoaSetApplicationBundleDir();
   659 	cocoaSetApplicationBundleDir();
   454 #else
   660 #else
   455 	_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
   661 	_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
   456 #endif
   662 #endif
       
   663 
       
   664 	ScanForTarFiles();
   457 }
   665 }
   458 #endif /* defined(WIN32) || defined(WINCE) */
   666 #endif /* defined(WIN32) || defined(WINCE) */
   459 
   667 
   460 char *_personal_dir;
   668 char *_personal_dir;
   461 
   669