1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /*  Grafx2 - The Ultimate 256-color bitmap paint program
4 
5 	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6 
7     Grafx2 is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; version 2
10     of the License.
11 
12     Grafx2 is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20 //////////////////////////////////////////////////////////////////////////////
21 ///@file loadsavefuncs.c
22 /// helper functions for load/save
23 //////////////////////////////////////////////////////////////////////////////
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #ifndef _MSC_VER
28 #include <strings.h>
29 #include <unistd.h>
30 #endif
31 #if defined(WIN32)
32 #include <windows.h>
33 #if defined(_MSC_VER)
34 #define strdup _strdup
35 #if _MSC_VER < 1900
36 #define snprintf _snprintf
37 #endif
38 #endif
39 #endif
40 #include "struct.h"
41 #include "global.h"
42 #include "loadsave.h"
43 #include "loadsavefuncs.h"
44 #include "io.h"
45 #include "unicode.h"
46 #include "gfx2mem.h"
47 #include "gfx2log.h"
48 
49 /// For use by Save_XXX() functions
Open_file_write(T_IO_Context * context)50 FILE * Open_file_write(T_IO_Context *context)
51 {
52   FILE * f;
53   char * filename; // filename with full path
54 #if defined(WIN32)
55   if (context->File_name_unicode != NULL && context->File_name_unicode[0] != 0)
56   {
57     size_t len;
58     WCHAR * filename_unicode;
59 
60     len = strlen(context->File_directory) + strlen(PATH_SEPARATOR)
61         + Unicode_strlen(context->File_name_unicode) + 1;
62     filename_unicode = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * len);
63     if (filename_unicode == NULL)
64       return NULL;
65 
66     Unicode_char_strlcpy((word *)filename_unicode, context->File_directory, len);
67     Unicode_char_strlcat((word *)filename_unicode, PATH_SEPARATOR, len);
68     Unicode_strlcat((word *)filename_unicode, context->File_name_unicode, len);
69 
70     f = _wfopen(filename_unicode, L"wb");
71     if (f != NULL)
72     {
73       // Now the file has been created, retrieve its short (ASCII) name
74       len = GetShortPathNameW(filename_unicode, NULL, 0);
75       if (len > 0)
76       {
77         WCHAR * shortpath = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * len);
78         if (shortpath != NULL)
79         {
80           len = GetShortPathNameW(filename_unicode, shortpath, len);
81           if (len > 0)
82           {
83             DWORD start, index;
84             for (start = len; start > 0 && shortpath[start-1] != '\\'; start--);
85             free(context->File_name);
86             context->File_name = (char *)GFX2_malloc(len + 1 - start);
87             if (context->File_name != NULL)
88             {
89               for (index = 0; index < len - start; index++)
90                 context->File_name[index] = shortpath[start + index];
91               context->File_name[index] = '\0';
92             }
93           }
94           else
95           {
96             GFX2_Log(GFX2_ERROR, "GetShortPathNameW(%p, %p, %u) failed !\n", filename_unicode, shortpath, len);
97           }
98         }
99       }
100       else
101       {
102         GFX2_Log(GFX2_ERROR, "GetShortPathNameW(%p, NULL, 0) failed !\n", filename_unicode);
103       }
104     }
105     free(filename_unicode);
106     return f;
107   }
108 #endif
109 
110   filename = Filepath_append_to_dir(context->File_directory, context->File_name);
111   if (filename == NULL)
112     return NULL;
113   f = fopen(filename, "wb");
114   free(filename);
115   return f;
116 }
117 
Open_file_write_with_alternate_ext(T_IO_Context * context,const char * ext)118 FILE * Open_file_write_with_alternate_ext(T_IO_Context *context, const char * ext)
119 {
120   FILE * f;
121   char *p;
122   char * filename; // filename with full path
123 #if defined(WIN32)
124   if (context->File_name_unicode != NULL && context->File_name_unicode[0] != 0)
125   {
126     size_t len;
127     WCHAR * filename_unicode;
128     WCHAR * pw;
129 
130     len = strlen(context->File_directory) + strlen(PATH_SEPARATOR)
131         + Unicode_strlen(context->File_name_unicode) + strlen(ext) + 1 + 1;
132     filename_unicode = (WCHAR *)GFX2_malloc(len * sizeof(WCHAR));
133     if (filename_unicode == NULL)
134       return NULL;
135     Unicode_char_strlcpy((word *)filename_unicode, context->File_directory, len);
136     Unicode_char_strlcat((word *)filename_unicode, PATH_SEPARATOR, len);
137     Unicode_strlcat((word *)filename_unicode, context->File_name_unicode, len);
138     pw = wcschr(filename_unicode, (WCHAR)'.');
139     if (pw != NULL)
140       *pw = 0;
141     Unicode_char_strlcat((word *)filename_unicode, ".", len);
142     Unicode_char_strlcat((word *)filename_unicode, ext, len);
143 
144     f = _wfopen(filename_unicode, L"wb");
145     free(filename_unicode);
146     return f;
147   }
148 #endif
149   filename = Filepath_append_to_dir(context->File_directory, context->File_name);
150 // TODO: fix ! (realloc if not enough space)
151   p = strrchr(filename, '.');
152   if (p != NULL)
153     *p = '\0';
154   strcat(filename, ".");
155   strcat(filename, ext);
156 
157   f = fopen(filename, "wb");
158   free(filename);
159   return f;
160 }
161 
162 /// For use by Load_XXX() and Test_XXX() functions
Open_file_read(T_IO_Context * context)163 FILE * Open_file_read(T_IO_Context *context)
164 {
165   FILE * f;
166   char * filename; // filename with full path
167 
168   filename = Filepath_append_to_dir(context->File_directory, context->File_name);
169   f = fopen(filename, "rb");
170   free(filename);
171   return f;
172 }
173 
174 struct T_Find_alternate_ext_data
175 {
176   const char * ext;
177   char * basename;
178   word * basename_unicode;
179   char * foundname;
180   word * foundname_unicode;
181 };
182 
Look_for_alternate_ext(void * pdata,const char * filename,const word * filename_unicode,byte is_file,byte is_directory,byte is_hidden)183 static void Look_for_alternate_ext(void * pdata, const char * filename, const word * filename_unicode, byte is_file, byte is_directory, byte is_hidden)
184 {
185   size_t base_len;
186   struct T_Find_alternate_ext_data * params = (struct T_Find_alternate_ext_data *)pdata;
187   (void)is_hidden;
188   (void)is_directory;
189 
190   if (!is_file)
191     return;
192 
193   if (filename_unicode != NULL && params->basename_unicode != NULL)
194   {
195     if (params->foundname_unicode != NULL)
196       return; // We already have found a file
197     base_len = Unicode_strlen(params->basename_unicode);
198     if (Unicode_strlen(filename_unicode) <= base_len)
199       return; // No match.
200     if (filename_unicode[base_len] != '.')
201       return; // No match.
202 #if defined(WIN32)
203     {
204       int cmp;
205       WCHAR * temp_string = (WCHAR *)GFX2_malloc((base_len + 1) * sizeof(WCHAR));
206       if (temp_string == NULL)
207         return;
208       memcpy(temp_string, filename_unicode, base_len * sizeof(word));
209       temp_string[base_len] = 0;
210       cmp = _wcsicmp((const WCHAR *)params->basename_unicode, temp_string);
211       free(temp_string);
212       if (cmp != 0)
213         return; // No match.
214     }
215 #else
216     if (memcmp(params->basename_unicode, filename_unicode, base_len * sizeof(word)) != 0)
217       return; // No match.
218 #endif
219     if (Unicode_char_strcasecmp(filename_unicode + base_len + 1, params->ext) != 0)
220       return; // No match.
221     // it is a match !
222     free(params->foundname);
223     params->foundname_unicode = Unicode_strdup(filename_unicode);
224     params->foundname = strdup(filename);
225   }
226   else
227   {
228     if (params->foundname != NULL)
229       return; // We already have found a file
230     base_len = strlen(params->basename);
231     if (filename[base_len] != '.')
232       return; // No match.
233 #if defined(WIN32)
234     if (_memicmp(params->basename, filename, base_len) != 0)  // Not case sensitive
235       return; // No match.
236 #else
237     if (memcmp(params->basename, filename, base_len) != 0)
238       return; // No match.
239 #endif
240     if (strcasecmp(filename + base_len + 1, params->ext) != 0)
241       return; // No match.
242     params->foundname_unicode = NULL;
243     params->foundname = strdup(filename);
244   }
245 }
246 
Open_file_read_with_alternate_ext(T_IO_Context * context,const char * ext)247 FILE * Open_file_read_with_alternate_ext(T_IO_Context *context, const char * ext)
248 {
249   FILE * f = NULL;
250   char * p;
251   struct T_Find_alternate_ext_data params;
252 
253   memset(&params, 0, sizeof(params));
254   params.ext = ext;
255   params.basename = strdup(context->File_name);
256   if (params.basename == NULL)
257   {
258     GFX2_Log(GFX2_ERROR, "Open_file_read_with_alternate_ext() strdup() failed\n");
259     return NULL;
260   }
261   p = strrchr(params.basename, '.');
262   if (p != NULL)
263     *p = '\0';
264   if (context->File_name_unicode != NULL)
265   {
266     size_t i = Unicode_strlen(context->File_name_unicode);
267     params.basename_unicode = GFX2_malloc(sizeof(word) * (i + 1));
268     if (params.basename_unicode != NULL)
269     {
270       memcpy(params.basename_unicode, context->File_name_unicode, (i + 1) * sizeof(word));
271       while (i-- > 0)
272         if (params.basename_unicode[i] == (word)'.')
273         {
274           params.basename_unicode[i] = 0;
275           break;
276         }
277     }
278   }
279 
280   For_each_directory_entry(context->File_directory, &params, Look_for_alternate_ext);
281   if (params.foundname != NULL)
282   {
283     char * filename; // filename with full path
284 
285     filename = Filepath_append_to_dir(context->File_directory, params.foundname);
286     f = fopen(filename, "rb");
287     free(filename);
288   }
289   free(params.basename);
290   free(params.basename_unicode);
291   free(params.foundname);
292   free(params.foundname_unicode);
293   return f;
294 }
295 
296 /// For use by Save_XXX() functions
Remove_file(T_IO_Context * context)297 void Remove_file(T_IO_Context *context)
298 {
299   char * filename; // filename with full path
300 
301   filename = Filepath_append_to_dir(context->File_directory, context->File_name);
302   Remove_path(filename);
303   free(filename);
304 }
305 
Palette_256_to_64(T_Palette palette)306 void Palette_256_to_64(T_Palette palette)
307 {
308   int i;
309   for(i=0;i<256;i++)
310   {
311     palette[i].R = palette[i].R >> 2;
312     palette[i].G = palette[i].G >> 2;
313     palette[i].B = palette[i].B >> 2;
314   }
315 }
316 
Palette_64_to_256(T_Palette palette)317 void Palette_64_to_256(T_Palette palette)
318 {
319   int i;
320   for(i=0;i<256;i++)
321   {
322     palette[i].R = (palette[i].R << 2)|(palette[i].R >> 4);
323     palette[i].G = (palette[i].G << 2)|(palette[i].G >> 4);
324     palette[i].B = (palette[i].B << 2)|(palette[i].B >> 4);
325   }
326 }
327 
Current_layer_count_used_colors(T_IO_Context * context,dword * usage)328 word Current_layer_count_used_colors(T_IO_Context *context, dword *usage)
329 {
330   dword nb_colors = 0;
331   int i;
332   word x, y;
333 
334   for (i = 0; i < 256; i++) usage[i] = 0;
335 
336   for (y = 0; y < context->Height; y++)
337   {
338     for (x = 0; x < context->Width; x++)
339       usage[Get_pixel(context, x, y)]++;
340   }
341 
342   // count the total number of unique used colors
343   for (i = 0; i < 256; i++)
344   {
345     if (usage[i] != 0)
346       nb_colors++;
347   }
348 
349   return nb_colors;
350 }
351