1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* main.c */ |
4 | /* */ |
5 | /* Main program of the sp65 sprite and bitmap utility */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | #include <errno.h> |
40 | |
41 | /* common */ |
42 | #include "abend.h" |
43 | #include "cmdline.h" |
44 | #include "print.h" |
45 | #include "version.h" |
46 | |
47 | /* sp65 */ |
48 | #include "attr.h" |
49 | #include "convert.h" |
50 | #include "error.h" |
51 | #include "input.h" |
52 | #include "output.h" |
53 | |
54 | |
55 | |
56 | /*****************************************************************************/ |
57 | /* Data */ |
58 | /*****************************************************************************/ |
59 | |
60 | |
61 | |
62 | /* Bitmap first read */ |
63 | static Bitmap* B; |
64 | |
65 | /* Bitmap working copy */ |
66 | static Bitmap* C; |
67 | |
68 | /* Output data from convertion */ |
69 | static StrBuf* D; |
70 | |
71 | |
72 | |
73 | /*****************************************************************************/ |
74 | /* Code */ |
75 | /*****************************************************************************/ |
76 | |
77 | |
78 | |
79 | static void Usage (void) |
80 | /* Print usage information and exit */ |
81 | { |
82 | printf ( |
83 | "Usage: %s [options] file [options] [file]\n" |
84 | "Short options:\n" |
85 | " -V\t\t\t\tPrint the version number and exit\n" |
86 | " -c fmt[,attrlist]\t\tConvert into target format\n" |
87 | " -h\t\t\t\tHelp (this text)\n" |
88 | " -lc\t\t\t\tList all possible conversions\n" |
89 | " -r file[,attrlist]\t\tRead an input file\n" |
90 | " -v\t\t\t\tIncrease verbosity\n" |
91 | " -w file[,attrlist]\t\tWrite the output to a file\n" |
92 | "\n" |
93 | "Long options:\n" |
94 | " --convert-to fmt[,attrlist]\tConvert into target format\n" |
95 | " --dump-palette\t\tDump palette as table\n" |
96 | " --help\t\t\tHelp (this text)\n" |
97 | " --list-conversions\t\tList all possible conversions\n" |
98 | " --pop\t\t\t\tRestore the original loaded image\n" |
99 | " --read file[,attrlist]\tRead an input file\n" |
100 | " --slice x,y,w,h\t\tGenerate a slice from the loaded bitmap\n" |
101 | " --verbose\t\t\tIncrease verbosity\n" |
102 | " --version\t\t\tPrint the version number and exit\n" |
103 | " --write file[,attrlist]\tWrite the output to a file\n" , |
104 | ProgName); |
105 | } |
106 | |
107 | |
108 | |
109 | static void SetWorkBitmap (Bitmap* N) |
110 | /* Delete an old working bitmap and set a new one. The new one may be NULL |
111 | ** to clear it. |
112 | */ |
113 | { |
114 | /* If we have a distinct work bitmap, delete it */ |
115 | if (C != 0 && C != B) { |
116 | FreeBitmap (C); |
117 | } |
118 | |
119 | /* Set the new one */ |
120 | C = N; |
121 | } |
122 | |
123 | |
124 | |
125 | static void SetOutputData (StrBuf* N) |
126 | /* Delete the old output data and replace it by the given one. The new one |
127 | ** may be NULL to clear it. |
128 | */ |
129 | { |
130 | /* Delete the old output data */ |
131 | if (D != 0) { |
132 | FreeStrBuf (D); |
133 | } |
134 | |
135 | /* Set the new one */ |
136 | D = N; |
137 | } |
138 | |
139 | |
140 | |
141 | static void OptConvertTo (const char* Opt attribute ((unused)), const char* Arg) |
142 | /* Convert the bitmap into a target format */ |
143 | { |
144 | static const char* const NameList[] = { |
145 | "format" |
146 | }; |
147 | |
148 | /* Parse the argument */ |
149 | Collection* A = ParseAttrList (Arg, NameList, 2); |
150 | |
151 | /* We must have a bitmap */ |
152 | if (C == 0) { |
153 | Error ("No bitmap to convert" ); |
154 | } |
155 | |
156 | /* Convert the bitmap */ |
157 | SetOutputData (ConvertTo (C, A)); |
158 | |
159 | /* Delete the attribute list */ |
160 | FreeAttrList (A); |
161 | } |
162 | |
163 | |
164 | |
165 | static void OptDumpPalette (const char* Opt attribute ((unused)), |
166 | const char* Arg attribute ((unused))) |
167 | /* Dump the palette of the current work bitmap */ |
168 | { |
169 | /* We must have a bitmap ... */ |
170 | if (C == 0) { |
171 | Error ("No bitmap" ); |
172 | } |
173 | |
174 | /* ... which must be indexed */ |
175 | if (!BitmapIsIndexed (C)) { |
176 | Error ("Current bitmap is not indexed" ); |
177 | } |
178 | |
179 | /* Dump the palette */ |
180 | DumpPalette (stdout, GetBitmapPalette (C)); |
181 | } |
182 | |
183 | |
184 | |
185 | static void OptHelp (const char* Opt attribute ((unused)), |
186 | const char* Arg attribute ((unused))) |
187 | /* Print usage information and exit */ |
188 | { |
189 | Usage (); |
190 | exit (EXIT_SUCCESS); |
191 | } |
192 | |
193 | |
194 | |
195 | static void OptListConversions (const char* Opt attribute ((unused)), |
196 | const char* Arg attribute ((unused))) |
197 | /* Print a list of all conversions */ |
198 | { |
199 | ListConversionTargets (stdout); |
200 | exit (EXIT_SUCCESS); |
201 | } |
202 | |
203 | |
204 | |
205 | static void OptPop (const char* Opt attribute ((unused)), |
206 | const char* Arg attribute ((unused))) |
207 | /* Restore the original image */ |
208 | { |
209 | /* C and B must differ and we must have an original */ |
210 | if (B == 0 || C == 0 || C == B) { |
211 | Error ("Nothing to pop" ); |
212 | } |
213 | |
214 | /* Delete the changed image and restore the original one */ |
215 | SetWorkBitmap (B); |
216 | } |
217 | |
218 | |
219 | |
220 | static void OptRead (const char* Opt attribute ((unused)), const char* Arg) |
221 | /* Read an input file */ |
222 | { |
223 | static const char* const NameList[] = { |
224 | "name" , "format" |
225 | }; |
226 | |
227 | |
228 | /* Parse the argument */ |
229 | Collection* A = ParseAttrList (Arg, NameList, 2); |
230 | |
231 | /* Clear the working copy */ |
232 | SetWorkBitmap (0); |
233 | |
234 | /* Delete the original */ |
235 | FreeBitmap (B); |
236 | |
237 | /* Read the file and use it as original and as working copy */ |
238 | B = C = ReadInputFile (A); |
239 | |
240 | /* Delete the attribute list */ |
241 | FreeAttrList (A); |
242 | } |
243 | |
244 | |
245 | |
246 | static void OptSlice (const char* Opt attribute ((unused)), const char* Arg) |
247 | /* Generate a slice of a bitmap */ |
248 | { |
249 | unsigned X, Y, W, H; |
250 | unsigned char T; |
251 | |
252 | /* We must have a bitmap otherwise we cannot slice */ |
253 | if (C == 0) { |
254 | Error ("Nothing to slice" ); |
255 | } |
256 | |
257 | /* The argument is X,Y,W,H */ |
258 | if (sscanf (Arg, "%u,%u,%u,%u,%c" , &X, &Y, &W, &H, &T) != 4) { |
259 | Error ("Invalid argument. Slice must be given as X,Y,W,H" ); |
260 | } |
261 | |
262 | /* Check the coordinates to be within the original bitmap */ |
263 | if (W > BM_MAX_WIDTH || H > BM_MAX_HEIGHT || |
264 | X + W > GetBitmapWidth (C) || |
265 | Y + H > GetBitmapHeight (C)) { |
266 | Error ("Invalid slice coordinates and/or size" ); |
267 | } |
268 | |
269 | /* Create the slice */ |
270 | SetWorkBitmap (SliceBitmap (C, X, Y, W, H)); |
271 | } |
272 | |
273 | |
274 | |
275 | static void OptVerbose (const char* Opt attribute ((unused)), |
276 | const char* Arg attribute ((unused))) |
277 | /* Increase verbosity */ |
278 | { |
279 | ++Verbosity; |
280 | } |
281 | |
282 | |
283 | |
284 | static void OptVersion (const char* Opt attribute ((unused)), |
285 | const char* Arg attribute ((unused))) |
286 | /* Print the assembler version */ |
287 | { |
288 | fprintf (stderr, "%s V%s\n" , ProgName, GetVersionAsString ()); |
289 | exit(EXIT_SUCCESS); |
290 | } |
291 | |
292 | |
293 | |
294 | static void OptWrite (const char* Opt attribute ((unused)), const char* Arg) |
295 | /* Write an output file */ |
296 | { |
297 | static const char* const NameList[] = { |
298 | "name" , "format" |
299 | }; |
300 | |
301 | |
302 | /* Parse the argument */ |
303 | Collection* A = ParseAttrList (Arg, NameList, 2); |
304 | |
305 | /* We must have output data */ |
306 | if (D == 0) { |
307 | Error ("No conversion, so there's nothing to write" ); |
308 | } |
309 | |
310 | /* Write the file */ |
311 | WriteOutputFile (D, A, C); |
312 | |
313 | /* Delete the attribute list */ |
314 | FreeAttrList (A); |
315 | } |
316 | |
317 | |
318 | |
319 | int main (int argc, char* argv []) |
320 | /* sp65 main program */ |
321 | { |
322 | /* Program long options */ |
323 | static const LongOpt OptTab[] = { |
324 | { "--convert-to" , 1, OptConvertTo }, |
325 | { "--dump-palette" , 0, OptDumpPalette }, |
326 | { "--help" , 0, OptHelp }, |
327 | { "--list-conversions" , 0, OptListConversions }, |
328 | { "--pop" , 0, OptPop }, |
329 | { "--read" , 1, OptRead }, |
330 | { "--slice" , 1, OptSlice }, |
331 | { "--verbose" , 0, OptVerbose }, |
332 | { "--version" , 0, OptVersion }, |
333 | { "--write" , 1, OptWrite }, |
334 | }; |
335 | |
336 | unsigned I; |
337 | |
338 | /* Initialize the cmdline module */ |
339 | InitCmdLine (&argc, &argv, "sp65" ); |
340 | |
341 | /* Check the parameters */ |
342 | I = 1; |
343 | while (I < ArgCount) { |
344 | |
345 | /* Get the argument */ |
346 | const char* Arg = ArgVec[I]; |
347 | |
348 | /* Check for an option */ |
349 | if (Arg[0] == '-') { |
350 | switch (Arg[1]) { |
351 | |
352 | case '-': |
353 | LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0])); |
354 | break; |
355 | |
356 | case 'V': |
357 | OptVersion (Arg, 0); |
358 | break; |
359 | |
360 | case 'c': |
361 | OptConvertTo (Arg, GetArg (&I, 2)); |
362 | break; |
363 | |
364 | case 'h': |
365 | OptHelp (Arg, 0); |
366 | break; |
367 | |
368 | case 'l': |
369 | if (Arg[2] == 'c') { |
370 | OptListConversions (Arg, 0); |
371 | } else { |
372 | UnknownOption (Arg); |
373 | } |
374 | break; |
375 | |
376 | case 'r': |
377 | OptRead (Arg, GetArg (&I, 2)); |
378 | break; |
379 | |
380 | case 'v': |
381 | OptVerbose (Arg, 0); |
382 | break; |
383 | |
384 | case 'w': |
385 | OptWrite (Arg, GetArg (&I, 2)); |
386 | break; |
387 | |
388 | default: |
389 | UnknownOption (Arg); |
390 | break; |
391 | |
392 | } |
393 | } else { |
394 | /* We don't accept anything else */ |
395 | AbEnd ("Don't know what to do with '%s'" , Arg); |
396 | } |
397 | |
398 | /* Next argument */ |
399 | ++I; |
400 | } |
401 | |
402 | /* Do we have an input file? */ |
403 | if (I == 1) { |
404 | Error ("No input file" ); |
405 | } |
406 | |
407 | /* Cleanup data */ |
408 | SetWorkBitmap (C); |
409 | FreeBitmap (B); |
410 | FreeStrBuf (D); |
411 | |
412 | /* Success */ |
413 | return EXIT_SUCCESS; |
414 | } |
415 | |