src/fileio.cpp
changeset 7581 24dab79dca15
parent 7575 3f76c2453685
child 7584 8f066de3f874
equal deleted inserted replaced
7580:fc9a8300f34e 7581:24dab79dca15
   202 	"data" PATHSEP,
   202 	"data" PATHSEP,
   203 	"lang" PATHSEP
   203 	"lang" PATHSEP
   204 };
   204 };
   205 
   205 
   206 const char *_searchpaths[NUM_SEARCHPATHS];
   206 const char *_searchpaths[NUM_SEARCHPATHS];
       
   207 std::list<const char *> _tar_list;
   207 
   208 
   208 /**
   209 /**
   209  * Check whether the given file exists
   210  * Check whether the given file exists
   210  * @param filename the file to try for existance
   211  * @param filename the file to try for existance
   211  * @param subdir the subdirectory to look in
   212  * @param subdir the subdirectory to look in
   300 		fseek(f, 0, SEEK_SET);
   301 		fseek(f, 0, SEEK_SET);
   301 	}
   302 	}
   302 	return f;
   303 	return f;
   303 }
   304 }
   304 
   305 
       
   306 FILE *FioTarFileList(const char *tar, const char *mode, size_t *filesize, FioTarFileListCallback *callback, void *userdata)
       
   307 {
       
   308 	/* The TAR-header, repeated for every file */
       
   309 	typedef struct TarHeader {
       
   310 		char name[100];      ///< Name of the file
       
   311 		char mode[8];
       
   312 		char uid[8];
       
   313 		char gid[8];
       
   314 		char size[12];       ///< Size of the file, in ASCII
       
   315 		char mtime[12];
       
   316 		char chksum[8];
       
   317 		char typeflag;
       
   318 		char linkname[100];
       
   319 		char magic[6];
       
   320 		char version[2];
       
   321 		char uname[32];
       
   322 		char gname[32];
       
   323 		char devmajor[8];
       
   324 		char devminor[8];
       
   325 		char prefix[155];    ///< Path of the file
       
   326 
       
   327 		char unused[12];
       
   328 	} TarHeader;
       
   329 
       
   330 	assert(mode[0] == 'r'); // Only reading is supported
       
   331 	assert(callback != NULL); // We need a callback, else this function doens't do much
       
   332 
       
   333 #if defined(WIN32) && defined(UNICODE)
       
   334 	/* fopen is implemented as a define with ellipses for
       
   335 	 * Unicode support (prepend an L). As we are not sending
       
   336 	 * a string, but a variable, it 'renames' the variable,
       
   337 	 * so make that variable to makes it compile happily */
       
   338 	wchar_t Lmode[5];
       
   339 	MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
       
   340 #endif
       
   341 
       
   342 	FILE *f = fopen(tar, mode);
       
   343 	assert(f != NULL);
       
   344 
       
   345 	TarHeader th;
       
   346 	char buf[sizeof(th.name) + 1], *end;
       
   347 	char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
       
   348 
       
   349 	while (!feof(f)) {
       
   350 		/* Read the header and make sure it is a valid one */
       
   351 		fread(&th, 1, 512, f);
       
   352 		if (strncmp(th.magic, "ustar", 5) != 0) return NULL;
       
   353 
       
   354 		name[0] = '\0';
       
   355 		int len = 0;
       
   356 
       
   357 		/* The prefix contains the directory-name */
       
   358 		if (th.prefix[0] != '\0') {
       
   359 			memcpy(name, th.prefix, sizeof(th.prefix));
       
   360 			name[sizeof(th.prefix)] = '\0';
       
   361 			len = strlen(name);
       
   362 			name[len] = PATHSEPCHAR;
       
   363 			len++;
       
   364 		}
       
   365 
       
   366 		/* Copy the name of the file in a safe way at the end of 'name' */
       
   367 		memcpy(&name[len], th.name, sizeof(th.name));
       
   368 		name[len + sizeof(th.name)] = '\0';
       
   369 
       
   370 		/* Calculate the size of the file.. for some strange reason this is stored as a string */
       
   371 		memcpy(buf, th.size, sizeof(th.size));
       
   372 		buf[sizeof(th.size)] = '\0';
       
   373 		int skip = strtol(buf, &end, 8);
       
   374 
       
   375 		/* Check in the callback if this is the file we want */
       
   376 		if (callback(name, skip, userdata)) {
       
   377 			if (filesize != NULL) *filesize = skip;
       
   378 			return f;
       
   379 		}
       
   380 
       
   381 		/* Skip to the next block.. */
       
   382 		fseek(f, ALIGN(skip, 512), SEEK_CUR);
       
   383 	}
       
   384 
       
   385 	fclose(f);
       
   386 	return NULL;
       
   387 }
       
   388 
       
   389 bool FioFOpenFileTarFileListCallback(const char *filename, int size, void *search_filename)
       
   390 {
       
   391 	return strcasecmp(filename, (const char *)search_filename) == 0;
       
   392 }
       
   393 
       
   394 FILE *FioFOpenFileTar(const char *filename, const char *tar_filename, size_t *filesize)
       
   395 {
       
   396 	return FioTarFileList(tar_filename, "rb", filesize, FioFOpenFileTarFileListCallback, (void *)filename);
       
   397 }
       
   398 
   305 /** Opens OpenTTD files somewhere in a personal or global directory */
   399 /** Opens OpenTTD files somewhere in a personal or global directory */
   306 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
   400 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
   307 {
   401 {
   308 	FILE *f = NULL;
   402 	FILE *f = NULL;
   309 	Searchpath sp;
   403 	Searchpath sp;
   311 	assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
   405 	assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
   312 
   406 
   313 	FOR_ALL_SEARCHPATHS(sp) {
   407 	FOR_ALL_SEARCHPATHS(sp) {
   314 		f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
   408 		f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
   315 		if (f != NULL || subdir == NO_DIRECTORY) break;
   409 		if (f != NULL || subdir == NO_DIRECTORY) break;
       
   410 	}
       
   411 	/* We can only use .tar in case of data-dir, and read-mode */
       
   412 	if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') {
       
   413 		const char *tar;
       
   414 		FOR_ALL_TARS(tar) {
       
   415 			f = FioFOpenFileTar(filename, tar, filesize);
       
   416 			if (f != NULL) break;
       
   417 		}
   316 	}
   418 	}
   317 
   419 
   318 	return f;
   420 	return f;
   319 }
   421 }
   320 
   422 
   408 #ifdef WITH_COCOA
   510 #ifdef WITH_COCOA
   409 	if (app_bundle != NULL) app_bundle[0] = '.';
   511 	if (app_bundle != NULL) app_bundle[0] = '.';
   410 #endif /* WITH_COCOA */
   512 #endif /* WITH_COCOA */
   411 }
   513 }
   412 
   514 
       
   515 static bool TarListAddFile(const char *filename)
       
   516 {
       
   517 	/* See if we already have a tar by that name; useless to have double entries in our list */
       
   518 	const char *tar;
       
   519 	FOR_ALL_TARS(tar) {
       
   520 		if (strcmp(tar, filename) == 0) return false;
       
   521 	}
       
   522 
       
   523 	DEBUG(misc, 1, "Found tar: %s", filename);
       
   524 	_tar_list.push_back(strdup(filename));
       
   525 
       
   526 	return true;
       
   527 }
       
   528 
       
   529 static int ScanPathForTarFiles(const char *path, int basepath_length)
       
   530 {
       
   531 	extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
       
   532 
       
   533 	uint num = 0;
       
   534 	struct stat sb;
       
   535 	struct dirent *dirent;
       
   536 	DIR *dir;
       
   537 
       
   538 	if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
       
   539 
       
   540 	while ((dirent = readdir(dir)) != NULL) {
       
   541 		const char *d_name = FS2OTTD(dirent->d_name);
       
   542 		char filename[MAX_PATH];
       
   543 
       
   544 		if (!FiosIsValidFile(path, dirent, &sb)) continue;
       
   545 
       
   546 		snprintf(filename, lengthof(filename), "%s%s", path, d_name);
       
   547 
       
   548 		if (sb.st_mode & S_IFDIR) {
       
   549 			/* Directory */
       
   550 			if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
       
   551 			AppendPathSeparator(filename, lengthof(filename));
       
   552 			num += ScanPathForTarFiles(filename, basepath_length);
       
   553 		} else if (sb.st_mode & S_IFREG) {
       
   554 			/* File */
       
   555 			char *ext = strrchr(filename, '.');
       
   556 
       
   557 			/* If no extension or extension isn't .tar, skip the file */
       
   558 			if (ext == NULL) continue;
       
   559 			if (strcasecmp(ext, ".tar") != 0) continue;
       
   560 
       
   561 			if (TarListAddFile(filename)) num++;
       
   562 		}
       
   563 	}
       
   564 
       
   565 	closedir(dir);
       
   566 	return num;
       
   567 }
       
   568 
       
   569 static void ScanForTarFiles()
       
   570 {
       
   571 	Searchpath sp;
       
   572 	char path[MAX_PATH];
       
   573 	uint num = 0;
       
   574 
       
   575 	DEBUG(misc, 1, "Scanning for tars");
       
   576 	FOR_ALL_SEARCHPATHS(sp) {
       
   577 		FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
       
   578 		num += ScanPathForTarFiles(path, strlen(path));
       
   579 	}
       
   580 	DEBUG(misc, 1, "Scan complete, found %d files", num);
       
   581 }
       
   582 
   413 /**
   583 /**
   414  * Determine the base (personal dir and game data dir) paths
   584  * Determine the base (personal dir and game data dir) paths
   415  * @param exe the path to the executable
   585  * @param exe the path to the executable
   416  */
   586  */
   417 void DetermineBasePaths(const char *exe)
   587 void DetermineBasePaths(const char *exe)
   459 extern void cocoaSetApplicationBundleDir();
   629 extern void cocoaSetApplicationBundleDir();
   460 	cocoaSetApplicationBundleDir();
   630 	cocoaSetApplicationBundleDir();
   461 #else
   631 #else
   462 	_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
   632 	_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
   463 #endif
   633 #endif
       
   634 
       
   635 	ScanForTarFiles();
   464 }
   636 }
   465 #endif /* defined(WIN32) || defined(WINCE) */
   637 #endif /* defined(WIN32) || defined(WINCE) */
   466 
   638 
   467 char *_personal_dir;
   639 char *_personal_dir;
   468 
   640