From 548aa5aee62424d009d85a53c49f3a034e67e769 Mon Sep 17 00:00:00 2001 From: Sergio Martin Date: Tue, 11 May 2021 07:19:06 +0200 Subject: [PATCH] Added read-only file cache --- src/options.c | 38 ++++++----- src/proto.h | 5 ++ src/seg000.c | 17 ++--- src/seg009.c | 184 +++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 182 insertions(+), 62 deletions(-) diff --git a/src/options.c b/src/options.c index 1bfe4734..eefafa9d 100644 --- a/src/options.c +++ b/src/options.c @@ -50,7 +50,7 @@ int ini_load(const char *filename, FILE *f; int cnt; - f = fopen(filename, "r"); + f = fcache_open(filename, "r"); if (!f) { return -1; } @@ -69,12 +69,12 @@ int ini_load(const char *filename, if (fscanf(f, " ;%*[^\n]") != 0 || fscanf(f, " \n") != 0) { fprintf(stderr, "short read from %s!?\n", filename); - fclose(f); + fcache_close(f); return -1; } } - fclose(f); + fcache_close(f); return 0; } @@ -505,12 +505,15 @@ int identify_dos_exe_version(int filesize) { void load_dos_exe_modifications(const char* folder_name) { char filename[POP_MAX_PATH]; snprintf(filename, sizeof(filename), "%s/%s", folder_name, "PRINCE.EXE"); - FILE* fp = fopen(filename, "rb"); + FILE* fp = fcache_open(filename, "rb"); + size_t execSize = 0; int dos_version = -1; - struct stat info; - if (fp != NULL && fstat(fileno(fp), &info) == 0 && info.st_size > 0) { - dos_version = identify_dos_exe_version(info.st_size); + if (fp != NULL) { + fcache_seek(fp, 0, SEEK_END); + execSize = fcache_tell(fp); + fcache_seek(fp, 0, SEEK_SET); + dos_version = identify_dos_exe_version(execSize); } else { // PRINCE.EXE not found, try to search for other .EXE files in the same folder. directory_listing_type* directory_listing = create_directory_listing_and_find_first_file(folder_name, "exe"); @@ -518,13 +521,16 @@ void load_dos_exe_modifications(const char* folder_name) { do { char* current_filename = get_current_filename_from_directory_listing(directory_listing); snprintf(filename, sizeof(filename), "%s/%s", folder_name, current_filename); - fp = fopen(filename, "rb"); - if (fp != NULL && fstat(fileno(fp), &info) == 0 && info.st_size > 0) { - dos_version = identify_dos_exe_version(info.st_size); + fp = fcache_open(filename, "rb"); + if (fp != NULL) { + fcache_seek(fp, 0, SEEK_END); + execSize = fcache_tell(fp); + fcache_seek(fp, 0, SEEK_SET); + dos_version = identify_dos_exe_version(execSize); if (dos_version >= 0) { break; // We found a DOS executable with the right size! } - fclose(fp); + fcache_close(fp); fp = NULL; } // Keep looking until we find an .EXE with the right size, or until there are no .EXE files left. @@ -535,10 +541,10 @@ void load_dos_exe_modifications(const char* folder_name) { if (dos_version >= 0) { turn_custom_options_on_off(1); - byte* exe_memory = malloc((size_t) info.st_size); - if (fread(exe_memory, (size_t) info.st_size, 1, fp) != 1) { + byte* exe_memory = malloc(execSize); + if (fcache_read(exe_memory, execSize, 1, fp) != 1) { fprintf(stderr, "Could not read %s!?\n", filename); - fclose(fp); + fcache_close(fp); return; } @@ -550,7 +556,7 @@ void load_dos_exe_modifications(const char* folder_name) { do { \ static const int offsets[6] = __VA_ARGS__; \ int offset = offsets[dos_version]; \ - read_ok = read_exe_bytes(x, nbytes, exe_memory, offset, info.st_size); \ + read_ok = read_exe_bytes(x, nbytes, exe_memory, offset, execSize); \ } while(0) // Offsets and comparisons are derived from princehack.xml @@ -702,7 +708,7 @@ void load_dos_exe_modifications(const char* folder_name) { free(exe_memory); } - if (fp != NULL) fclose(fp); + if (fp != NULL) fcache_close(fp); } diff --git a/src/proto.h b/src/proto.h index 9a48c65e..b992a42b 100644 --- a/src/proto.h +++ b/src/proto.h @@ -509,6 +509,11 @@ void __pascal far draw_right_mark (word arg2, word arg1); image_type* get_image(short chtab_id, int id); // SEG009.C +FILE* fcache_open(const char * filename, const char * mode); +size_t fcache_read(void * ptr, size_t size, size_t count, FILE * stream); +int fcache_seek(FILE * stream, long int offset, int origin); +int fcache_tell(FILE * stream); +int fcache_close(FILE* file); void sdlperror(const char* header); bool file_exists(const char* filename); #define locate_file(filename) locate_file_(filename, alloca(POP_MAX_PATH), POP_MAX_PATH) diff --git a/src/seg000.c b/src/seg000.c index 0ce2fb4f..eca4ea0e 100644 --- a/src/seg000.c +++ b/src/seg000.c @@ -245,7 +245,7 @@ int process_save(void* data, size_t data_size) { } int process_load(void* data, size_t data_size) { - return fread(data, data_size, 1, quick_fp) == 1; + return fcache_read(data, data_size, 1, quick_fp) == 1; } typedef int process_func_type(void* data, size_t data_size); @@ -426,12 +426,12 @@ int quick_load() { int ok = 0; char custom_quick_path[POP_MAX_PATH]; const char* path = get_quick_path(custom_quick_path, sizeof(custom_quick_path)); - quick_fp = fopen(path, "rb"); + quick_fp = fcache_open(path, "rb"); if (quick_fp != NULL) { // check quicksave version is compatible process_load(quick_control, COUNT(quick_control)); if (strcmp(quick_control, quick_version) != 0) { - fclose(quick_fp); + fcache_close(quick_fp); quick_fp = NULL; return 0; } @@ -445,7 +445,7 @@ int quick_load() { word old_rem_tick = rem_tick; ok = quick_process(process_load); - fclose(quick_fp); + fcache_close(quick_fp); quick_fp = NULL; restore_room_after_quick_load(); @@ -1032,6 +1032,7 @@ void __pascal far load_sounds(int first,int last) { sound_pointers[current] = load_sound(current); } } + if (midi_dat) close_dat(midi_dat); if (digi1_dat) close_dat(digi1_dat); // if (digi2_dat) close_dat(digi2_dat); @@ -2057,7 +2058,7 @@ void __pascal far save_game() { if (handle == NULL) goto loc_1DB8; if (fwrite(&rem_min, 1, 2, handle) == 2) goto loc_1DC9; loc_1D9B: - fclose(handle); + fcache_close(handle); if (!success) { remove(save_path); } @@ -2085,11 +2086,11 @@ short __pascal far load_game() { success = 0; char custom_save_path[POP_MAX_PATH]; const char* save_path = get_save_path(custom_save_path, sizeof(custom_save_path)); - handle = fopen(save_path, "rb"); + handle = fcache_open(save_path, "rb"); if (handle == NULL) goto loc_1E99; - if (fread(&rem_min, 1, 2, handle) == 2) goto loc_1E9E; + if (fcache_read(&rem_min, 1, 2, handle) == 2) goto loc_1E9E; loc_1E8E: - fclose(handle); + fcache_close(handle); loc_1E99: return success; loc_1E9E: diff --git a/src/seg009.c b/src/seg009.c index e8ff9148..ace5a6f5 100644 --- a/src/seg009.c +++ b/src/seg009.c @@ -21,6 +21,7 @@ The authors of this program may be contacted at https://forum.princed.org #include "common.h" #include #include +#include #ifdef _WIN32 #include @@ -29,6 +30,111 @@ The authors of this program may be contacted at https://forum.princed.org #include "dirent.h" #endif +#define __MAX_CACHED_FILES 2048 +long int _cachedFilePointerTable[__MAX_CACHED_FILES]; +char* _cachedFileBufferTable[__MAX_CACHED_FILES]; +size_t _cachedFileBufferSizes[__MAX_CACHED_FILES]; +char _cachedFilePathTable[__MAX_CACHED_FILES][POP_MAX_PATH]; +size_t _cachedFileCounter = 0; + +FILE* fcache_open(const char * filename, const char * mode) +{ + // Check if already cached, and return it directly from cache if it is + for (size_t i = 0; i < _cachedFileCounter; i++) + if (strcmp(filename, _cachedFilePathTable[i]) == 0) + { + _cachedFilePointerTable[i] = 0; // Rewinding + return (FILE*)i; + } + + // Checking if file is directory. Do not open in that case. + struct stat path_stat; + stat(filename, &path_stat); + if (S_ISREG(path_stat.st_mode) == 0) return NULL; + + // Open source file + FILE *srcFile = fopen(filename, "rb"); + + // If failed to load, return immediately + if (srcFile == NULL) return NULL; + + // Finding out file's size + fseek(srcFile, 0, SEEK_END); + long int fileSize = ftell(srcFile); + fseek(srcFile, 0, SEEK_SET); + + // Creating cached file buffer + _cachedFileBufferTable[_cachedFileCounter] = malloc(fileSize); + + // Reading source file info buffer + fread(_cachedFileBufferTable[_cachedFileCounter], 1, fileSize, srcFile); + + // Closing source file + fclose(srcFile); + + // Add filepath to the cache table + strcpy(_cachedFilePathTable[_cachedFileCounter], filename); + + // Storing file size + _cachedFileBufferSizes[_cachedFileCounter] = fileSize; + + // Resetting pointer + _cachedFilePointerTable[_cachedFileCounter] = 0; + + // Setting found ptr + FILE* foundPtr = (FILE*) _cachedFileCounter; + + // Increase cached file counter + _cachedFileCounter++; + + return foundPtr; +} + +size_t fcache_read(void * ptr, size_t size, size_t count, FILE * stream) +{ + size_t fileId = (size_t) stream; + size_t readCount = _cachedFileBufferSizes[fileId] / size; + if (readCount > count) readCount = count; + + // Copying data from cached file to pointer + memcpy(ptr, &_cachedFileBufferTable[fileId][_cachedFilePointerTable[fileId]], readCount * size); + + // Advancing file pointer + _cachedFilePointerTable[fileId] += readCount * size; + + // Returning number of elements read + return readCount; +} + +int fcache_seek(FILE * stream, long int offset, int origin) +{ + long int base; + size_t fileId = (size_t) stream; + if (origin == SEEK_SET) base = 0; + if (origin == SEEK_CUR) base = _cachedFilePointerTable[fileId]; + if (origin == SEEK_END) base = _cachedFileBufferSizes[fileId] - 1; + + long int dest = base + offset; + if (dest > _cachedFileBufferSizes[fileId] - 1) return -1; + if (dest < 0) return -1; + + _cachedFilePointerTable[fileId] = base + offset; + + return 0; +} + +int fcache_tell(FILE * stream) +{ + size_t fileId = (size_t) stream; + return _cachedFilePointerTable[fileId] + 1; +} + +int fcache_close(FILE* file) +{ + // Do not actually close it. This will be done at the end + return 0; +} + // Most functions in this file are different from those in the original game. void sdlperror(const char* header) { @@ -329,7 +435,7 @@ int __pascal far pop_wait(int timer_index,int time) { static FILE* open_dat_from_root_or_data_dir(const char* filename) { FILE* fp = NULL; - fp = fopen(filename, "rb"); + fp = fcache_open(filename, "rb"); // if failed, try if the DAT file can be opened in the data/ directory, instead of the main folder if (fp == NULL) { @@ -342,11 +448,7 @@ static FILE* open_dat_from_root_or_data_dir(const char* filename) { } // verify that this is a regular file and not a directory (otherwise, don't open) - struct stat path_stat; - stat(data_path, &path_stat); - if (S_ISREG(path_stat.st_mode)) { - fp = fopen(data_path, "rb"); - } + fp = fcache_open(data_path, "rb"); } return fp; } @@ -364,7 +466,7 @@ dat_type *__pascal open_dat(const char *filename,int drive) { char filename_mod[POP_MAX_PATH]; // before checking the root directory, first try mods/MODNAME/ snprintf_check(filename_mod, sizeof(filename_mod), "%s/%s", mod_data_path, filename); - fp = fopen(filename_mod, "rb"); + fp = fcache_open(filename_mod, "rb"); } if (fp == NULL && !skip_normal_data_files) { fp = open_dat_from_root_or_data_dir(filename); @@ -379,12 +481,12 @@ dat_type *__pascal open_dat(const char *filename,int drive) { dat_chain_ptr = pointer; if (fp != NULL) { - if (fread(&dat_header, 6, 1, fp) != 1) + if (fcache_read(&dat_header, 6, 1, fp) != 1) goto failed; dat_table = (dat_table_type*) malloc(dat_header.table_size); if (dat_table == NULL || - fseek(fp, dat_header.table_offset, SEEK_SET) || - fread(dat_table, dat_header.table_size, 1, fp) != 1) + fcache_seek(fp, dat_header.table_offset, SEEK_SET) || + fcache_read(dat_table, dat_header.table_size, 1, fp) != 1) goto failed; pointer->handle = fp; pointer->dat_table = dat_table; @@ -419,7 +521,7 @@ dat_type *__pascal open_dat(const char *filename,int drive) { failed: perror(filename); if (fp) - fclose(fp); + fcache_close(fp); if (dat_table) free(dat_table); goto out; @@ -2101,6 +2203,7 @@ char* sound_name(int index) { sound_buffer_type* convert_digi_sound(sound_buffer_type* digi_buffer); sound_buffer_type* load_sound(int index) { + sound_buffer_type* result = NULL; //printf("load_sound(%d)\n", index); init_digi(); @@ -2116,27 +2219,26 @@ sound_buffer_type* load_sound(int index) { if (!skip_mod_data_files) { // before checking the root directory, first try mods/MODNAME/ snprintf_check(filename, sizeof(filename), "%s/music/%s.ogg", mod_data_path, sound_name(index)); - fp = fopen(filename, "rb"); + fp = fcache_open(filename, "rb"); } if (fp == NULL && !skip_normal_data_files) { snprintf_check(filename, sizeof(filename), "data/music/%s.ogg", sound_name(index)); - fp = fopen(locate_file(filename), "rb"); + fp = fcache_open(locate_file(filename), "rb"); } if (fp == NULL) { break; } // Read the entire file (undecoded) into memory. - struct stat info; - if (fstat(fileno(fp), &info)) - break; - size_t file_size = (size_t) MAX(0, info.st_size); + fcache_seek(fp, 0, SEEK_END); + long int file_size = fcache_tell(fp); + fcache_seek(fp, 0, SEEK_SET); byte* file_contents = malloc(file_size); - if (fread(file_contents, 1, file_size, fp) != file_size) { + if (fcache_read(file_contents, 1, file_size, fp) != file_size) { free(file_contents); - fclose(fp); + fcache_close(fp); break; } - fclose(fp); + fcache_close(fp); // Decoding the entire file immediately would make the loading time much longer. // However, we can also create the decoder now, and only use it when we are actually playing the file. @@ -2169,6 +2271,7 @@ sound_buffer_type* load_sound(int index) { if (result == NULL && !skip_normal_data_files) { fprintf(stderr, "Failed to load sound %d '%s'\n", index, sound_name(index)); } + return result; } @@ -2727,8 +2830,8 @@ void load_from_opendats_metadata(int resource_id, const char* extension, FILE** // found *result = data_DAT; *size = dat_table->entries[i].size; - if (fseek(fp, dat_table->entries[i].offset, SEEK_SET) || - fread(checksum, 1, 1, fp) != 1) { + if (fcache_seek(fp, dat_table->entries[i].offset, SEEK_SET) || + fcache_read(checksum, 1, 1, fp) != 1) { perror(pointer->filename); fp = NULL; } @@ -2746,9 +2849,15 @@ void load_from_opendats_metadata(int resource_id, const char* extension, FILE** filename_no_ext[len-4] = '\0'; // terminate, so ".DAT" is deleted from the filename } snprintf_check(image_filename,sizeof(image_filename),"data/%s/res%d.%s",filename_no_ext, resource_id, extension); + if (!use_custom_levelset) { //printf("loading (binary) %s",image_filename); - fp = fopen(locate_file(image_filename), "rb"); + const char* filename = locate_file(image_filename); + //printf("File: %s\n", filename); + fp = fcache_open(filename, "rb"); + if (fp != NULL) { + *result = data_directory; + } } else { if (!skip_mod_data_files) { @@ -2756,23 +2865,19 @@ void load_from_opendats_metadata(int resource_id, const char* extension, FILE** // before checking data/, first try mods/MODNAME/data/ snprintf_check(image_filename_mod, sizeof(image_filename_mod), "%s/%s", mod_data_path, image_filename); //printf("loading (binary) %s",image_filename_mod); - fp = fopen(locate_file(image_filename_mod), "rb"); + fp = fcache_open(locate_file(image_filename_mod), "rb"); } if (fp == NULL && !skip_normal_data_files) { - fp = fopen(locate_file(image_filename), "rb"); + fp = fcache_open(locate_file(image_filename), "rb"); } } if (fp != NULL) { - struct stat buf; - if (fstat(fileno(fp), &buf) == 0) { *result = data_directory; - *size = buf.st_size; - } else { - perror(image_filename); - fclose(fp); - fp = NULL; - } + + fcache_seek(fp, 0L, SEEK_END); + *size = fcache_tell(fp); + fcache_seek(fp, 0L, SEEK_SET); } } } @@ -2792,7 +2897,7 @@ void __pascal far close_dat(dat_type far *pointer) { while (curr != NULL) { if (curr == pointer) { *prev = curr->next_dat; - if (curr->handle) fclose(curr->handle); + if (curr->handle) fcache_close(curr->handle); if (curr->dat_table) free(curr->dat_table); free(curr); return; @@ -2816,16 +2921,19 @@ void far *__pascal load_from_opendats_alloc(int resource, const char* extension, if (out_result != NULL) *out_result = result; if (out_size != NULL) *out_size = size; if (result == data_none) return NULL; + void* area = malloc(size); //read(fd, area, size); - if (fread(area, size, 1, fp) != 1) { + if (fcache_read(area, size, 1, fp) != 1) { + printf("ERROR\n"); fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n", __func__, pointer->filename, resource, size, strerror(errno)); free(area); area = NULL; } - if (result == data_directory) fclose(fp); + + if (result == data_directory) fcache_close(fp); /* XXX: check checksum */ return area; } @@ -2841,13 +2949,13 @@ int __pascal far load_from_opendats_to_area(int resource,void far *area,int leng FILE* fp = NULL; load_from_opendats_metadata(resource, extension, &fp, &result, &checksum, &size, &pointer); if (result == data_none) return 0; - if (fread(area, MIN(size, length), 1, fp) != 1) { + if (fcache_read(area, MIN(size, length), 1, fp) != 1) { fprintf(stderr, "%s: %s, resource %d, size %d, failed: %s\n", __func__, pointer->filename, resource, size, strerror(errno)); memset(area, 0, MIN(size, length)); } - if (result == data_directory) fclose(fp); + if (result == data_directory) fcache_close(fp); /* XXX: check checksum */ return 0; }