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