1 /**
2  * Free fastfile.cpp replacement
3 
4  * Copyright (C) 2003  Shawn Betts
5  * Copyright (C) 2003, 2004, 2007, 2009  Sylvain Beucler
6 
7  * This file is part of GNU FreeDink
8 
9  * GNU FreeDink is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13 
14  * GNU FreeDink is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18 
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see
21  * <http://www.gnu.org/licenses/>.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 
32 #ifdef _WIN32
33 #  include <windows.h>
34 #  include <io.h>
35 /* #define strcasecmp(a,b) stricmp(a,b) */
36 #else
37 #  include <unistd.h>
38 #  ifdef HAVE_MMAP
39 #    include <sys/mman.h>
40 #  endif
41 #  include <sys/types.h>
42 #  include <sys/stat.h>
43 #  include <fcntl.h>
44 #endif
45 #include "str_util.h"
46 #include "io_util.h"
47 extern void FastFileFini(void);
48 
49 #define HEADER_NB_ENTRIES_LEN 4
50 
51 struct FF_Entry
52 {
53   long off;
54   char name[13];
55 };
56 
57 struct FF_Handle
58 {
59   int alive;
60   long pos, off, len;
61 };
62 
63 static struct FF_Entry *g_Entries = NULL;
64 static struct FF_Handle *g_Handles = NULL;
65 
66 static unsigned int g_FileSize = 0;
67 static unsigned int g_numEntries = 0;
68 static unsigned int g_numHandles = 0;
69 
70 #ifdef HAVE_MMAP
71 static int g_File = 0;
72 #else
73 #  ifdef _WIN32
74 HANDLE g_File;  /* HANDLE == void*, cf. winnt.h */
75 HANDLE g_FileMap;
76 #  else
77 FILE* g_File = NULL;
78 #  endif
79 #endif
80 
81 #if defined HAVE_MMAP || defined _WIN32
82 unsigned char *g_MemMap = NULL;
83 #endif
84 
85 int
FastFileInit(char * filename,int max_handles)86 FastFileInit(char *filename, int max_handles)
87 {
88   long count = 0;
89   FastFileFini();
90 
91 #if _WIN32 | HAVE_MMAP
92   unsigned char *buf = NULL;
93 #endif
94 
95 #ifdef HAVE_MMAP
96   /* Open and mmap the file (Unix) */
97   g_File = open(filename, O_RDONLY);
98   if (g_File < 0)
99     return 0/*false*/;
100   g_FileSize = lseek(g_File, 0, SEEK_END); /* needed by munmap */
101   lseek (g_File, 0, SEEK_SET);
102 
103   g_MemMap = mmap(NULL, g_FileSize, PROT_READ, MAP_PRIVATE, g_File, 0);
104 #else
105 #  ifdef _WIN32
106   /* Open and mmap the file (Windows) */
107 
108   /* first: file size (for later sanity checks) */
109   FILE* t = fopen(filename, "rb");
110   if (t == NULL)
111     return FALSE;
112   fseek(t, 0, SEEK_END);
113   g_FileSize = ftell(t);
114   fseek(t, 0, SEEK_SET);
115   fclose(t);
116 
117   g_File =
118     CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
119 	       FILE_FLAG_RANDOM_ACCESS, 0);
120   if (g_File == NULL)
121     return FALSE;
122 
123   g_FileMap = CreateFileMapping (g_File, NULL, PAGE_READONLY, 0, 0, NULL);
124   g_MemMap = MapViewOfFile (g_FileMap, FILE_MAP_READ, 0, 0, 0);
125 #  else
126   /* C stdio (portable) */
127   g_File = fopen(filename, "rb");
128   if (g_File == NULL)
129     return 0/*false*/;
130 
131   fseek(g_File, 0, SEEK_END);
132   g_FileSize = ftell(g_File);
133   fseek(g_File, 0, SEEK_SET);
134 #  endif
135 #endif
136 
137   g_numHandles = max_handles;
138   /* Get the number of entries (from stored LSB int32) */
139 #if defined HAVE_MMAP || defined _WIN32
140   buf = g_MemMap;
141   g_numEntries =
142     (buf[3] << 24) | (buf[2] << 16)
143     | (buf[1] << 8) | (buf[0]);
144   buf += 4;
145 #else
146   g_numEntries = read_lsb_int(g_File);
147 #endif
148 
149   /* Allocate the memory */
150   g_Entries = calloc(sizeof(struct FF_Entry), g_numEntries);
151   g_Handles = calloc(sizeof(struct FF_Handle), max_handles);
152 
153   for (count = 0; count < g_numEntries; count++)
154     {
155 #if defined HAVE_MMAP || defined _WIN32
156       g_Entries[count].off = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);
157       buf += 4;
158       strncpy (g_Entries[count].name, (char*)buf, 13);
159       buf += 13;
160 #else
161       g_Entries[count].off = read_lsb_int(g_File);
162       fread(g_Entries[count].name, 13, 1, g_File);
163 #endif
164       /* Ensure string is null-terminated */
165       g_Entries[count].name[12] = '\0';
166     }
167 
168   return 1;
169 }
170 
171 
172 void
FastFileFini(void)173 FastFileFini (void)
174 {
175   if (g_Entries != NULL)
176     {
177       free(g_Entries);
178       g_Entries = NULL;
179     }
180   if (g_Handles != NULL)
181     {
182       free(g_Handles);
183       g_Handles = NULL;
184     }
185 
186 #ifdef HAVE_MMAP
187   /* Unmap and close the file (Unix) */
188   if (g_MemMap != NULL)
189     {
190       munmap(g_MemMap, g_FileSize);
191       g_MemMap = NULL;
192     }
193   if (g_File != 0)
194     {
195       close(g_File);
196       g_File = 0;
197     }
198 #else
199 #  ifdef _WIN32
200   if (g_MemMap != NULL)
201     {
202       /* Unmap and close the file (Windows) */
203       CloseHandle(g_FileMap);
204       CloseHandle(g_File);
205       g_MemMap = NULL;
206     }
207 #  else
208   if (g_File != NULL)
209     {
210       fclose(g_File);
211       g_File = NULL;
212     }
213 #  endif
214 #endif
215 }
216 
217 
218 void *
FastFileOpen(char * name)219 FastFileOpen(char *name)
220 {
221   struct FF_Handle *i;
222   long fCount;
223   long hCount;
224 
225   /* Check for the file, dont' include directory */
226   for (fCount = 0; fCount < (long)g_numEntries - 1; fCount++)
227     {
228       if (strcasecmp(g_Entries[fCount].name, name) == 0)
229 	{
230 	  for (hCount = 0; hCount < (long)g_numHandles; hCount++)
231 	    {
232 	      i = &g_Handles[hCount];
233 
234 	      if (!i->alive)
235 		{
236 		  i->alive = 1;
237 		  i->off = g_Entries[fCount].off;
238 		  i->pos = 0;
239 		  /* Normal offset, tells where next the image bytes
240 		     start */
241 		  int next_off = g_Entries[fCount + 1].off;
242 		  if (next_off == 0)
243 		    /* Support badly generated dir.ff such as Mystery
244 		       Island's (skip 1 empty entry) */
245 		    /* TODO: check that fCount+2 is valid. */
246 		    next_off = g_Entries[fCount + 2].off;
247 		  /* Watch for buffer overflows - check that 'off' is
248 		     in a reasonable range [0, len(file)], and doesn't
249 		     overlap another fastfile */
250 		  if ((i->off < 0 || i->off > g_FileSize)
251 		      || i->off > next_off)
252 		    i->len = 0;
253 		  else
254 		    i->len = next_off - i->off;
255 		  return (void*)i;
256 		}
257 	    }
258 	  return NULL;
259 	}
260     }
261   return NULL;
262 }
263 
264 
265 int
FastFileClose(struct FF_Handle * i)266 FastFileClose (struct FF_Handle *i)
267 {
268   if (!i)
269     return 0;
270 
271 #if defined HAVE_MMAP || defined _WIN32
272   if (!g_MemMap)
273     return 0;
274 #endif
275 
276   i->alive = 0;
277   return 1;
278 }
279 
280 
281 SDL_RWops*
FastFileLock(struct FF_Handle * i)282 FastFileLock(struct FF_Handle *i)
283 {
284   if (!i)
285     return NULL;
286 
287 #if defined HAVE_MMAP || defined _WIN32
288   char *buffer = NULL;
289   if(!g_MemMap)
290     return NULL;
291 #endif
292 
293 #if defined HAVE_MMAP || defined _WIN32
294   buffer = (char*)g_MemMap;
295   buffer += i->off;
296   return SDL_RWFromMem(buffer, i->len);
297 #else
298   fseek(g_File, i->off, SEEK_SET);
299   return SDL_RWFromFP(g_File, /*autoclose=*/0);
300 #endif
301 }
302 
303 
304 int
FastFileLen(struct FF_Handle * i)305 FastFileLen(struct FF_Handle *i)
306 {
307   return i->len;
308 }
309 
310 
311 
312 /* comment out unused functions to ease portability */
313 #if 0
314 int
315 FastFileUnlock(struct FF_Handle *i, int off, int len)
316 {
317   return 1;
318 }
319 
320 int
321 FastFileSeek (struct FF_Handle *i, int offset, int whence)
322 {
323   long oldpos;
324 
325   if (!i || !g_MemMap)
326     return 0;
327 
328   oldpos = i->pos;
329 
330   switch (whence)
331     {
332     case SEEK_SET:
333       i->pos = offset;
334       break;
335     case SEEK_CUR:
336       i->pos += offset;
337       break;
338     case SEEK_END:
339       i->pos = i->len - offset;
340       break;
341     }
342 
343   if (i->pos > i->len)
344     {
345       i->pos = oldpos;
346       return 0;
347     }
348 
349   return 1;
350 }
351 
352 
353 int
354 FastFileRead (struct FF_Handle *i, void *bigBuffer, int size)
355 {
356   unsigned char *srcBuffer;
357 
358   if (!i || !bigBuffer || !g_MemMap)
359     return 0;
360   if (i->pos + size > i->len)
361     return 0;
362 
363   srcBuffer = g_MemMap;
364   srcBuffer += i->pos;
365   srcBuffer += i->off;
366 
367   memcpy (bigBuffer, srcBuffer, size);
368 
369   i->pos += size;
370 
371   return 1;
372 
373 }
374 
375 
376 long
377 FastFileTell (struct FF_Handle *i)
378 {
379   if (!i)
380     return 0;
381   return i->pos;
382 }
383 #endif
384