Mercurial > hg > dmlib
annotate packed.c @ 285:245b15cd1919
Don't link libSDL uselessly to utilities that do not actually use it.
Provide a fake implementation of SDL mutexes to satisfy the requirement of
having the functions around -- as these utilities do not actually use
threading, it does not matter if the mutexes actually work.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 11 Oct 2012 10:07:59 +0300 |
parents | 3dc35ce354f7 |
children | e9c6aee5d2f2 |
rev | line source |
---|---|
0 | 1 /* |
2 * PACKed - PACKfile EDitor | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2011 Tecnic Software productions (TNSP) | |
5 */ | |
6 #include "dmlib.h" | |
7 #include "dmargs.h" | |
8 #include "dmpack.h" | |
9 #include "dmpackutil.h" | |
115
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
10 #include "dmres.h" |
285
245b15cd1919
Don't link libSDL uselessly to utilities that do not actually use it.
Matti Hamalainen <ccr@tnsp.org>
parents:
211
diff
changeset
|
11 #include "dmmutex.h" |
0 | 12 #include <errno.h> |
13 | |
14 #define SET_MAX_FILES (4096) | |
15 #define SET_DEFAULT_PACK "data.pak" | |
16 | |
17 enum | |
18 { | |
19 CMD_NONE = 0, | |
20 CMD_CREATE, | |
21 CMD_ADD, | |
22 CMD_LIST, | |
23 CMD_EXTRACT | |
24 } DCOMMAND; | |
25 | |
26 enum | |
27 { | |
28 PACK_EXTRACTED = 0x0001, | |
29 }; | |
30 | |
31 int nsrcFilenames = 0; | |
32 char * srcFilenames[SET_MAX_FILES]; | |
33 char * optPackFilename = NULL; | |
34 BOOL optCompress = TRUE; | |
35 int optCommand = CMD_NONE; | |
36 int optDefResFlags = 0; | |
37 | |
38 | |
39 static DMOptArg optList[] = | |
40 { | |
41 { 0, '?', "help", "Show this help", OPT_NONE }, | |
42 { 1, 'p', "pack", "Set pack filename (default: " SET_DEFAULT_PACK ")", OPT_ARGREQ }, | |
43 { 2, 'c', "create", "Create and add files to PACK", OPT_NONE }, | |
44 { 3, 'a', "add", "Add files to PACK", OPT_NONE }, | |
45 { 4, 'l', "list", "List files in PACK", OPT_NONE }, | |
46 { 5, 'e', "extract", "Extract files from PACK", OPT_NONE }, | |
47 { 6, 'n', "nocompress", "No compression", OPT_NONE }, | |
48 { 7, 'v', "verbose", "Increase verbosity", OPT_NONE }, | |
49 { 8, 'f', "resflags", "Set default resource flags (-f 0xff)", OPT_ARGREQ }, | |
50 }; | |
51 | |
52 static const int optListN = sizeof(optList) / sizeof(optList[0]); | |
53 | |
54 | |
55 void argShowHelp() | |
56 { | |
117
b56ce9981d79
Correct help of 'packed' utility.
Matti Hamalainen <ccr@tnsp.org>
parents:
115
diff
changeset
|
57 dmPrintBanner(stdout, dmProgName, "[options] [-p <packfilename>] [filename[s]]"); |
0 | 58 dmArgsPrintHelp(stdout, optList, optListN); |
59 fprintf(stdout, | |
60 "\n" | |
61 "Examples:\n" | |
62 "$ %s -p test.pak -l -- list files in test.pak\n" | |
63 "$ %s -a foobar.jpg -- add foobar.jpg in " SET_DEFAULT_PACK "\n" | |
64 "$ %s -x foobar.jpg -- extract foobar.jpg from " SET_DEFAULT_PACK "\n", | |
65 dmProgName, dmProgName, dmProgName); | |
66 } | |
67 | |
68 | |
69 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
70 { | |
71 (void) optArg; | |
72 switch (optN) | |
73 { | |
211 | 74 case 0: |
75 argShowHelp(); | |
76 exit(0); | |
77 break; | |
0 | 78 |
211 | 79 case 1: |
80 optPackFilename = optArg; | |
81 break; | |
82 case 2: | |
83 optCommand = CMD_CREATE; | |
84 break; | |
85 case 3: | |
86 optCommand = CMD_ADD; | |
87 break; | |
88 case 4: | |
89 optCommand = CMD_LIST; | |
90 break; | |
91 case 5: | |
92 optCommand = CMD_EXTRACT; | |
93 break; | |
0 | 94 |
211 | 95 case 6: |
96 optCompress = FALSE; | |
97 break; | |
0 | 98 |
211 | 99 case 7: |
100 dmVerbosity++; | |
101 break; | |
0 | 102 |
211 | 103 case 8: |
0 | 104 { |
211 | 105 int i; |
106 if (!dmGetIntVal(optArg, &i)) | |
107 { | |
108 dmError("Invalid flags value '%s'.\n", optArg); | |
109 return FALSE; | |
110 } | |
111 optDefResFlags = i; | |
0 | 112 } |
211 | 113 break; |
0 | 114 |
211 | 115 default: |
116 dmError("Unknown argument '%s'.\n", currArg); | |
117 return FALSE; | |
0 | 118 } |
119 | |
120 return TRUE; | |
121 } | |
122 | |
123 | |
124 BOOL argHandleFile(char *currArg) | |
125 { | |
126 if (nsrcFilenames < SET_MAX_FILES) | |
127 { | |
128 srcFilenames[nsrcFilenames] = currArg; | |
129 nsrcFilenames++; | |
130 } | |
131 else | |
132 { | |
133 dmError("Maximum number of input files (%i) exceeded!\n", | |
134 SET_MAX_FILES); | |
135 return FALSE; | |
136 } | |
137 return TRUE; | |
138 } | |
139 | |
140 | |
141 /* Compare a string to a pattern. Case-SENSITIVE version. | |
142 * The matching pattern can consist of any normal characters plus | |
143 * wildcards ? and *. "?" matches any character and "*" matches | |
144 * any number of characters. | |
145 */ | |
146 BOOL dm_strmatch(const char *str, const char *pattern) | |
147 { | |
148 BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE; | |
149 const char *tmpPattern = NULL; | |
150 | |
151 // Check given pattern and string | |
152 if (str == NULL || pattern == NULL) | |
153 return FALSE; | |
154 | |
155 // Start comparision | |
156 do { | |
157 didMatch = FALSE; | |
158 switch (*pattern) | |
159 { | |
160 case '?': | |
161 // Any single character matches | |
162 if (*str) | |
163 { | |
164 didMatch = TRUE; | |
165 pattern++; | |
166 str++; | |
167 } | |
168 break; | |
169 | |
170 case '*': | |
171 didMatch = TRUE; | |
172 pattern++; | |
173 if (!*pattern) | |
174 isEnd = TRUE; | |
175 isAnyMode = TRUE; | |
176 tmpPattern = pattern; | |
177 break; | |
178 | |
179 case 0: | |
180 if (isAnyMode) | |
181 { | |
182 if (*str) | |
183 str++; | |
184 else | |
185 isEnd = TRUE; | |
186 } | |
187 else | |
188 { | |
189 if (*str) | |
190 { | |
191 if (tmpPattern) | |
192 { | |
193 isAnyMode = TRUE; | |
194 pattern = tmpPattern; | |
195 } | |
196 else | |
197 didMatch = FALSE; | |
198 } | |
199 else | |
200 isEnd = TRUE; | |
201 } | |
202 break; | |
203 default: | |
204 if (isAnyMode) | |
205 { | |
206 if (*pattern == *str) | |
207 { | |
208 isAnyMode = FALSE; | |
209 didMatch = TRUE; | |
210 } | |
211 else | |
212 { | |
213 if (*str) | |
214 { | |
215 didMatch = TRUE; | |
216 str++; | |
217 } | |
218 } | |
219 } | |
220 else | |
221 { | |
222 if (*pattern == *str) | |
223 { | |
224 didMatch = TRUE; | |
225 if (*pattern) | |
226 pattern++; | |
227 if (*str) | |
228 str++; | |
229 } | |
230 else | |
231 { | |
232 if (tmpPattern) | |
233 { | |
234 didMatch = TRUE; | |
235 isAnyMode = TRUE; | |
236 pattern = tmpPattern; | |
237 } | |
238 } | |
239 } | |
240 | |
241 if (!*str && !*pattern) | |
242 isEnd = TRUE; | |
243 break; | |
244 | |
245 } // switch | |
246 | |
247 } while (didMatch && !isEnd); | |
248 | |
249 return didMatch; | |
250 } | |
251 | |
252 | |
253 int dmAddFileToPack(DMPackFile *pack, const char *filename, int compression, int resFlags) | |
254 { | |
255 DMPackEntry *node; | |
256 int res = dm_pack_add_file(pack, filename, compression, resFlags, &node); | |
257 | |
258 if (res != DMERR_OK) | |
259 { | |
260 dmPrint(1, "%-32s [ERROR:%i]\n", | |
261 filename, res); | |
262 } | |
263 else | |
264 { | |
265 dmPrint(1, "%-32s ['%s', s=%d, c=%d, o=%ld, f=0x%04x]\n", | |
266 filename, node->filename, | |
267 node->size, node->length, node->offset, | |
268 node->resFlags); | |
269 } | |
270 | |
271 return res; | |
272 } | |
273 | |
274 | |
275 int main(int argc, char *argv[]) | |
276 { | |
277 int i, res = 0; | |
278 DMPackFile *pack = NULL; | |
279 | |
31
7ad2c6b57932
This ugly trick won't work under MinGW, so disable it there.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
280 #ifndef __WIN32 |
0 | 281 stderr = stdout; |
31
7ad2c6b57932
This ugly trick won't work under MinGW, so disable it there.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
282 #endif |
0 | 283 |
284 // Parse arguments | |
285 dmInitProg("packed", "Pack File Editor", "0.4", NULL, NULL); | |
286 dmVerbosity = 1; | |
287 | |
288 if (!dmArgsProcess(argc, argv, optList, optListN, | |
289 argHandleOpt, argHandleFile, TRUE)) | |
290 exit(1); | |
291 | |
292 // Check PACK filename | |
293 if (optPackFilename == NULL) | |
294 optPackFilename = SET_DEFAULT_PACK; | |
295 | |
296 if (optCommand == CMD_NONE) | |
297 { | |
298 argShowHelp(); | |
299 dmError("Nothing to do.\n"); | |
300 exit(0); | |
301 return 0; | |
302 } | |
303 | |
304 dmMsg(1, "Processing %s ...\n", optPackFilename); | |
305 | |
306 // Execute command | |
307 switch (optCommand) | |
308 { | |
309 case CMD_CREATE: | |
310 case CMD_ADD: | |
311 switch (optCommand) | |
312 { | |
313 case CMD_CREATE: | |
314 dmMsg(1, "Creating new PACK\n"); | |
315 res = dm_pack_create(optPackFilename, &pack); | |
316 break; | |
317 | |
318 case CMD_ADD: | |
319 dmMsg(1, "Opening existing PACK\n"); | |
320 res = dm_pack_open(optPackFilename, &pack, FALSE); | |
321 break; | |
322 } | |
323 | |
324 // Add files into PACK | |
325 if (res == DMERR_OK) | |
326 { | |
327 dmMsg(1, "Adding %d files...\n", nsrcFilenames); | |
328 | |
329 for (i = 0; i < nsrcFilenames; i++) | |
330 { | |
331 // Handle resource definition files | |
332 if (srcFilenames[i][0] == '@') | |
333 { | |
334 } | |
335 else | |
336 { | |
337 dmAddFileToPack(pack, srcFilenames[i], optCompress, optDefResFlags); | |
338 } | |
339 } | |
340 | |
341 dmMsg(1, "w=%i\n", dm_pack_write(pack)); | |
342 dmMsg(1, "c=%i\n", dm_pack_close(pack)); | |
343 } | |
344 else | |
345 { | |
346 dmError("Could not open packfile, error #%i: %s\n", res, | |
347 dmErrorStr(res)); | |
348 } | |
349 break; | |
350 | |
351 case CMD_LIST: | |
352 // List files in PACK | |
353 res = dm_pack_open(optPackFilename, &pack, TRUE); | |
354 if (res == DMERR_OK) | |
355 { | |
356 DMPackEntry *node; | |
357 for (i = 0, node = pack->entries; node; i++) | |
358 node = node->next; | |
359 dmMsg(1, "%d files total\n", i); | |
360 | |
115
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
361 dmPrint(0, "%-32s | %8s | %8s | %8s | %s\n", |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
362 "Name", "Size", "CSize", "Offset", "ResFlags"); |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
363 |
0 | 364 for (node = pack->entries; node != NULL; node = node->next) |
365 { | |
366 BOOL match; | |
367 | |
368 // Check for matches | |
369 if (nsrcFilenames > 0) | |
370 { | |
371 match = FALSE; | |
372 for (i = 0; i < nsrcFilenames && !match; i++) | |
373 { | |
374 match = dm_strmatch(node->filename, srcFilenames[i]); | |
375 } | |
376 } | |
377 else | |
378 match = TRUE; | |
379 | |
380 if (match) | |
381 { | |
115
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
382 char flags[16]; |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
383 dmres_flags_to_symbolic(flags, sizeof(flags), node->resFlags); |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
384 |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
385 dmPrint(0, "%-32s | %8d | %8d | %08x | %s\n", |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
386 node->filename, node->size, node->length, |
23e03defa759
Prettify pack content listing output.
Matti Hamalainen <ccr@tnsp.org>
parents:
31
diff
changeset
|
387 node->offset, flags); |
0 | 388 } |
389 } | |
390 | |
391 dmMsg(1, "c=%i\n", dm_pack_close(pack)); | |
392 } | |
393 else | |
394 dmError("Could not open packfile, error #%i: %s\n", res, | |
395 dmErrorStr(res)); | |
396 break; | |
397 | |
398 case CMD_EXTRACT: | |
399 // Extract files from PACK | |
400 res = dm_pack_open(optPackFilename, &pack, TRUE); | |
401 if (res == DMERR_OK) | |
402 { | |
403 DMPackEntry *node; | |
404 FILE *resFile = fopen(DMRES_RES_FILE, "w"); | |
405 if (resFile == NULL) | |
406 { | |
407 dmError("Could not create resource output file '%s' #%i: %s\n", | |
408 DMRES_RES_FILE, errno, strerror(errno)); | |
409 } | |
410 | |
411 for (node = pack->entries; node != NULL; node = node->next) | |
412 { | |
413 BOOL match; | |
414 | |
415 // Check for matches | |
416 if (nsrcFilenames > 0) | |
417 { | |
418 match = FALSE; | |
419 for (i = 0; (i < nsrcFilenames) && !match; i++) | |
420 { | |
421 match = dm_strmatch(node->filename, srcFilenames[i]); | |
422 } | |
423 } | |
424 else | |
425 match = TRUE; | |
426 | |
427 if (match && (node->privFlags & PACK_EXTRACTED) == 0) | |
428 { | |
429 // Mark as done | |
430 node->privFlags |= PACK_EXTRACTED; | |
431 | |
432 // Print one entry | |
433 dmPrint(0, "Extracting: %-32s [siz=%d, cmp=%d, offs=0x%08x, flags=0x%04x]\n", | |
434 node->filename, node->size, node->length, | |
435 node->offset, node->resFlags); | |
436 | |
437 dm_pack_extract_file(pack, node); | |
438 | |
439 if (resFile != NULL) | |
440 { | |
441 fprintf(resFile, | |
442 "%s|%04x", node->filename, node->resFlags); | |
443 } | |
444 } | |
445 } | |
446 | |
447 dmMsg(1, "c=%i\n", dm_pack_close(pack)); | |
448 | |
449 if (resFile != NULL) | |
450 fclose(resFile); | |
451 } | |
452 else | |
453 dmError("Could not open packfile, error #%i: %s\n", res, | |
454 dmErrorStr(res)); | |
455 break; | |
456 | |
457 } | |
458 | |
459 return 0; | |
460 } |