nrg: Improved support for v2
[deark.git] / modules / spectrum512.c
blob40befc283dda7d6478c84ddc855d49e611326f33
1 // This file is part of Deark.
2 // Copyright (C) 2017 Jason Summers
3 // See the file COPYING for terms of use.
5 // Spectrum 512 Uncompressed (.SPU)
6 // Spectrum 512 Compressed (.SPC)
7 // Spectrum 512 Smooshed (.SPS)
9 #include <deark-config.h>
10 #include <deark-private.h>
11 #include <deark-fmtutil.h>
12 DE_DECLARE_MODULE(de_module_spectrum512u);
13 DE_DECLARE_MODULE(de_module_spectrum512c);
14 DE_DECLARE_MODULE(de_module_spectrum512s);
16 // **************************************************************************
18 static void do_spu_internal(deark *c, dbuf *inf, int is_enhanced)
20 struct atari_img_decode_data *adata = NULL;
21 de_finfo *fi = NULL;
22 static const i64 num_colors = 199*48;
24 adata = de_malloc(c, sizeof(struct atari_img_decode_data));
25 adata->is_spectrum512 = 1;
26 adata->pal = de_mallocarray(c, num_colors, sizeof(u32));
27 adata->bpp = 4;
28 adata->w = 320;
29 adata->h = 199;
30 adata->ncolors = num_colors;
32 fmtutil_read_atari_palette(c, inf, 32000, adata->pal, num_colors, num_colors,
33 is_enhanced?DE_FLAG_ATARI_15BIT_PAL:0);
35 adata->unc_pixels = dbuf_open_input_subfile(inf, 160, inf->len-160);
36 adata->img = de_bitmap_create(c, adata->w, adata->h, 3);
37 fi = de_finfo_create(c);
38 fmtutil_atari_set_standard_density(c, adata, fi);
39 fmtutil_atari_decode_image(c, adata);
40 de_bitmap_write_to_file_finfo(adata->img, fi, 0);
42 if(adata) {
43 de_bitmap_destroy(adata->img);
44 de_free(c, adata->pal);
45 dbuf_close(adata->unc_pixels);
46 de_free(c, adata);
48 de_finfo_destroy(c, fi);
51 static void de_run_spectrum512u(deark *c, de_module_params *mparams)
53 int is_enhanced = 0;
55 if(!dbuf_memcmp(c->infile, 0, (const void*)"5BIT", 4)) {
56 is_enhanced = 1;
57 de_declare_fmt(c, "Spectrum 512 Uncompressed Enhanced");
59 else {
60 de_declare_fmt(c, "Spectrum 512 Uncompressed");
63 do_spu_internal(c, c->infile, is_enhanced);
66 static int de_identify_spectrum512u(deark *c)
68 if(c->infile->len!=51104 && c->infile->len!=51200)
69 return 0;
71 if(de_input_file_has_ext(c, "spu")) {
72 return (c->infile->len==51104) ? 90 : 10;
74 return 0;
77 static void de_help_spectrum512u(deark *c)
79 fmtutil_atari_help_palbits(c);
82 void de_module_spectrum512u(deark *c, struct deark_module_info *mi)
84 mi->id = "spectrum512u";
85 mi->desc = "Spectrum 512 Uncompressed";
86 mi->run_fn = de_run_spectrum512u;
87 mi->identify_fn = de_identify_spectrum512u;
88 mi->help_fn = de_help_spectrum512u;
91 // **************************************************************************
93 // This is almost PackBits, but not quite.
94 static void spc_uncompress_pixels(dbuf *f, i64 pos1, i64 len,
95 dbuf *unc_pixels)
97 i64 pos;
98 u8 b, b2;
99 i64 count;
100 i64 endpos;
102 pos = pos1;
103 endpos = pos1+len;
105 while(1) {
106 if(unc_pixels->has_len_limit && unc_pixels->len>=unc_pixels->len_limit) {
107 break; // Decompressed the requested amount of dst data.
110 if(pos>=endpos) {
111 break; // Reached the end of source data
113 b = dbuf_getbyte(f, pos++);
115 if(b>=128) { // A compressed run
116 count = 258 - (i64)b;
117 b2 = dbuf_getbyte(f, pos++);
118 dbuf_write_run(unc_pixels, b2, count);
120 else { // An uncompressed run
121 count = 1 + (i64)b;
122 dbuf_copy(f, pos, count, unc_pixels);
123 pos += count;
128 static void sps_uncompress_pixels(dbuf *f, i64 pos1, i64 len,
129 dbuf *unc_pixels)
131 i64 pos;
132 u8 b, b2;
133 i64 count;
134 i64 endpos;
136 pos = pos1;
137 endpos = pos1+len;
139 while(1) {
140 if(unc_pixels->has_len_limit && unc_pixels->len>=unc_pixels->len_limit) {
141 break; // Decompressed the requested amount of dst data.
144 if(pos>=endpos) {
145 break; // Reached the end of source data
147 b = dbuf_getbyte(f, pos++);
149 if(b<=127) { // A compressed run
150 count = (i64)b +3;
151 b2 = dbuf_getbyte(f, pos++);
152 dbuf_write_run(unc_pixels, b2, count);
154 else { // An uncompressed run
155 count = (i64)b - 127;
156 dbuf_copy(f, pos, count, unc_pixels);
157 pos += count;
163 // After SPC decompression, we have a 320x199 image, but with the
164 // bytes in an inconvenient order, different from SPU format.
165 // This converts an SPC after-decompression byte offset (from 0 to 31839)
166 // to the corresponding SPU file offset (from 160 to 31999).
167 static i64 reorderfn_spc(i64 a)
169 i64 b;
171 if(a%2)
172 b = 160 + (a-1)*4 + 1; // if odd
173 else
174 b = 160 + a*4; // if even
176 while(b>=32000) {
177 b -= (32000-(160+2));
179 return b;
182 // For SPS type 0. See reorderfn_spc() comments for details.
183 static i64 reorderfn_sps0(i64 a)
185 i64 b;
186 b = 160 + (a%199)*160 + ((a%7960)/398)*8 + (a/7960)*2 + (a%398)/199;
187 return b;
190 typedef i64 (*reorder_fn)(i64 a);
192 static void reorder_img_bytes(deark *c, dbuf *src, dbuf *dst, reorder_fn rfn)
194 i64 i;
195 u8 b;
197 for(i=0; i<src->len; i++) {
198 b = dbuf_getbyte(src, i);
199 dbuf_writebyte_at(dst, rfn(i), b);
203 // Read from c->infile at offset pos1, append to uncmpr_pal
204 static void spc_uncompress_pal(deark *c, i64 pos1, dbuf *uncmpr_pal)
206 static const i64 num_pals = 199*3;
207 i64 pos = pos1;
208 i64 i, k;
209 unsigned int code;
211 for(i=0; i<num_pals; i++) {
212 code = (unsigned int)de_getu16be(pos);
213 pos += 2;
214 for(k=0; k<16; k++) {
215 // Bit 15 is ignored. The corresponding pal entry will always be black.
216 if(k<=14 && (code&0x1)) {
217 dbuf_copy(c->infile, pos, 2, uncmpr_pal);
218 pos += 2;
220 else {
221 dbuf_write_zeroes(uncmpr_pal, 2);
223 code >>= 1;
228 // Read from c->infile at offset pos1, append to uncmpr_pal
229 static void sps_uncompress_pal(deark *c, i64 pos1, dbuf *uncmpr_pal)
231 static const i64 num_pals = 199*3;
232 i64 i;
233 unsigned int k;
234 unsigned int code;
235 struct de_bitreader bitrd;
237 de_zeromem(&bitrd, sizeof(struct de_bitreader));
238 bitrd.f = c->infile;
239 bitrd.curpos = pos1;
240 bitrd.endpos = c->infile->len;
242 for(i=0; i<num_pals; i++) {
243 code = (UI)de_bitreader_getbits(&bitrd, 14);
245 for(k=0; k<16; k++) {
246 // Palette entries 0 and 15 are always black
247 if(k>=1 && k<=14 && (code&(1<<(14-k)))) {
248 unsigned int cr, cg, cb;
249 unsigned int palcode;
251 cr = (UI)de_bitreader_getbits(&bitrd, 3);
252 cg = (UI)de_bitreader_getbits(&bitrd, 3);
253 cb = (UI)de_bitreader_getbits(&bitrd, 3);
254 palcode = (cr<<8)|(cg<<4)|cb;
255 dbuf_writeu16be(uncmpr_pal, palcode);
257 else {
258 dbuf_write_zeroes(uncmpr_pal, 2);
264 static void do_run_spectrum512c_s_internal(deark *c, de_module_params *mparams, int is_sps)
266 i64 pixels_cmpr_len;
267 i64 pal_cmpr_len;
268 i64 pal_pos;
269 i64 pos;
270 dbuf *unc_pixels_planar = NULL;
271 dbuf *spufile = NULL;
272 int to_spu = 0;
273 unsigned int sps_format_code = 0;
275 if(de_get_ext_option(c, "spectrum512:tospu")) {
276 to_spu = 1;
279 pos = 4;
280 pixels_cmpr_len = de_getu32be(pos);
281 de_dbg(c, "pixels compressed len: %d", (int)pixels_cmpr_len);
282 pos += 4;
283 pal_cmpr_len = de_getu32be(pos);
284 de_dbg(c, "palette compressed len: %d", (int)pal_cmpr_len);
285 pos += 4;
287 pal_pos = pos + pixels_cmpr_len;
289 if(pal_pos + pal_cmpr_len > c->infile->len) {
290 de_err(c, "Invalid or truncated file");
291 goto done;
294 if(is_sps) {
295 sps_format_code = de_getbyte(pal_pos + pal_cmpr_len-1);
296 sps_format_code &= 0x1;
297 de_dbg(c, "format code: %u", sps_format_code);
300 de_dbg(c, "pixels at %d", (int)pos);
301 // Decompress the pixel data into an in-memory buffer.
302 unc_pixels_planar = dbuf_create_membuf(c, 32000, 1);
303 if(is_sps) {
304 sps_uncompress_pixels(c->infile, pos, pixels_cmpr_len, unc_pixels_planar);
306 else {
307 spc_uncompress_pixels(c->infile, pos, pixels_cmpr_len, unc_pixels_planar);
309 //pos += pixels_cmpr_len;
311 // We'll construct an in-memory SPU file, then (usually) use our
312 // SPU module's decoder to process it.
313 spufile = dbuf_create_membuf(c, 51104, 0x1);
315 // Rearrange the bytes in the image data, as we write them to our
316 // in-memory image of an SPU file.
317 // (This could be done more efficiently during decompression,
318 // but the code would be messier.)
319 if(is_sps && sps_format_code==0) {
320 reorder_img_bytes(c, unc_pixels_planar, spufile, reorderfn_sps0);
322 else {
323 reorder_img_bytes(c, unc_pixels_planar, spufile, reorderfn_spc);
326 // Make sure we write the uncompressed palette at exactly offset 32000.
327 dbuf_truncate(spufile, 32000);
329 pos = pal_pos;
330 de_dbg(c, "palette at %d", (int)pos);
331 if(is_sps) {
332 sps_uncompress_pal(c, pos, spufile);
334 else {
335 spc_uncompress_pal(c, pos, spufile);
338 if(to_spu) {
339 // Instead of decoding the image, write it in .SPU format
340 dbuf *outf = NULL;
341 outf = dbuf_create_output_file(c, "spu", NULL, 0);
342 dbuf_copy(spufile, 0, spufile->len, outf);
343 dbuf_close(outf);
345 else {
346 do_spu_internal(c, spufile, 0);
349 done:
350 dbuf_close(unc_pixels_planar);
351 dbuf_close(spufile);
354 static void de_run_spectrum512c(deark *c, de_module_params *mparams)
356 do_run_spectrum512c_s_internal(c, mparams, 0);
359 static int de_identify_spectrum512c(deark *c)
361 if(dbuf_memcmp(c->infile, 0, "\x53\x50\x00\x00", 4))
362 return 0;
364 if(de_input_file_has_ext(c, "spc")) {
365 return 100;
368 if(de_input_file_has_ext(c, "sps")) {
369 return 0;
372 return 10;
375 static void de_help_spectrum512cs(deark *c)
377 de_msg(c, "-opt spectrum512:tospu : Output to an .spu file");
378 fmtutil_atari_help_palbits(c);
381 void de_module_spectrum512c(deark *c, struct deark_module_info *mi)
383 mi->id = "spectrum512c";
384 mi->desc = "Spectrum 512 Compressed";
385 mi->run_fn = de_run_spectrum512c;
386 mi->identify_fn = de_identify_spectrum512c;
387 mi->help_fn = de_help_spectrum512cs;
390 static void de_run_spectrum512s(deark *c, de_module_params *mparams)
392 do_run_spectrum512c_s_internal(c, mparams, 1);
395 static int de_identify_spectrum512s(deark *c)
397 if(dbuf_memcmp(c->infile, 0, "\x53\x50\x00\x00", 4))
398 return 0;
400 if(de_input_file_has_ext(c, "sps")) {
401 return 100;
404 // No reason to return anything but 0. The file will be identified as SPC.
405 return 0;
408 void de_module_spectrum512s(deark *c, struct deark_module_info *mi)
410 mi->id = "spectrum512s";
411 mi->desc = "Spectrum 512 Smooshed";
412 mi->run_fn = de_run_spectrum512s;
413 mi->identify_fn = de_identify_spectrum512s;
414 mi->help_fn = de_help_spectrum512cs;