From be824627cebb1bed6531e5dc8821662f1cb9e1a0 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sat, 17 Feb 2018 16:05:42 +0100 Subject: [PATCH] loadsave.c: Add functions to open file with alternate extension. Used by CPC format which are in several files : PPH format uses PPH, ODD, EVE CM5 uses CM5 + GFX --- src/filesel.c | 1 + src/loadsave.c | 128 +++++++++++++++++++++++++ src/loadsave.h | 7 ++ src/miscfileformats.c | 213 +++++++++++++++++++----------------------- src/unicode.c | 22 +++++ src/unicode.h | 3 + 6 files changed, 257 insertions(+), 117 deletions(-) diff --git a/src/filesel.c b/src/filesel.c index b45bd55e..d3ad4043 100644 --- a/src/filesel.c +++ b/src/filesel.c @@ -2321,6 +2321,7 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context T_IO_Context preview_context; Init_context_preview(&preview_context, Selector_filename, Selector->Directory); + preview_context.File_name_unicode = Selector_filename_unicode; Hide_cursor(); if (context->Type == CONTEXT_PALETTE) preview_context.Type = CONTEXT_PREVIEW_PALETTE; diff --git a/src/loadsave.c b/src/loadsave.c index 8f1a166a..354f75b9 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -1787,6 +1787,38 @@ FILE * Open_file_write(T_IO_Context *context) return fopen(filename, "wb"); } +FILE * Open_file_write_with_alternate_ext(T_IO_Context *context, const char * ext) +{ + char *p; + char filename[MAX_PATH_CHARACTERS]; // filename with full path +#if defined(WIN32) + WCHAR filename_unicode[MAX_PATH_CHARACTERS]; + WCHAR * pw; + + if (context->File_name_unicode != NULL) + { + Unicode_char_strlcpy((word *)filename_unicode, context->File_directory, MAX_PATH_CHARACTERS); + Unicode_char_strlcat((word *)filename_unicode, PATH_SEPARATOR, MAX_PATH_CHARACTERS); + Unicode_strlcat((word *)filename_unicode, context->File_name_unicode, MAX_PATH_CHARACTERS); + pw = wcschr(filename_unicode, (WCHAR)'.'); + if (pw != NULL) + *pw = 0; + Unicode_char_strlcat((word *)filename_unicode, ".", MAX_PATH_CHARACTERS); + Unicode_char_strlcat((word *)filename_unicode, ext, MAX_PATH_CHARACTERS); + + return _wfopen(filename_unicode, L"wb"); + } +#endif + Get_full_filename(filename, context->File_name, context->File_directory); + p = strrchr(filename, '.'); + if (p != NULL) + *p = '\0'; + strcat(filename, "."); + strcat(filename, ext); + + return fopen(filename, "wb"); +} + /// For use by Load_XXX() and Test_XXX() functions FILE * Open_file_read(T_IO_Context *context) { @@ -1797,6 +1829,102 @@ FILE * Open_file_read(T_IO_Context *context) return fopen(filename, "rb"); } +struct T_Find_alternate_ext_data +{ + const char * ext; + char basename[MAX_PATH_CHARACTERS]; + word basename_unicode[MAX_PATH_CHARACTERS]; + char foundname[MAX_PATH_CHARACTERS]; + word foundname_unicode[MAX_PATH_CHARACTERS]; +}; + +static void Look_for_alternate_ext(void * pdata, const char * filename, const word * filename_unicode, byte is_file, byte is_directory, byte is_hidden) +{ + size_t base_len; + struct T_Find_alternate_ext_data * params = (struct T_Find_alternate_ext_data *)pdata; + (void)is_hidden; + (void)is_directory; + + if (!is_file) + return; + + if (filename_unicode != NULL && params->basename_unicode[0] != 0) + { + base_len = Unicode_strlen(params->basename_unicode); + if (filename_unicode[base_len] != '.') + return; // No match. +#if defined(WIN32) + { + WCHAR temp_string[MAX_PATH_CHARACTERS]; + memcpy(temp_string, filename_unicode, base_len * sizeof(word)); + temp_string[base_len] = 0; + if (_wcsicmp((const WCHAR *)params->basename_unicode, temp_string) != 0) + return; // No match. + } +#else + if (memcmp(params->basename_unicode, filename_unicode, base_len * sizeof(word)) != 0) + return; // No match. +#endif + if (Unicode_char_strcasecmp(filename_unicode + base_len + 1, params->ext) != 0) + return; // No match. + // it is a match ! + Unicode_strlcpy(params->foundname_unicode, filename_unicode, MAX_PATH_CHARACTERS); + strncpy(params->foundname, filename, MAX_PATH_CHARACTERS); + } + else + { + base_len = strlen(params->basename); + if (filename[base_len] != '.') + return; // No match. +#if defined(WIN32) + if (_memicmp(params->basename, filename, base_len) != 0) // Not case sensitive + return; // No match. +#else + if (memcmp(params->basename, filename, base_len) != 0) + return; // No match. +#endif + if (strcasecmp(filename + base_len + 1, params->ext) != 0) + return; // No match. + params->foundname_unicode[0] = 0; + strncpy(params->foundname, filename, MAX_PATH_CHARACTERS); + } +} + +FILE * Open_file_read_with_alternate_ext(T_IO_Context *context, const char * ext) +{ + char * p; + struct T_Find_alternate_ext_data params; + + memset(¶ms, 0, sizeof(params)); + params.ext = ext; + strncpy(params.basename, context->File_name, MAX_PATH_CHARACTERS); + p = strrchr(params.basename, '.'); + if (p != NULL) + *p = '\0'; + if (context->File_name_unicode != NULL) + { + size_t i = Unicode_strlen(context->File_name_unicode); + memcpy(params.basename_unicode, context->File_name_unicode, (i + 1) * sizeof(word)); + while (i-- > 0) + if (params.basename_unicode[i] == (word)'.') + { + params.basename_unicode[i] = 0; + break; + } + } + + For_each_directory_entry(context->File_directory, ¶ms, Look_for_alternate_ext); + if (params.foundname[0] != '\0') + { + char filename[MAX_PATH_CHARACTERS]; // filename with full path + + Get_full_filename(filename, params.foundname, context->File_directory); + + return fopen(filename, "rb"); + } + return NULL; +} + /// For use by Save_XXX() functions void Remove_file(T_IO_Context *context) { diff --git a/src/loadsave.h b/src/loadsave.h index 4be84074..c075f99b 100644 --- a/src/loadsave.h +++ b/src/loadsave.h @@ -267,6 +267,13 @@ FILE * Open_file_write(T_IO_Context *context); /// For use by Load_XXX() and Test_XXX() functions FILE * Open_file_read(T_IO_Context *context); +/// For use by Load_XXX() and Test_XXX() functions +FILE * Open_file_read_with_alternate_ext(T_IO_Context *context, const char * ext); + /// For use by Save_XXX() functions void Remove_file(T_IO_Context *context); + +/// For use by Save_XXX() functions +FILE * Open_file_write_with_alternate_ext(T_IO_Context *context, const char * ext); + #endif diff --git a/src/miscfileformats.c b/src/miscfileformats.c index 22b9df44..09dbce79 100644 --- a/src/miscfileformats.c +++ b/src/miscfileformats.c @@ -3217,15 +3217,12 @@ void Load_CM5(T_IO_Context* context) // Set palette to the CPC hardware colors // Load the palette data to the 4 colorlayers FILE *file; - char filename[MAX_PATH_CHARACTERS]; byte value = 0; int mod=0; short line = 0; int tx, ty; byte buffer[48*6/4]; - Get_full_filename(filename, context->File_name, context->File_directory); - if (!(file = Open_file_read(context))) { File_error = 1; @@ -3330,22 +3327,11 @@ void Load_CM5(T_IO_Context* context) fclose(file); // Load the pixeldata to the 5th layer + file = Open_file_read_with_alternate_ext(context, "gfx"); + if (file == NULL) { - char* ext = filename + strlen(filename) - 3; - int idx = 8; - do { - if (-- idx < 0) - { - File_error = 1; - return; - } - - ext[0] = (idx & 1) ? 'g':'G'; - ext[1] = (idx & 2) ? 'f':'F'; - ext[2] = (idx & 4) ? 'x':'X'; - - file = fopen(filename, "rb"); - } while(file == NULL); + File_error = 1; + return; } Set_loading_layer(context, 4); @@ -3368,12 +3354,9 @@ void Load_CM5(T_IO_Context* context) void Save_CM5(T_IO_Context* context) { - char filename[MAX_PATH_CHARACTERS]; FILE* file; int tx, ty; - - Get_full_filename(filename, context->File_name, context->File_directory); // TODO: Check picture has 5 layers // TODO: Check the constraints on the layers // Layer 1 : 1 color Only @@ -3407,12 +3390,10 @@ void Save_CM5(T_IO_Context* context) fclose(file); // Now the pixeldata - filename[strlen(filename) - 3] = 0; - strcat(filename,"gfx"); - if (!(file = fopen(filename, "wb"))) + if (!(file = Open_file_write_with_alternate_ext(context, "gfx"))) { - File_error = 2; - return; + File_error = 2; + return; } setvbuf(file, NULL, _IOFBF, 64*1024); @@ -3460,91 +3441,92 @@ void Save_CM5(T_IO_Context* context) void Test_PPH(T_IO_Context * context) { FILE *file; - char buffer[MAX_PATH_CHARACTERS]; + byte buffer[6]; long file_size; int w; int expected; File_error = 1; - if ((file = Open_file_read(context))) - { - // First check file size is large enough to hold the header - file_size = File_length_file(file); - if (file_size < 11) { - File_error = 1; - goto abort; - } + file = Open_file_read(context); + if (file == NULL) + return; - // File is large enough for the header, now check if the data makes some sense - fread(buffer, 1, 6, file); - if (buffer[0] > 5) { - // Unknown mode - File_error = 2; - goto abort; - } - - w = buffer[1] | (buffer[2] << 8); - if (w < 2 || w > 384) { - // Invalid width - File_error = 3; - goto abort; - } - - w = buffer[3] | (buffer[4] << 8); - if (w < 1 || w > 272) { - // Invalid height - File_error = 4; - goto abort; - } - - if (buffer[5] < 1 || buffer[5] > 28) - { - // Invalid palettes count - File_error = 5; - goto abort; - } - expected = 6; // Size of header - switch(buffer[0]) - { - case 0: - case 3: - case 4: - // Palette size should be 16 bytes, only 1 palette. - if (buffer[5] != 1) { - File_error = 7; - goto abort; - } - expected += 16; - break; - - case 1: - case 5: - expected += buffer[5] * 5 - 1; - break; - - case 2: - // Palete size should be 2 bytes - if (buffer[5] != 1) { - File_error = 7; - goto abort; - } - expected += 2; - break; - } - - if (file_size != expected) - { - File_error = 6; - goto abort; - } - File_error = 0; - } else { - File_error = 8; + // First check file size is large enough to hold the header + file_size = File_length_file(file); + if (file_size < 11) { + File_error = 1; + goto abort; } + // File is large enough for the header, now check if the data makes some sense + if (!Read_bytes(file, buffer, 6)) + goto abort; + if (buffer[0] > 5) { + // Unknown mode + File_error = 2; + goto abort; + } + + w = buffer[1] | (buffer[2] << 8); + if (w < 2 || w > 384) { + // Invalid width + File_error = 3; + goto abort; + } + + w = buffer[3] | (buffer[4] << 8); + if (w < 1 || w > 272) { + // Invalid height + File_error = 4; + goto abort; + } + + if (buffer[5] < 1 || buffer[5] > 28) + { + // Invalid palettes count + File_error = 5; + goto abort; + } + expected = 6; // Size of header + switch(buffer[0]) + { + case 0: + case 3: + case 4: + // Palette size should be 16 bytes, only 1 palette. + if (buffer[5] != 1) { + File_error = 7; + goto abort; + } + expected += 16; + break; + + case 1: + case 5: + expected += buffer[5] * 5 - 1; + break; + + case 2: + // Palette size should be 2 bytes + if (buffer[5] != 1) { + File_error = 7; + goto abort; + } + expected += 2; + break; + } + + if (file_size != expected) + { + File_error = 6; + goto abort; + } + File_error = 0; + abort: - fclose(file); + if (file != NULL) + fclose(file); // TODO: check existence of .ODD/.EVE files with the same name } @@ -3564,7 +3546,6 @@ void Load_PPH(T_IO_Context* context) { FILE *file; FILE *feven; - char filename[MAX_PATH_CHARACTERS]; // Read in the header uint8_t mode; @@ -3574,7 +3555,6 @@ void Load_PPH(T_IO_Context* context) int i,j; uint8_t a,b,c,d; int file_size; - char* ext; uint8_t pl[16]; static const T_Components CPCPAL[27] = @@ -3590,8 +3570,6 @@ void Load_PPH(T_IO_Context* context) { 0xF3, 0xF3, 0x0D }, { 0xF3, 0xF3, 0x6D }, { 0xFF, 0xF3, 0xF9 } }; - Get_full_filename(filename, context->File_name, context->File_directory); - if (!(file = Open_file_read(context))) { File_error = 1; @@ -3713,18 +3691,19 @@ void Load_PPH(T_IO_Context* context) // Load the picture data // There are two pages, each storing bytes in the CPC vram format but lines in // linear order. - ext = filename + strlen(filename) - 3; // TODO : make a function to load file with another extension ! - ext[0] = 'O'; - ext[1] = 'D'; - ext[2] = 'D'; - - file = fopen(filename, "rb"); - - ext[0] = 'E'; - ext[1] = 'V'; - ext[2] = 'E'; - - feven = fopen(filename, "rb"); + file = Open_file_read_with_alternate_ext(context, "odd"); + if (file == NULL) + { + File_error = 3; + return; + } + feven = Open_file_read_with_alternate_ext(context, "eve"); + if (feven == NULL) + { + File_error = 4; + fclose(file); + return; + } c = 0; d = 0; diff --git a/src/unicode.c b/src/unicode.c index cfde19a0..453fb555 100644 --- a/src/unicode.c +++ b/src/unicode.c @@ -87,6 +87,28 @@ int Unicode_char_strcmp(const word * s1, const char * s2) return (*s1 > *str2) ? 1 : -1; } +/// Compare an unicode string with a regular Latin1 string. Ignoring case +int Unicode_char_strcasecmp(const word * s1, const char * s2) +{ + const byte * str2 = (const byte *)s2; + unsigned int c1, c2; + + for (;;) + { + c1 = *s1++; + c2 = *str2++; + // first convert to lower case + if ('a' <= c1 && c1 <= 'z') + c1 -= ('a'-'A'); + if ('a' <= c2 && c2 <= 'z') + c2 -= ('a'-'A'); + if (c1 != c2) + return (c1 > c2) ? 1 : -1; + if (c1 == 0) + return 0; + } +} + /// Copy a regular Latin1 string to an unicode string void Unicode_char_strlcpy(word * dst, const char * src, size_t len) { diff --git a/src/unicode.h b/src/unicode.h index 4e88473e..1a54ebb0 100644 --- a/src/unicode.h +++ b/src/unicode.h @@ -39,6 +39,9 @@ void Unicode_strlcat(word * dst, const word * src, size_t len); /// Compare an unicode string with a regular Latin1 string int Unicode_char_strcmp(const word * s1, const char * s2); +/// Compare an unicode string with a regular Latin1 string. Ignoring case +int Unicode_char_strcasecmp(const word * s1, const char * s2); + /// Copy a regular Latin1 string to an unicode string void Unicode_char_strlcpy(word * dst, const char * src, size_t len);