1 /*
2 * (c) Copyright 2015 by Einar Saukas. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * The name of its author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 /*
27 * Modified for ZX Next by z88dk.org. Program performs identically.
28 * zcc +zxn -v -startup=30 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 --opt-code-size @zproject.lst -o dzx7 -pragma-include:zpragma.inc -subtype=dotn -Cz"--clean --exclude-sections IGNORE" -create-app
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <arch/zxn.h>
36 #include <arch/zxn/esxdos.h>
37
38 #include "user_interaction.h"
39
40 #define BUFFER_SIZE 16384 // must be > MAX_OFFSET
41
42 unsigned char ifp = 0xff;
43 unsigned char ofp = 0xff;
44
45 unsigned char *input_name;
46 unsigned int input_name_sz;
47
48 unsigned char output_name[ESX_FILENAME_LFN_MAX + 1];
49
50 extern unsigned char input_data[BUFFER_SIZE];
51 extern unsigned char output_data[BUFFER_SIZE];
52
53 unsigned int input_index;
54 unsigned int output_index;
55
56 unsigned long input_size;
57 unsigned long output_size;
58
59 struct esx_stat es;
60
61 struct esx_cat catalog;
62 struct esx_lfn lfn;
63
64 unsigned int partial_counter;
65
66 unsigned char bit_mask;
67 unsigned char bit_value;
68
69 // custom esxdos error report
70
71 #define ebuf input_data
72
error(char * fmt,...)73 int error(char *fmt, ...)
74 {
75 unsigned char *p;
76
77 va_list v;
78 va_start(v, fmt);
79
80 #ifdef __SCCZ80
81 vsnprintf(ebuf, sizeof(ebuf), va_ptr(v,char *), v);
82 #else
83 vsnprintf(ebuf, sizeof(ebuf), fmt, v);
84 #endif
85
86 for (p = ebuf; p = strchr(p, '\n'); )
87 *p = '\r';
88
89 ebuf[strlen(ebuf) - 1] += 0x80;
90 return (int)ebuf;
91 }
92
93 // dzx7 functions
94
read_byte(void)95 unsigned char read_byte(void)
96 {
97 if (input_index == partial_counter)
98 {
99 input_index = 0;
100 partial_counter = esx_f_read(ifp, input_data, BUFFER_SIZE);
101 input_size += partial_counter;
102
103 if (partial_counter == 0)
104 exit(error(input_size ? "Truncated input file %s" : "Empty input file %s", input_name));
105 }
106
107 return input_data[input_index++];
108 }
109
read_bit(void)110 unsigned char read_bit(void)
111 {
112 bit_mask >>= 1;
113
114 if (bit_mask == 0)
115 {
116 bit_mask = 0x80;
117 bit_value = read_byte();
118 }
119
120 return (bit_value & bit_mask) ? 1 : 0;
121 }
122
read_elias_gamma(void)123 unsigned int read_elias_gamma(void)
124 {
125 unsigned int value;
126 unsigned char i;
127
128 for (i = 0; !read_bit(); ++i) ;
129
130 if (i > 15)
131 return -1;
132
133 for (value = 1; i; --i)
134 value = (value << 1) | read_bit();
135
136 return value;
137 }
138
read_offset(void)139 unsigned int read_offset(void)
140 {
141 unsigned int value;
142 unsigned char i;
143
144 value = read_byte();
145
146 if (value < 128)
147 return value;
148
149 i = read_bit();
150 i = (i << 1) | read_bit();
151 i = (i << 1) | read_bit();
152 i = (i << 1) | read_bit();
153
154 return (value & 0x7f) | ((unsigned int)i << 7) + 0x80;
155 }
156
save_output(void)157 void save_output(void)
158 {
159 if (output_index)
160 {
161 if (esx_f_write(ofp, output_data, output_index) != output_index)
162 exit(error("Can't write output file %s", output_name));
163
164 output_size += output_index;
165 output_index = 0;
166
167 // print percentage progress
168
169 printf("%03u%%" "\x08\x08\x08\x08", (unsigned int)(input_size * 100 / es.size));
170 user_interaction();
171 }
172 }
173
write_byte(unsigned char value)174 void write_byte(unsigned char value)
175 {
176 output_data[output_index++] = value;
177
178 if (output_index == BUFFER_SIZE)
179 save_output();
180 }
181
write_bytes(unsigned int offset,unsigned int length)182 void write_bytes(unsigned int offset, unsigned int length)
183 {
184 int i;
185
186 if (offset > output_size+output_index)
187 exit(error("Invalid data in input file %s", input_name));
188
189 while (length-- > 0)
190 {
191 i = output_index - offset;
192 write_byte(output_data[(i >= 0) ? i : BUFFER_SIZE+i]);
193 }
194 }
195
decompress(void)196 void decompress(void)
197 {
198 unsigned int length;
199
200 input_size = 0;
201 input_index = 0;
202 partial_counter = 0;
203 output_index = 0;
204 output_size = 0;
205 bit_mask = 0;
206
207 write_byte(read_byte());
208 while (1)
209 {
210 if (!read_bit())
211 write_byte(read_byte());
212 else
213 {
214 length = read_elias_gamma() + 1;
215
216 if (length == 0)
217 {
218 save_output();
219
220 if (input_index != partial_counter)
221 exit(error("Input file %s too long", input_name));
222
223 return;
224 }
225
226 write_bytes(read_offset() + 1, length);
227 }
228
229 // allow user to interrupt
230
231 user_interaction();
232 }
233 }
234
235 // cleanup on exit
236
237 static unsigned char old_cpu_speed;
238
cleanup(void)239 void cleanup(void)
240 {
241 if (ifp != 0xff) esx_f_close(ifp);
242 if (ofp != 0xff) esx_f_close(ofp);
243
244 puts(" ");
245
246 ZXN_NEXTREGA(REG_TURBO_MODE, old_cpu_speed);
247 }
248
249 // program start
250
main(int argc,char ** argv)251 int main(int argc, char **argv)
252 {
253 static unsigned char forced_mode;
254 unsigned char i;
255
256 // initialization
257
258 old_cpu_speed = ZXN_READ_REG(REG_TURBO_MODE);
259 ZXN_NEXTREG(REG_TURBO_MODE, RTM_14MHZ);
260
261 atexit(cleanup);
262
263 puts("\nDZX7: LZ77/LZSS decompression\n(C) 2015 Einar Saukas\n\nv1.1 zx-next 128k z88dk.org\n");
264
265 // process optional parameters
266
267 for (i = 1; (i < (unsigned char)argc) && (*argv[i] == '-'); ++i)
268 {
269 if (stricmp(argv[i], "-f") == 0)
270 forced_mode = 1;
271 else
272 exit(error("Invalid parameter %s", argv[i]));
273 }
274
275 // determine output filename
276
277 if (argc == i + 1)
278 {
279 // operate on the lfn name to ensure file extension can be inferred
280
281 strcpy(output_name, argv[i]);
282
283 catalog.filter = ESX_CAT_FILTER_SYSTEM | ESX_CAT_FILTER_LFN;
284 catalog.filename = p3dos_cstr_to_pstr(output_name);
285 catalog.cat_sz = 2;
286
287 lfn.cat = &catalog;
288
289 if (esx_dos_catalog(&catalog) == 1)
290 {
291 esx_ide_get_lfn(&lfn, &catalog.cat[1]);
292
293 input_name = lfn.filename;
294 input_name_sz = strlen(lfn.filename);
295 }
296 else
297 {
298 input_name = argv[i];
299 input_name_sz = strlen(input_name);
300 }
301
302 // generate output filename
303
304 if ((input_name_sz > 4) && (stricmp(input_name + input_name_sz - 4, ".ZX7") == 0))
305 snprintf(output_name, sizeof(output_name), "%.*s", input_name_sz - 4, input_name);
306 else
307 exit(error("Can't infer output filename"));
308 }
309 else if (argc == i + 2)
310 {
311 input_name = argv[i];
312 snprintf(output_name, sizeof(output_name), "%s", argv[i + 1]);
313 }
314 else
315 {
316 printf(".%s [-f] inname.zx7 [outname]\n"
317 "-f Overwrite output file\n", argv[0]);
318 exit(0);
319 }
320
321 if (stricmp(output_name, input_name) == 0)
322 exit(error("In and out files are the same"));
323
324 // open input file
325
326 if ((ifp = esx_f_open(input_name, ESX_MODE_OPEN_EXIST | ESX_MODE_R)) == 0xff)
327 exit(error("Can't open input file %s", input_name));
328
329 if (esx_f_fstat(ifp, &es))
330 exit(error("Can't stat input file %s", input_name));
331
332 // check output file
333
334 if ((ofp = esx_f_open(output_name, forced_mode ? (ESX_MODE_OPEN_CREAT_TRUNC | ESX_MODE_W) : (ESX_MODE_OPEN_CREAT_NOEXIST | ESX_MODE_W))) == 0xff)
335 exit(error("Can't create output file %s", output_name));
336
337 // generate output file
338
339 decompress();
340
341 // done!
342
343 printf("File decompressed from %lu to %lu bytes!", input_size, output_size);
344 return 0;
345 }
346