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