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