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) |