1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 //		ResAcc.c		Resource access
20 //		Rex E. Bradford
21 /*
22  * $Header: r:/prj/lib/src/res/rcs/resacc.c 1.4 1994/08/30 15:18:20 rex Exp $
23  * $Log: resacc.c $
24  * Revision 1.4  1994/08/30  15:18:20  rex
25  * Made sure ResGet() returns NULL if ResLoadResource() did
26  *
27  * Revision 1.3  1994/08/30  15:14:32  rex
28  * Put in check for NULL return from ResLoadResource
29  *
30  * Revision 1.2  1994/06/16  11:06:05  rex
31  * Modified routines to handle LRU list better (keep locked and nodrop stuff
32  * out)
33  *
34  * Revision 1.1  1994/02/17  11:23:31  rex
35  * Initial revision
36  *
37  */
38 
39 #include "res.h"
40 #include "res_.h"
41 #include "lg.h"
42 
43 #include <assert.h>
44 #include <stdlib.h> // free()
45 #include <string.h>
46 
47 // An empty ResourceFormat struct means no translation is needed.
48 const ResourceFormat RawFormat = { NULL, NULL, 0, NULL };
49 
ResDecode(void * raw,size_t * size,UserDecodeData ud)50 void *ResDecode(void *raw, size_t *size, UserDecodeData ud)
51 {
52     // Layout.
53     const ResLayout *layout = (const ResLayout*)ud;
54     // Working pointer into the raw data.
55     uchar *rp = raw;
56     // Number of entries, if it's an array.
57     int nentries = (layout->flags & LAYOUT_FLAG_ARRAY) ? *size / layout->dsize : 1;
58     // Total size of the decoded data.
59     size_t bufsize = layout->msize * nentries;
60     if (layout->flags & LAYOUT_FLAG_RAW_DATA_FOLLOWS) {
61         // Additional raw data follows; add its size to the buffer.
62         assert(nentries == 1);
63         bufsize += *size - layout->dsize;
64     }
65     void *buff = malloc(bufsize);
66     int i;
67     for (i = 0; i < nentries; ++i) {
68         uchar *b = ((uchar *)buff) + i * layout->msize;
69         uchar *bp;
70         const ResField *field = layout->fields;
71 
72         while (field->type != RFFT_END) {
73             bp = b + field->offset;
74 	    if (field->type > RFFT_BIN_BASE) {
75 		// Fixed size binary data, treated as flat byte array.
76 		int binsize = field->type - RFFT_BIN_BASE;
77 		memcpy(bp, rp, binsize);
78 		rp += binsize;
79 	    } else switch (field->type) {
80             case RFFT_PAD:
81                 rp += field->offset;
82                 break;
83             case RFFT_UINT8:
84                 *bp = *rp++;
85                 break;
86             case RFFT_UINT16:
87                 *(uint16_t*)bp = (uint16_t)rp[0] | ((uint16_t)rp[1] << 8);
88                 rp += 2;
89                 break;
90             case RFFT_UINT32:
91                 *(uint32_t*)bp = (uint32_t)rp[0] | ((uint32_t)rp[1] << 8) |
92                     ((uint32_t)rp[2] << 16) | ((uint32_t)rp[3] << 24);
93                 rp += 4;
94                 break;
95             case RFFT_INTPTR:
96                 // These occupy 32 bits in file but expand to pointer size in memory.
97                 *(uintptr_t*)bp = (uint32_t)rp[0] | ((uint32_t)rp[1] << 8) |
98                     ((uint32_t)rp[2] << 16) | ((uint32_t)rp[3] << 24);
99                 rp += 4;
100                 break;
101             case RFFT_RAW: // should be last entry
102                 memcpy(bp, rp, *size - (rp-(uchar*)raw));
103                 break;
104             default:
105                 assert(!"Invalid resource field type");
106             }
107             ++field;
108         }
109     }
110     // Update size with the decoded data size.
111     *size = bufsize;
112     return buff;
113 }
114 
ResEncode(void * cooked,size_t * size,UserDecodeData ud)115 void *ResEncode(void *cooked, size_t *size, UserDecodeData ud)
116 {
117     // Layout.
118     const ResLayout *layout = (const ResLayout*)ud;
119     // Number of entries, if it's an array.
120     int nentries = (layout->flags & LAYOUT_FLAG_ARRAY) ? *size / layout->msize : 1;
121     // Total size of the data on disc.
122     size_t bufsize = layout->dsize * nentries;
123     if (layout->flags & LAYOUT_FLAG_RAW_DATA_FOLLOWS) {
124         // Additional raw data follows; add its size to the buffer.
125         assert(nentries == 1);
126         bufsize += *size - layout->msize;
127     }
128     void *buff = malloc(bufsize);
129     // Working pointer into the "raw" data.
130     uchar *rp = buff;
131     int i;
132     for (i = 0; i < nentries; ++i) {
133         uchar *b = ((uchar *)cooked) + i * layout->msize;
134         uchar *bp;
135         const ResField *field = layout->fields;
136 
137         while (field->type != RFFT_END) {
138             bp = b + field->offset;
139 	    if (field->type > RFFT_BIN_BASE) {
140 		// Fixed size binary data, treated as flat byte array.
141 		int binsize = field->type - RFFT_BIN_BASE;
142 		memcpy(rp, bp, binsize);
143 		rp += binsize;
144 	    } else switch (field->type) {
145             case RFFT_PAD:
146                 rp += field->offset;
147                 break;
148             case RFFT_UINT8:
149                 *rp++ = *bp;
150                 break;
151             case RFFT_UINT16:
152 		*rp++ = (*(uint16_t*)bp) & 0xff;
153 		*rp++ = (*(uint16_t*)bp) >> 8;
154                 break;
155             case RFFT_UINT32:
156 		*rp++ = (*(uint32_t*)bp) & 0xff;
157 		*rp++ = ((*(uint32_t*)bp) >> 8) & 0xff;
158 		*rp++ = ((*(uint32_t*)bp) >> 16) & 0xff;
159 		*rp++ = ((*(uint32_t*)bp) >> 24) & 0xff;
160                 break;
161             case RFFT_INTPTR:
162                 // These occupy 32 bits in file but expand to pointer size in memory.
163 		*rp++ = (*(uintptr_t*)bp) & 0xff;
164 		*rp++ = ((*(uintptr_t*)bp) >> 8) & 0xff;
165 		*rp++ = ((*(uintptr_t*)bp) >> 16) & 0xff;
166 		*rp++ = ((*(uintptr_t*)bp) >> 24) & 0xff;
167                 break;
168             case RFFT_RAW: // should be last entry
169                 memcpy(rp, bp, bufsize - (rp-(uchar*)buff));
170                 break;
171             default:
172                 assert(!"Invalid resource field type");
173             }
174             ++field;
175         }
176     }
177     // Return the size of the 'raw' data.
178     *size = bufsize;
179     return buff;
180 }
181 
182 //	---------------------------------------------------------
183 //
184 //	ResLock() locks a resource and returns ptr.
185 //
186 //		id = resource id
187 //
188 //	Returns: ptr to locked resource
189 //	---------------------------------------------------------
ResLock(Id id)190 void *ResLock(Id id) {
191     ResDesc *prd;
192 
193     //  Check if valid id
194     //  DBG(DSRC_RES_ChkIdRef, {if (!ResCheckId(id)) return NULL;});
195 
196 
197     prd = RESDESC(id);
198 
199     // CC: If already loaded, use the existing bytes
200     if (prd->ptr != NULL) {
201         prd->lock++;
202         return prd->ptr;
203     }
204 
205     // If resource not loaded, load it now
206     if (ResLoadResource(id, NULL) == NULL) {
207         ERROR("ResLock: Could not load %x", id);
208         return (NULL);
209     } else if (prd->lock == 0)
210         ResRemoveFromLRU(prd);
211 
212     prd->lock++;
213 
214     // Return ptr
215     return prd->ptr;
216 }
217 
218 //	---------------------------------------------------------
219 //
220 //	ResUnlock() unlocks a resource.
221 //
222 //		id = resource id
223 //	---------------------------------------------------------
ResUnlock(Id id)224 void ResUnlock(Id id) {
225     ResDesc *prd;
226 
227     // Check if valid id
228     if (!ResCheckId(id))
229         return;
230 
231     // Check for under-lock
232     prd = RESDESC(id);
233 
234     if (prd->lock == 0) {
235         DEBUG("%s: id $%x already unlocked",  __FUNCTION__, id);
236         return;
237     }
238 
239     // Else decrement lock, if 0 move to tail and tally stats
240     if (prd->lock > 0)
241         prd->lock--;
242 
243     if (prd->lock == 0) {
244         // CC: Should we free the prd ptr here?
245         ResAddToTail(prd);
246     }
247 }
248 
249 //	-------------------------------------------------------------
250 //
251 //	ResGet() gets a ptr to a resource
252 //
253 //		id = resource id
254 //
255 //	Returns: ptr to resource (ptr only guaranteed until next Malloc(),
256 //				Lock(), Get(), etc.
257 //	---------------------------------------------------------
258 
ResGet(Id id)259 void *ResGet(Id id) {
260     ResDesc *prd;
261     // Check if valid id
262     // ValidateRes(id);
263 
264     if (!ResCheckId(id))
265         return NULL;
266 
267     // Load resource or move to tail
268     prd = RESDESC(id);
269     if (prd->ptr == NULL) {
270         if (ResLoadResource(id, NULL) == NULL) {
271             return (NULL);
272         }
273         ResAddToTail(prd);
274     } else if (prd->lock == 0) {
275         ResMoveToTail(prd);
276     }
277 
278     // ValidateRes(id);
279 
280     //  Return ptr
281     return (prd->ptr);
282 }
283 
284 //	---------------------------------------------------------
285 //
286 //	ResExtract() extracts a resource from an open resource file.
287 //
288 //		id   = id
289 //		buff = ptr to buffer
290 //
291 //	Returns: ptr to supplied buffer, or NULL if problem
292 //	---------------------------------------------------------
293 //  For Mac version:  Copies information from resource handle into the buffer.
294 
ResExtract(Id id,const ResourceFormat * format,void * buffer)295 void *ResExtract(Id id, const ResourceFormat *format, void *buffer) {
296     ResDecodeFunc decoder = format->decoder;
297     ResDesc *prd = RESDESC(id);
298     if (decoder != NULL) {
299 	// Get the raw data into a temporary buffer.
300 	size_t size = prd->fsize;
301 	void *tbuf = malloc(size);
302 	if (ResRetrieve(id, tbuf)) {
303 	    void *dbuf = decoder(tbuf, &size, format->data);
304 	    memcpy(buffer, dbuf, size);
305 	    prd->msize = size;
306 	    if (format->freer != NULL) {
307 		format->freer(dbuf);
308 	    } else {
309 		free(dbuf);
310 	    }
311 	    free(tbuf);
312 	    return buffer;
313 	}
314 	free(tbuf);
315     } else {
316         // Retrieve the data into the buffer, please
317         if (ResRetrieve(id, buffer)) {
318 	    prd->msize = prd->fsize;
319             return (buffer);
320         }
321     }
322 
323     ERROR("%s: failed for %x", __FUNCTION__, id);
324     // If ResRetreive failed, return NULL ptr
325     return (NULL);
326 }
327 
328 //	----------------------------------------------------------
329 //
330 //	ResDrop() drops a resource from memory for awhile.
331 //
332 //		id = resource id
333 //	----------------------------------------------------------
ResDrop(Id id)334 void ResDrop(Id id) {
335     ResDesc *prd;
336 
337     if (!ResCheckId(id))
338         return;
339 
340     prd = RESDESC(id);
341     if (prd->lock)
342         WARN("%s: Block $%x is locked, dropping anyway", __FUNCTION__, id);
343 
344     // Remove from LRU chain
345     if (prd->lock == 0)
346         ResRemoveFromLRU(prd);
347 
348     //	Free memory and set ptr to NULL
349 
350     if (prd->ptr == NULL) {
351         TRACE("%s: Block $%x not in memory, ignoring request\n", __FUNCTION__, id);
352         return;
353     }
354 
355     if (prd->lock != 0) {
356         TRACE("%s: Dropping resource 0x%x that's in use.", __FUNCTION__, id);
357         prd->lock = 0;
358     }
359 
360     // Free the raw data.
361     if (prd->ptr != NULL) {
362 	assert(prd->format != NULL);
363         if (prd->format->freer != NULL) {
364             prd->format->freer(prd->ptr);
365         }
366         else {
367             free(prd->ptr);
368         }
369         prd->ptr = NULL;
370     }
371 }
372 
373 //	-------------------------------------------------------
374 //
375 //	ResDelete() deletes a resource forever.
376 //
377 //		Id = id of resource
378 //	-------------------------------------------------------
379 //  For Mac version:  Call ReleaseResource on the handle and set its ref to
380 //  null. The next ResLoadResource on the resource will load it back in.
381 
ResDelete(Id id)382 void ResDelete(Id id) {
383     ResDesc *prd;
384 
385     // If locked, issue warning
386     if (!ResCheckId(id))
387         return;
388 
389     prd = RESDESC(id);
390 
391     // If in use: if in ram, free memory & LRU, then in any case zap entry
392     if (prd->offset) {
393         if (prd->ptr) {
394             if (prd->lock == 0)
395                 ResRemoveFromLRU(prd);
396             ResDrop(id);
397         }
398         memset(prd, 0, sizeof(ResDesc));
399     }
400 }
401 
402 //	--------------------------------------------------------
403 //		INTERNAL ROUTINES
404 //	--------------------------------------------------------
405 //
406 //	ResCheckId() checks if id valid.
407 //
408 //		id = id to be checked
409 //
410 //	Returns: TRUE if id ok, FALSE if invalid & prints warning
411 
ResCheckId(Id id)412 bool ResCheckId(Id id) {
413     if (id < ID_MIN) {
414         DEBUG("%s: id $%x invalid", __FUNCTION__, id);
415         return false;
416     }
417     if (id > resDescMax) {
418         DEBUG("%s: id $%x exceeds table", __FUNCTION__, id);
419         return false;
420     }
421     return true;
422 }
423