summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/fileop.c606
-rw-r--r--apps/fileop.h70
-rw-r--r--apps/lang/deutsch.lang118
-rw-r--r--apps/misc.c18
-rw-r--r--apps/misc.h4
-rw-r--r--apps/onplay.c784
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS4
-rw-r--r--apps/plugins/brickmania.c139
-rw-r--r--apps/plugins/fft/fft.c5
-rw-r--r--apps/plugins/xrick/3rd_party/miniz/miniz.c4933
-rw-r--r--apps/plugins/xrick/README.md88
-rw-r--r--apps/plugins/xrick/README.rockbox16
-rw-r--r--apps/plugins/xrick/SOURCES40
-rw-r--r--apps/plugins/xrick/config.h58
-rw-r--r--apps/plugins/xrick/control.c24
-rw-r--r--apps/plugins/xrick/control.h41
-rw-r--r--apps/plugins/xrick/data/img.c23
-rw-r--r--apps/plugins/xrick/data/img.h39
-rw-r--r--apps/plugins/xrick/data/pics.c29
-rw-r--r--apps/plugins/xrick/data/pics.h41
-rw-r--r--apps/plugins/xrick/data/sounds.c44
-rw-r--r--apps/plugins/xrick/data/sounds.h87
-rw-r--r--apps/plugins/xrick/data/sprites.c22
-rw-r--r--apps/plugins/xrick/data/sprites.h70
-rw-r--r--apps/plugins/xrick/data/tiles.c22
-rw-r--r--apps/plugins/xrick/data/tiles.h70
-rw-r--r--apps/plugins/xrick/debug.h84
-rw-r--r--apps/plugins/xrick/devtools.c247
-rw-r--r--apps/plugins/xrick/devtools.h25
-rw-r--r--apps/plugins/xrick/draw.c702
-rw-r--r--apps/plugins/xrick/draw.h65
-rw-r--r--apps/plugins/xrick/e_bomb.c158
-rw-r--r--apps/plugins/xrick/e_bomb.h36
-rw-r--r--apps/plugins/xrick/e_bonus.c60
-rw-r--r--apps/plugins/xrick/e_bonus.h25
-rw-r--r--apps/plugins/xrick/e_box.c109
-rw-r--r--apps/plugins/xrick/e_box.h25
-rw-r--r--apps/plugins/xrick/e_bullet.c84
-rw-r--r--apps/plugins/xrick/e_bullet.h32
-rw-r--r--apps/plugins/xrick/e_rick.c606
-rw-r--r--apps/plugins/xrick/e_rick.h50
-rw-r--r--apps/plugins/xrick/e_sbonus.c88
-rw-r--r--apps/plugins/xrick/e_sbonus.h30
-rw-r--r--apps/plugins/xrick/e_them.c738
-rw-r--r--apps/plugins/xrick/e_them.h31
-rw-r--r--apps/plugins/xrick/ents.c540
-rw-r--r--apps/plugins/xrick/ents.h118
-rw-r--r--apps/plugins/xrick/game.c722
-rw-r--r--apps/plugins/xrick/game.h73
-rw-r--r--apps/plugins/xrick/maps.c253
-rw-r--r--apps/plugins/xrick/maps.h159
-rw-r--r--apps/plugins/xrick/rects.c57
-rw-r--r--apps/plugins/xrick/rects.h32
-rw-r--r--apps/plugins/xrick/res_magic.c18
-rw-r--r--apps/plugins/xrick/resources.c1297
-rw-r--r--apps/plugins/xrick/resources.h165
-rw-r--r--apps/plugins/xrick/scr_gameover.c92
-rw-r--r--apps/plugins/xrick/scr_getname.c290
-rw-r--r--apps/plugins/xrick/scr_imain.c170
-rw-r--r--apps/plugins/xrick/scr_imap.c293
-rw-r--r--apps/plugins/xrick/scr_pause.c50
-rw-r--r--apps/plugins/xrick/scr_xrick.c101
-rw-r--r--apps/plugins/xrick/screens.h74
-rw-r--r--apps/plugins/xrick/scroller.c165
-rw-r--r--apps/plugins/xrick/scroller.h31
-rw-r--r--apps/plugins/xrick/system/basic_funcs.c33
-rw-r--r--apps/plugins/xrick/system/basic_funcs.h141
-rw-r--r--apps/plugins/xrick/system/basic_types.h48
-rw-r--r--apps/plugins/xrick/system/main_rockbox.c43
-rw-r--r--apps/plugins/xrick/system/miniz_config.h38
-rw-r--r--apps/plugins/xrick/system/rockboxcodes.h110
-rw-r--r--apps/plugins/xrick/system/sysarg_rockbox.c49
-rw-r--r--apps/plugins/xrick/system/sysevt_rockbox.c156
-rw-r--r--apps/plugins/xrick/system/sysfile_rockbox.c122
-rw-r--r--apps/plugins/xrick/system/sysmem_rockbox.c156
-rw-r--r--apps/plugins/xrick/system/sysmenu_rockbox.c200
-rw-r--r--apps/plugins/xrick/system/sysmenu_rockbox.h32
-rw-r--r--apps/plugins/xrick/system/syssnd_rockbox.c483
-rw-r--r--apps/plugins/xrick/system/syssnd_rockbox.h48
-rw-r--r--apps/plugins/xrick/system/system.h178
-rw-r--r--apps/plugins/xrick/system/system_rockbox.c262
-rw-r--r--apps/plugins/xrick/system/sysvid_rockbox.c409
-rw-r--r--apps/plugins/xrick/util.c230
-rw-r--r--apps/plugins/xrick/util.h29
-rw-r--r--apps/plugins/xrick/xrick.make31
-rw-r--r--bootloader/rocker_linux.c60
-rw-r--r--docs/CREDITS3
-rw-r--r--firmware/drivers/ata.c10
-rw-r--r--firmware/target/hosted/lcd-linuxfb.c24
-rw-r--r--firmware/target/hosted/samsungypr/lcd-ypr.c14
-rwxr-xr-xmanual/plugins/images/ss-xrick-220x176x16.pngbin0 -> 7938 bytes
-rwxr-xr-xmanual/plugins/images/ss-xrick-240x320x16.pngbin0 -> 9160 bytes
-rwxr-xr-xmanual/plugins/images/ss-xrick-320x240x16.pngbin0 -> 9346 bytes
-rw-r--r--manual/plugins/main.tex5
-rwxr-xr-xmanual/plugins/xrick.tex102
-rw-r--r--tools/builds.pm36
-rwxr-xr-xtools/list_targets.pl17
-rwxr-xr-xtools/release/sims.pl28
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.cpp1
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.h1
-rw-r--r--utils/rbutilqt/gui/selectiveinstallwidget.cpp1
-rw-r--r--utils/rbutilqt/rbutil.ini1
104 files changed, 17633 insertions, 760 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 444951bbcb..2c002b51cd 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -10,6 +10,7 @@ bookmark.c
10core_keymap.c 10core_keymap.c
11debug_menu.c 11debug_menu.c
12filetypes.c 12filetypes.c
13fileop.c
13language.c 14language.c
14main.c 15main.c
15menu.c 16menu.c
diff --git a/apps/fileop.c b/apps/fileop.c
new file mode 100644
index 0000000000..0d2dc774b9
--- /dev/null
+++ b/apps/fileop.c
@@ -0,0 +1,606 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Björn Stenberg
11 * Copyright (C) 2024 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include <stdbool.h>
23#include <errno.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include "string-extra.h"
28#include "debug.h"
29
30#include "misc.h"
31#include "plugin.h"
32#include "dir.h"
33#include "tree.h"
34#include "fileop.h"
35#include "pathfuncs.h"
36
37#include "settings.h"
38#include "lang.h"
39#include "yesno.h"
40#include "splash.h"
41#include "keyboard.h"
42
43/* Used for directory move, copy and delete */
44struct file_op_params
45{
46 char path[MAX_PATH]; /* Buffer for full path */
47 bool is_dir;
48 int objects; /* how many files and subdirectories*/
49 int processed;
50 size_t append; /* Append position in 'path' for stack push */
51};
52
53static bool poll_cancel_action(const char *path, int operation, int current, int total)
54{
55 const char *op_str = "";
56
57 clear_screen_buffer(false);
58
59 if (operation == FOC_COPY)
60 op_str = str(LANG_COPYING);
61 else if (operation == FOC_MOVE)
62 op_str = str(LANG_MOVING);
63 else if (operation == FOC_COUNT)
64 op_str = str(LANG_SCANNING_DISK);
65 else if (operation == FOC_DELETE)
66 op_str = str(LANG_DELETING);
67
68 path_basename(path, &path);
69
70 if (total <= 0)
71 splashf(0, "%s (%d) %s", op_str, current, path);
72 else
73 splash_progress(current, total, "%s %s", op_str, path);
74
75 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
76}
77
78static struct file_op_params* init_file_op(struct file_op_params *param,
79 const char *basename,
80 const char *selected_file)
81{
82 /* Assumes: basename will never be NULL */
83 if (selected_file == NULL)
84 {
85 param->append = strlcpy(param->path, basename, sizeof (param->path));
86 }
87 else
88 param->append = path_append(param->path, basename,
89 selected_file, sizeof (param->path));
90 param->is_dir = dir_exists(param->path);
91 param->objects = 0; /* how many files and subdirectories*/
92 param->processed = 0;
93
94 return param;
95}
96
97/* counts file objects, deletes file objects */
98static int directory_fileop(struct file_op_params *param, enum file_op_current fileop)
99{
100 errno = 0;
101 DIR *dir = opendir(param->path);
102 if (!dir) {
103 if (errno == EMFILE) {
104 return FORC_TOO_MANY_SUBDIRS;
105 }
106 return FORC_PATH_NOT_EXIST; /* open error */
107 }
108 int rc = FORC_SUCCESS;
109 size_t append = param->append;
110
111 /* walk through the directory content */
112 while (rc == FORC_SUCCESS) {
113 errno = 0; /* distinguish failure from eod */
114 struct dirent *entry = readdir(dir);
115 if (!entry) {
116 if (errno) {
117 rc = FORC_PATH_NOT_EXIST;
118 }
119 break;
120 }
121
122 struct dirinfo info = dir_get_info(dir, entry);
123 if ((info.attribute & ATTR_DIRECTORY) &&
124 is_dotdir_name(entry->d_name)) {
125 continue; /* skip these */
126 }
127
128 /* append name to current directory */
129 param->append = append + path_append(&param->path[append],
130 PA_SEP_HARD, entry->d_name,
131 sizeof (param->path) - append);
132
133 if (fileop == FOC_DELETE)
134 {
135 param->processed++;
136 /* at this point we've already counted and never had a path too long
137 in the other branch so we 'should not' encounter one here either */
138
139 if (param->processed > param->objects)
140 {
141 rc = FORC_UNKNOWN_FAILURE;
142 break;
143 }
144
145 if (info.attribute & ATTR_DIRECTORY) {
146 /* remove a subdirectory */
147 rc = directory_fileop(param, fileop);
148 } else {
149 /* remove a file */
150 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects))
151 {
152 rc = FORC_CANCELLED;
153 break;
154 }
155 rc = remove(param->path);
156 }
157 }
158 else /* count objects */
159 {
160 param->objects++;
161
162 if (param->append >= sizeof (param->path)) {
163 rc = FORC_PATH_TOO_LONG;
164 break; /* no space left in buffer */
165 }
166
167 if (info.attribute & ATTR_DIRECTORY) {
168 /* remove a subdirectory */
169 rc = directory_fileop(param, FOC_COUNT);
170 } else {
171 if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0))
172 {
173 rc = FORC_CANCELLED;
174 break;
175 }
176 }
177 }
178 param->append = append; /* other functions may use param, reset append */
179 /* Remove basename we added above */
180 param->path[append] = '\0';
181 }
182
183 closedir(dir);
184
185 if (fileop == FOC_DELETE && rc == FORC_SUCCESS) {
186 /* remove the now empty directory */
187 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects))
188 {
189 rc = FORC_CANCELLED;
190 } else {
191 rc = rmdir(param->path);
192 }
193 }
194
195 return rc;
196}
197
198static int check_count_fileobjects(struct file_op_params *param)
199{
200 int rc = directory_fileop(param, FOC_COUNT);
201 DEBUGF("%s res:(%d) objects %d \n", __func__, rc, param->objects);
202 return rc;
203}
204
205static bool check_new_name(const char *basename)
206{
207 /* at least prevent escapes out of the base directory from keyboard-
208 entered filenames; the file code should reject other invalidities */
209 return *basename != '\0' && !strchr(basename, PATH_SEPCH) &&
210 !is_dotdir_name(basename);
211}
212
213int create_dir(void)
214{
215 int rc = FORC_UNKNOWN_FAILURE;
216 char dirname[MAX_PATH];
217 size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD,
218 sizeof (dirname));
219 char *basename = dirname + pathlen;
220
221 if (pathlen >= sizeof (dirname)) {
222 /* Too long */
223 } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) {
224 rc = FORC_CANCELLED;
225 } else if (check_new_name(basename)) {
226 rc = mkdir(dirname);
227 }
228
229 return rc;
230}
231
232/************************************************************************************/
233/* share code for file and directory deletion, saves space */
234static int delete_file_dir(struct file_op_params *param)
235{
236 /* Note: delete_file_dir() will happily delete whatever
237 * path is passed (after confirmation) */
238 if (confirm_delete_yesno(param->path) != YESNO_YES) {
239 return FORC_CANCELLED;
240 }
241
242 clear_screen_buffer(true);
243 poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects);
244
245 int rc = FORC_UNKNOWN_FAILURE;
246
247 if (param->is_dir) { /* if directory */
248 cpu_boost(true);
249 rc = directory_fileop(param, FOC_DELETE);
250 cpu_boost(false);
251 } else {
252 rc = remove(param->path);
253 }
254
255 return rc;
256}
257
258int delete_fileobject(const char *selected_file)
259{
260 struct file_op_params param;
261 if (init_file_op(&param, selected_file, NULL)->is_dir == true)
262 {
263 int rc = check_count_fileobjects(&param);
264 DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path);
265 if (rc != FORC_SUCCESS)
266 return rc;
267 }
268
269 return delete_file_dir(&param);
270}
271
272int rename_file(const char *selected_file)
273{
274 int rc = FORC_UNKNOWN_FAILURE;
275 char newname[MAX_PATH];
276 const char *oldbase, *selection = selected_file;
277
278 path_basename(selection, &oldbase);
279 size_t pathlen = oldbase - selection;
280 char *newbase = newname + pathlen;
281
282 if (strmemccpy(newname, selection, sizeof (newname)) == NULL) {
283 /* Too long */
284 } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) {
285 rc = FORC_CANCELLED;
286 } else if (!strcmp(oldbase, newbase)) {
287 rc = FORC_NOOP; /* No change at all */
288 } else if (check_new_name(newbase)) {
289 switch (relate(selection, newname))
290 {
291 case RELATE_DIFFERENT:
292 if (file_exists(newname)) {
293 break; /* don't overwrite */
294 }
295 /* Fall-through */
296 case RELATE_SAME:
297 rc = rename(selection, newname);
298 break;
299 case RELATE_PREFIX:
300 default:
301 break;
302 }
303 }
304
305 return rc;
306}
307
308static int move_by_rename(const char *src_path,
309 const char *dst_path,
310 unsigned int *pflags)
311{
312 unsigned int flags = *pflags;
313 int rc = FORC_UNKNOWN_FAILURE;
314 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
315 if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) {
316 /* Just try to move the directory / file */
317 if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) {
318 rc = FORC_CANCELLED;
319 } else {
320 rc = rename(src_path, dst_path);
321 }
322
323 if (rc < 0) {
324 int errnum = errno;
325 if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
326 /* Directory is not empty thus rename() will not do a quick
327 overwrite */
328 break;
329 }
330 #ifdef HAVE_MULTIVOLUME
331 else if (errnum == EXDEV) {
332 /* Failed because cross volume rename doesn't work; force
333 a move instead */
334 *pflags |= PASTE_EXDEV;
335 break;
336 }
337 #endif /* HAVE_MULTIVOLUME */
338 }
339 }
340
341 break;
342 }
343 return rc;
344}
345
346/* Paste a file */
347static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags)
348{
349 /* Try renaming first */
350 int rc = move_by_rename(src_path, dst_path, &flags);
351 if (rc == FORC_SUCCESS)
352 return rc;
353
354 /* See if we can get the plugin buffer for the file copy buffer */
355 size_t buffersize;
356 char *buffer = (char *) plugin_get_buffer(&buffersize);
357 if (buffer == NULL || buffersize < 512) {
358 /* Not large enough, try for a disk sector worth of stack
359 instead */
360 buffersize = 512;
361 buffer = (char *)alloca(buffersize);
362 }
363
364 if (buffer == NULL) {
365 return FORC_NO_BUFFER_AVAIL;
366 }
367
368 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
369 size */
370
371 int src_fd = open(src_path, O_RDONLY);
372 if (src_fd >= 0) {
373 off_t src_sz = lseek(src_fd, 0, SEEK_END);
374 lseek(src_fd, 0, SEEK_SET);
375
376 int oflag = O_WRONLY|O_CREAT;
377
378 if (!(flags & PASTE_OVERWRITE)) {
379 oflag |= O_EXCL;
380 }
381
382 int dst_fd = open(dst_path, oflag, 0666);
383 if (dst_fd >= 0) {
384 off_t total_size = 0;
385 off_t next_cancel_test = 0; /* No excessive button polling */
386
387 rc = FORC_SUCCESS;
388
389 while (rc == FORC_SUCCESS) {
390 if (total_size >= next_cancel_test) {
391 next_cancel_test = total_size + 0x10000;
392 if (poll_cancel_action(src_path,
393 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY,
394 total_size, src_sz))
395 {
396 rc = FORC_CANCELLED;
397 break;
398 }
399 }
400
401 ssize_t bytesread = read(src_fd, buffer, buffersize);
402 if (bytesread <= 0) {
403 if (bytesread < 0) {
404 rc = FORC_READ_FAILURE;
405 }
406 /* else eof on buffer boundary; nothing to write */
407 break;
408 }
409
410 ssize_t byteswritten = write(dst_fd, buffer, bytesread);
411 if (byteswritten < bytesread) {
412 /* Some I/O error */
413 rc = FORC_WRITE_FAILURE;
414 break;
415 }
416
417 total_size += byteswritten;
418
419 if (bytesread < (ssize_t)buffersize) {
420 /* EOF with trailing bytes */
421 break;
422 }
423 }
424
425 if (rc == FORC_SUCCESS) {
426 /* If overwriting, set the correct length if original was
427 longer */
428 rc = ftruncate(dst_fd, total_size) * 10;
429 }
430
431 close(dst_fd);
432
433 if (rc != FORC_SUCCESS) {
434 /* Copy failed. Cleanup. */
435 remove(dst_path);
436 }
437 }
438
439 close(src_fd);
440 }
441
442 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) {
443 /* Remove the source file */
444 rc = remove(src_path) * 10;
445 }
446
447 return rc;
448}
449
450/* Paste a directory */
451static int copy_move_directory(struct file_op_params *src,
452 struct file_op_params *dst,
453 unsigned int flags)
454{
455 int rc = FORC_UNKNOWN_FAILURE;
456
457 DIR *srcdir = opendir(src->path);
458
459 if (srcdir) {
460 /* Make a directory to copy things to */
461 rc = mkdir(dst->path) * 10;
462 if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) {
463 /* Exists and overwrite was approved */
464 rc = FORC_SUCCESS;
465 }
466 }
467
468 size_t srcap = src->append, dstap = dst->append;
469
470 /* Walk through the directory content; this loop will exit as soon as
471 there's a problem */
472 while (rc == FORC_SUCCESS) {
473 errno = 0; /* Distinguish failure from eod */
474 struct dirent *entry = readdir(srcdir);
475 if (!entry) {
476 if (errno) {
477 rc = FORC_PATH_NOT_EXIST;
478 }
479 break;
480 }
481
482 struct dirinfo info = dir_get_info(srcdir, entry);
483 if ((info.attribute & ATTR_DIRECTORY) &&
484 is_dotdir_name(entry->d_name)) {
485 continue; /* Skip these */
486 }
487
488 /* Append names to current directories */
489 src->append = srcap +
490 path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name,
491 sizeof(src->path) - srcap);
492
493 dst->append = dstap +
494 path_append(&dst->path[dstap], PA_SEP_HARD, entry->d_name,
495 sizeof (dst->path) - dstap);
496 /* src length was already checked by check_count_fileobjects() */
497 if (dst->append >= sizeof (dst->path)) {
498 rc = FORC_PATH_TOO_LONG; /* No space left in buffer */
499 break;
500 }
501
502 src->processed++;
503 if (src->processed > src->objects)
504 {
505 rc = FORC_UNKNOWN_FAILURE;
506 break;
507 }
508
509 if (poll_cancel_action(src->path,
510 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY,
511 src->processed, src->objects))
512 {
513 rc = FORC_CANCELLED;
514 break;
515 }
516
517 DEBUGF("Copy %s to %s\n", src->path, dst->path);
518
519 if (info.attribute & ATTR_DIRECTORY) {
520 /* Copy/move a subdirectory */
521 rc = copy_move_directory(src, dst, flags); /* recursion */;
522 } else {
523 /* Copy/move a file */
524 rc = copy_move_file(src->path, dst->path, flags);
525 }
526
527 /* Remove basenames we added above */
528 src->path[srcap] = '\0';
529 dst->path[dstap] = '\0';
530 }
531
532 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) {
533 /* Remove the now empty directory */
534 rc = rmdir(src->path) * 10;
535 }
536
537 closedir(srcdir);
538 return rc;
539}
540
541int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned int flags)
542{
543 if (!src_path[0])
544 return FORC_NOOP;
545
546 int rc = FORC_UNKNOWN_FAILURE;
547
548 struct file_op_params src, dst;
549
550 /* Figure out the name of the selection */
551 const char *nameptr;
552 path_basename(src_path, &nameptr);
553
554 /* Final target is current directory plus name of selection */
555 init_file_op(&dst, dst_path, nameptr);
556
557 switch (dst.append < sizeof (dst.path) ?
558 relate(src_path, dst.path) : FORC_PATH_TOO_LONG)
559 {
560 case RELATE_SAME:
561 rc = FORC_NOOP;
562 break;
563
564 case RELATE_DIFFERENT:
565 if (file_exists(dst.path)) {
566 /* If user chooses not to overwrite, cancel */
567 if (confirm_overwrite_yesno() == YESNO_NO) {
568 rc = FORC_NOOVERWRT;
569 break;
570 }
571
572 flags |= PASTE_OVERWRITE;
573 }
574
575 /* Now figure out what we're doing */
576 cpu_boost(true);
577
578 init_file_op(&src, src_path, NULL);
579
580 if (src.is_dir) {
581 /* Copy or move a subdirectory */
582
583 if (src.append < sizeof (src.path)) {
584 /* Try renaming first */
585 rc = move_by_rename(src.path, dst.path, &flags);
586 if (rc != FORC_SUCCESS && rc != FORC_CANCELLED) {
587 if (check_count_fileobjects(&src) == FORC_SUCCESS) {
588 rc = copy_move_directory(&src, &dst, flags);
589 }
590 }
591 }
592 } else {
593 /* Copy or move a file */
594 rc = copy_move_file(src_path, dst.path, flags);
595 }
596
597 cpu_boost(false);
598 break;
599
600 case RELATE_PREFIX:
601 default: /* Some other relation / failure */
602 break;
603 }
604
605 return rc;
606}
diff --git a/apps/fileop.h b/apps/fileop.h
new file mode 100644
index 0000000000..f8237dc64f
--- /dev/null
+++ b/apps/fileop.h
@@ -0,0 +1,70 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Björn Stenberg
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef FILEOP_H
22#define FILEOP_H
23
24#include "file.h"
25
26/* result codes of various file operations */
27enum fileop_result_code
28{
29 FORC_READ_FAILURE = -7,
30 FORC_WRITE_FAILURE = -6,
31 FORC_NO_BUFFER_AVAIL = -5,
32 FORC_TOO_MANY_SUBDIRS = -4,
33 FORC_PATH_TOO_LONG = -3,
34 FORC_PATH_NOT_EXIST = -2,
35 FORC_UNKNOWN_FAILURE = -1,
36 /* Anything < 0 is failure */
37 FORC_SUCCESS = 0, /* All operations completed successfully */
38 FORC_NOOP = 1, /* Operation didn't need to do anything */
39 FORC_CANCELLED = 2, /* Operation was cancelled by user */
40 FORC_NOOVERWRT = 3,
41};
42
43enum file_op_flags
44{
45 PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */
46 PASTE_COPY = 0x01, /* Is a copy operation */
47 PASTE_OVERWRITE = 0x02, /* Overwrite destination */
48 PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */
49};
50
51enum file_op_current
52{
53 FOC_NONE = 0,
54 FOC_COUNT,
55 FOC_MOVE,
56 FOC_COPY,
57 FOC_DELETE,
58 FOC_CREATE,
59};
60
61int create_dir(void);
62
63int rename_file(const char *selected_file);
64
65int delete_fileobject(const char *selected_file);
66
67int copy_move_fileobject(const char *src_path, const char *dst_path,
68 unsigned int flags);
69
70#endif /* FILEOP_H */
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index 4a3ceda374..096a945bd9 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -4,7 +4,6 @@
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/ 6# \/ \/ \/ \/ \/
7# $Id$
8# 7#
9# This program is free software; you can redistribute it and/or 8# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License 9# modify it under the terms of the GNU General Public License
@@ -35,6 +34,7 @@
35# - Kai Posadowsky 34# - Kai Posadowsky
36# - Udo Schläpfer 35# - Udo Schläpfer
37# - Johannes Rauh 36# - Johannes Rauh
37# - Wilfried Winkler
38<phrase> 38<phrase>
39 id: LANG_SET_BOOL_YES 39 id: LANG_SET_BOOL_YES
40 desc: bool true representation 40 desc: bool true representation
@@ -12130,10 +12130,10 @@
12130 desc: playing time screen 12130 desc: playing time screen
12131 user: core 12131 user: core
12132 <source> 12132 <source>
12133 *: "Track remaining: %s" 12133 *: "Track remaining:"
12134 </source> 12134 </source>
12135 <dest> 12135 <dest>
12136 *: "Titel verbleibend: %s" 12136 *: "Titel verbleibend:"
12137 </dest> 12137 </dest>
12138 <voice> 12138 <voice>
12139 *: "Titel verbleibend" 12139 *: "Titel verbleibend"
@@ -12256,10 +12256,10 @@
12256 desc: playing time screen 12256 desc: playing time screen
12257 user: core 12257 user: core
12258 <source> 12258 <source>
12259 *: "Playlist elapsed: %s / %s %ld%%" 12259 *: "Playlist elapsed:"
12260 </source> 12260 </source>
12261 <dest> 12261 <dest>
12262 *: "Wiedergabeliste vergangen: %s / %s %ld%%" 12262 *: "Wiedergabeliste vergangen:"
12263 </dest> 12263 </dest>
12264 <voice> 12264 <voice>
12265 *: "Wiedergabeliste vergangen" 12265 *: "Wiedergabeliste vergangen"
@@ -12430,10 +12430,10 @@
12430 desc: playing time screen 12430 desc: playing time screen
12431 user: core 12431 user: core
12432 <source> 12432 <source>
12433 *: "Track %d / %d %d%%" 12433 *: "Track:"
12434 </source> 12434 </source>
12435 <dest> 12435 <dest>
12436 *: "Titel %d / %d %d%%" 12436 *: "Titel:"
12437 </dest> 12437 </dest>
12438 <voice> 12438 <voice>
12439 *: "Titel" 12439 *: "Titel"
@@ -13118,10 +13118,10 @@
13118 desc: playing time screen 13118 desc: playing time screen
13119 user: core 13119 user: core
13120 <source> 13120 <source>
13121 *: "Storage: %s (done %s, remaining %s)" 13121 *: "Storage (Done / Remaining):"
13122 </source> 13122 </source>
13123 <dest> 13123 <dest>
13124 *: "Speicher: %s (done %s, remaining %s)" 13124 *: "Speicher (fertig/ verbleibend):"
13125 </dest> 13125 </dest>
13126 <voice> 13126 <voice>
13127 *: "Speicher" 13127 *: "Speicher"
@@ -13581,10 +13581,10 @@
13581 desc: playing time screen 13581 desc: playing time screen
13582 user: core 13582 user: core
13583 <source> 13583 <source>
13584 *: "Playlist remaining: %s" 13584 *: "Playlist remaining:"
13585 </source> 13585 </source>
13586 <dest> 13586 <dest>
13587 *: "Wiedrgabeliste verbleibend: %s" 13587 *: "Wiedrgabeliste verbleibend:"
13588 </dest> 13588 </dest>
13589 <voice> 13589 <voice>
13590 *: "Wiedrgabeliste verbleibend" 13590 *: "Wiedrgabeliste verbleibend"
@@ -13873,10 +13873,10 @@
13873 desc: playing time screen 13873 desc: playing time screen
13874 user: core 13874 user: core
13875 <source> 13875 <source>
13876 *: "Track elapsed: %s / %s %ld%%" 13876 *: "Track elapsed:"
13877 </source> 13877 </source>
13878 <dest> 13878 <dest>
13879 *: "Titel vergangen: %s / %s %ld%%" 13879 *: "Titel vergangen:"
13880 </dest> 13880 </dest>
13881 <voice> 13881 <voice>
13882 *: "Titel vergangen" 13882 *: "Titel vergangen"
@@ -13929,10 +13929,10 @@
13929 desc: playing time screen 13929 desc: playing time screen
13930 user: core 13930 user: core
13931 <source> 13931 <source>
13932 *: "Average bitrate: %ld kbps" 13932 *: "Average bitrate:"
13933 </source> 13933 </source>
13934 <dest> 13934 <dest>
13935 *: "Durchschnittliche Bitrate: %ld kbps" 13935 *: "Durchschnittliche Bitrate:"
13936 </dest> 13936 </dest>
13937 <voice> 13937 <voice>
13938 *: "Durchschnittliche Bitrate" 13938 *: "Durchschnittliche Bitrate"
@@ -14050,10 +14050,10 @@
14050 desc: playing time screen 14050 desc: playing time screen
14051 user: core 14051 user: core
14052 <source> 14052 <source>
14053 *: "Average track size: %s" 14053 *: "Average track size:"
14054 </source> 14054 </source>
14055 <dest> 14055 <dest>
14056 *: "Durchschnittliche Titelgröße: %s" 14056 *: "Durchschnittliche Titelgröße:"
14057 </dest> 14057 </dest>
14058 <voice> 14058 <voice>
14059 *: "Durchschnittliche Titelgröße" 14059 *: "Durchschnittliche Titelgröße"
@@ -16345,3 +16345,87 @@
16345 *: "Deustch" 16345 *: "Deustch"
16346 </voice> 16346 </voice>
16347</phrase> 16347</phrase>
16348<phrase>
16349 id: LANG_ERROR_FORMATSTR
16350 desc: for general use
16351 user: core
16352 <source>
16353 *: "Error: %s"
16354 </source>
16355 <dest>
16356 *: "Fehler: %s"
16357 </dest>
16358 <voice>
16359 *: "Fehler"
16360 </voice>
16361</phrase>
16362<phrase>
16363 id: LANG_MIKMOD_SETTINGS
16364 desc: mikmod plugin
16365 user: core
16366 <source>
16367 *: "Mikmod Settings"
16368 </source>
16369 <dest>
16370 *: "Mikmod Einstellungen"
16371 </dest>
16372 <voice>
16373 *: "Mik mod Einstellungen"
16374 </voice>
16375</phrase>
16376<phrase>
16377 id: LANG_MIKMOD_MENU
16378 desc: mikmod plugin
16379 user: core
16380 <source>
16381 *: "Mikmod Menu"
16382 </source>
16383 <dest>
16384 *: "Mikmod Menü"
16385 </dest>
16386 <voice>
16387 *: "Mik mod Menü"
16388 </voice>
16389</phrase>
16390<phrase>
16391 id: LANG_CHESSBOX_MENU
16392 desc: chessbox plugin
16393 user: core
16394 <source>
16395 *: "Chessbox Menu"
16396 </source>
16397 <dest>
16398 *: "Schachbox Menü"
16399 </dest>
16400 <voice>
16401 *: "Schach box Menü"
16402 </voice>
16403</phrase>
16404<phrase>
16405 id: VOICE_INVALID_VOICE_FILE
16406 desc: played if the voice file fails to load
16407 user: core
16408 <source>
16409 *: ""
16410 </source>
16411 <dest>
16412 *: ""
16413 </dest>
16414 <voice>
16415 *: "Ungültige Sprach Datei"
16416 </voice>
16417</phrase>
16418<phrase>
16419 id: LANG_PERCENT_FORMAT
16420 desc: percent formatting ( `10%` is default , for `10 %` use '%ld %%' , for `%10` use '%%%ld' and so on)
16421 user: core
16422 <source>
16423 *: "%ld%%"
16424 </source>
16425 <dest>
16426 *: "~%ld%%"
16427 </dest>
16428 <voice>
16429 *: none
16430 </voice>
16431</phrase>
diff --git a/apps/misc.c b/apps/misc.c
index dd73c98a69..d8caabd397 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -1999,4 +1999,22 @@ long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm
1999 1999
2000 return vol >> NVOL_FRACBITS; 2000 return vol >> NVOL_FRACBITS;
2001} 2001}
2002
2003void clear_screen_buffer(bool update)
2004{
2005 struct viewport vp;
2006 struct viewport *last_vp;
2007 FOR_NB_SCREENS(i)
2008 {
2009 struct screen * screen = &screens[i];
2010 viewport_set_defaults(&vp, screen->screen_type);
2011 last_vp = screen->set_viewport(&vp);
2012 screen->clear_viewport();
2013 if (update) {
2014 screen->update_viewport();
2015 }
2016 screen->set_viewport(last_vp);
2017 }
2018}
2019
2002#endif /* ndef __PCTOOL__ */ 2020#endif /* ndef __PCTOOL__ */
diff --git a/apps/misc.h b/apps/misc.h
index c6485db4ff..e5fb7a3d1f 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -277,4 +277,8 @@ long to_normalized_volume(long vol, long min_vol, long max_vol, long max_norm);
277 * for the given normalized volume. */ 277 * for the given normalized volume. */
278long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm); 278long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm);
279 279
280/* clear the lcd output buffer, if update is true the cleared buffer
281 * will be written to the lcd */
282void clear_screen_buffer(bool update);
283
280#endif /* MISC_H */ 284#endif /* MISC_H */
diff --git a/apps/onplay.c b/apps/onplay.c
index 572138e583..4880af58f3 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -26,7 +26,6 @@
26 26
27#include "debug.h" 27#include "debug.h"
28#include "lcd.h" 28#include "lcd.h"
29#include "file.h"
30#include "audio.h" 29#include "audio.h"
31#include "menu.h" 30#include "menu.h"
32#include "lang.h" 31#include "lang.h"
@@ -43,6 +42,7 @@
43#include "talk.h" 42#include "talk.h"
44#include "onplay.h" 43#include "onplay.h"
45#include "filetypes.h" 44#include "filetypes.h"
45#include "fileop.h"
46#include "open_plugin.h" 46#include "open_plugin.h"
47#include "plugin.h" 47#include "plugin.h"
48#include "bookmark.h" 48#include "bookmark.h"
@@ -84,31 +84,6 @@ extern struct menu_item_ex file_menu; /* settings_menu.c */
84 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ 84 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
85 { (void*)name##_},{.callback_and_desc = & name##__}}; 85 { (void*)name##_},{.callback_and_desc = & name##__}};
86 86
87/* Used for directory move, copy and delete */
88struct dirrecurse_params
89{
90 char path[MAX_PATH]; /* Buffer for full path */
91 size_t append; /* Append position in 'path' for stack push */
92};
93
94enum clipboard_op_flags
95{
96 PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */
97 PASTE_COPY = 0x01, /* Is a copy operation */
98 PASTE_OVERWRITE = 0x02, /* Overwrite destination */
99 PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */
100};
101
102/* result codec of various onplay operations */
103enum onplay_result_code
104{
105 /* Anything < 0 is failure */
106 OPRC_SUCCESS = 0, /* All operations completed successfully */
107 OPRC_NOOP = 1, /* Operation didn't need to do anything */
108 OPRC_CANCELLED = 2, /* Operation was cancelled by user */
109 OPRC_NOOVERWRT = 3,
110};
111
112static struct clipboard 87static struct clipboard
113{ 88{
114 char path[MAX_PATH]; /* Clipped file's path */ 89 char path[MAX_PATH]; /* Clipped file's path */
@@ -191,8 +166,6 @@ static int bookmark_menu_callback(int action,
191 return action; 166 return action;
192} 167}
193 168
194
195
196/* CONTEXT_WPS playlist options */ 169/* CONTEXT_WPS playlist options */
197static bool shuffle_playlist(void) 170static bool shuffle_playlist(void)
198{ 171{
@@ -244,46 +217,82 @@ MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
244 ); 217 );
245 218
246/* argument for add_to_playlist (for use by menu callbacks) */ 219/* argument for add_to_playlist (for use by menu callbacks) */
220#define PL_NONE 0x00
221#define PL_QUEUE 0x01
222#define PL_REPLACE 0x02
247struct add_to_pl_param 223struct add_to_pl_param
248{ 224{
249 int8_t position; 225 int8_t position;
250 unsigned int queue: 1; 226 uint8_t flags;
251 unsigned int replace: 1;
252}; 227};
253 228
254static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, 0, 0}; 229static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, PL_NONE};
255static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, 0, 0}; 230static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, PL_NONE};
256static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, 0, 0}; 231static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, PL_NONE};
257static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, 0, 0}; 232static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_NONE};
258static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 0}; 233static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_NONE};
259 234
260static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, 1, 0}; 235static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, PL_QUEUE};
261static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, 1, 0}; 236static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, PL_QUEUE};
262static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, 1, 0}; 237static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, PL_QUEUE};
263static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, 1, 0}; 238static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_QUEUE};
264static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 1, 0}; 239static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_QUEUE};
265 240
266static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, 0, 1}; 241static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, PL_REPLACE};
267static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 1}; 242static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_REPLACE};
243
244static void op_playlist_insert_selected(int position, bool queue)
245{
246#ifdef HAVE_TAGCACHE
247 if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL)
248 {
249 ctx_current_playlist_insert(position, queue, false);
250 return;
251 }
252#endif
253 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
254 playlist_insert_track(NULL, selected_file, position, queue, true);
255 else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
256 playlist_insert_playlist(NULL, selected_file, position, queue);
257 else if (selected_file_attr & ATTR_DIRECTORY)
258 {
259#ifdef HAVE_TAGCACHE
260 if (context == CONTEXT_ID3DB)
261 {
262 tagtree_current_playlist_insert(position, queue);
263 return;
264 }
265#endif
266 bool recurse = (global_settings.recursive_dir_insert == RECURSE_ON);
267 if (global_settings.recursive_dir_insert == RECURSE_ASK)
268 {
269
270 const char *lines[] = {
271 ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
272 selected_file
273 };
274 const struct text_message message={lines, 2};
275 /* Ask if user wants to recurse directory */
276 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
277 }
278
279 playlist_insert_directory(NULL, selected_file, position, queue,
280 recurse == RECURSE_ON);
281 }
282}
268 283
269/* CONTEXT_[TREE|ID3DB|STD] playlist options */ 284/* CONTEXT_[TREE|ID3DB|STD] playlist options */
270static int add_to_playlist(void* arg) 285static int add_to_playlist(void* arg)
271{ 286{
272 struct add_to_pl_param* param = arg; 287 struct add_to_pl_param* param = arg;
273 int position = param->position; 288 int position = param->position;
274 bool new_playlist = !!param->replace; 289 bool new_playlist = (param->flags & PL_REPLACE) == PL_REPLACE;
275 bool queue = !!param->queue; 290 bool queue = (param->flags & PL_QUEUE) == PL_QUEUE;
276 291
277 /* warn if replacing the playlist */ 292 /* warn if replacing the playlist */
278 if (new_playlist && !warn_on_pl_erase()) 293 if (new_playlist && !warn_on_pl_erase())
279 return 0; 294 return 0;
280 295
281 const char *lines[] = {
282 ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
283 selected_file
284 };
285 const struct text_message message={lines, 2};
286
287 splash(0, ID2P(LANG_WAIT)); 296 splash(0, ID2P(LANG_WAIT));
288 297
289 if (new_playlist && global_settings.keep_current_track_on_replace_playlist) 298 if (new_playlist && global_settings.keep_current_track_on_replace_playlist)
@@ -307,38 +316,7 @@ static int add_to_playlist(void* arg)
307 playlist_set_last_shuffled_start(); 316 playlist_set_last_shuffled_start();
308 } 317 }
309 318
310#ifdef HAVE_TAGCACHE 319 op_playlist_insert_selected(position, queue);
311 if ((context == CONTEXT_ID3DB) && (selected_file_attr & ATTR_DIRECTORY))
312 {
313 tagtree_current_playlist_insert(position, queue);
314 }
315 else if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL)
316 {
317 ctx_current_playlist_insert(position, queue, false);
318 }
319 else
320#endif
321 {
322 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
323 playlist_insert_track(NULL, selected_file, position, queue, true);
324 else if (selected_file_attr & ATTR_DIRECTORY)
325 {
326 bool recurse = false;
327
328 if (global_settings.recursive_dir_insert != RECURSE_ASK)
329 recurse = (bool)global_settings.recursive_dir_insert;
330 else
331 {
332 /* Ask if user wants to recurse directory */
333 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
334 }
335
336 playlist_insert_directory(NULL, selected_file, position, queue,
337 recurse);
338 }
339 else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
340 playlist_insert_playlist(NULL, selected_file, position, queue);
341 }
342 320
343 if (new_playlist && (playlist_amount() > 0)) 321 if (new_playlist && (playlist_amount() > 0))
344 { 322 {
@@ -447,18 +425,21 @@ MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_PLAYING_NEXT),
447 &replace_pl_item, 425 &replace_pl_item,
448 &replace_shuf_pl_item 426 &replace_shuf_pl_item
449 ); 427 );
428
450static int treeplaylist_callback(int action, 429static int treeplaylist_callback(int action,
451 const struct menu_item_ex *this_item, 430 const struct menu_item_ex *this_item,
452 struct gui_synclist *this_list) 431 struct gui_synclist *this_list)
453{ 432{
454 (void)this_list; 433 (void)this_list;
434 int sel_file_attr = (selected_file_attr & FILE_ATTR_MASK);
435
455 switch (action) 436 switch (action)
456 { 437 {
457 case ACTION_REQUEST_MENUITEM: 438 case ACTION_REQUEST_MENUITEM:
458 if (this_item == &tree_playlist_menu) 439 if (this_item == &tree_playlist_menu)
459 { 440 {
460 if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO && 441 if (sel_file_attr != FILE_ATTR_AUDIO &&
461 (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && 442 sel_file_attr != FILE_ATTR_M3U &&
462 (selected_file_attr & ATTR_DIRECTORY) == 0) 443 (selected_file_attr & ATTR_DIRECTORY) == 0)
463 return ACTION_EXIT_MENUITEM; 444 return ACTION_EXIT_MENUITEM;
464 } 445 }
@@ -476,7 +457,7 @@ static int treeplaylist_callback(int action,
476 { 457 {
477 struct add_to_pl_param *param = this_item->function_param->param; 458 struct add_to_pl_param *param = this_item->function_param->param;
478 459
479 if (param->queue) 460 if ((param->flags & PL_QUEUE) == PL_QUEUE)
480 { 461 {
481 if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL && 462 if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL &&
482 !in_queue_submenu) 463 !in_queue_submenu)
@@ -489,12 +470,12 @@ static int treeplaylist_callback(int action,
489 if (!global_settings.show_shuffled_adding_options) 470 if (!global_settings.show_shuffled_adding_options)
490 return ACTION_EXIT_MENUITEM; 471 return ACTION_EXIT_MENUITEM;
491 472
492 if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && 473 if (sel_file_attr != FILE_ATTR_M3U &&
493 (selected_file_attr & ATTR_DIRECTORY) == 0) 474 (selected_file_attr & ATTR_DIRECTORY) == 0)
494 return ACTION_EXIT_MENUITEM; 475 return ACTION_EXIT_MENUITEM;
495 } 476 }
496 477
497 if (!param->replace) 478 if ((param->flags & PL_REPLACE) != PL_REPLACE)
498 { 479 {
499 if (!(audio_status() & AUDIO_STATUS_PLAY)) 480 if (!(audio_status() & AUDIO_STATUS_PLAY))
500 return ACTION_EXIT_MENUITEM; 481 return ACTION_EXIT_MENUITEM;
@@ -583,482 +564,17 @@ static int cat_playlist_callback(int action,
583 return action; 564 return action;
584} 565}
585 566
586static void draw_slider(void)
587{
588 struct viewport *last_vp;
589 FOR_NB_SCREENS(i)
590 {
591 struct viewport vp;
592 int slider_height = 2*screens[i].getcharheight();
593 viewport_set_defaults(&vp, i);
594 last_vp = screens[i].set_viewport(&vp);
595 show_busy_slider(&screens[i], 1, vp.height - slider_height,
596 vp.width-2, slider_height-1);
597 screens[i].update_viewport();
598 screens[i].set_viewport(last_vp);
599 }
600}
601
602static void clear_display(bool update)
603{
604 struct viewport vp;
605 struct viewport *last_vp;
606 FOR_NB_SCREENS(i)
607 {
608 struct screen * screen = &screens[i];
609 viewport_set_defaults(&vp, screen->screen_type);
610 last_vp = screen->set_viewport(&vp);
611 screen->clear_viewport();
612 if (update) {
613 screen->update_viewport();
614 }
615 screen->set_viewport(last_vp);
616 }
617}
618
619static void splash_path(const char *path)
620{
621 clear_display(false);
622 path_basename(path, &path);
623 splash(0, path);
624 draw_slider();
625}
626
627/* Splashes the path and checks the keys */
628static bool poll_cancel_action(const char *path)
629{
630 splash_path(path);
631 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
632}
633
634static bool check_new_name(const char *basename)
635{
636 /* at least prevent escapes out of the base directory from keyboard-
637 entered filenames; the file code should reject other invalidities */
638 return *basename != '\0' && !strchr(basename, PATH_SEPCH) &&
639 !is_dotdir_name(basename);
640}
641
642static void splash_cancelled(void) 567static void splash_cancelled(void)
643{ 568{
644 clear_display(true); 569 clear_screen_buffer(true);
645 splash(HZ, ID2P(LANG_CANCEL)); 570 splash(HZ, ID2P(LANG_CANCEL));
646} 571}
647 572
648static void splash_failed(int lang_what) 573static void splash_failed(int lang_what, int err)
649{ 574{
650 cond_talk_ids_fq(lang_what, LANG_FAILED); 575 cond_talk_ids_fq(lang_what, LANG_FAILED);
651 clear_display(true); 576 clear_screen_buffer(true);
652 splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED)); 577 splashf(HZ*2, "%s %s (%d)", str(lang_what), str(LANG_FAILED), err);
653}
654
655/* helper function to remove a non-empty directory */
656static int remove_dir(struct dirrecurse_params *parm)
657{
658 DIR *dir = opendir(parm->path);
659 if (!dir) {
660 return -1; /* open error */
661 }
662
663 size_t append = parm->append;
664 int rc = OPRC_SUCCESS;
665
666 /* walk through the directory content */
667 while (rc == OPRC_SUCCESS) {
668 errno = 0; /* distinguish failure from eod */
669 struct dirent *entry = readdir(dir);
670 if (!entry) {
671 if (errno) {
672 rc = -1;
673 }
674 break;
675 }
676
677 struct dirinfo info = dir_get_info(dir, entry);
678 if ((info.attribute & ATTR_DIRECTORY) &&
679 is_dotdir_name(entry->d_name)) {
680 continue; /* skip these */
681 }
682
683 /* append name to current directory */
684 parm->append = append + path_append(&parm->path[append],
685 PA_SEP_HARD, entry->d_name,
686 sizeof (parm->path) - append);
687 if (parm->append >= sizeof (parm->path)) {
688 rc = -1;
689 break; /* no space left in buffer */
690 }
691
692 if (info.attribute & ATTR_DIRECTORY) {
693 /* remove a subdirectory */
694 rc = remove_dir(parm);
695 } else {
696 /* remove a file */
697 if (poll_cancel_action(parm->path)) {
698 rc = OPRC_CANCELLED;
699 break;
700 }
701
702 rc = remove(parm->path);
703 }
704
705 /* Remove basename we added above */
706 parm->path[append] = '\0';
707 }
708
709 closedir(dir);
710
711 if (rc == 0) {
712 /* remove the now empty directory */
713 if (poll_cancel_action(parm->path)) {
714 rc = OPRC_CANCELLED;
715 } else {
716 rc = rmdir(parm->path);
717 }
718 }
719
720 return rc;
721}
722
723/* share code for file and directory deletion, saves space */
724static int delete_file_dir(void)
725{
726 const char *to_delete=selected_file;
727 const int to_delete_attr=selected_file_attr;
728 if (confirm_delete_yesno(to_delete) != YESNO_YES) {
729 return 1;
730 }
731
732 clear_display(true);
733 splash(HZ/2, ID2P(LANG_DELETING));
734
735 int rc = -1;
736
737 if (to_delete_attr & ATTR_DIRECTORY) { /* true if directory */
738 struct dirrecurse_params parm;
739 parm.append = strlcpy(parm.path, to_delete, sizeof (parm.path));
740
741 if (parm.append < sizeof (parm.path)) {
742 cpu_boost(true);
743 rc = remove_dir(&parm);
744 cpu_boost(false);
745 }
746 } else {
747 rc = remove(to_delete);
748 }
749
750 if (rc < OPRC_SUCCESS) {
751 splash_failed(LANG_DELETE);
752 } else if (rc == OPRC_CANCELLED) {
753 splash_cancelled();
754 }
755
756 if (rc != OPRC_NOOP) {
757 /* Could have failed after some but not all needed changes; reload */
758 onplay_result = ONPLAY_RELOAD_DIR;
759 }
760
761 return 1;
762}
763
764static int rename_file(void)
765{
766 int rc = -1;
767 char newname[MAX_PATH];
768 const char *oldbase, *selection = selected_file;
769
770 path_basename(selection, &oldbase);
771 size_t pathlen = oldbase - selection;
772 char *newbase = newname + pathlen;
773
774 if (strmemccpy(newname, selection, sizeof (newname)) == NULL) {
775 /* Too long */
776 } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) {
777 rc = OPRC_CANCELLED;
778 } else if (!strcmp(oldbase, newbase)) {
779 rc = OPRC_NOOP; /* No change at all */
780 } else if (check_new_name(newbase)) {
781 switch (relate(selection, newname))
782 {
783 case RELATE_DIFFERENT:
784 if (file_exists(newname)) {
785 break; /* don't overwrite */
786 }
787 /* Fall-through */
788 case RELATE_SAME:
789 rc = rename(selection, newname);
790 break;
791 case RELATE_PREFIX:
792 default:
793 break;
794 }
795 }
796
797 if (rc < OPRC_SUCCESS) {
798 splash_failed(LANG_RENAME);
799 } else if (rc == OPRC_CANCELLED) {
800 /* splash_cancelled(); kbd_input() splashes it */
801 } else if (rc == OPRC_SUCCESS) {
802 onplay_result = ONPLAY_RELOAD_DIR;
803 }
804
805 return 1;
806}
807
808static int create_dir(void)
809{
810 int rc = -1;
811 char dirname[MAX_PATH];
812 size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD,
813 sizeof (dirname));
814 char *basename = dirname + pathlen;
815
816 if (pathlen >= sizeof (dirname)) {
817 /* Too long */
818 } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) {
819 rc = OPRC_CANCELLED;
820 } else if (check_new_name(basename)) {
821 rc = mkdir(dirname);
822 }
823
824 if (rc < OPRC_SUCCESS) {
825 splash_failed(LANG_CREATE_DIR);
826 } else if (rc == OPRC_CANCELLED) {
827 /* splash_cancelled(); kbd_input() splashes it */
828 } else if (rc == OPRC_SUCCESS) {
829 onplay_result = ONPLAY_RELOAD_DIR;
830 }
831
832 return 1;
833}
834
835/* Paste a file */
836static int clipboard_pastefile(const char *src, const char *target,
837 unsigned int flags)
838{
839 int rc = -1;
840
841 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
842 if ((flags & PASTE_OVERWRITE) || !file_exists(target)) {
843 /* Rename and possibly overwrite the file */
844 if (poll_cancel_action(src)) {
845 rc = OPRC_CANCELLED;
846 } else {
847 rc = rename(src, target);
848 }
849
850 #ifdef HAVE_MULTIVOLUME
851 if (rc < 0 && errno == EXDEV) {
852 /* Failed because cross volume rename doesn't work; force
853 a move instead */
854 flags |= PASTE_EXDEV;
855 break;
856 }
857 #endif /* HAVE_MULTIVOLUME */
858 }
859
860 return rc;
861 }
862
863 /* See if we can get the plugin buffer for the file copy buffer */
864 size_t buffersize;
865 char *buffer = (char *) plugin_get_buffer(&buffersize);
866 if (buffer == NULL || buffersize < 512) {
867 /* Not large enough, try for a disk sector worth of stack
868 instead */
869 buffersize = 512;
870 buffer = (char *)alloca(buffersize);
871 }
872
873 if (buffer == NULL) {
874 return -1;
875 }
876
877 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
878 size */
879
880 int src_fd = open(src, O_RDONLY);
881 if (src_fd >= 0) {
882 int oflag = O_WRONLY|O_CREAT;
883
884 if (!(flags & PASTE_OVERWRITE)) {
885 oflag |= O_EXCL;
886 }
887
888 int target_fd = open(target, oflag, 0666);
889 if (target_fd >= 0) {
890 off_t total_size = 0;
891 off_t next_cancel_test = 0; /* No excessive button polling */
892
893 rc = OPRC_SUCCESS;
894
895 while (rc == OPRC_SUCCESS) {
896 if (total_size >= next_cancel_test) {
897 next_cancel_test = total_size + 0x10000;
898 if (poll_cancel_action(src)) {
899 rc = OPRC_CANCELLED;
900 break;
901 }
902 }
903
904 ssize_t bytesread = read(src_fd, buffer, buffersize);
905 if (bytesread <= 0) {
906 if (bytesread < 0) {
907 rc = -1;
908 }
909 /* else eof on buffer boundary; nothing to write */
910 break;
911 }
912
913 ssize_t byteswritten = write(target_fd, buffer, bytesread);
914 if (byteswritten < bytesread) {
915 /* Some I/O error */
916 rc = -1;
917 break;
918 }
919
920 total_size += byteswritten;
921
922 if (bytesread < (ssize_t)buffersize) {
923 /* EOF with trailing bytes */
924 break;
925 }
926 }
927
928 if (rc == OPRC_SUCCESS) {
929 /* If overwriting, set the correct length if original was
930 longer */
931 rc = ftruncate(target_fd, total_size);
932 }
933
934 close(target_fd);
935
936 if (rc != OPRC_SUCCESS) {
937 /* Copy failed. Cleanup. */
938 remove(target);
939 }
940 }
941
942 close(src_fd);
943 }
944
945 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
946 /* Remove the source file */
947 rc = remove(src);
948 }
949
950 return rc;
951}
952
953/* Paste a directory */
954static int clipboard_pastedirectory(struct dirrecurse_params *src,
955 struct dirrecurse_params *target,
956 unsigned int flags)
957{
958 int rc = -1;
959
960 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
961 if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) {
962 /* Just try to move the directory */
963 if (poll_cancel_action(src->path)) {
964 rc = OPRC_CANCELLED;
965 } else {
966 rc = rename(src->path, target->path);
967 }
968
969 if (rc < 0) {
970 int errnum = errno;
971 if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
972 /* Directory is not empty thus rename() will not do a quick
973 overwrite */
974 break;
975 }
976 #ifdef HAVE_MULTIVOLUME
977 else if (errnum == EXDEV) {
978 /* Failed because cross volume rename doesn't work; force
979 a move instead */
980 flags |= PASTE_EXDEV;
981 break;
982 }
983 #endif /* HAVE_MULTIVOLUME */
984 }
985 }
986
987 return rc;
988 }
989
990 DIR *srcdir = opendir(src->path);
991
992 if (srcdir) {
993 /* Make a directory to copy things to */
994 rc = mkdir(target->path);
995 if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) {
996 /* Exists and overwrite was approved */
997 rc = OPRC_SUCCESS;
998 }
999 }
1000
1001 size_t srcap = src->append, targetap = target->append;
1002
1003 /* Walk through the directory content; this loop will exit as soon as
1004 there's a problem */
1005 while (rc == OPRC_SUCCESS) {
1006 errno = 0; /* Distinguish failure from eod */
1007 struct dirent *entry = readdir(srcdir);
1008 if (!entry) {
1009 if (errno) {
1010 rc = -1;
1011 }
1012 break;
1013 }
1014
1015 struct dirinfo info = dir_get_info(srcdir, entry);
1016 if ((info.attribute & ATTR_DIRECTORY) &&
1017 is_dotdir_name(entry->d_name)) {
1018 continue; /* Skip these */
1019 }
1020
1021 /* Append names to current directories */
1022 src->append = srcap +
1023 path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name,
1024 sizeof(src->path) - srcap);
1025
1026 target->append = targetap +
1027 path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name,
1028 sizeof (target->path) - targetap);
1029
1030 if (src->append >= sizeof (src->path) ||
1031 target->append >= sizeof (target->path)) {
1032 rc = -1; /* No space left in buffer */
1033 break;
1034 }
1035
1036 if (poll_cancel_action(src->path)) {
1037 rc = OPRC_CANCELLED;
1038 break;
1039 }
1040
1041 DEBUGF("Copy %s to %s\n", src->path, target->path);
1042
1043 if (info.attribute & ATTR_DIRECTORY) {
1044 /* Copy/move a subdirectory */
1045 rc = clipboard_pastedirectory(src, target, flags); /* recursion */
1046 } else {
1047 /* Copy/move a file */
1048 rc = clipboard_pastefile(src->path, target->path, flags);
1049 }
1050
1051 /* Remove basenames we added above */
1052 src->path[srcap] = target->path[targetap] = '\0';
1053 }
1054
1055 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
1056 /* Remove the now empty directory */
1057 rc = rmdir(src->path);
1058 }
1059
1060 closedir(srcdir);
1061 return rc;
1062} 578}
1063 579
1064static bool clipboard_cut(void) 580static bool clipboard_cut(void)
@@ -1079,82 +595,27 @@ static int clipboard_paste(void)
1079 if (!clipboard.path[0]) 595 if (!clipboard.path[0])
1080 return 1; 596 return 1;
1081 597
1082 int rc = -1; 598 int rc = copy_move_fileobject(clipboard.path, getcwd(NULL, 0), clipboard.flags);
1083
1084 struct dirrecurse_params src, target;
1085 unsigned int flags = clipboard.flags;
1086
1087 /* Figure out the name of the selection */
1088 const char *nameptr;
1089 path_basename(clipboard.path, &nameptr);
1090 599
1091 /* Final target is current directory plus name of selection */
1092 target.append = path_append(target.path, getcwd(NULL, 0),
1093 nameptr, sizeof (target.path));
1094 600
1095 switch (target.append < sizeof (target.path) ? 601 clear_screen_buffer(true);
1096 relate(clipboard.path, target.path) : -1)
1097 {
1098 case RELATE_SAME:
1099 rc = OPRC_NOOP;
1100 break;
1101
1102 case RELATE_DIFFERENT:
1103 if (file_exists(target.path)) {
1104 /* If user chooses not to overwrite, cancel */
1105 if (confirm_overwrite_yesno() == YESNO_NO) {
1106 rc = OPRC_NOOVERWRT;
1107 break;
1108 }
1109
1110 flags |= PASTE_OVERWRITE;
1111 }
1112
1113 clear_display(true);
1114 splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) :
1115 ID2P(LANG_MOVING));
1116
1117 /* Now figure out what we're doing */
1118 cpu_boost(true);
1119
1120 if (clipboard.attr & ATTR_DIRECTORY) {
1121 /* Copy or move a subdirectory */
1122 src.append = strlcpy(src.path, clipboard.path,
1123 sizeof (src.path));
1124 if (src.append < sizeof (src.path)) {
1125 rc = clipboard_pastedirectory(&src, &target, flags);
1126 }
1127 } else {
1128 /* Copy or move a file */
1129 rc = clipboard_pastefile(clipboard.path, target.path, flags);
1130 }
1131
1132 cpu_boost(false);
1133 break;
1134
1135 case RELATE_PREFIX:
1136 default: /* Some other relation / failure */
1137 break;
1138 }
1139
1140 clear_display(true);
1141 602
1142 switch (rc) 603 switch (rc)
1143 { 604 {
1144 case OPRC_CANCELLED: 605 case FORC_CANCELLED:
1145 splash_cancelled(); 606 splash_cancelled();
1146 /* Fallthrough */ 607 /* Fallthrough */
1147 case OPRC_SUCCESS: 608 case FORC_SUCCESS:
1148 onplay_result = ONPLAY_RELOAD_DIR; 609 onplay_result = ONPLAY_RELOAD_DIR;
1149 /* Fallthrough */ 610 /* Fallthrough */
1150 case OPRC_NOOP: 611 case FORC_NOOP:
1151 clipboard_clear_selection(&clipboard); 612 clipboard_clear_selection(&clipboard);
1152 /* Fallthrough */ 613 /* Fallthrough */
1153 case OPRC_NOOVERWRT: 614 case FORC_NOOVERWRT:
1154 break; 615 break;
1155 default: 616 default:
1156 if (rc < OPRC_SUCCESS) { 617 if (rc < FORC_SUCCESS) {
1157 splash_failed(LANG_PASTE); 618 splash_failed(LANG_PASTE, rc);
1158 onplay_result = ONPLAY_RELOAD_DIR; 619 onplay_result = ONPLAY_RELOAD_DIR;
1159 } 620 }
1160 } 621 }
@@ -1249,13 +710,57 @@ MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH),
1249 gui_syncpitchscreen_run, NULL, Icon_Audio); 710 gui_syncpitchscreen_run, NULL, Icon_Audio);
1250#endif 711#endif
1251 712
713static int clipboard_delete_selected_fileobject(void)
714{
715 int rc = delete_fileobject(selected_file);
716 if (rc < FORC_SUCCESS) {
717 splash_failed(LANG_DELETE, rc);
718 } else if (rc == FORC_CANCELLED) {
719 splash_cancelled();
720 }
721 if (rc != FORC_NOOP) {
722 /* Could have failed after some but not all needed changes; reload */
723 onplay_result = ONPLAY_RELOAD_DIR;
724 }
725 return 1;
726}
727
728static void show_result(int rc, int lang_what)
729{
730 if (rc < FORC_SUCCESS) {
731 splash_failed(lang_what, rc);
732 } else if (rc == FORC_CANCELLED) {
733 /* splash_cancelled(); kbd_input() splashes it */
734 } else if (rc == FORC_SUCCESS) {
735 onplay_result = ONPLAY_RELOAD_DIR;
736 }
737}
738
739static int clipboard_create_dir(void)
740{
741 int rc = create_dir();
742
743 show_result(rc, LANG_CREATE_DIR);
744
745 return 1;
746}
747
748static int clipboard_rename_selected_file(void)
749{
750 int rc = rename_file(selected_file);
751
752 show_result(rc, LANG_RENAME);
753
754 return 1;
755}
756
1252/* CONTEXT_[TREE|ID3DB] items */ 757/* CONTEXT_[TREE|ID3DB] items */
1253static int clipboard_callback(int action, 758static int clipboard_callback(int action,
1254 const struct menu_item_ex *this_item, 759 const struct menu_item_ex *this_item,
1255 struct gui_synclist *this_list); 760 struct gui_synclist *this_list);
1256 761
1257MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME), 762MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME),
1258 rename_file, clipboard_callback, Icon_NOICON); 763 clipboard_rename_selected_file, clipboard_callback, Icon_NOICON);
1259MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT), 764MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT),
1260 clipboard_cut, clipboard_callback, Icon_NOICON); 765 clipboard_cut, clipboard_callback, Icon_NOICON);
1261MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), 766MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
@@ -1263,11 +768,11 @@ MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
1263MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE), 768MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE),
1264 clipboard_paste, clipboard_callback, Icon_NOICON); 769 clipboard_paste, clipboard_callback, Icon_NOICON);
1265MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE), 770MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE),
1266 delete_file_dir, clipboard_callback, Icon_NOICON); 771 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON);
1267MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR), 772MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR),
1268 delete_file_dir, clipboard_callback, Icon_NOICON); 773 clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON);
1269MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), 774MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR),
1270 create_dir, clipboard_callback, Icon_NOICON); 775 clipboard_create_dir, clipboard_callback, Icon_NOICON);
1271 776
1272/* other items */ 777/* other items */
1273static bool list_viewers(void) 778static bool list_viewers(void)
@@ -1337,12 +842,18 @@ MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
1337 onplay_add_to_shortcuts, 842 onplay_add_to_shortcuts,
1338 clipboard_callback, Icon_NOICON); 843 clipboard_callback, Icon_NOICON);
1339 844
845static void set_dir_helper(char* dirnamebuf, size_t bufsz)
846{
847 path_append(dirnamebuf, selected_file, PA_SEP_HARD, bufsz);
848 settings_save();
849}
850
1340#if LCD_DEPTH > 1 851#if LCD_DEPTH > 1
1341static bool set_backdrop(void) 852static bool set_backdrop(void)
1342{ 853{
1343 path_append(global_settings.backdrop_file, selected_file, 854 set_dir_helper(global_settings.backdrop_file,
1344 PA_SEP_HARD, sizeof(global_settings.backdrop_file)); 855 sizeof(global_settings.backdrop_file));
1345 settings_save(); 856
1346 skin_backdrop_load_setting(); 857 skin_backdrop_load_setting();
1347 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN)); 858 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN));
1348 return true; 859 return true;
@@ -1353,9 +864,8 @@ MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP),
1353#ifdef HAVE_RECORDING 864#ifdef HAVE_RECORDING
1354static bool set_recdir(void) 865static bool set_recdir(void)
1355{ 866{
1356 path_append(global_settings.rec_directory, selected_file, 867 set_dir_helper(global_settings.rec_directory,
1357 PA_SEP_HARD, sizeof(global_settings.rec_directory)); 868 sizeof(global_settings.rec_directory));
1358 settings_save();
1359 return false; 869 return false;
1360} 870}
1361MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), 871MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR),
@@ -1363,10 +873,8 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR),
1363#endif 873#endif
1364static bool set_startdir(void) 874static bool set_startdir(void)
1365{ 875{
1366 path_append(global_settings.start_directory, selected_file, 876 set_dir_helper(global_settings.start_directory,
1367 PA_SEP_HARD, sizeof(global_settings.start_directory)); 877 sizeof(global_settings.start_directory));
1368
1369 settings_save();
1370 return false; 878 return false;
1371} 879}
1372MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR), 880MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR),
@@ -1384,16 +892,14 @@ MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_PLAYLIST_DIR),
1384#ifdef HAVE_TAGCACHE 892#ifdef HAVE_TAGCACHE
1385static bool set_databasedir(void) 893static bool set_databasedir(void)
1386{ 894{
1387 path_append(global_settings.tagcache_db_path, selected_file,
1388 PA_SEP_SOFT, sizeof(global_settings.tagcache_db_path));
1389
1390 struct tagcache_stat *tc_stat = tagcache_get_stat(); 895 struct tagcache_stat *tc_stat = tagcache_get_stat();
1391 if (strcasecmp(global_settings.tagcache_db_path, tc_stat->db_path)) 896 if (strcasecmp(selected_file, tc_stat->db_path))
1392 { 897 {
1393 splash(HZ, ID2P(LANG_PLEASE_REBOOT)); 898 splash(HZ, ID2P(LANG_PLEASE_REBOOT));
1394 } 899 }
1395 900
1396 settings_save(); 901 set_dir_helper(global_settings.tagcache_db_path,
902 sizeof(global_settings.tagcache_db_path));
1397 return false; 903 return false;
1398} 904}
1399MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR), 905MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR),
@@ -1589,7 +1095,7 @@ static bool hotkey_delete_item(void)
1589 return false; 1095 return false;
1590#endif 1096#endif
1591 1097
1592 return delete_file_dir(); 1098 return clipboard_delete_selected_fileobject();
1593} 1099}
1594 1100
1595static bool hotkey_open_with(void) 1101static bool hotkey_open_with(void)
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index c91d02ade8..5e22bea980 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -200,5 +200,6 @@ wavview,viewers
200wolf3d,games 200wolf3d,games
201wormlet,games 201wormlet,games
202xobox,games 202xobox,games
203xrick,games
203xworld,games 204xworld,games
204zxbox,viewers 205zxbox,viewers
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 7884989d1c..c874927205 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -89,3 +89,7 @@ lua
89lua_scripts 89lua_scripts
90picross 90picross
91#endif 91#endif
92
93#if (LCD_DEPTH > 1)
94xrick
95#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index a26ff8edd8..a638fcc5b9 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -9,6 +9,7 @@
9 * 9 *
10 * Copyright (C) 2005, 2006 Ben Basha (Paprica) 10 * Copyright (C) 2005, 2006 Ben Basha (Paprica)
11 * Copyright (C) 2009 Karl Kurbjun 11 * Copyright (C) 2009 Karl Kurbjun
12 * Copyright (C) 2010 Matteo Italia (MItaly)
12 * check_lines is based off an explanation and expanded math presented by Paul 13 * check_lines is based off an explanation and expanded math presented by Paul
13 * Bourke: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ 14 * Bourke: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
14 * 15 *
@@ -392,6 +393,21 @@ CONFIG_KEYPAD == SANSA_M200_PAD
392#endif 393#endif
393#endif 394#endif
394 395
396/* MI: fallback for the suicide combination */
397#ifndef SUICIDE
398/* Some targets do not define DOWN (which isn't actually used in the game) */
399#ifndef DOWN
400#define TWO_KEYS_SUICIDE
401/* No third key to add safety to the suicide button -> press it longer */
402#define SUICIDE (SELECT|QUIT|BUTTON_REPEAT)
403#else
404#define THREE_KEYS_SUICIDE
405/* Three keys provide enough safety to avoid the long press, which may lead to
406 more suicides than necessary :) */
407#define SUICIDE (DOWN|SELECT|QUIT)
408#endif /* #ifndef DOWN */
409#endif /* #ifndef SUICIDE */
410
395/* Continue text is used as a string later when the game is paused. This allows 411/* Continue text is used as a string later when the game is paused. This allows
396 * targets to specify their own text if needed. 412 * targets to specify their own text if needed.
397 */ 413 */
@@ -1441,16 +1457,16 @@ static void brickmania_sleep(int secs)
1441static int brickmania_help(void) 1457static int brickmania_help(void)
1442{ 1458{
1443 static char *help_text[] = { 1459 static char *help_text[] = {
1444 "Brickmania", "", "Aim", "", 1460 "Brickmania", "", "Aim", "", /* 3 */
1445 "Destroy", "all", "the", "bricks", "by", "bouncing", 1461 "Destroy", "all", "the", "bricks", "by", "bouncing", /* 9 */
1446 "the", "ball", "of", "them", "using", "the", "paddle.", "", "", 1462 "the", "ball", "of", "them", "using", "the", "paddle.", "", "", /* 18 */
1447 "Controls", "", 1463 "Controls", "", /* 20 */
1448#if CONFIG_KEYPAD == COWON_D2_PAD 1464#if CONFIG_KEYPAD == COWON_D2_PAD
1449 "- & +:", 1465 "- & +:",
1450#else 1466#else
1451 "< & >:", 1467 "< & >:",
1452#endif 1468#endif
1453 "Moves", "the", "paddle", "", 1469 "Moves", "the", "paddle", "", /* 25 */
1454#if (CONFIG_KEYPAD == IAUDIO_M3_PAD) 1470#if (CONFIG_KEYPAD == IAUDIO_M3_PAD)
1455 "PLAY:", 1471 "PLAY:",
1456#elif CONFIG_KEYPAD == IRIVER_H300_PAD 1472#elif CONFIG_KEYPAD == IRIVER_H300_PAD
@@ -1460,7 +1476,7 @@ static int brickmania_help(void)
1460#else 1476#else
1461 "SELECT:", 1477 "SELECT:",
1462#endif 1478#endif
1463 "Releases", "the", "ball/Fire!", "", 1479 "Releases", "the", "ball/Fire!", "", /* 30 */
1464#if CONFIG_KEYPAD == IAUDIO_M3_PAD 1480#if CONFIG_KEYPAD == IAUDIO_M3_PAD
1465 "REC:", 1481 "REC:",
1466#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \ 1482#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
@@ -1477,30 +1493,52 @@ static int brickmania_help(void)
1477#else 1493#else
1478 "POWER:", 1494 "POWER:",
1479#endif 1495#endif
1480 "Returns", "to", "menu", "", "", 1496 "Returns", "to", "menu", "", /* 35 */
1481 "Specials", "", 1497#if defined(THREE_KEYS_SUICIDE)
1482 "N", "Normal:", "returns", "paddle", "to", "normal", "", 1498 "Press", "Fire,", "Down,", "Quit", "together:",
1483 "D", "DIE!:", "loses", "a", "life", "", 1499#elif defined(TWO_KEYS_SUICIDE)
1484 "L", "Life:", "gains", "a", "life/power", "up", "", 1500 "Hold", "Fire", "and", "Quit", "together:",
1485 "F", "Fire:", "allows", "you", "to", "shoot", "bricks", "", 1501#else
1486 "G", "Glue:", "ball", "sticks", "to", "paddle", "", 1502#error No info about the suicide button, cannot complete help screen.
1487 "B", "Ball:", "generates", "another", "ball", "", 1503#endif
1488 "FL", "Flip:", "flips", "left / right", "movement", "", 1504 "Kills", "all","the", "balls","(suicide)","", /* 46 */
1489 "<->", "or", "<E>:", "enlarges", "the", "paddle", "", 1505 "", /* 47 */
1490 ">-<", "or", ">S<:", "shrinks", "the", "paddle", "", 1506 "Specials", "", /* 49 */
1507 "N", "Normal:", "returns", "paddle", "to", "normal", "", /* 56 */
1508 "D", "DIE!:", "loses", "a", "life", "", /* 62 */
1509 "L", "Life:", "gains", "a", "life/power", "up", "", /* 69 */
1510 "F", "Fire:", "allows", "you", "to", "shoot", "bricks", "", /* 77 */
1511 "G", "Glue:", "ball", "sticks", "to", "paddle", "", /* 84 */
1512 "B", "Ball:", "generates", "another", "ball", "", /* 90 */
1513 "FL", "Flip:", "flips", "left / right", "movement", "", /* 96 */
1514 "<->", "or", "<E>:", "enlarges", "the", "paddle", "", /* 103 */
1515 ">-<", "or", ">S<:", "shrinks", "the", "paddle", "", /* 110 */
1491 }; 1516 };
1492 static struct style_text formation[]={ 1517 static struct style_text formation[]={
1493 { 0, TEXT_CENTER|TEXT_UNDERLINE }, 1518 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1494 { 2, C_RED }, 1519 { 2, C_RED },
1495 { 19, C_RED }, 1520 { 19, C_RED },
1496 { 37, C_RED }, 1521 { 21, TEXT_UNDERLINE},
1497 { 39, C_BLUE }, 1522 { 26, TEXT_UNDERLINE},
1498 { 46, C_RED }, 1523 { 31, TEXT_UNDERLINE},
1499 { 52, C_GREEN }, 1524#if defined(THREE_KEYS_SUICIDE)
1500 { 59, C_ORANGE }, 1525 { 37, TEXT_UNDERLINE},
1501 { 67, C_GREEN }, 1526 { 38, TEXT_UNDERLINE},
1502 { 74, C_YELLOW }, 1527 { 39, TEXT_UNDERLINE},
1503 { 80, C_RED }, 1528#elif defined(TWO_KEYS_SUICIDE)
1529 { 37, TEXT_UNDERLINE},
1530 { 39, TEXT_UNDERLINE},
1531#else
1532#error No info about the suicide button, cannot complete help screen.
1533#endif
1534 { 48, C_RED },
1535 { 50, C_BLUE },
1536 { 57, C_RED },
1537 { 63, C_GREEN },
1538 { 70, C_ORANGE },
1539 { 78, C_GREEN },
1540 { 85, C_YELLOW },
1541 { 91, C_RED },
1504 LAST_STYLE_ITEM 1542 LAST_STYLE_ITEM
1505 }; 1543 };
1506 1544
@@ -1522,9 +1560,13 @@ static int brickmania_menu_cb(int action,
1522{ 1560{
1523 (void)this_list; 1561 (void)this_list;
1524 int i = ((intptr_t)this_item); 1562 int i = ((intptr_t)this_item);
1525 if(action == ACTION_REQUEST_MENUITEM 1563 if(action == ACTION_REQUEST_MENUITEM)
1526 && !resume && (i==0 || i==6)) 1564 {
1527 return ACTION_EXIT_MENUITEM; 1565 if(!resume && (i==0 || i==7))
1566 return ACTION_EXIT_MENUITEM;
1567 if(i==6 && !rb->file_exists(SAVE_FILE))
1568 return ACTION_EXIT_MENUITEM;
1569 }
1528 return action; 1570 return action;
1529} 1571}
1530 1572
@@ -1546,16 +1588,14 @@ static int brickmania_menu(void)
1546 "Resume Game", "Start New Game", 1588 "Resume Game", "Start New Game",
1547 "Difficulty", "Help", "High Scores", 1589 "Difficulty", "Help", "High Scores",
1548 "Playback Control", 1590 "Playback Control",
1591 "Remove savefile",
1549 "Quit without Saving", "Quit"); 1592 "Quit without Saving", "Quit");
1550
1551 rb->button_clear_queue(); 1593 rb->button_clear_queue();
1552 while (true) { 1594 while (true) {
1553 switch (rb->do_menu(&main_menu, &selected, NULL, false)) { 1595 switch (rb->do_menu(&main_menu, &selected, NULL, false)) {
1554 case 0: 1596 case 0:
1555 if(game_state!=ST_READY) 1597 if(game_state!=ST_READY)
1556 game_state = ST_PAUSE; 1598 game_state = ST_PAUSE;
1557 if(resume_file)
1558 rb->remove(SAVE_FILE);
1559 return 0; 1599 return 0;
1560 case 1: 1600 case 1:
1561 score=0; 1601 score=0;
@@ -1580,8 +1620,11 @@ static int brickmania_menu(void)
1580 return 1; 1620 return 1;
1581 break; 1621 break;
1582 case 6: 1622 case 6:
1583 return 1; 1623 rb->remove(SAVE_FILE);
1624 break;
1584 case 7: 1625 case 7:
1626 return 1;
1627 case 8:
1585 if (resume) { 1628 if (resume) {
1586 rb->splash(HZ*1, "Saving game ..."); 1629 rb->splash(HZ*1, "Saving game ...");
1587 brickmania_savegame(); 1630 brickmania_savegame();
@@ -1631,6 +1674,20 @@ static void brick_hit(int i, int j)
1631 } 1674 }
1632} 1675}
1633 1676
1677static void newround(void)
1678{
1679 brickmania_init_game(false);
1680 brickmania_sleep(2);
1681}
1682
1683static void error_newround(const char * message)
1684{
1685 rb->splash(HZ*3,"Some error happened, so the current balls will be killed.");
1686 rb->splashf(HZ*3,"The reason is: %s",message);
1687 rb->splash(HZ*3,"Don't worry, it's my fault, you won't lose any life for this. :)");
1688 newround();
1689}
1690
1634static int brickmania_game_loop(void) 1691static int brickmania_game_loop(void)
1635{ 1692{
1636 int j,i,k; 1693 int j,i,k;
@@ -1723,10 +1780,17 @@ static int brickmania_game_loop(void)
1723 1780
1724 /* draw the ball */ 1781 /* draw the ball */
1725 for(i=0;i<used_balls;i++) 1782 for(i=0;i<used_balls;i++)
1783 {
1784 /* MI: workaround for balls exiting from the screen */
1785 const int H_OFFSCREEN_LIMIT=GAMESCREEN_WIDTH*3/2;
1786 const int V_OFFSCREEN_LIMIT=GAMESCREEN_HEIGHT*3/2;
1787 if(abs(ball[i].pos_x)>H_OFFSCREEN_LIMIT || abs(ball[i].pos_y)>V_OFFSCREEN_LIMIT)
1788 error_newround("Ball went offscreen");
1726 rb->lcd_bitmap(brickmania_ball, 1789 rb->lcd_bitmap(brickmania_ball,
1727 INT3(ball[i].pos_x - HALFBALL), 1790 INT3(ball[i].pos_x - HALFBALL),
1728 INT3(ball[i].pos_y - HALFBALL), 1791 INT3(ball[i].pos_y - HALFBALL),
1729 INT3(BALL), INT3(BALL)); 1792 INT3(BALL), INT3(BALL));
1793 }
1730 1794
1731 if (brick_on_board==0) 1795 if (brick_on_board==0)
1732 brick_on_board--; 1796 brick_on_board--;
@@ -2474,7 +2538,16 @@ static int brickmania_game_loop(void)
2474 resume = true; 2538 resume = true;
2475 return 0; 2539 return 0;
2476 break; 2540 break;
2477 2541
2542 /* MI: added suicide button in case the ball gets stuck or
2543 whatever */
2544 case SUICIDE:
2545 life--;
2546 rb->splash(HZ,"Balls killed!");
2547 if(life>=0)
2548 newround();
2549 rb->button_clear_queue();
2550 break;
2478 default: 2551 default:
2479 if(rb->default_event_handler(button) == SYS_USB_CONNECTED) 2552 if(rb->default_event_handler(button) == SYS_USB_CONNECTED)
2480 return 1; 2553 return 1;
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 4d36302ddf..99d9e033fc 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -33,8 +33,13 @@
33#include "lib/osd.h" 33#include "lib/osd.h"
34 34
35#ifndef HAVE_LCD_COLOR 35#ifndef HAVE_LCD_COLOR
36#if defined(SIMULATOR)
37// Use the one in pluginlib/osd
38extern GREY_INFO_STRUCT
39#else
36GREY_INFO_STRUCT 40GREY_INFO_STRUCT
37#endif 41#endif
42#endif
38 43
39#include "lib/pluginlib_actions.h" 44#include "lib/pluginlib_actions.h"
40 45
diff --git a/apps/plugins/xrick/3rd_party/miniz/miniz.c b/apps/plugins/xrick/3rd_party/miniz/miniz.c
new file mode 100644
index 0000000000..380c9a9502
--- /dev/null
+++ b/apps/plugins/xrick/3rd_party/miniz/miniz.c
@@ -0,0 +1,4933 @@
1/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
2 See "unlicense" statement at the end of this file.
3 Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
4 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
5
6 Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
7 MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
8
9 * Change History
10 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!):
11 - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
12 would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
13 (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
14 - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
15 - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
16 Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
17 - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
18 - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
19 - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
20 - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
21 - Merged MZ_FORCEINLINE fix from hdeanclark
22 - Fix <time.h> include before config #ifdef, thanks emil.brink
23 - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
24 set it to 1 for real-time compression).
25 - Merged in some compiler fixes from paulharris's github repro.
26 - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3.
27 - Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
28 - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
29 - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
30 - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
31 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
32 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
33 - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
34 - Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
35 - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
36 "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
37 - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
38 - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
39 - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
40 - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
41 - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
42 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
43 level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
44 5/28/11 v1.11 - Added statement from unlicense.org
45 5/27/11 v1.10 - Substantial compressor optimizations:
46 - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
47 - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
48 - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
49 - Refactored the compression code for better readability and maintainability.
50 - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
51 drop in throughput on some files).
52 5/15/11 v1.09 - Initial stable release.
53
54 * Low-level Deflate/Inflate implementation notes:
55
56 Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
57 greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
58 approximately as well as zlib.
59
60 Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
61 coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
62 block large enough to hold the entire file.
63
64 The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
65
66 * zlib-style API notes:
67
68 miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
69 zlib replacement in many apps:
70 The z_stream struct, optional memory allocation callbacks
71 deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
72 inflateInit/inflateInit2/inflate/inflateEnd
73 compress, compress2, compressBound, uncompress
74 CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
75 Supports raw deflate streams or standard zlib streams with adler-32 checking.
76
77 Limitations:
78 The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
79 I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
80 there are no guarantees that miniz.c pulls this off perfectly.
81
82 * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
83 Alex Evans. Supports 1-4 bytes/pixel images.
84
85 * ZIP archive API notes:
86
87 The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
88 get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
89 existing archives, create new archives, append new files to existing archives, or clone archive data from
90 one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
91 or you can specify custom file read/write callbacks.
92
93 - Archive reading: Just call this function to read a single file from a disk archive:
94
95 void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
96 size_t *pSize, mz_uint zip_flags);
97
98 For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
99 directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
100
101 - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
102
103 int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
104
105 The locate operation can optionally check file comments too, which (as one example) can be used to identify
106 multiple versions of the same file in an archive. This function uses a simple linear search through the central
107 directory, so it's not very fast.
108
109 Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
110 retrieve detailed info on each file by calling mz_zip_reader_file_stat().
111
112 - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
113 to disk and builds an exact image of the central directory in memory. The central directory image is written
114 all at once at the end of the archive file when the archive is finalized.
115
116 The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
117 which can be useful when the archive will be read from optical media. Also, the writer supports placing
118 arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
119 readable by any ZIP tool.
120
121 - Archive appending: The simple way to add a single file to an archive is to call this function:
122
123 mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
124 const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
125
126 The archive will be created if it doesn't already exist, otherwise it'll be appended to.
127 Note the appending is done in-place and is not an atomic operation, so if something goes wrong
128 during the operation it's possible the archive could be left without a central directory (although the local
129 file headers and file data will be fine, so the archive will be recoverable).
130
131 For more complex archive modification scenarios:
132 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
133 preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
134 compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
135 you're done. This is safe but requires a bunch of temporary disk space or heap memory.
136
137 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
138 append new files as needed, then finalize the archive which will write an updated central directory to the
139 original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
140 possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
141
142 - ZIP archive support limitations:
143 No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
144 Requires streams capable of seeking.
145
146 * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
147 below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
148
149 * Important: For best perf. be sure to customize the below macros for your target platform:
150 #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
151 #define MINIZ_LITTLE_ENDIAN 1
152 #define MINIZ_HAS_64BIT_REGISTERS 1
153
154 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
155 uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
156 (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
157*/
158
159#ifndef MINIZ_HEADER_INCLUDED
160#define MINIZ_HEADER_INCLUDED
161
162#include <stdlib.h>
163
164// Defines to completely disable specific portions of miniz.c:
165// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
166
167// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
168//#define MINIZ_NO_STDIO
169
170// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
171// get/set file times, and the C run-time funcs that get/set times won't be called.
172// The current downside is the times written to your archives will be from 1979.
173//#define MINIZ_NO_TIME
174
175// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
176//#define MINIZ_NO_ARCHIVE_APIS
177
178// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
179//#define MINIZ_NO_ARCHIVE_WRITING_APIS
180
181// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
182//#define MINIZ_NO_ZLIB_APIS
183
184// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
185//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
186
187// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
188// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
189// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
190// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
191//#define MINIZ_NO_MALLOC
192
193#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
194 // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
195 #define MINIZ_NO_TIME
196#endif
197
198#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
199 #include <time.h>
200#endif
201
202#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
203// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
204#define MINIZ_X86_OR_X64_CPU 1
205#endif
206
207#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
208// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
209#define MINIZ_LITTLE_ENDIAN 1
210#endif
211
212#if MINIZ_X86_OR_X64_CPU
213// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
214#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
215#endif
216
217#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
218// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
219#define MINIZ_HAS_64BIT_REGISTERS 1
220#endif
221
222#if (__GNUC__ >= 7)
223#pragma GCC diagnostic push
224#pragma GCC diagnostic ignored "-Wmisleading-indentation"
225#endif
226
227#ifdef __cplusplus
228extern "C" {
229#endif
230
231// ------------------- zlib-style API Definitions.
232
233// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
234typedef unsigned long mz_ulong;
235
236// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
237void mz_free(void *p);
238
239#define MZ_ADLER32_INIT (1)
240// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
241mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
242
243#define MZ_CRC32_INIT (0)
244// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
245mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
246
247// Compression strategies.
248enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
249
250// Method
251#define MZ_DEFLATED 8
252
253#ifndef MINIZ_NO_ZLIB_APIS
254
255// Heap allocation callbacks.
256// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
257typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
258typedef void (*mz_free_func)(void *opaque, void *address);
259typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
260
261#define MZ_VERSION "9.1.15"
262#define MZ_VERNUM 0x91F0
263#define MZ_VER_MAJOR 9
264#define MZ_VER_MINOR 1
265#define MZ_VER_REVISION 15
266#define MZ_VER_SUBREVISION 0
267
268// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
269enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
270
271// Return status codes. MZ_PARAM_ERROR is non-standard.
272enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
273
274// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
275enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
276
277// Window bits
278#define MZ_DEFAULT_WINDOW_BITS 15
279
280struct mz_internal_state;
281
282// Compression/decompression stream struct.
283typedef struct mz_stream_s
284{
285 const unsigned char *next_in; // pointer to next byte to read
286 unsigned int avail_in; // number of bytes available at next_in
287 mz_ulong total_in; // total number of bytes consumed so far
288
289 unsigned char *next_out; // pointer to next byte to write
290 unsigned int avail_out; // number of bytes that can be written to next_out
291 mz_ulong total_out; // total number of bytes produced so far
292
293 char *msg; // error msg (unused)
294 struct mz_internal_state *state; // internal state, allocated by zalloc/zfree
295
296 mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc)
297 mz_free_func zfree; // optional heap free function (defaults to free)
298 void *opaque; // heap alloc function user pointer
299
300 int data_type; // data_type (unused)
301 mz_ulong adler; // adler32 of the source or uncompressed data
302 mz_ulong reserved; // not used
303} mz_stream;
304
305typedef mz_stream *mz_streamp;
306
307// Returns the version string of miniz.c.
308const char *mz_version(void);
309
310// mz_deflateInit() initializes a compressor with default options:
311// Parameters:
312// pStream must point to an initialized mz_stream struct.
313// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
314// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
315// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
316// Return values:
317// MZ_OK on success.
318// MZ_STREAM_ERROR if the stream is bogus.
319// MZ_PARAM_ERROR if the input parameters are bogus.
320// MZ_MEM_ERROR on out of memory.
321int mz_deflateInit(mz_streamp pStream, int level);
322
323// mz_deflateInit2() is like mz_deflate(), except with more control:
324// Additional parameters:
325// method must be MZ_DEFLATED
326// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
327// mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
328int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
329
330// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
331int mz_deflateReset(mz_streamp pStream);
332
333// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
334// Parameters:
335// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
336// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
337// Return values:
338// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
339// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
340// MZ_STREAM_ERROR if the stream is bogus.
341// MZ_PARAM_ERROR if one of the parameters is invalid.
342// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
343int mz_deflate(mz_streamp pStream, int flush);
344
345// mz_deflateEnd() deinitializes a compressor:
346// Return values:
347// MZ_OK on success.
348// MZ_STREAM_ERROR if the stream is bogus.
349int mz_deflateEnd(mz_streamp pStream);
350
351// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
352mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
353
354// Single-call compression functions mz_compress() and mz_compress2():
355// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
356int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
357int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
358
359// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
360mz_ulong mz_compressBound(mz_ulong source_len);
361
362// Initializes a decompressor.
363int mz_inflateInit(mz_streamp pStream);
364
365// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
366// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
367int mz_inflateInit2(mz_streamp pStream, int window_bits);
368
369// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
370// Parameters:
371// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
372// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
373// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
374// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
375// Return values:
376// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
377// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
378// MZ_STREAM_ERROR if the stream is bogus.
379// MZ_DATA_ERROR if the deflate stream is invalid.
380// MZ_PARAM_ERROR if one of the parameters is invalid.
381// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
382// with more input data, or with more room in the output buffer (except when using single call decompression, described above).
383int mz_inflate(mz_streamp pStream, int flush);
384
385// Deinitializes a decompressor.
386int mz_inflateEnd(mz_streamp pStream);
387
388// Single-call decompression.
389// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
390int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
391
392// Returns a string description of the specified error code, or NULL if the error code is invalid.
393const char *mz_error(int err);
394
395// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
396// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
397#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
398 typedef unsigned char Byte;
399 typedef unsigned int uInt;
400 typedef mz_ulong uLong;
401 typedef Byte Bytef;
402 typedef uInt uIntf;
403 typedef char charf;
404 typedef int intf;
405 typedef void *voidpf;
406 typedef uLong uLongf;
407 typedef void *voidp;
408 typedef void *const voidpc;
409 #define Z_NULL 0
410 #define Z_NO_FLUSH MZ_NO_FLUSH
411 #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH
412 #define Z_SYNC_FLUSH MZ_SYNC_FLUSH
413 #define Z_FULL_FLUSH MZ_FULL_FLUSH
414 #define Z_FINISH MZ_FINISH
415 #define Z_BLOCK MZ_BLOCK
416 #define Z_OK MZ_OK
417 #define Z_STREAM_END MZ_STREAM_END
418 #define Z_NEED_DICT MZ_NEED_DICT
419 #define Z_ERRNO MZ_ERRNO
420 #define Z_STREAM_ERROR MZ_STREAM_ERROR
421 #define Z_DATA_ERROR MZ_DATA_ERROR
422 #define Z_MEM_ERROR MZ_MEM_ERROR
423 #define Z_BUF_ERROR MZ_BUF_ERROR
424 #define Z_VERSION_ERROR MZ_VERSION_ERROR
425 #define Z_PARAM_ERROR MZ_PARAM_ERROR
426 #define Z_NO_COMPRESSION MZ_NO_COMPRESSION
427 #define Z_BEST_SPEED MZ_BEST_SPEED
428 #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION
429 #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
430 #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY
431 #define Z_FILTERED MZ_FILTERED
432 #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY
433 #define Z_RLE MZ_RLE
434 #define Z_FIXED MZ_FIXED
435 #define Z_DEFLATED MZ_DEFLATED
436 #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
437 #define alloc_func mz_alloc_func
438 #define free_func mz_free_func
439 #define internal_state mz_internal_state
440 #define z_stream mz_stream
441 #define deflateInit mz_deflateInit
442 #define deflateInit2 mz_deflateInit2
443 #define deflateReset mz_deflateReset
444 #define deflate mz_deflate
445 #define deflateEnd mz_deflateEnd
446 #define deflateBound mz_deflateBound
447 #define compress mz_compress
448 #define compress2 mz_compress2
449 #define compressBound mz_compressBound
450 #define inflateInit mz_inflateInit
451 #define inflateInit2 mz_inflateInit2
452 #define inflate mz_inflate
453 #define inflateEnd mz_inflateEnd
454 #define uncompress mz_uncompress
455 #define crc32 mz_crc32
456 #define adler32 mz_adler32
457 #define MAX_WBITS 15
458 #define MAX_MEM_LEVEL 9
459 #define zError mz_error
460 #define ZLIB_VERSION MZ_VERSION
461 #define ZLIB_VERNUM MZ_VERNUM
462 #define ZLIB_VER_MAJOR MZ_VER_MAJOR
463 #define ZLIB_VER_MINOR MZ_VER_MINOR
464 #define ZLIB_VER_REVISION MZ_VER_REVISION
465 #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION
466 #define zlibVersion mz_version
467 #define zlib_version mz_version()
468#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
469
470#endif // MINIZ_NO_ZLIB_APIS
471
472// ------------------- Types and macros
473
474typedef unsigned char mz_uint8;
475typedef signed short mz_int16;
476typedef unsigned short mz_uint16;
477typedef unsigned int mz_uint32;
478typedef unsigned int mz_uint;
479typedef long long mz_int64;
480typedef unsigned long long mz_uint64;
481typedef int mz_bool;
482
483#define MZ_FALSE (0)
484#define MZ_TRUE (1)
485
486// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
487#ifdef _MSC_VER
488 #define MZ_MACRO_END while (0, 0)
489#else
490 #define MZ_MACRO_END while (0)
491#endif
492
493// ------------------- ZIP archive reading/writing
494
495#ifndef MINIZ_NO_ARCHIVE_APIS
496
497enum
498{
499 MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
500 MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
501 MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
502};
503
504typedef struct
505{
506 mz_uint32 m_file_index;
507 mz_uint32 m_central_dir_ofs;
508 mz_uint16 m_version_made_by;
509 mz_uint16 m_version_needed;
510 mz_uint16 m_bit_flag;
511 mz_uint16 m_method;
512#ifndef MINIZ_NO_TIME
513 time_t m_time;
514#endif
515 mz_uint32 m_crc32;
516 mz_uint64 m_comp_size;
517 mz_uint64 m_uncomp_size;
518 mz_uint16 m_internal_attr;
519 mz_uint32 m_external_attr;
520 mz_uint64 m_local_header_ofs;
521 mz_uint32 m_comment_size;
522 char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
523 char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
524} mz_zip_archive_file_stat;
525
526typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
527typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
528
529struct mz_zip_internal_state_tag;
530typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
531
532typedef enum
533{
534 MZ_ZIP_MODE_INVALID = 0,
535 MZ_ZIP_MODE_READING = 1,
536 MZ_ZIP_MODE_WRITING = 2,
537 MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
538} mz_zip_mode;
539
540typedef struct mz_zip_archive_tag
541{
542 mz_uint64 m_archive_size;
543 mz_uint64 m_central_directory_file_ofs;
544 mz_uint m_total_files;
545 mz_zip_mode m_zip_mode;
546
547 mz_uint m_file_offset_alignment;
548
549 mz_alloc_func m_pAlloc;
550 mz_free_func m_pFree;
551 mz_realloc_func m_pRealloc;
552 void *m_pAlloc_opaque;
553
554 mz_file_read_func m_pRead;
555 mz_file_write_func m_pWrite;
556 void *m_pIO_opaque;
557
558 mz_zip_internal_state *m_pState;
559
560} mz_zip_archive;
561
562typedef enum
563{
564 MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
565 MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
566 MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
567 MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
568} mz_zip_flags;
569
570// ZIP archive reading
571
572// Inits a ZIP archive reader.
573// These functions read and validate the archive's central directory.
574mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
575mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
576
577#ifndef MINIZ_NO_STDIO
578mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
579#endif
580
581// Returns the total number of files in the archive.
582mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
583
584// Returns detailed information about an archive file entry.
585mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
586
587// Determines if an archive file entry is a directory entry.
588mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
589mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
590
591// Retrieves the filename of an archive file entry.
592// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
593mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
594
595// Attempts to locates a file in the archive's central directory.
596// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
597// Returns -1 if the file cannot be found.
598int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
599
600// Extracts a archive file to a memory buffer using no memory allocation.
601mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
602mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
603
604// Extracts a archive file to a memory buffer.
605mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
606mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
607
608// Extracts a archive file to a dynamically allocated heap buffer.
609void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
610void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
611
612// Extracts a archive file using a callback function to output the file's data.
613mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
614mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
615
616#ifndef MINIZ_NO_STDIO
617// Extracts a archive file to a disk file and sets its last accessed and modified times.
618// This function only extracts files, not archive directory records.
619mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
620mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
621#endif
622
623// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
624mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
625
626// ZIP archive writing
627
628#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
629
630// Inits a ZIP archive writer.
631mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
632mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
633
634#ifndef MINIZ_NO_STDIO
635mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
636#endif
637
638// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
639// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
640// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
641// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
642// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
643// the archive is finalized the file's central directory will be hosed.
644mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
645
646// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
647// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
648// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
649mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
650mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
651
652#ifndef MINIZ_NO_STDIO
653// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
654// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
655mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
656#endif
657
658// Adds a file to an archive by fully cloning the data from another archive.
659// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
660mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
661
662// Finalizes the archive by writing the central directory records followed by the end of central directory record.
663// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
664// An archive must be manually finalized by calling this function for it to be valid.
665mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
666mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
667
668// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
669// Note for the archive to be valid, it must have been finalized before ending.
670mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
671
672// Misc. high-level helper functions:
673
674// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
675// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
676mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
677
678// Reads a single file from an archive into a heap block.
679// Returns NULL on failure.
680void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);
681
682#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
683
684#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
685
686// ------------------- Low-level Decompression API Definitions
687
688// Decompression flags used by tinfl_decompress().
689// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
690// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
691// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
692// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
693enum
694{
695 TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
696 TINFL_FLAG_HAS_MORE_INPUT = 2,
697 TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
698 TINFL_FLAG_COMPUTE_ADLER32 = 8
699};
700
701// High level decompression functions:
702// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
703// On entry:
704// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
705// On return:
706// Function returns a pointer to the decompressed data, or NULL on failure.
707// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
708// The caller must call mz_free() on the returned block when it's no longer needed.
709void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
710
711// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
712// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
713#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
714size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
715
716// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
717// Returns 1 on success or 0 on failure.
718typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
719int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
720
721struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
722
723// Max size of LZ dictionary.
724#define TINFL_LZ_DICT_SIZE 32768
725
726// Return status.
727typedef enum
728{
729 TINFL_STATUS_BAD_PARAM = -3,
730 TINFL_STATUS_ADLER32_MISMATCH = -2,
731 TINFL_STATUS_FAILED = -1,
732 TINFL_STATUS_DONE = 0,
733 TINFL_STATUS_NEEDS_MORE_INPUT = 1,
734 TINFL_STATUS_HAS_MORE_OUTPUT = 2
735} tinfl_status;
736
737// Initializes the decompressor to its initial state.
738#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
739#define tinfl_get_adler32(r) (r)->m_check_adler32
740
741// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
742// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
743tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
744
745// Internal/private bits follow.
746enum
747{
748 TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
749 TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
750};
751
752typedef struct
753{
754 mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
755 mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
756} tinfl_huff_table;
757
758#if MINIZ_HAS_64BIT_REGISTERS
759 #define TINFL_USE_64BIT_BITBUF 1
760#endif
761
762#if TINFL_USE_64BIT_BITBUF
763 typedef mz_uint64 tinfl_bit_buf_t;
764 #define TINFL_BITBUF_SIZE (64)
765#else
766 typedef mz_uint32 tinfl_bit_buf_t;
767 #define TINFL_BITBUF_SIZE (32)
768#endif
769
770struct tinfl_decompressor_tag
771{
772 mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
773 tinfl_bit_buf_t m_bit_buf;
774 size_t m_dist_from_out_buf_start;
775 tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
776 mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
777};
778
779// ------------------- Low-level Compression API Definitions
780
781// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
782#define TDEFL_LESS_MEMORY 0
783
784// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
785// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
786enum
787{
788 TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
789};
790
791// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
792// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
793// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
794// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
795// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
796// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
797// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
798// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
799// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).
800enum
801{
802 TDEFL_WRITE_ZLIB_HEADER = 0x01000,
803 TDEFL_COMPUTE_ADLER32 = 0x02000,
804 TDEFL_GREEDY_PARSING_FLAG = 0x04000,
805 TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
806 TDEFL_RLE_MATCHES = 0x10000,
807 TDEFL_FILTER_MATCHES = 0x20000,
808 TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
809 TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
810};
811
812// High level compression functions:
813// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
814// On entry:
815// pSrc_buf, src_buf_len: Pointer and size of source block to compress.
816// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
817// On return:
818// Function returns a pointer to the compressed data, or NULL on failure.
819// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
820// The caller must free() the returned block when it's no longer needed.
821void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
822
823// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
824// Returns 0 on failure.
825size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
826
827// Compresses an image to a compressed PNG file in memory.
828// On entry:
829// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
830// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
831// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
832// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
833// On return:
834// Function returns a pointer to the compressed data, or NULL on failure.
835// *pLen_out will be set to the size of the PNG image file.
836// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
837void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
838void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
839
840// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
841typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
842
843// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
844mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
845
846enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
847
848// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
849#if TDEFL_LESS_MEMORY
850enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
851#else
852enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
853#endif
854
855// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
856typedef enum
857{
858 TDEFL_STATUS_BAD_PARAM = -2,
859 TDEFL_STATUS_PUT_BUF_FAILED = -1,
860 TDEFL_STATUS_OKAY = 0,
861 TDEFL_STATUS_DONE = 1,
862} tdefl_status;
863
864// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
865typedef enum
866{
867 TDEFL_NO_FLUSH = 0,
868 TDEFL_SYNC_FLUSH = 2,
869 TDEFL_FULL_FLUSH = 3,
870 TDEFL_FINISH = 4
871} tdefl_flush;
872
873// tdefl's compression state structure.
874typedef struct
875{
876 tdefl_put_buf_func_ptr m_pPut_buf_func;
877 void *m_pPut_buf_user;
878 mz_uint m_flags, m_max_probes[2];
879 int m_greedy_parsing;
880 mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
881 mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
882 mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
883 mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
884 tdefl_status m_prev_return_status;
885 const void *m_pIn_buf;
886 void *m_pOut_buf;
887 size_t *m_pIn_buf_size, *m_pOut_buf_size;
888 tdefl_flush m_flush;
889 const mz_uint8 *m_pSrc;
890 size_t m_src_buf_left, m_out_buf_ofs;
891 mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
892 mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
893 mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
894 mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
895 mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
896 mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
897 mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
898 mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
899} tdefl_compressor;
900
901// Initializes the compressor.
902// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
903// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
904// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
905// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
906tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
907
908// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
909tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
910
911// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
912// tdefl_compress_buffer() always consumes the entire input buffer.
913tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
914
915tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
916mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
917
918// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
919#ifndef MINIZ_NO_ZLIB_APIS
920// Create tdefl_compress() flags given zlib-style compression parameters.
921// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
922// window_bits may be -15 (raw deflate) or 15 (zlib)
923// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
924mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
925#endif // #ifndef MINIZ_NO_ZLIB_APIS
926
927#ifdef __cplusplus
928}
929#endif
930
931#endif // MINIZ_HEADER_INCLUDED
932
933// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
934
935#ifndef MINIZ_HEADER_FILE_ONLY
936
937typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
938typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
939typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];
940
941#include <string.h>
942
943#ifdef MINIZ_NO_ASSERT
944 #define MZ_ASSERT(x)
945#else
946 #include <assert.h>
947 #define MZ_ASSERT(x) assert(x)
948#endif
949
950#ifdef MINIZ_NO_MALLOC
951 #define MZ_MALLOC(x) NULL
952 #define MZ_FREE(x) (void)x, ((void)0)
953 #define MZ_REALLOC(p, x) NULL
954#else
955 #define MZ_MALLOC(x) malloc(x)
956 #define MZ_FREE(x) free(x)
957 #define MZ_REALLOC(p, x) realloc(p, x)
958#endif
959
960#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
961#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
962#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
963
964#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
965 #define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
966 #define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
967#else
968 #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
969 #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
970#endif
971
972#ifdef _MSC_VER
973 #define MZ_FORCEINLINE __forceinline
974#elif defined(__GNUC__)
975 #define MZ_FORCEINLINE inline __attribute__((__always_inline__))
976#else
977 #define MZ_FORCEINLINE inline
978#endif
979
980#ifdef __cplusplus
981 extern "C" {
982#endif
983
984// ------------------- zlib-style API's
985
986mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
987{
988 mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
989 if (!ptr) return MZ_ADLER32_INIT;
990 while (buf_len) {
991 for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
992 s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
993 s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
994 }
995 for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
996 s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
997 }
998 return (s2 << 16) + s1;
999}
1000
1001// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
1002mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
1003{
1004 static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
1005 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
1006 mz_uint32 crcu32 = (mz_uint32)crc;
1007 if (!ptr) return MZ_CRC32_INIT;
1008 crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
1009 return ~crcu32;
1010}
1011
1012void mz_free(void *p)
1013{
1014 MZ_FREE(p);
1015}
1016
1017#ifndef MINIZ_NO_ZLIB_APIS
1018
1019static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
1020static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
1021static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }
1022
1023const char *mz_version(void)
1024{
1025 return MZ_VERSION;
1026}
1027
1028int mz_deflateInit(mz_streamp pStream, int level)
1029{
1030 return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
1031}
1032
1033int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
1034{
1035 tdefl_compressor *pComp;
1036 mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
1037
1038 if (!pStream) return MZ_STREAM_ERROR;
1039 if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;
1040
1041 pStream->data_type = 0;
1042 pStream->adler = MZ_ADLER32_INIT;
1043 pStream->msg = NULL;
1044 pStream->reserved = 0;
1045 pStream->total_in = 0;
1046 pStream->total_out = 0;
1047 if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
1048 if (!pStream->zfree) pStream->zfree = def_free_func;
1049
1050 pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
1051 if (!pComp)
1052 return MZ_MEM_ERROR;
1053
1054 pStream->state = (struct mz_internal_state *)pComp;
1055
1056 if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
1057 {
1058 mz_deflateEnd(pStream);
1059 return MZ_PARAM_ERROR;
1060 }
1061
1062 return MZ_OK;
1063}
1064
1065int mz_deflateReset(mz_streamp pStream)
1066{
1067 if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
1068 pStream->total_in = pStream->total_out = 0;
1069 tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
1070 return MZ_OK;
1071}
1072
1073int mz_deflate(mz_streamp pStream, int flush)
1074{
1075 size_t in_bytes, out_bytes;
1076 mz_ulong orig_total_in, orig_total_out;
1077 int mz_status = MZ_OK;
1078
1079 if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
1080 if (!pStream->avail_out) return MZ_BUF_ERROR;
1081
1082 if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
1083
1084 if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
1085 return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
1086
1087 orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
1088 for ( ; ; )
1089 {
1090 tdefl_status defl_status;
1091 in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
1092
1093 defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
1094 pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
1095 pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);
1096
1097 pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
1098 pStream->total_out += (mz_uint)out_bytes;
1099
1100 if (defl_status < 0)
1101 {
1102 mz_status = MZ_STREAM_ERROR;
1103 break;
1104 }
1105 else if (defl_status == TDEFL_STATUS_DONE)
1106 {
1107 mz_status = MZ_STREAM_END;
1108 break;
1109 }
1110 else if (!pStream->avail_out)
1111 break;
1112 else if ((!pStream->avail_in) && (flush != MZ_FINISH))
1113 {
1114 if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
1115 break;
1116 return MZ_BUF_ERROR; // Can't make forward progress without some input.
1117 }
1118 }
1119 return mz_status;
1120}
1121
1122int mz_deflateEnd(mz_streamp pStream)
1123{
1124 if (!pStream) return MZ_STREAM_ERROR;
1125 if (pStream->state)
1126 {
1127 pStream->zfree(pStream->opaque, pStream->state);
1128 pStream->state = NULL;
1129 }
1130 return MZ_OK;
1131}
1132
1133mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
1134{
1135 (void)pStream;
1136 // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
1137 return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
1138}
1139
1140int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
1141{
1142 int status;
1143 mz_stream stream;
1144 memset(&stream, 0, sizeof(stream));
1145
1146 // In case mz_ulong is 64-bits (argh I hate longs).
1147 if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
1148
1149 stream.next_in = pSource;
1150 stream.avail_in = (mz_uint32)source_len;
1151 stream.next_out = pDest;
1152 stream.avail_out = (mz_uint32)*pDest_len;
1153
1154 status = mz_deflateInit(&stream, level);
1155 if (status != MZ_OK) return status;
1156
1157 status = mz_deflate(&stream, MZ_FINISH);
1158 if (status != MZ_STREAM_END)
1159 {
1160 mz_deflateEnd(&stream);
1161 return (status == MZ_OK) ? MZ_BUF_ERROR : status;
1162 }
1163
1164 *pDest_len = stream.total_out;
1165 return mz_deflateEnd(&stream);
1166}
1167
1168int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
1169{
1170 return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
1171}
1172
1173mz_ulong mz_compressBound(mz_ulong source_len)
1174{
1175 return mz_deflateBound(NULL, source_len);
1176}
1177
1178typedef struct
1179{
1180 tinfl_decompressor m_decomp;
1181 mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
1182 mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
1183 tinfl_status m_last_status;
1184} inflate_state;
1185
1186int mz_inflateInit2(mz_streamp pStream, int window_bits)
1187{
1188 inflate_state *pDecomp;
1189 if (!pStream) return MZ_STREAM_ERROR;
1190 if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
1191
1192 pStream->data_type = 0;
1193 pStream->adler = 0;
1194 pStream->msg = NULL;
1195 pStream->total_in = 0;
1196 pStream->total_out = 0;
1197 pStream->reserved = 0;
1198 if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
1199 if (!pStream->zfree) pStream->zfree = def_free_func;
1200
1201 pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
1202 if (!pDecomp) return MZ_MEM_ERROR;
1203
1204 pStream->state = (struct mz_internal_state *)pDecomp;
1205
1206 tinfl_init(&pDecomp->m_decomp);
1207 pDecomp->m_dict_ofs = 0;
1208 pDecomp->m_dict_avail = 0;
1209 pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
1210 pDecomp->m_first_call = 1;
1211 pDecomp->m_has_flushed = 0;
1212 pDecomp->m_window_bits = window_bits;
1213
1214 return MZ_OK;
1215}
1216
1217int mz_inflateInit(mz_streamp pStream)
1218{
1219 return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
1220}
1221
1222int mz_inflate(mz_streamp pStream, int flush)
1223{
1224 inflate_state* pState;
1225 mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
1226 size_t in_bytes, out_bytes, orig_avail_in;
1227 tinfl_status status;
1228
1229 if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
1230 if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
1231 if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
1232
1233 pState = (inflate_state*)pStream->state;
1234 if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
1235 orig_avail_in = pStream->avail_in;
1236
1237 first_call = pState->m_first_call; pState->m_first_call = 0;
1238 if (pState->m_last_status < 0) return MZ_DATA_ERROR;
1239
1240 if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
1241 pState->m_has_flushed |= (flush == MZ_FINISH);
1242
1243 if ((flush == MZ_FINISH) && (first_call))
1244 {
1245 // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
1246 decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
1247 in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
1248 status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
1249 pState->m_last_status = status;
1250 pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
1251 pStream->adler = tinfl_get_adler32(&pState->m_decomp);
1252 pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
1253
1254 if (status < 0)
1255 return MZ_DATA_ERROR;
1256 else if (status != TINFL_STATUS_DONE)
1257 {
1258 pState->m_last_status = TINFL_STATUS_FAILED;
1259 return MZ_BUF_ERROR;
1260 }
1261 return MZ_STREAM_END;
1262 }
1263 // flush != MZ_FINISH then we must assume there's more input.
1264 if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
1265
1266 if (pState->m_dict_avail)
1267 {
1268 n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
1269 memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
1270 pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
1271 pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
1272 return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
1273 }
1274
1275 for ( ; ; )
1276 {
1277 in_bytes = pStream->avail_in;
1278 out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
1279
1280 status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
1281 pState->m_last_status = status;
1282
1283 pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
1284 pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
1285
1286 pState->m_dict_avail = (mz_uint)out_bytes;
1287
1288 n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
1289 memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
1290 pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
1291 pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
1292
1293 if (status < 0)
1294 return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
1295 else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
1296 return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
1297 else if (flush == MZ_FINISH)
1298 {
1299 // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
1300 if (status == TINFL_STATUS_DONE)
1301 return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
1302 // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
1303 else if (!pStream->avail_out)
1304 return MZ_BUF_ERROR;
1305 }
1306 else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
1307 break;
1308 }
1309
1310 return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
1311}
1312
1313int mz_inflateEnd(mz_streamp pStream)
1314{
1315 if (!pStream)
1316 return MZ_STREAM_ERROR;
1317 if (pStream->state)
1318 {
1319 pStream->zfree(pStream->opaque, pStream->state);
1320 pStream->state = NULL;
1321 }
1322 return MZ_OK;
1323}
1324
1325int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
1326{
1327 mz_stream stream;
1328 int status;
1329 memset(&stream, 0, sizeof(stream));
1330
1331 // In case mz_ulong is 64-bits (argh I hate longs).
1332 if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
1333
1334 stream.next_in = pSource;
1335 stream.avail_in = (mz_uint32)source_len;
1336 stream.next_out = pDest;
1337 stream.avail_out = (mz_uint32)*pDest_len;
1338
1339 status = mz_inflateInit(&stream);
1340 if (status != MZ_OK)
1341 return status;
1342
1343 status = mz_inflate(&stream, MZ_FINISH);
1344 if (status != MZ_STREAM_END)
1345 {
1346 mz_inflateEnd(&stream);
1347 return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
1348 }
1349 *pDest_len = stream.total_out;
1350
1351 return mz_inflateEnd(&stream);
1352}
1353
1354const char *mz_error(int err)
1355{
1356 static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
1357 {
1358 { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
1359 { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
1360 };
1361 mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
1362 return NULL;
1363}
1364
1365#endif //MINIZ_NO_ZLIB_APIS
1366
1367// ------------------- Low-level Decompression (completely independent from all compression API's)
1368
1369#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
1370#define TINFL_MEMSET(p, c, l) memset(p, c, l)
1371
1372#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
1373#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
1374#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
1375#define TINFL_CR_FINISH }
1376
1377// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
1378// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
1379#define TINFL_GET_BYTE(state_index, c) do { \
1380 if (pIn_buf_cur >= pIn_buf_end) { \
1381 for ( ; ; ) { \
1382 if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
1383 TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
1384 if (pIn_buf_cur < pIn_buf_end) { \
1385 c = *pIn_buf_cur++; \
1386 break; \
1387 } \
1388 } else { \
1389 c = 0; \
1390 break; \
1391 } \
1392 } \
1393 } else c = *pIn_buf_cur++; } MZ_MACRO_END
1394
1395#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
1396#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
1397#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
1398
1399// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
1400// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
1401// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
1402// bit buffer contains >=15 bits (deflate's max. Huffman code size).
1403#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
1404 do { \
1405 temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
1406 if (temp >= 0) { \
1407 code_len = temp >> 9; \
1408 if ((code_len) && (num_bits >= code_len)) \
1409 break; \
1410 } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
1411 code_len = TINFL_FAST_LOOKUP_BITS; \
1412 do { \
1413 temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
1414 } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
1415 } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
1416 } while (num_bits < 15);
1417
1418// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
1419// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
1420// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
1421// The slow path is only executed at the very end of the input buffer.
1422#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
1423 int temp; mz_uint code_len, c; \
1424 if (num_bits < 15) { \
1425 if ((pIn_buf_end - pIn_buf_cur) < 2) { \
1426 TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
1427 } else { \
1428 bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
1429 } \
1430 } \
1431 if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
1432 code_len = temp >> 9, temp &= 511; \
1433 else { \
1434 code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
1435 } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
1436
1437tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
1438{
1439 static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
1440 static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
1441 static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
1442 static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
1443 static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
1444 static const int s_min_table_sizes[3] = { 257, 1, 4 };
1445
1446 tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
1447 const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
1448 mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
1449 size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
1450
1451 // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
1452 if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
1453
1454 num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
1455 TINFL_CR_BEGIN
1456
1457 bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
1458 if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
1459 {
1460 TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
1461 counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
1462 if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
1463 if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
1464 }
1465
1466 do
1467 {
1468 TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
1469 if (r->m_type == 0)
1470 {
1471 TINFL_SKIP_BITS(5, num_bits & 7);
1472 for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
1473 if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
1474 while ((counter) && (num_bits))
1475 {
1476 TINFL_GET_BITS(51, dist, 8);
1477 while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
1478 *pOut_buf_cur++ = (mz_uint8)dist;
1479 counter--;
1480 }
1481 while (counter)
1482 {
1483 size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
1484 while (pIn_buf_cur >= pIn_buf_end)
1485 {
1486 if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
1487 {
1488 TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
1489 }
1490 else
1491 {
1492 TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
1493 }
1494 }
1495 n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
1496 TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
1497 }
1498 }
1499 else if (r->m_type == 3)
1500 {
1501 TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
1502 }
1503 else
1504 {
1505 if (r->m_type == 1)
1506 {
1507 mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
1508 r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
1509 for ( i = 0; i <= 143; ++i) *p++ = 8;
1510 for ( ; i <= 255; ++i) *p++ = 9;
1511 for ( ; i <= 279; ++i) *p++ = 7;
1512 for ( ; i <= 287; ++i) *p++ = 8;
1513 }
1514 else
1515 {
1516 for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
1517 MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
1518 r->m_table_sizes[2] = 19;
1519 }
1520 for ( ; (int)r->m_type >= 0; r->m_type--)
1521 {
1522 int tree_next, tree_cur; tinfl_huff_table *pTable;
1523 mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
1524 for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
1525 used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
1526 for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
1527 if ((65536 != total) && (used_syms > 1))
1528 {
1529 TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
1530 }
1531 for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
1532 {
1533 mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
1534 cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
1535 if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
1536 if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
1537 rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
1538 for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
1539 {
1540 tree_cur -= ((rev_code >>= 1) & 1);
1541 if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
1542 }
1543 tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
1544 }
1545 if (r->m_type == 2)
1546 {
1547 for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
1548 {
1549 mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
1550 if ((dist == 16) && (!counter))
1551 {
1552 TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
1553 }
1554 num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
1555 TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
1556 }
1557 if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
1558 {
1559 TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
1560 }
1561 TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
1562 }
1563 }
1564 for ( ; ; )
1565 {
1566 mz_uint8 *pSrc;
1567 for ( ; ; )
1568 {
1569 if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
1570 {
1571 TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
1572 if (counter >= 256)
1573 break;
1574 while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
1575 *pOut_buf_cur++ = (mz_uint8)counter;
1576 }
1577 else
1578 {
1579 int sym2; mz_uint code_len;
1580#if TINFL_USE_64BIT_BITBUF
1581 if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
1582#else
1583 if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
1584#endif
1585 if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
1586 code_len = sym2 >> 9;
1587 else
1588 {
1589 code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
1590 }
1591 counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
1592 if (counter & 256)
1593 break;
1594
1595#if !TINFL_USE_64BIT_BITBUF
1596 if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
1597#endif
1598 if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
1599 code_len = sym2 >> 9;
1600 else
1601 {
1602 code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
1603 }
1604 bit_buf >>= code_len; num_bits -= code_len;
1605
1606 pOut_buf_cur[0] = (mz_uint8)counter;
1607 if (sym2 & 256)
1608 {
1609 pOut_buf_cur++;
1610 counter = sym2;
1611 break;
1612 }
1613 pOut_buf_cur[1] = (mz_uint8)sym2;
1614 pOut_buf_cur += 2;
1615 }
1616 }
1617 if ((counter &= 511) == 256) break;
1618
1619 num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
1620 if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
1621
1622 TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
1623 num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
1624 if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
1625
1626 dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
1627 if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
1628 {
1629 TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
1630 }
1631
1632 pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
1633
1634 if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
1635 {
1636 while (counter--)
1637 {
1638 while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
1639 *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
1640 }
1641 continue;
1642 }
1643#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
1644 else if ((counter >= 9) && (counter <= dist))
1645 {
1646 const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
1647 do
1648 {
1649 ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
1650 ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
1651 pOut_buf_cur += 8;
1652 } while ((pSrc += 8) < pSrc_end);
1653 if ((counter &= 7) < 3)
1654 {
1655 if (counter)
1656 {
1657 pOut_buf_cur[0] = pSrc[0];
1658 if (counter > 1)
1659 pOut_buf_cur[1] = pSrc[1];
1660 pOut_buf_cur += counter;
1661 }
1662 continue;
1663 }
1664 }
1665#endif
1666 do
1667 {
1668 pOut_buf_cur[0] = pSrc[0];
1669 pOut_buf_cur[1] = pSrc[1];
1670 pOut_buf_cur[2] = pSrc[2];
1671 pOut_buf_cur += 3; pSrc += 3;
1672 } while ((int)(counter -= 3) > 2);
1673 if ((int)counter > 0)
1674 {
1675 pOut_buf_cur[0] = pSrc[0];
1676 if ((int)counter > 1)
1677 pOut_buf_cur[1] = pSrc[1];
1678 pOut_buf_cur += counter;
1679 }
1680 }
1681 }
1682 } while (!(r->m_final & 1));
1683 if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
1684 {
1685 TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
1686 }
1687 TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
1688 TINFL_CR_FINISH
1689
1690common_exit:
1691 r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
1692 *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
1693 if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
1694 {
1695 const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
1696 mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
1697 while (buf_len)
1698 {
1699 for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
1700 {
1701 s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
1702 s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
1703 }
1704 for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
1705 s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
1706 }
1707 r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
1708 }
1709 return status;
1710}
1711
1712// Higher level helper functions.
1713void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
1714{
1715 tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
1716 *pOut_len = 0;
1717 tinfl_init(&decomp);
1718 for ( ; ; )
1719 {
1720 size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
1721 tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
1722 (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
1723 if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
1724 {
1725 MZ_FREE(pBuf); *pOut_len = 0; return NULL;
1726 }
1727 src_buf_ofs += src_buf_size;
1728 *pOut_len += dst_buf_size;
1729 if (status == TINFL_STATUS_DONE) break;
1730 new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
1731 pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
1732 if (!pNew_buf)
1733 {
1734 MZ_FREE(pBuf); *pOut_len = 0; return NULL;
1735 }
1736 pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
1737 }
1738 return pBuf;
1739}
1740
1741size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
1742{
1743 tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
1744 status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
1745 return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
1746}
1747
1748int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
1749{
1750 int result = 0;
1751 tinfl_decompressor decomp;
1752 mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
1753 if (!pDict)
1754 return TINFL_STATUS_FAILED;
1755 tinfl_init(&decomp);
1756 for ( ; ; )
1757 {
1758 size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
1759 tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
1760 (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
1761 in_buf_ofs += in_buf_size;
1762 if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
1763 break;
1764 if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
1765 {
1766 result = (status == TINFL_STATUS_DONE);
1767 break;
1768 }
1769 dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
1770 }
1771 MZ_FREE(pDict);
1772 *pIn_buf_size = in_buf_ofs;
1773 return result;
1774}
1775
1776// ------------------- Low-level Compression (independent from all decompression API's)
1777
1778// Purposely making these tables static for faster init and thread safety.
1779static const mz_uint16 s_tdefl_len_sym[256] = {
1780 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
1781 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
1782 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
1783 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
1784 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
1785 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
1786 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
1787 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };
1788
1789static const mz_uint8 s_tdefl_len_extra[256] = {
1790 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
1791 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
1792 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
1793 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };
1794
1795static const mz_uint8 s_tdefl_small_dist_sym[512] = {
1796 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
1797 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
1798 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
1799 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
1800 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
1801 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
1802 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
1803 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
1804 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
1805 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
1806 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
1807 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };
1808
1809static const mz_uint8 s_tdefl_small_dist_extra[512] = {
1810 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
1811 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1812 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1813 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1814 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1815 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1816 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1817 7,7,7,7,7,7,7,7 };
1818
1819static const mz_uint8 s_tdefl_large_dist_sym[128] = {
1820 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
1821 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
1822 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };
1823
1824static const mz_uint8 s_tdefl_large_dist_extra[128] = {
1825 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
1826 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
1827 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };
1828
1829// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
1830typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
1831static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
1832{
1833 mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
1834 for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
1835 while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
1836 for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
1837 {
1838 const mz_uint32* pHist = &hist[pass << 8];
1839 mz_uint offsets[256], cur_ofs = 0;
1840 for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
1841 for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
1842 { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
1843 }
1844 return pCur_syms;
1845}
1846
1847// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
1848static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
1849{
1850 int root, leaf, next, avbl, used, dpth;
1851 if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
1852 A[0].m_key += A[1].m_key; root = 0; leaf = 2;
1853 for (next=1; next < n-1; next++)
1854 {
1855 if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
1856 if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
1857 }
1858 A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
1859 avbl = 1; used = dpth = 0; root = n-2; next = n-1;
1860 while (avbl>0)
1861 {
1862 while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
1863 while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
1864 avbl = 2*used; dpth++; used = 0;
1865 }
1866}
1867
1868// Limits canonical Huffman code table's max code size.
1869enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
1870static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
1871{
1872 int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
1873 for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
1874 for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
1875 while (total != (1UL << max_code_size))
1876 {
1877 pNum_codes[max_code_size]--;
1878 for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
1879 total--;
1880 }
1881}
1882
1883static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
1884{
1885 int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
1886 if (static_table)
1887 {
1888 for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
1889 }
1890 else
1891 {
1892 tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
1893 int num_used_syms = 0;
1894 const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
1895 for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }
1896
1897 pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
1898
1899 for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;
1900
1901 tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
1902
1903 MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
1904 for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
1905 for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
1906 }
1907
1908 next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);
1909
1910 for (i = 0; i < table_len; i++)
1911 {
1912 mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
1913 code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
1914 d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
1915 }
1916}
1917
1918#define TDEFL_PUT_BITS(b, l) do { \
1919 mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
1920 d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
1921 while (d->m_bits_in >= 8) { \
1922 if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
1923 *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
1924 d->m_bit_buffer >>= 8; \
1925 d->m_bits_in -= 8; \
1926 } \
1927} MZ_MACRO_END
1928
1929#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
1930 if (rle_repeat_count < 3) { \
1931 d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
1932 while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
1933 } else { \
1934 d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
1935} rle_repeat_count = 0; } }
1936
1937#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
1938 if (rle_z_count < 3) { \
1939 d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
1940 } else if (rle_z_count <= 10) { \
1941 d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
1942 } else { \
1943 d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
1944} rle_z_count = 0; } }
1945
1946static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
1947
1948static void tdefl_start_dynamic_block(tdefl_compressor *d)
1949{
1950 int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
1951 mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
1952
1953 d->m_huff_count[0][256] = 1;
1954
1955 tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
1956 tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
1957
1958 for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
1959 for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;
1960
1961 memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
1962 memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
1963 total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;
1964
1965 memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
1966 for (i = 0; i < total_code_sizes_to_pack; i++)
1967 {
1968 mz_uint8 code_size = code_sizes_to_pack[i];
1969 if (!code_size)
1970 {
1971 TDEFL_RLE_PREV_CODE_SIZE();
1972 if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
1973 }
1974 else
1975 {
1976 TDEFL_RLE_ZERO_CODE_SIZE();
1977 if (code_size != prev_code_size)
1978 {
1979 TDEFL_RLE_PREV_CODE_SIZE();
1980 d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
1981 }
1982 else if (++rle_repeat_count == 6)
1983 {
1984 TDEFL_RLE_PREV_CODE_SIZE();
1985 }
1986 }
1987 prev_code_size = code_size;
1988 }
1989 if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }
1990
1991 tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
1992
1993 TDEFL_PUT_BITS(2, 2);
1994
1995 TDEFL_PUT_BITS(num_lit_codes - 257, 5);
1996 TDEFL_PUT_BITS(num_dist_codes - 1, 5);
1997
1998 for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
1999 num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
2000 for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
2001
2002 for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
2003 {
2004 mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
2005 TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
2006 if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
2007 }
2008}
2009
2010static void tdefl_start_static_block(tdefl_compressor *d)
2011{
2012 mz_uint i;
2013 mz_uint8 *p = &d->m_huff_code_sizes[0][0];
2014
2015 for (i = 0; i <= 143; ++i) *p++ = 8;
2016 for ( ; i <= 255; ++i) *p++ = 9;
2017 for ( ; i <= 279; ++i) *p++ = 7;
2018 for ( ; i <= 287; ++i) *p++ = 8;
2019
2020 memset(d->m_huff_code_sizes[1], 5, 32);
2021
2022 tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
2023 tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
2024
2025 TDEFL_PUT_BITS(1, 2);
2026}
2027
2028static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
2029
2030#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
2031static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
2032{
2033 mz_uint flags;
2034 mz_uint8 *pLZ_codes;
2035 mz_uint8 *pOutput_buf = d->m_pOutput_buf;
2036 mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
2037 mz_uint64 bit_buffer = d->m_bit_buffer;
2038 mz_uint bits_in = d->m_bits_in;
2039
2040#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }
2041
2042 flags = 1;
2043 for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
2044 {
2045 if (flags == 1)
2046 flags = *pLZ_codes++ | 0x100;
2047
2048 if (flags & 1)
2049 {
2050 mz_uint s0, s1, n0, n1, sym, num_extra_bits;
2051 mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;
2052
2053 MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
2054 TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
2055 TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
2056
2057 // This sequence coaxes MSVC into using cmov's vs. jmp's.
2058 s0 = s_tdefl_small_dist_sym[match_dist & 511];
2059 n0 = s_tdefl_small_dist_extra[match_dist & 511];
2060 s1 = s_tdefl_large_dist_sym[match_dist >> 8];
2061 n1 = s_tdefl_large_dist_extra[match_dist >> 8];
2062 sym = (match_dist < 512) ? s0 : s1;
2063 num_extra_bits = (match_dist < 512) ? n0 : n1;
2064
2065 MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
2066 TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
2067 TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
2068 }
2069 else
2070 {
2071 mz_uint lit = *pLZ_codes++;
2072 MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
2073 TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
2074
2075 if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
2076 {
2077 flags >>= 1;
2078 lit = *pLZ_codes++;
2079 MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
2080 TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
2081
2082 if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
2083 {
2084 flags >>= 1;
2085 lit = *pLZ_codes++;
2086 MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
2087 TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
2088 }
2089 }
2090 }
2091
2092 if (pOutput_buf >= d->m_pOutput_buf_end)
2093 return MZ_FALSE;
2094
2095 *(mz_uint64*)pOutput_buf = bit_buffer;
2096 pOutput_buf += (bits_in >> 3);
2097 bit_buffer >>= (bits_in & ~7);
2098 bits_in &= 7;
2099 }
2100
2101#undef TDEFL_PUT_BITS_FAST
2102
2103 d->m_pOutput_buf = pOutput_buf;
2104 d->m_bits_in = 0;
2105 d->m_bit_buffer = 0;
2106
2107 while (bits_in)
2108 {
2109 mz_uint32 n = MZ_MIN(bits_in, 16);
2110 TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
2111 bit_buffer >>= n;
2112 bits_in -= n;
2113 }
2114
2115 TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
2116
2117 return (d->m_pOutput_buf < d->m_pOutput_buf_end);
2118}
2119#else
2120static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
2121{
2122 mz_uint flags;
2123 mz_uint8 *pLZ_codes;
2124
2125 flags = 1;
2126 for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
2127 {
2128 if (flags == 1)
2129 flags = *pLZ_codes++ | 0x100;
2130 if (flags & 1)
2131 {
2132 mz_uint sym, num_extra_bits;
2133 mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;
2134
2135 MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
2136 TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
2137 TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
2138
2139 if (match_dist < 512)
2140 {
2141 sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
2142 }
2143 else
2144 {
2145 sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
2146 }
2147 MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
2148 TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
2149 TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
2150 }
2151 else
2152 {
2153 mz_uint lit = *pLZ_codes++;
2154 MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
2155 TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
2156 }
2157 }
2158
2159 TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
2160
2161 return (d->m_pOutput_buf < d->m_pOutput_buf_end);
2162}
2163#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
2164
2165static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
2166{
2167 if (static_block)
2168 tdefl_start_static_block(d);
2169 else
2170 tdefl_start_dynamic_block(d);
2171 return tdefl_compress_lz_codes(d);
2172}
2173
2174static int tdefl_flush_block(tdefl_compressor *d, int flush)
2175{
2176 mz_uint saved_bit_buf, saved_bits_in;
2177 mz_uint8 *pSaved_output_buf;
2178 mz_bool comp_block_succeeded = MZ_FALSE;
2179 int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
2180 mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
2181
2182 d->m_pOutput_buf = pOutput_buf_start;
2183 d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
2184
2185 MZ_ASSERT(!d->m_output_flush_remaining);
2186 d->m_output_flush_ofs = 0;
2187 d->m_output_flush_remaining = 0;
2188
2189 *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
2190 d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
2191
2192 if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
2193 {
2194 TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
2195 }
2196
2197 TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
2198
2199 pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;
2200
2201 if (!use_raw_block)
2202 comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
2203
2204 // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
2205 if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
2206 ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
2207 {
2208 mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
2209 TDEFL_PUT_BITS(0, 2);
2210 if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
2211 for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
2212 {
2213 TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
2214 }
2215 for (i = 0; i < d->m_total_lz_bytes; ++i)
2216 {
2217 TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
2218 }
2219 }
2220 // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
2221 else if (!comp_block_succeeded)
2222 {
2223 d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
2224 tdefl_compress_block(d, MZ_TRUE);
2225 }
2226
2227 if (flush)
2228 {
2229 if (flush == TDEFL_FINISH)
2230 {
2231 if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
2232 if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
2233 }
2234 else
2235 {
2236 mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
2237 }
2238 }
2239
2240 MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
2241
2242 memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
2243 memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
2244
2245 d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;
2246
2247 if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
2248 {
2249 if (d->m_pPut_buf_func)
2250 {
2251 *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
2252 if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
2253 return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
2254 }
2255 else if (pOutput_buf_start == d->m_output_buf)
2256 {
2257 int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
2258 memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
2259 d->m_out_buf_ofs += bytes_to_copy;
2260 if ((n -= bytes_to_copy) != 0)
2261 {
2262 d->m_output_flush_ofs = bytes_to_copy;
2263 d->m_output_flush_remaining = n;
2264 }
2265 }
2266 else
2267 {
2268 d->m_out_buf_ofs += n;
2269 }
2270 }
2271
2272 return d->m_output_flush_remaining;
2273}
2274
2275#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
2276#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
2277static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
2278{
2279 mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
2280 mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
2281 const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
2282 mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
2283 MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
2284 for ( ; ; )
2285 {
2286 for ( ; ; )
2287 {
2288 if (--num_probes_left == 0) return;
2289 #define TDEFL_PROBE \
2290 next_probe_pos = d->m_next[probe_pos]; \
2291 if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
2292 probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
2293 if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
2294 TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
2295 }
2296 if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
2297 do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
2298 (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
2299 if (!probe_len)
2300 {
2301 *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
2302 }
2303 else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
2304 {
2305 *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
2306 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
2307 }
2308 }
2309}
2310#else
2311static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
2312{
2313 mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
2314 mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
2315 const mz_uint8 *s = d->m_dict + pos, *p, *q;
2316 mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
2317 MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
2318 for ( ; ; )
2319 {
2320 for ( ; ; )
2321 {
2322 if (--num_probes_left == 0) return;
2323 #define TDEFL_PROBE \
2324 next_probe_pos = d->m_next[probe_pos]; \
2325 if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
2326 probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
2327 if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
2328 TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
2329 }
2330 if (!dist)
2331 break;
2332 p = s;
2333 q = d->m_dict + probe_pos;
2334 for (probe_len = 0; probe_len < max_match_len; probe_len++)
2335 if (*p++ != *q++) break;
2336 if (probe_len > match_len)
2337 {
2338 *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
2339 c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
2340 }
2341 }
2342}
2343#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
2344
2345#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
2346static mz_bool tdefl_compress_fast(tdefl_compressor *d)
2347{
2348 // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
2349 mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
2350 mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
2351 mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
2352
2353 while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
2354 {
2355 const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
2356 mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
2357 mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
2358 d->m_src_buf_left -= num_bytes_to_process;
2359 lookahead_size += num_bytes_to_process;
2360
2361 while (num_bytes_to_process)
2362 {
2363 mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
2364 memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
2365 if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
2366 memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
2367 d->m_pSrc += n;
2368 dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
2369 num_bytes_to_process -= n;
2370 }
2371
2372 dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
2373 if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;
2374
2375 while (lookahead_size >= 4)
2376 {
2377 mz_uint cur_match_dist, cur_match_len = 1;
2378 mz_uint8 *pCur_dict = d->m_dict + cur_pos;
2379 mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
2380 mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
2381 mz_uint probe_pos = d->m_hash[hash];
2382 d->m_hash[hash] = (mz_uint16)lookahead_pos;
2383
2384 if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
2385 {
2386 const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
2387 const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
2388 mz_uint32 probe_len = 32;
2389 do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
2390 (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
2391 cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
2392 if (!probe_len)
2393 cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
2394
2395 if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
2396 {
2397 cur_match_len = 1;
2398 *pLZ_code_buf++ = (mz_uint8)first_trigram;
2399 *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
2400 d->m_huff_count[0][(mz_uint8)first_trigram]++;
2401 }
2402 else
2403 {
2404 mz_uint32 s0, s1;
2405 cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
2406
2407 MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
2408
2409 cur_match_dist--;
2410
2411 pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
2412 *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
2413 pLZ_code_buf += 3;
2414 *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
2415
2416 s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
2417 s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
2418 d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
2419
2420 d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
2421 }
2422 }
2423 else
2424 {
2425 *pLZ_code_buf++ = (mz_uint8)first_trigram;
2426 *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
2427 d->m_huff_count[0][(mz_uint8)first_trigram]++;
2428 }
2429
2430 if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
2431
2432 total_lz_bytes += cur_match_len;
2433 lookahead_pos += cur_match_len;
2434 dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
2435 cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
2436 MZ_ASSERT(lookahead_size >= cur_match_len);
2437 lookahead_size -= cur_match_len;
2438
2439 if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
2440 {
2441 int n;
2442 d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
2443 d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
2444 if ((n = tdefl_flush_block(d, 0)) != 0)
2445 return (n < 0) ? MZ_FALSE : MZ_TRUE;
2446 total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
2447 }
2448 }
2449
2450 while (lookahead_size)
2451 {
2452 mz_uint8 lit = d->m_dict[cur_pos];
2453
2454 total_lz_bytes++;
2455 *pLZ_code_buf++ = lit;
2456 *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
2457 if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
2458
2459 d->m_huff_count[0][lit]++;
2460
2461 lookahead_pos++;
2462 dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
2463 cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
2464 lookahead_size--;
2465
2466 if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
2467 {
2468 int n;
2469 d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
2470 d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
2471 if ((n = tdefl_flush_block(d, 0)) != 0)
2472 return (n < 0) ? MZ_FALSE : MZ_TRUE;
2473 total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
2474 }
2475 }
2476 }
2477
2478 d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
2479 d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
2480 return MZ_TRUE;
2481}
2482#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
2483
2484static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
2485{
2486 d->m_total_lz_bytes++;
2487 *d->m_pLZ_code_buf++ = lit;
2488 *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
2489 d->m_huff_count[0][lit]++;
2490}
2491
2492static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
2493{
2494 mz_uint32 s0, s1;
2495
2496 MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
2497
2498 d->m_total_lz_bytes += match_len;
2499
2500 d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
2501
2502 match_dist -= 1;
2503 d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
2504 d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;
2505
2506 *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
2507
2508 s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
2509 d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
2510
2511 if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
2512}
2513
2514static mz_bool tdefl_compress_normal(tdefl_compressor *d)
2515{
2516 const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
2517 tdefl_flush flush = d->m_flush;
2518
2519 while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
2520 {
2521 mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
2522 // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
2523 if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
2524 {
2525 mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
2526 mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
2527 mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
2528 const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
2529 src_buf_left -= num_bytes_to_process;
2530 d->m_lookahead_size += num_bytes_to_process;
2531 while (pSrc != pSrc_end)
2532 {
2533 mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
2534 hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
2535 d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
2536 dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
2537 }
2538 }
2539 else
2540 {
2541 while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
2542 {
2543 mz_uint8 c = *pSrc++;
2544 mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
2545 src_buf_left--;
2546 d->m_dict[dst_pos] = c;
2547 if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
2548 d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
2549 if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
2550 {
2551 mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
2552 mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
2553 d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
2554 }
2555 }
2556 }
2557 d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
2558 if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
2559 break;
2560
2561 // Simple lazy/greedy parsing state machine.
2562 len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
2563 if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
2564 {
2565 if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
2566 {
2567 mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
2568 cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
2569 if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
2570 }
2571 }
2572 else
2573 {
2574 tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
2575 }
2576 if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
2577 {
2578 cur_match_dist = cur_match_len = 0;
2579 }
2580 if (d->m_saved_match_len)
2581 {
2582 if (cur_match_len > d->m_saved_match_len)
2583 {
2584 tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
2585 if (cur_match_len >= 128)
2586 {
2587 tdefl_record_match(d, cur_match_len, cur_match_dist);
2588 d->m_saved_match_len = 0; len_to_move = cur_match_len;
2589 }
2590 else
2591 {
2592 d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
2593 }
2594 }
2595 else
2596 {
2597 tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
2598 len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
2599 }
2600 }
2601 else if (!cur_match_dist)
2602 tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
2603 else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
2604 {
2605 tdefl_record_match(d, cur_match_len, cur_match_dist);
2606 len_to_move = cur_match_len;
2607 }
2608 else
2609 {
2610 d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
2611 }
2612 // Move the lookahead forward by len_to_move bytes.
2613 d->m_lookahead_pos += len_to_move;
2614 MZ_ASSERT(d->m_lookahead_size >= len_to_move);
2615 d->m_lookahead_size -= len_to_move;
2616 d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
2617 // Check if it's time to flush the current LZ codes to the internal output buffer.
2618 if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
2619 ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
2620 {
2621 int n;
2622 d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
2623 if ((n = tdefl_flush_block(d, 0)) != 0)
2624 return (n < 0) ? MZ_FALSE : MZ_TRUE;
2625 }
2626 }
2627
2628 d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
2629 return MZ_TRUE;
2630}
2631
2632static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
2633{
2634 if (d->m_pIn_buf_size)
2635 {
2636 *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
2637 }
2638
2639 if (d->m_pOut_buf_size)
2640 {
2641 size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
2642 memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
2643 d->m_output_flush_ofs += (mz_uint)n;
2644 d->m_output_flush_remaining -= (mz_uint)n;
2645 d->m_out_buf_ofs += n;
2646
2647 *d->m_pOut_buf_size = d->m_out_buf_ofs;
2648 }
2649
2650 return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
2651}
2652
2653tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
2654{
2655 if (!d)
2656 {
2657 if (pIn_buf_size) *pIn_buf_size = 0;
2658 if (pOut_buf_size) *pOut_buf_size = 0;
2659 return TDEFL_STATUS_BAD_PARAM;
2660 }
2661
2662 d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
2663 d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
2664 d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
2665 d->m_out_buf_ofs = 0;
2666 d->m_flush = flush;
2667
2668 if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
2669 (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
2670 {
2671 if (pIn_buf_size) *pIn_buf_size = 0;
2672 if (pOut_buf_size) *pOut_buf_size = 0;
2673 return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
2674 }
2675 d->m_wants_to_finish |= (flush == TDEFL_FINISH);
2676
2677 if ((d->m_output_flush_remaining) || (d->m_finished))
2678 return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
2679
2680#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
2681 if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
2682 ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
2683 ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
2684 {
2685 if (!tdefl_compress_fast(d))
2686 return d->m_prev_return_status;
2687 }
2688 else
2689#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
2690 {
2691 if (!tdefl_compress_normal(d))
2692 return d->m_prev_return_status;
2693 }
2694
2695 if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
2696 d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
2697
2698 if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
2699 {
2700 if (tdefl_flush_block(d, flush) < 0)
2701 return d->m_prev_return_status;
2702 d->m_finished = (flush == TDEFL_FINISH);
2703 if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
2704 }
2705
2706 return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
2707}
2708
2709tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
2710{
2711 MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
2712}
2713
2714tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
2715{
2716 d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
2717 d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
2718 d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
2719 if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
2720 d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
2721 d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
2722 d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
2723 d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
2724 d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
2725 d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
2726 d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
2727 d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
2728 memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
2729 memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
2730 return TDEFL_STATUS_OKAY;
2731}
2732
2733tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
2734{
2735 return d->m_prev_return_status;
2736}
2737
2738mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
2739{
2740 return d->m_adler32;
2741}
2742
2743mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
2744{
2745 tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
2746 pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
2747 succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
2748 succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
2749 MZ_FREE(pComp); return succeeded;
2750}
2751
2752typedef struct
2753{
2754 size_t m_size, m_capacity;
2755 mz_uint8 *m_pBuf;
2756 mz_bool m_expandable;
2757} tdefl_output_buffer;
2758
2759static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
2760{
2761 tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
2762 size_t new_size = p->m_size + len;
2763 if (new_size > p->m_capacity)
2764 {
2765 size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
2766 do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
2767 pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
2768 p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
2769 }
2770 memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
2771 return MZ_TRUE;
2772}
2773
2774void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
2775{
2776 tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
2777 if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
2778 out_buf.m_expandable = MZ_TRUE;
2779 if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
2780 *pOut_len = out_buf.m_size; return out_buf.m_pBuf;
2781}
2782
2783size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
2784{
2785 tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
2786 if (!pOut_buf) return 0;
2787 out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
2788 if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
2789 return out_buf.m_size;
2790}
2791
2792#ifndef MINIZ_NO_ZLIB_APIS
2793static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
2794
2795// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
2796mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
2797{
2798 mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
2799 if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
2800
2801 if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
2802 else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
2803 else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
2804 else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
2805 else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;
2806
2807 return comp_flags;
2808}
2809#endif //MINIZ_NO_ZLIB_APIS
2810
2811#ifdef _MSC_VER
2812#pragma warning (push)
2813#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
2814#endif
2815
2816// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
2817// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
2818// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck.
2819void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
2820{
2821 // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined.
2822 static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
2823 tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
2824 if (!pComp) return NULL;
2825 MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
2826 // write dummy header
2827 for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
2828 // compress image data
2829 tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
2830 for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
2831 if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
2832 // write real header
2833 *pLen_out = out_buf.m_size-41;
2834 {
2835 static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06};
2836 mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
2837 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0,
2838 (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
2839 c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
2840 memcpy(out_buf.m_pBuf, pnghdr, 41);
2841 }
2842 // write footer (IDAT CRC-32, followed by IEND chunk)
2843 if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
2844 c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
2845 // compute final size of file, grab compressed data buffer and return
2846 *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
2847}
2848void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
2849{
2850 // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out)
2851 return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
2852}
2853
2854#ifdef _MSC_VER
2855#pragma warning (pop)
2856#endif
2857
2858// ------------------- .ZIP archive reading
2859
2860#ifndef MINIZ_NO_ARCHIVE_APIS
2861
2862#ifdef MINIZ_NO_STDIO
2863 #define MZ_FILE void *
2864#else
2865 #include <stdio.h>
2866 #include <sys/stat.h>
2867
2868 #if defined(_MSC_VER) || defined(__MINGW64__)
2869 static FILE *mz_fopen(const char *pFilename, const char *pMode)
2870 {
2871 FILE* pFile = NULL;
2872 fopen_s(&pFile, pFilename, pMode);
2873 return pFile;
2874 }
2875 static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
2876 {
2877 FILE* pFile = NULL;
2878 if (freopen_s(&pFile, pPath, pMode, pStream))
2879 return NULL;
2880 return pFile;
2881 }
2882 #ifndef MINIZ_NO_TIME
2883 #include <sys/utime.h>
2884 #endif
2885 #define MZ_FILE FILE
2886 #define MZ_FOPEN mz_fopen
2887 #define MZ_FCLOSE fclose
2888 #define MZ_FREAD fread
2889 #define MZ_FWRITE fwrite
2890 #define MZ_FTELL64 _ftelli64
2891 #define MZ_FSEEK64 _fseeki64
2892 #define MZ_FILE_STAT_STRUCT _stat
2893 #define MZ_FILE_STAT _stat
2894 #define MZ_FFLUSH fflush
2895 #define MZ_FREOPEN mz_freopen
2896 #define MZ_DELETE_FILE remove
2897 #elif defined(__MINGW32__)
2898 #ifndef MINIZ_NO_TIME
2899 #include <sys/utime.h>
2900 #endif
2901 #define MZ_FILE FILE
2902 #define MZ_FOPEN(f, m) fopen(f, m)
2903 #define MZ_FCLOSE fclose
2904 #define MZ_FREAD fread
2905 #define MZ_FWRITE fwrite
2906 #define MZ_FTELL64 ftello64
2907 #define MZ_FSEEK64 fseeko64
2908 #define MZ_FILE_STAT_STRUCT _stat
2909 #define MZ_FILE_STAT _stat
2910 #define MZ_FFLUSH fflush
2911 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
2912 #define MZ_DELETE_FILE remove
2913 #elif defined(__TINYC__)
2914 #ifndef MINIZ_NO_TIME
2915 #include <sys/utime.h>
2916 #endif
2917 #define MZ_FILE FILE
2918 #define MZ_FOPEN(f, m) fopen(f, m)
2919 #define MZ_FCLOSE fclose
2920 #define MZ_FREAD fread
2921 #define MZ_FWRITE fwrite
2922 #define MZ_FTELL64 ftell
2923 #define MZ_FSEEK64 fseek
2924 #define MZ_FILE_STAT_STRUCT stat
2925 #define MZ_FILE_STAT stat
2926 #define MZ_FFLUSH fflush
2927 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
2928 #define MZ_DELETE_FILE remove
2929 #elif defined(__GNUC__) && _LARGEFILE64_SOURCE
2930 #ifndef MINIZ_NO_TIME
2931 #include <utime.h>
2932 #endif
2933 #define MZ_FILE FILE
2934 #define MZ_FOPEN(f, m) fopen64(f, m)
2935 #define MZ_FCLOSE fclose
2936 #define MZ_FREAD fread
2937 #define MZ_FWRITE fwrite
2938 #define MZ_FTELL64 ftello64
2939 #define MZ_FSEEK64 fseeko64
2940 #define MZ_FILE_STAT_STRUCT stat64
2941 #define MZ_FILE_STAT stat64
2942 #define MZ_FFLUSH fflush
2943 #define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
2944 #define MZ_DELETE_FILE remove
2945 #else
2946 #ifndef MINIZ_NO_TIME
2947 #include <utime.h>
2948 #endif
2949 #define MZ_FILE FILE
2950 #define MZ_FOPEN(f, m) fopen(f, m)
2951 #define MZ_FCLOSE fclose
2952 #define MZ_FREAD fread
2953 #define MZ_FWRITE fwrite
2954 #define MZ_FTELL64 ftello
2955 #define MZ_FSEEK64 fseeko
2956 #define MZ_FILE_STAT_STRUCT stat
2957 #define MZ_FILE_STAT stat
2958 #define MZ_FFLUSH fflush
2959 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
2960 #define MZ_DELETE_FILE remove
2961 #endif // #ifdef _MSC_VER
2962#endif // #ifdef MINIZ_NO_STDIO
2963
2964#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
2965
2966// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
2967enum
2968{
2969 // ZIP archive identifiers and record sizes
2970 MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
2971 MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
2972 // Central directory header record offsets
2973 MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
2974 MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
2975 MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
2976 MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
2977 // Local directory header offsets
2978 MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
2979 MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
2980 MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
2981 // End of central directory offsets
2982 MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
2983 MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
2984};
2985
2986typedef struct
2987{
2988 void *m_p;
2989 size_t m_size, m_capacity;
2990 mz_uint m_element_size;
2991} mz_zip_array;
2992
2993struct mz_zip_internal_state_tag
2994{
2995 mz_zip_array m_central_dir;
2996 mz_zip_array m_central_dir_offsets;
2997 mz_zip_array m_sorted_central_dir_offsets;
2998 MZ_FILE *m_pFile;
2999 void *m_pMem;
3000 size_t m_mem_size;
3001 size_t m_mem_capacity;
3002};
3003
3004#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
3005#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
3006
3007static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
3008{
3009 pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
3010 memset(pArray, 0, sizeof(mz_zip_array));
3011}
3012
3013static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
3014{
3015 void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
3016 if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
3017 if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
3018 pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
3019 return MZ_TRUE;
3020}
3021
3022static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
3023{
3024 if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
3025 return MZ_TRUE;
3026}
3027
3028static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
3029{
3030 if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
3031 pArray->m_size = new_size;
3032 return MZ_TRUE;
3033}
3034
3035static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
3036{
3037 return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
3038}
3039
3040static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
3041{
3042 size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
3043 memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
3044 return MZ_TRUE;
3045}
3046
3047#ifndef MINIZ_NO_TIME
3048static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
3049{
3050 struct tm tm;
3051 memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
3052 tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
3053 tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
3054 return mktime(&tm);
3055}
3056
3057static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
3058{
3059#ifdef _MSC_VER
3060 struct tm tm_struct;
3061 struct tm *tm = &tm_struct;
3062 errno_t err = localtime_s(tm, &time);
3063 if (err)
3064 {
3065 *pDOS_date = 0; *pDOS_time = 0;
3066 return;
3067 }
3068#else
3069 struct tm *tm = localtime(&time);
3070#endif
3071 *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
3072 *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
3073}
3074#endif
3075
3076#ifndef MINIZ_NO_STDIO
3077static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
3078{
3079#ifdef MINIZ_NO_TIME
3080 (void)pFilename; *pDOS_date = *pDOS_time = 0;
3081#else
3082 struct MZ_FILE_STAT_STRUCT file_stat;
3083 // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
3084 if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
3085 return MZ_FALSE;
3086 mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
3087#endif // #ifdef MINIZ_NO_TIME
3088 return MZ_TRUE;
3089}
3090
3091#ifndef MINIZ_NO_TIME
3092static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
3093{
3094 struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
3095 return !utime(pFilename, &t);
3096}
3097#endif // #ifndef MINIZ_NO_TIME
3098#endif // #ifndef MINIZ_NO_STDIO
3099
3100static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
3101{
3102 (void)flags;
3103 if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
3104 return MZ_FALSE;
3105
3106 if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
3107 if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
3108 if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
3109
3110 pZip->m_zip_mode = MZ_ZIP_MODE_READING;
3111 pZip->m_archive_size = 0;
3112 pZip->m_central_directory_file_ofs = 0;
3113 pZip->m_total_files = 0;
3114
3115 if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
3116 return MZ_FALSE;
3117 memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
3118 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
3119 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
3120 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
3121 return MZ_TRUE;
3122}
3123
3124static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
3125{
3126 const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
3127 const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
3128 mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3129 mz_uint8 l = 0, r = 0;
3130 pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
3131 pE = pL + MZ_MIN(l_len, r_len);
3132 while (pL < pE)
3133 {
3134 if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
3135 break;
3136 pL++; pR++;
3137 }
3138 return (pL == pE) ? (l_len < r_len) : (l < r);
3139}
3140
3141#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END
3142
3143// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
3144static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
3145{
3146 mz_zip_internal_state *pState = pZip->m_pState;
3147 const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
3148 const mz_zip_array *pCentral_dir = &pState->m_central_dir;
3149 mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
3150 const int size = pZip->m_total_files;
3151 int start = (size - 2) >> 1, end;
3152 while (start >= 0)
3153 {
3154 int child, root = start;
3155 for ( ; ; )
3156 {
3157 if ((child = (root << 1) + 1) >= size)
3158 break;
3159 child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
3160 if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
3161 break;
3162 MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
3163 }
3164 start--;
3165 }
3166
3167 end = size - 1;
3168 while (end > 0)
3169 {
3170 int child, root = 0;
3171 MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
3172 for ( ; ; )
3173 {
3174 if ((child = (root << 1) + 1) >= end)
3175 break;
3176 child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
3177 if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
3178 break;
3179 MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
3180 }
3181 end--;
3182 }
3183}
3184
3185static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
3186{
3187 mz_uint cdir_size, num_this_disk, cdir_disk_index;
3188 mz_uint64 cdir_ofs;
3189 mz_int64 cur_file_ofs;
3190 const mz_uint8 *p;
3191 mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
3192 mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
3193 // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
3194 if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
3195 return MZ_FALSE;
3196 // Find the end of central directory record by scanning the file from the end towards the beginning.
3197 cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
3198 for ( ; ; )
3199 {
3200 int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
3201 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
3202 return MZ_FALSE;
3203 for (i = n - 4; i >= 0; --i)
3204 if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
3205 break;
3206 if (i >= 0)
3207 {
3208 cur_file_ofs += i;
3209 break;
3210 }
3211 if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
3212 return MZ_FALSE;
3213 cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
3214 }
3215 // Read and verify the end of central directory record.
3216 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
3217 return MZ_FALSE;
3218 if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
3219 ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
3220 return MZ_FALSE;
3221
3222 num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
3223 cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
3224 if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
3225 return MZ_FALSE;
3226
3227 if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
3228 return MZ_FALSE;
3229
3230 cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
3231 if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
3232 return MZ_FALSE;
3233
3234 pZip->m_central_directory_file_ofs = cdir_ofs;
3235
3236 if (pZip->m_total_files)
3237 {
3238 mz_uint i, n;
3239
3240 // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
3241 if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
3242 (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
3243 return MZ_FALSE;
3244
3245 if (sort_central_dir)
3246 {
3247 if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
3248 return MZ_FALSE;
3249 }
3250
3251 if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
3252 return MZ_FALSE;
3253
3254 // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
3255 p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
3256 for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
3257 {
3258 mz_uint total_header_size, comp_size, decomp_size, disk_index;
3259 if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
3260 return MZ_FALSE;
3261 MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
3262 if (sort_central_dir)
3263 MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
3264 comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
3265 decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
3266 if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
3267 return MZ_FALSE;
3268 disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
3269 if ((disk_index != num_this_disk) && (disk_index != 1))
3270 return MZ_FALSE;
3271 if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
3272 return MZ_FALSE;
3273 if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
3274 return MZ_FALSE;
3275 n -= total_header_size; p += total_header_size;
3276 }
3277 }
3278
3279 if (sort_central_dir)
3280 mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
3281
3282 return MZ_TRUE;
3283}
3284
3285mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
3286{
3287 if ((!pZip) || (!pZip->m_pRead))
3288 return MZ_FALSE;
3289 if (!mz_zip_reader_init_internal(pZip, flags))
3290 return MZ_FALSE;
3291 pZip->m_archive_size = size;
3292 if (!mz_zip_reader_read_central_dir(pZip, flags))
3293 {
3294 mz_zip_reader_end(pZip);
3295 return MZ_FALSE;
3296 }
3297 return MZ_TRUE;
3298}
3299
3300static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
3301{
3302 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
3303 size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
3304 memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
3305 return s;
3306}
3307
3308mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
3309{
3310 if (!mz_zip_reader_init_internal(pZip, flags))
3311 return MZ_FALSE;
3312 pZip->m_archive_size = size;
3313 pZip->m_pRead = mz_zip_mem_read_func;
3314 pZip->m_pIO_opaque = pZip;
3315#ifdef __cplusplus
3316 pZip->m_pState->m_pMem = const_cast<void *>(pMem);
3317#else
3318 pZip->m_pState->m_pMem = (void *)pMem;
3319#endif
3320 pZip->m_pState->m_mem_size = size;
3321 if (!mz_zip_reader_read_central_dir(pZip, flags))
3322 {
3323 mz_zip_reader_end(pZip);
3324 return MZ_FALSE;
3325 }
3326 return MZ_TRUE;
3327}
3328
3329#ifndef MINIZ_NO_STDIO
3330static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
3331{
3332 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
3333 mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
3334 if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
3335 return 0;
3336 return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
3337}
3338
3339mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
3340{
3341 mz_uint64 file_size;
3342 MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
3343 if (!pFile)
3344 return MZ_FALSE;
3345 if (MZ_FSEEK64(pFile, 0, SEEK_END))
3346 {
3347 MZ_FCLOSE(pFile);
3348 return MZ_FALSE;
3349 }
3350 file_size = MZ_FTELL64(pFile);
3351 if (!mz_zip_reader_init_internal(pZip, flags))
3352 {
3353 MZ_FCLOSE(pFile);
3354 return MZ_FALSE;
3355 }
3356 pZip->m_pRead = mz_zip_file_read_func;
3357 pZip->m_pIO_opaque = pZip;
3358 pZip->m_pState->m_pFile = pFile;
3359 pZip->m_archive_size = file_size;
3360 if (!mz_zip_reader_read_central_dir(pZip, flags))
3361 {
3362 mz_zip_reader_end(pZip);
3363 return MZ_FALSE;
3364 }
3365 return MZ_TRUE;
3366}
3367#endif // #ifndef MINIZ_NO_STDIO
3368
3369mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
3370{
3371 return pZip ? pZip->m_total_files : 0;
3372}
3373
3374static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
3375{
3376 if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
3377 return NULL;
3378 return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
3379}
3380
3381mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
3382{
3383 mz_uint m_bit_flag;
3384 const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
3385 if (!p)
3386 return MZ_FALSE;
3387 m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
3388 return (m_bit_flag & 1);
3389}
3390
3391mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
3392{
3393 mz_uint filename_len, external_attr;
3394 const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
3395 if (!p)
3396 return MZ_FALSE;
3397
3398 // First see if the filename ends with a '/' character.
3399 filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3400 if (filename_len)
3401 {
3402 if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
3403 return MZ_TRUE;
3404 }
3405
3406 // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
3407 // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
3408 // FIXME: Remove this check? Is it necessary - we already check the filename.
3409 external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
3410 if ((external_attr & 0x10) != 0)
3411 return MZ_TRUE;
3412
3413 return MZ_FALSE;
3414}
3415
3416mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
3417{
3418 mz_uint n;
3419 const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
3420 if ((!p) || (!pStat))
3421 return MZ_FALSE;
3422
3423 // Unpack the central directory record.
3424 pStat->m_file_index = file_index;
3425 pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
3426 pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
3427 pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
3428 pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
3429 pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
3430#ifndef MINIZ_NO_TIME
3431 pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
3432#endif
3433 pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
3434 pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
3435 pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
3436 pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
3437 pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
3438 pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
3439
3440 // Copy as much of the filename and comment as possible.
3441 n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
3442 memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
3443
3444 n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
3445 pStat->m_comment_size = n;
3446 memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
3447
3448 return MZ_TRUE;
3449}
3450
3451mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
3452{
3453 mz_uint n;
3454 const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
3455 if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
3456 n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3457 if (filename_buf_size)
3458 {
3459 n = MZ_MIN(n, filename_buf_size - 1);
3460 memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
3461 pFilename[n] = '\0';
3462 }
3463 return n + 1;
3464}
3465
3466static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
3467{
3468 mz_uint i;
3469 if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
3470 return 0 == memcmp(pA, pB, len);
3471 for (i = 0; i < len; ++i)
3472 if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
3473 return MZ_FALSE;
3474 return MZ_TRUE;
3475}
3476
3477static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
3478{
3479 const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
3480 mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3481 mz_uint8 l = 0, r = 0;
3482 pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
3483 pE = pL + MZ_MIN(l_len, r_len);
3484 while (pL < pE)
3485 {
3486 if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
3487 break;
3488 pL++; pR++;
3489 }
3490 return (pL == pE) ? (int)(l_len - r_len) : (l - r);
3491}
3492
3493static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
3494{
3495 mz_zip_internal_state *pState = pZip->m_pState;
3496 const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
3497 const mz_zip_array *pCentral_dir = &pState->m_central_dir;
3498 mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
3499 const int size = pZip->m_total_files;
3500 const mz_uint filename_len = (mz_uint)strlen(pFilename);
3501 int l = 0, h = size - 1;
3502 while (l <= h)
3503 {
3504 int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
3505 if (!comp)
3506 return file_index;
3507 else if (comp < 0)
3508 l = m + 1;
3509 else
3510 h = m - 1;
3511 }
3512 return -1;
3513}
3514
3515int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
3516{
3517 mz_uint file_index; size_t name_len, comment_len;
3518 if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
3519 return -1;
3520 if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
3521 return mz_zip_reader_locate_file_binary_search(pZip, pName);
3522 name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
3523 comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
3524 for (file_index = 0; file_index < pZip->m_total_files; file_index++)
3525 {
3526 const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
3527 mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3528 const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
3529 if (filename_len < name_len)
3530 continue;
3531 if (comment_len)
3532 {
3533 mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
3534 const char *pFile_comment = pFilename + filename_len + file_extra_len;
3535 if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
3536 continue;
3537 }
3538 if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
3539 {
3540 int ofs = filename_len - 1;
3541 do
3542 {
3543 if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
3544 break;
3545 } while (--ofs >= 0);
3546 ofs++;
3547 pFilename += ofs; filename_len -= ofs;
3548 }
3549 if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
3550 return file_index;
3551 }
3552 return -1;
3553}
3554
3555mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
3556{
3557 int status = TINFL_STATUS_DONE;
3558 mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
3559 mz_zip_archive_file_stat file_stat;
3560 void *pRead_buf;
3561 mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
3562 tinfl_decompressor inflator;
3563
3564 if ((buf_size) && (!pBuf))
3565 return MZ_FALSE;
3566
3567 if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
3568 return MZ_FALSE;
3569
3570 // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
3571 if (!file_stat.m_comp_size)
3572 return MZ_TRUE;
3573
3574 // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
3575 // I'm torn how to handle this case - should it fail instead?
3576 if (mz_zip_reader_is_file_a_directory(pZip, file_index))
3577 return MZ_TRUE;
3578
3579 // Encryption and patch files are not supported.
3580 if (file_stat.m_bit_flag & (1 | 32))
3581 return MZ_FALSE;
3582
3583 // This function only supports stored and deflate.
3584 if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
3585 return MZ_FALSE;
3586
3587 // Ensure supplied output buffer is large enough.
3588 needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
3589 if (buf_size < needed_size)
3590 return MZ_FALSE;
3591
3592 // Read and parse the local directory entry.
3593 cur_file_ofs = file_stat.m_local_header_ofs;
3594 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
3595 return MZ_FALSE;
3596 if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
3597 return MZ_FALSE;
3598
3599 cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
3600 if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
3601 return MZ_FALSE;
3602
3603 if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
3604 {
3605 // The file is stored or the caller has requested the compressed data.
3606 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
3607 return MZ_FALSE;
3608 return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
3609 }
3610
3611 // Decompress the file either directly from memory or from a file input buffer.
3612 tinfl_init(&inflator);
3613
3614 if (pZip->m_pState->m_pMem)
3615 {
3616 // Read directly from the archive in memory.
3617 pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
3618 read_buf_size = read_buf_avail = file_stat.m_comp_size;
3619 comp_remaining = 0;
3620 }
3621 else if (pUser_read_buf)
3622 {
3623 // Use a user provided read buffer.
3624 if (!user_read_buf_size)
3625 return MZ_FALSE;
3626 pRead_buf = (mz_uint8 *)pUser_read_buf;
3627 read_buf_size = user_read_buf_size;
3628 read_buf_avail = 0;
3629 comp_remaining = file_stat.m_comp_size;
3630 }
3631 else
3632 {
3633 // Temporarily allocate a read buffer.
3634 read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
3635#ifdef _MSC_VER
3636 if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
3637#else
3638 if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
3639#endif
3640 return MZ_FALSE;
3641 if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
3642 return MZ_FALSE;
3643 read_buf_avail = 0;
3644 comp_remaining = file_stat.m_comp_size;
3645 }
3646
3647 do
3648 {
3649 size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
3650 if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
3651 {
3652 read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
3653 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
3654 {
3655 status = TINFL_STATUS_FAILED;
3656 break;
3657 }
3658 cur_file_ofs += read_buf_avail;
3659 comp_remaining -= read_buf_avail;
3660 read_buf_ofs = 0;
3661 }
3662 in_buf_size = (size_t)read_buf_avail;
3663 status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
3664 read_buf_avail -= in_buf_size;
3665 read_buf_ofs += in_buf_size;
3666 out_buf_ofs += out_buf_size;
3667 } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
3668
3669 if (status == TINFL_STATUS_DONE)
3670 {
3671 // Make sure the entire file was decompressed, and check its CRC.
3672 if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
3673 status = TINFL_STATUS_FAILED;
3674 }
3675
3676 if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
3677 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3678
3679 return status == TINFL_STATUS_DONE;
3680}
3681
3682mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
3683{
3684 int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
3685 if (file_index < 0)
3686 return MZ_FALSE;
3687 return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
3688}
3689
3690mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
3691{
3692 return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
3693}
3694
3695mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
3696{
3697 return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
3698}
3699
3700void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
3701{
3702 mz_uint64 comp_size, uncomp_size, alloc_size;
3703 const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
3704 void *pBuf;
3705
3706 if (pSize)
3707 *pSize = 0;
3708 if (!p)
3709 return NULL;
3710
3711 comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
3712 uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
3713
3714 alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
3715#ifdef _MSC_VER
3716 if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
3717#else
3718 if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
3719#endif
3720 return NULL;
3721 if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
3722 return NULL;
3723
3724 if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
3725 {
3726 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3727 return NULL;
3728 }
3729
3730 if (pSize) *pSize = (size_t)alloc_size;
3731 return pBuf;
3732}
3733
3734void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
3735{
3736 int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
3737 if (file_index < 0)
3738 {
3739 if (pSize) *pSize = 0;
3740 return MZ_FALSE;
3741 }
3742 return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
3743}
3744
3745mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
3746{
3747 int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
3748 mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
3749 mz_zip_archive_file_stat file_stat;
3750 void *pRead_buf = NULL; void *pWrite_buf = NULL;
3751 mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
3752
3753 if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
3754 return MZ_FALSE;
3755
3756 // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
3757 if (!file_stat.m_comp_size)
3758 return MZ_TRUE;
3759
3760 // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
3761 // I'm torn how to handle this case - should it fail instead?
3762 if (mz_zip_reader_is_file_a_directory(pZip, file_index))
3763 return MZ_TRUE;
3764
3765 // Encryption and patch files are not supported.
3766 if (file_stat.m_bit_flag & (1 | 32))
3767 return MZ_FALSE;
3768
3769 // This function only supports stored and deflate.
3770 if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
3771 return MZ_FALSE;
3772
3773 // Read and parse the local directory entry.
3774 cur_file_ofs = file_stat.m_local_header_ofs;
3775 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
3776 return MZ_FALSE;
3777 if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
3778 return MZ_FALSE;
3779
3780 cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
3781 if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
3782 return MZ_FALSE;
3783
3784 // Decompress the file either directly from memory or from a file input buffer.
3785 if (pZip->m_pState->m_pMem)
3786 {
3787 pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
3788 read_buf_size = read_buf_avail = file_stat.m_comp_size;
3789 comp_remaining = 0;
3790 }
3791 else
3792 {
3793 read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
3794 if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
3795 return MZ_FALSE;
3796 read_buf_avail = 0;
3797 comp_remaining = file_stat.m_comp_size;
3798 }
3799
3800 if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
3801 {
3802 // The file is stored or the caller has requested the compressed data.
3803 if (pZip->m_pState->m_pMem)
3804 {
3805#ifdef _MSC_VER
3806 if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
3807#else
3808 if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
3809#endif
3810 return MZ_FALSE;
3811 if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
3812 status = TINFL_STATUS_FAILED;
3813 else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
3814 file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
3815 cur_file_ofs += file_stat.m_comp_size;
3816 out_buf_ofs += file_stat.m_comp_size;
3817 comp_remaining = 0;
3818 }
3819 else
3820 {
3821 while (comp_remaining)
3822 {
3823 read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
3824 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
3825 {
3826 status = TINFL_STATUS_FAILED;
3827 break;
3828 }
3829
3830 if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
3831 file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
3832
3833 if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
3834 {
3835 status = TINFL_STATUS_FAILED;
3836 break;
3837 }
3838 cur_file_ofs += read_buf_avail;
3839 out_buf_ofs += read_buf_avail;
3840 comp_remaining -= read_buf_avail;
3841 }
3842 }
3843 }
3844 else
3845 {
3846 tinfl_decompressor inflator;
3847 tinfl_init(&inflator);
3848
3849 if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
3850 status = TINFL_STATUS_FAILED;
3851 else
3852 {
3853 do
3854 {
3855 mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
3856 size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
3857 if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
3858 {
3859 read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
3860 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
3861 {
3862 status = TINFL_STATUS_FAILED;
3863 break;
3864 }
3865 cur_file_ofs += read_buf_avail;
3866 comp_remaining -= read_buf_avail;
3867 read_buf_ofs = 0;
3868 }
3869
3870 in_buf_size = (size_t)read_buf_avail;
3871 status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
3872 read_buf_avail -= in_buf_size;
3873 read_buf_ofs += in_buf_size;
3874
3875 if (out_buf_size)
3876 {
3877 if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
3878 {
3879 status = TINFL_STATUS_FAILED;
3880 break;
3881 }
3882 file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
3883 if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
3884 {
3885 status = TINFL_STATUS_FAILED;
3886 break;
3887 }
3888 }
3889 } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
3890 }
3891 }
3892
3893 if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
3894 {
3895 // Make sure the entire file was decompressed, and check its CRC.
3896 if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
3897 status = TINFL_STATUS_FAILED;
3898 }
3899
3900 if (!pZip->m_pState->m_pMem)
3901 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3902 if (pWrite_buf)
3903 pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
3904
3905 return status == TINFL_STATUS_DONE;
3906}
3907
3908mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
3909{
3910 int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
3911 if (file_index < 0)
3912 return MZ_FALSE;
3913 return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
3914}
3915
3916#ifndef MINIZ_NO_STDIO
3917static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
3918{
3919 (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
3920}
3921
3922mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
3923{
3924 mz_bool status;
3925 mz_zip_archive_file_stat file_stat;
3926 MZ_FILE *pFile;
3927 if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
3928 return MZ_FALSE;
3929 pFile = MZ_FOPEN(pDst_filename, "wb");
3930 if (!pFile)
3931 return MZ_FALSE;
3932 status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
3933 if (MZ_FCLOSE(pFile) == EOF)
3934 return MZ_FALSE;
3935#ifndef MINIZ_NO_TIME
3936 if (status)
3937 mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
3938#endif
3939 return status;
3940}
3941#endif // #ifndef MINIZ_NO_STDIO
3942
3943mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
3944{
3945 if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
3946 return MZ_FALSE;
3947
3948 if (pZip->m_pState)
3949 {
3950 mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
3951 mz_zip_array_clear(pZip, &pState->m_central_dir);
3952 mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
3953 mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
3954
3955#ifndef MINIZ_NO_STDIO
3956 if (pState->m_pFile)
3957 {
3958 MZ_FCLOSE(pState->m_pFile);
3959 pState->m_pFile = NULL;
3960 }
3961#endif // #ifndef MINIZ_NO_STDIO
3962
3963 pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
3964 }
3965 pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
3966
3967 return MZ_TRUE;
3968}
3969
3970#ifndef MINIZ_NO_STDIO
3971mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
3972{
3973 int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
3974 if (file_index < 0)
3975 return MZ_FALSE;
3976 return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
3977}
3978#endif
3979
3980// ------------------- .ZIP archive writing
3981
3982#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
3983
3984static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
3985static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
3986#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
3987#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
3988
3989mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
3990{
3991 if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
3992 return MZ_FALSE;
3993
3994 if (pZip->m_file_offset_alignment)
3995 {
3996 // Ensure user specified file offset alignment is a power of 2.
3997 if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
3998 return MZ_FALSE;
3999 }
4000
4001 if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
4002 if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
4003 if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
4004
4005 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
4006 pZip->m_archive_size = existing_size;
4007 pZip->m_central_directory_file_ofs = 0;
4008 pZip->m_total_files = 0;
4009
4010 if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
4011 return MZ_FALSE;
4012 memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
4013 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
4014 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
4015 MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
4016 return MZ_TRUE;
4017}
4018
4019static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
4020{
4021 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
4022 mz_zip_internal_state *pState = pZip->m_pState;
4023 mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
4024#ifdef _MSC_VER
4025 if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
4026#else
4027 if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
4028#endif
4029 return 0;
4030 if (new_size > pState->m_mem_capacity)
4031 {
4032 void *pNew_block;
4033 size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
4034 if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
4035 return 0;
4036 pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
4037 }
4038 memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
4039 pState->m_mem_size = (size_t)new_size;
4040 return n;
4041}
4042
4043mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
4044{
4045 pZip->m_pWrite = mz_zip_heap_write_func;
4046 pZip->m_pIO_opaque = pZip;
4047 if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
4048 return MZ_FALSE;
4049 if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
4050 {
4051 if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
4052 {
4053 mz_zip_writer_end(pZip);
4054 return MZ_FALSE;
4055 }
4056 pZip->m_pState->m_mem_capacity = initial_allocation_size;
4057 }
4058 return MZ_TRUE;
4059}
4060
4061#ifndef MINIZ_NO_STDIO
4062static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
4063{
4064 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
4065 mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
4066 if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
4067 return 0;
4068 return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
4069}
4070
4071mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
4072{
4073 MZ_FILE *pFile;
4074 pZip->m_pWrite = mz_zip_file_write_func;
4075 pZip->m_pIO_opaque = pZip;
4076 if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
4077 return MZ_FALSE;
4078 if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
4079 {
4080 mz_zip_writer_end(pZip);
4081 return MZ_FALSE;
4082 }
4083 pZip->m_pState->m_pFile = pFile;
4084 if (size_to_reserve_at_beginning)
4085 {
4086 mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
4087 do
4088 {
4089 size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
4090 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
4091 {
4092 mz_zip_writer_end(pZip);
4093 return MZ_FALSE;
4094 }
4095 cur_ofs += n; size_to_reserve_at_beginning -= n;
4096 } while (size_to_reserve_at_beginning);
4097 }
4098 return MZ_TRUE;
4099}
4100#endif // #ifndef MINIZ_NO_STDIO
4101
4102mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
4103{
4104 mz_zip_internal_state *pState;
4105 if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
4106 return MZ_FALSE;
4107 // No sense in trying to write to an archive that's already at the support max size
4108 if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
4109 return MZ_FALSE;
4110
4111 pState = pZip->m_pState;
4112
4113 if (pState->m_pFile)
4114 {
4115#ifdef MINIZ_NO_STDIO
4116 pFilename; return MZ_FALSE;
4117#else
4118 // Archive is being read from stdio - try to reopen as writable.
4119 if (pZip->m_pIO_opaque != pZip)
4120 return MZ_FALSE;
4121 if (!pFilename)
4122 return MZ_FALSE;
4123 pZip->m_pWrite = mz_zip_file_write_func;
4124 if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
4125 {
4126 // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
4127 mz_zip_reader_end(pZip);
4128 return MZ_FALSE;
4129 }
4130#endif // #ifdef MINIZ_NO_STDIO
4131 }
4132 else if (pState->m_pMem)
4133 {
4134 // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
4135 if (pZip->m_pIO_opaque != pZip)
4136 return MZ_FALSE;
4137 pState->m_mem_capacity = pState->m_mem_size;
4138 pZip->m_pWrite = mz_zip_heap_write_func;
4139 }
4140 // Archive is being read via a user provided read function - make sure the user has specified a write function too.
4141 else if (!pZip->m_pWrite)
4142 return MZ_FALSE;
4143
4144 // Start writing new files at the archive's current central directory location.
4145 pZip->m_archive_size = pZip->m_central_directory_file_ofs;
4146 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
4147 pZip->m_central_directory_file_ofs = 0;
4148
4149 return MZ_TRUE;
4150}
4151
4152mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
4153{
4154 return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
4155}
4156
4157typedef struct
4158{
4159 mz_zip_archive *m_pZip;
4160 mz_uint64 m_cur_archive_file_ofs;
4161 mz_uint64 m_comp_size;
4162} mz_zip_writer_add_state;
4163
4164static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
4165{
4166 mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
4167 if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
4168 return MZ_FALSE;
4169 pState->m_cur_archive_file_ofs += len;
4170 pState->m_comp_size += len;
4171 return MZ_TRUE;
4172}
4173
4174static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
4175{
4176 (void)pZip;
4177 memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
4178 MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
4179 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
4180 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
4181 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
4182 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
4183 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
4184 MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
4185 MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
4186 MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
4187 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
4188 MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
4189 return MZ_TRUE;
4190}
4191
4192static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
4193{
4194 (void)pZip;
4195 memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
4196 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
4197 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
4198 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
4199 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
4200 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
4201 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
4202 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
4203 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
4204 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
4205 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
4206 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
4207 MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
4208 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
4209 MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
4210 return MZ_TRUE;
4211}
4212
4213static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
4214{
4215 mz_zip_internal_state *pState = pZip->m_pState;
4216 mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
4217 size_t orig_central_dir_size = pState->m_central_dir.m_size;
4218 mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
4219
4220 // No zip64 support yet
4221 if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
4222 return MZ_FALSE;
4223
4224 if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
4225 return MZ_FALSE;
4226
4227 if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
4228 (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
4229 (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
4230 (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
4231 (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
4232 {
4233 // Try to push the central directory array back into its original state.
4234 mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
4235 return MZ_FALSE;
4236 }
4237
4238 return MZ_TRUE;
4239}
4240
4241static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
4242{
4243 // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
4244 if (*pArchive_name == '/')
4245 return MZ_FALSE;
4246 while (*pArchive_name)
4247 {
4248 if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
4249 return MZ_FALSE;
4250 pArchive_name++;
4251 }
4252 return MZ_TRUE;
4253}
4254
4255static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
4256{
4257 mz_uint32 n;
4258 if (!pZip->m_file_offset_alignment)
4259 return 0;
4260 n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
4261 return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
4262}
4263
4264static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
4265{
4266 char buf[4096];
4267 memset(buf, 0, MZ_MIN(sizeof(buf), n));
4268 while (n)
4269 {
4270 mz_uint32 s = MZ_MIN(sizeof(buf), n);
4271 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
4272 return MZ_FALSE;
4273 cur_file_ofs += s; n -= s;
4274 }
4275 return MZ_TRUE;
4276}
4277
4278mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
4279{
4280 mz_uint16 method = 0, dos_time = 0, dos_date = 0;
4281 mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
4282 mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
4283 size_t archive_name_size;
4284 mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
4285 tdefl_compressor *pComp = NULL;
4286 mz_bool store_data_uncompressed;
4287 mz_zip_internal_state *pState;
4288
4289 if ((int)level_and_flags < 0)
4290 level_and_flags = MZ_DEFAULT_LEVEL;
4291 level = level_and_flags & 0xF;
4292 store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
4293
4294 if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
4295 return MZ_FALSE;
4296
4297 pState = pZip->m_pState;
4298
4299 if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
4300 return MZ_FALSE;
4301 // No zip64 support yet
4302 if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
4303 return MZ_FALSE;
4304 if (!mz_zip_writer_validate_archive_name(pArchive_name))
4305 return MZ_FALSE;
4306
4307#ifndef MINIZ_NO_TIME
4308 {
4309 time_t cur_time; time(&cur_time);
4310 mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
4311 }
4312#endif // #ifndef MINIZ_NO_TIME
4313
4314 archive_name_size = strlen(pArchive_name);
4315 if (archive_name_size > 0xFFFF)
4316 return MZ_FALSE;
4317
4318 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
4319
4320 // no zip64 support yet
4321 if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
4322 return MZ_FALSE;
4323
4324 if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
4325 {
4326 // Set DOS Subdirectory attribute bit.
4327 ext_attributes |= 0x10;
4328 // Subdirectories cannot contain data.
4329 if ((buf_size) || (uncomp_size))
4330 return MZ_FALSE;
4331 }
4332
4333 // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
4334 if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
4335 return MZ_FALSE;
4336
4337 if ((!store_data_uncompressed) && (buf_size))
4338 {
4339 if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
4340 return MZ_FALSE;
4341 }
4342
4343 if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
4344 {
4345 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4346 return MZ_FALSE;
4347 }
4348 local_dir_header_ofs += num_alignment_padding_bytes;
4349 if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
4350 cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
4351
4352 MZ_CLEAR_OBJ(local_dir_header);
4353 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
4354 {
4355 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4356 return MZ_FALSE;
4357 }
4358 cur_archive_file_ofs += archive_name_size;
4359
4360 if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
4361 {
4362 uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
4363 uncomp_size = buf_size;
4364 if (uncomp_size <= 3)
4365 {
4366 level = 0;
4367 store_data_uncompressed = MZ_TRUE;
4368 }
4369 }
4370
4371 if (store_data_uncompressed)
4372 {
4373 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
4374 {
4375 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4376 return MZ_FALSE;
4377 }
4378
4379 cur_archive_file_ofs += buf_size;
4380 comp_size = buf_size;
4381
4382 if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
4383 method = MZ_DEFLATED;
4384 }
4385 else if (buf_size)
4386 {
4387 mz_zip_writer_add_state state;
4388
4389 state.m_pZip = pZip;
4390 state.m_cur_archive_file_ofs = cur_archive_file_ofs;
4391 state.m_comp_size = 0;
4392
4393 if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
4394 (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
4395 {
4396 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4397 return MZ_FALSE;
4398 }
4399
4400 comp_size = state.m_comp_size;
4401 cur_archive_file_ofs = state.m_cur_archive_file_ofs;
4402
4403 method = MZ_DEFLATED;
4404 }
4405
4406 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4407 pComp = NULL;
4408
4409 // no zip64 support yet
4410 if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
4411 return MZ_FALSE;
4412
4413 if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
4414 return MZ_FALSE;
4415
4416 if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
4417 return MZ_FALSE;
4418
4419 if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
4420 return MZ_FALSE;
4421
4422 pZip->m_total_files++;
4423 pZip->m_archive_size = cur_archive_file_ofs;
4424
4425 return MZ_TRUE;
4426}
4427
4428#ifndef MINIZ_NO_STDIO
4429mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
4430{
4431 mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
4432 mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
4433 mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
4434 size_t archive_name_size;
4435 mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
4436 MZ_FILE *pSrc_file = NULL;
4437
4438 if ((int)level_and_flags < 0)
4439 level_and_flags = MZ_DEFAULT_LEVEL;
4440 level = level_and_flags & 0xF;
4441
4442 if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
4443 return MZ_FALSE;
4444 if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
4445 return MZ_FALSE;
4446 if (!mz_zip_writer_validate_archive_name(pArchive_name))
4447 return MZ_FALSE;
4448
4449 archive_name_size = strlen(pArchive_name);
4450 if (archive_name_size > 0xFFFF)
4451 return MZ_FALSE;
4452
4453 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
4454
4455 // no zip64 support yet
4456 if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
4457 return MZ_FALSE;
4458
4459 if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
4460 return MZ_FALSE;
4461
4462 pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
4463 if (!pSrc_file)
4464 return MZ_FALSE;
4465 MZ_FSEEK64(pSrc_file, 0, SEEK_END);
4466 uncomp_size = MZ_FTELL64(pSrc_file);
4467 MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
4468
4469 if (uncomp_size > 0xFFFFFFFF)
4470 {
4471 // No zip64 support yet
4472 MZ_FCLOSE(pSrc_file);
4473 return MZ_FALSE;
4474 }
4475 if (uncomp_size <= 3)
4476 level = 0;
4477
4478 if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
4479 {
4480 MZ_FCLOSE(pSrc_file);
4481 return MZ_FALSE;
4482 }
4483 local_dir_header_ofs += num_alignment_padding_bytes;
4484 if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
4485 cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
4486
4487 MZ_CLEAR_OBJ(local_dir_header);
4488 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
4489 {
4490 MZ_FCLOSE(pSrc_file);
4491 return MZ_FALSE;
4492 }
4493 cur_archive_file_ofs += archive_name_size;
4494
4495 if (uncomp_size)
4496 {
4497 mz_uint64 uncomp_remaining = uncomp_size;
4498 void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
4499 if (!pRead_buf)
4500 {
4501 MZ_FCLOSE(pSrc_file);
4502 return MZ_FALSE;
4503 }
4504
4505 if (!level)
4506 {
4507 while (uncomp_remaining)
4508 {
4509 mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
4510 if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
4511 {
4512 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
4513 MZ_FCLOSE(pSrc_file);
4514 return MZ_FALSE;
4515 }
4516 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
4517 uncomp_remaining -= n;
4518 cur_archive_file_ofs += n;
4519 }
4520 comp_size = uncomp_size;
4521 }
4522 else
4523 {
4524 mz_bool result = MZ_FALSE;
4525 mz_zip_writer_add_state state;
4526 tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
4527 if (!pComp)
4528 {
4529 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
4530 MZ_FCLOSE(pSrc_file);
4531 return MZ_FALSE;
4532 }
4533
4534 state.m_pZip = pZip;
4535 state.m_cur_archive_file_ofs = cur_archive_file_ofs;
4536 state.m_comp_size = 0;
4537
4538 if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
4539 {
4540 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4541 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
4542 MZ_FCLOSE(pSrc_file);
4543 return MZ_FALSE;
4544 }
4545
4546 for ( ; ; )
4547 {
4548 size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
4549 tdefl_status status;
4550
4551 if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
4552 break;
4553
4554 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
4555 uncomp_remaining -= in_buf_size;
4556
4557 status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
4558 if (status == TDEFL_STATUS_DONE)
4559 {
4560 result = MZ_TRUE;
4561 break;
4562 }
4563 else if (status != TDEFL_STATUS_OKAY)
4564 break;
4565 }
4566
4567 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
4568
4569 if (!result)
4570 {
4571 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
4572 MZ_FCLOSE(pSrc_file);
4573 return MZ_FALSE;
4574 }
4575
4576 comp_size = state.m_comp_size;
4577 cur_archive_file_ofs = state.m_cur_archive_file_ofs;
4578
4579 method = MZ_DEFLATED;
4580 }
4581
4582 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
4583 }
4584
4585 MZ_FCLOSE(pSrc_file); pSrc_file = NULL;
4586
4587 // no zip64 support yet
4588 if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
4589 return MZ_FALSE;
4590
4591 if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
4592 return MZ_FALSE;
4593
4594 if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
4595 return MZ_FALSE;
4596
4597 if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
4598 return MZ_FALSE;
4599
4600 pZip->m_total_files++;
4601 pZip->m_archive_size = cur_archive_file_ofs;
4602
4603 return MZ_TRUE;
4604}
4605#endif // #ifndef MINIZ_NO_STDIO
4606
4607mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
4608{
4609 mz_uint n, bit_flags, num_alignment_padding_bytes;
4610 mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
4611 mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
4612 mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
4613 mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
4614 size_t orig_central_dir_size;
4615 mz_zip_internal_state *pState;
4616 void *pBuf; const mz_uint8 *pSrc_central_header;
4617
4618 if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
4619 return MZ_FALSE;
4620 if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
4621 return MZ_FALSE;
4622 pState = pZip->m_pState;
4623
4624 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
4625
4626 // no zip64 support yet
4627 if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
4628 return MZ_FALSE;
4629
4630 cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
4631 cur_dst_file_ofs = pZip->m_archive_size;
4632
4633 if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
4634 return MZ_FALSE;
4635 if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
4636 return MZ_FALSE;
4637 cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
4638
4639 if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
4640 return MZ_FALSE;
4641 cur_dst_file_ofs += num_alignment_padding_bytes;
4642 local_dir_header_ofs = cur_dst_file_ofs;
4643 if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
4644
4645 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
4646 return MZ_FALSE;
4647 cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
4648
4649 n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
4650 comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
4651
4652 if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
4653 return MZ_FALSE;
4654
4655 while (comp_bytes_remaining)
4656 {
4657 n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
4658 if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
4659 {
4660 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
4661 return MZ_FALSE;
4662 }
4663 cur_src_file_ofs += n;
4664
4665 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
4666 {
4667 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
4668 return MZ_FALSE;
4669 }
4670 cur_dst_file_ofs += n;
4671
4672 comp_bytes_remaining -= n;
4673 }
4674
4675 bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
4676 if (bit_flags & 8)
4677 {
4678 // Copy data descriptor
4679 if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
4680 {
4681 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
4682 return MZ_FALSE;
4683 }
4684
4685 n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
4686 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
4687 {
4688 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
4689 return MZ_FALSE;
4690 }
4691
4692 cur_src_file_ofs += n;
4693 cur_dst_file_ofs += n;
4694 }
4695 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
4696
4697 // no zip64 support yet
4698 if (cur_dst_file_ofs > 0xFFFFFFFF)
4699 return MZ_FALSE;
4700
4701 orig_central_dir_size = pState->m_central_dir.m_size;
4702
4703 memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
4704 MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
4705 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
4706 return MZ_FALSE;
4707
4708 n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
4709 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
4710 {
4711 mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
4712 return MZ_FALSE;
4713 }
4714
4715 if (pState->m_central_dir.m_size > 0xFFFFFFFF)
4716 return MZ_FALSE;
4717 n = (mz_uint32)orig_central_dir_size;
4718 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
4719 {
4720 mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
4721 return MZ_FALSE;
4722 }
4723
4724 pZip->m_total_files++;
4725 pZip->m_archive_size = cur_dst_file_ofs;
4726
4727 return MZ_TRUE;
4728}
4729
4730mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
4731{
4732 mz_zip_internal_state *pState;
4733 mz_uint64 central_dir_ofs, central_dir_size;
4734 mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];
4735
4736 if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
4737 return MZ_FALSE;
4738
4739 pState = pZip->m_pState;
4740
4741 // no zip64 support yet
4742 if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
4743 return MZ_FALSE;
4744
4745 central_dir_ofs = 0;
4746 central_dir_size = 0;
4747 if (pZip->m_total_files)
4748 {
4749 // Write central directory
4750 central_dir_ofs = pZip->m_archive_size;
4751 central_dir_size = pState->m_central_dir.m_size;
4752 pZip->m_central_directory_file_ofs = central_dir_ofs;
4753 if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
4754 return MZ_FALSE;
4755 pZip->m_archive_size += central_dir_size;
4756 }
4757
4758 // Write end of central directory record
4759 MZ_CLEAR_OBJ(hdr);
4760 MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
4761 MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
4762 MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
4763 MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
4764 MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);
4765
4766 if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
4767 return MZ_FALSE;
4768#ifndef MINIZ_NO_STDIO
4769 if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
4770 return MZ_FALSE;
4771#endif // #ifndef MINIZ_NO_STDIO
4772
4773 pZip->m_archive_size += sizeof(hdr);
4774
4775 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
4776 return MZ_TRUE;
4777}
4778
4779mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
4780{
4781 if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
4782 return MZ_FALSE;
4783 if (pZip->m_pWrite != mz_zip_heap_write_func)
4784 return MZ_FALSE;
4785 if (!mz_zip_writer_finalize_archive(pZip))
4786 return MZ_FALSE;
4787
4788 *pBuf = pZip->m_pState->m_pMem;
4789 *pSize = pZip->m_pState->m_mem_size;
4790 pZip->m_pState->m_pMem = NULL;
4791 pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
4792 return MZ_TRUE;
4793}
4794
4795mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
4796{
4797 mz_zip_internal_state *pState;
4798 mz_bool status = MZ_TRUE;
4799 if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
4800 return MZ_FALSE;
4801
4802 pState = pZip->m_pState;
4803 pZip->m_pState = NULL;
4804 mz_zip_array_clear(pZip, &pState->m_central_dir);
4805 mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
4806 mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
4807
4808#ifndef MINIZ_NO_STDIO
4809 if (pState->m_pFile)
4810 {
4811 MZ_FCLOSE(pState->m_pFile);
4812 pState->m_pFile = NULL;
4813 }
4814#endif // #ifndef MINIZ_NO_STDIO
4815
4816 if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
4817 {
4818 pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
4819 pState->m_pMem = NULL;
4820 }
4821
4822 pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
4823 pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
4824 return status;
4825}
4826
4827#ifndef MINIZ_NO_STDIO
4828mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
4829{
4830 mz_bool status, created_new_archive = MZ_FALSE;
4831 mz_zip_archive zip_archive;
4832 struct MZ_FILE_STAT_STRUCT file_stat;
4833 MZ_CLEAR_OBJ(zip_archive);
4834 if ((int)level_and_flags < 0)
4835 level_and_flags = MZ_DEFAULT_LEVEL;
4836 if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
4837 return MZ_FALSE;
4838 if (!mz_zip_writer_validate_archive_name(pArchive_name))
4839 return MZ_FALSE;
4840 if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
4841 {
4842 // Create a new archive.
4843 if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
4844 return MZ_FALSE;
4845 created_new_archive = MZ_TRUE;
4846 }
4847 else
4848 {
4849 // Append to an existing archive.
4850 if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
4851 return MZ_FALSE;
4852 if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
4853 {
4854 mz_zip_reader_end(&zip_archive);
4855 return MZ_FALSE;
4856 }
4857 }
4858 status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
4859 // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
4860 if (!mz_zip_writer_finalize_archive(&zip_archive))
4861 status = MZ_FALSE;
4862 if (!mz_zip_writer_end(&zip_archive))
4863 status = MZ_FALSE;
4864 if ((!status) && (created_new_archive))
4865 {
4866 // It's a new archive and something went wrong, so just delete it.
4867 int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
4868 (void)ignoredStatus;
4869 }
4870 return status;
4871}
4872
4873void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
4874{
4875 int file_index;
4876 mz_zip_archive zip_archive;
4877 void *p = NULL;
4878
4879 if (pSize)
4880 *pSize = 0;
4881
4882 if ((!pZip_filename) || (!pArchive_name))
4883 return NULL;
4884
4885 MZ_CLEAR_OBJ(zip_archive);
4886 if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
4887 return NULL;
4888
4889 if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
4890 p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
4891
4892 mz_zip_reader_end(&zip_archive);
4893 return p;
4894}
4895
4896#endif // #ifndef MINIZ_NO_STDIO
4897
4898#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
4899
4900#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
4901
4902#ifdef __cplusplus
4903}
4904#endif
4905
4906#endif // MINIZ_HEADER_FILE_ONLY
4907
4908/*
4909 This is free and unencumbered software released into the public domain.
4910
4911 Anyone is free to copy, modify, publish, use, compile, sell, or
4912 distribute this software, either in source code form or as a compiled
4913 binary, for any purpose, commercial or non-commercial, and by any
4914 means.
4915
4916 In jurisdictions that recognize copyright laws, the author or authors
4917 of this software dedicate any and all copyright interest in the
4918 software to the public domain. We make this dedication for the benefit
4919 of the public at large and to the detriment of our heirs and
4920 successors. We intend this dedication to be an overt act of
4921 relinquishment in perpetuity of all present and future rights to this
4922 software under copyright law.
4923
4924 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
4925 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
4926 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
4927 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
4928 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
4929 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
4930 OTHER DEALINGS IN THE SOFTWARE.
4931
4932 For more information, please refer to <http://unlicense.org/>
4933*/
diff --git a/apps/plugins/xrick/README.md b/apps/plugins/xrick/README.md
new file mode 100644
index 0000000000..ecb4c19b50
--- /dev/null
+++ b/apps/plugins/xrick/README.md
@@ -0,0 +1,88 @@
1xrick
2=====
3
4xrick is a clone of [Rick Dangerous](http://en.wikipedia.org/wiki/Rick_Dangerous),
5known to run on Linux, Windows, BeOs, Amiga, QNX, all sorts of gaming consoles...
6
7License agreement & legal bable
8-------------------------------
9
10* Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net) (http://www.bigorno.net/xrick/)
11* Copyright (C) 2008-2014 Pierluigi Vicinanza (pierluigi DOT vicinanza AT gmail.com)
12
13I (BigOrno) have written the initial [xrick](http://www.bigorno.net/xrick/) code.
14However, graphics and maps and sounds are by the authors of the original Rick Dangerous
15game, and "Rick Dangerous" remains a trademark of its owner(s) -- maybe
16Core Design (who wrote the game) or FireBird (who published it).
17As of today, I have not been successful at contacting Core Design.
18
19This makes it a bit difficult to formally release the whole code,
20including data for graphics and maps and sounds, under the terms of
21licences such as the GNU General Public Licence. So the code is
22released "in the spirit" of the GNU GPL. Whatever that means.
23
24This program is distributed in the hope that it will be useful, but
25WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26or FITNESS FOR A PARTICULAR PURPOSE.
27
28Building
29--------
30
31**Requirements:**
32
33* [CMake](http://www.cmake.org/)
34* [SDL](https://www.libsdl.org/download-1.2.php) version 1.2.x
35* [zlib](http://www.zlib.net/)
36
371. *Create a build directory*
38
39 ```
40 $ cd xrick-x.x.x
41 $ mkdir build
42 $ cd build
43 ```
44
452. *Generate your Makefile*
46
47 `$ cmake ../source/xrick/projects/cmake`
48
493. *Build*
50
51 `$ make`
52
534. *Install (optional)*
54
55 `$ make install`
56
57Platform specific notes can be found in README.platforms.
58
59Usage
60-----
61
62`xrick --help` will tell you all about command-line options.
63
64Controls
65--------
66
67- left, right, up (jump) or down (crawl): arrow keys or Z, X, O and K.
68- fire: SPACE, end: E, pause: P, exit: ESC.
69- use left, right, up, down + fire to poke something with your stick,
70 lay a stick of dynamite, or fire a bullet.
71- toggle fullscreen: F1 ; zoom in/out: F2, F3.
72- mute: F4 ; volume up/down: F5, F6.
73- cheat modes, "trainer": F7 ; "never die": F8 ; "expose": F9.
74
75More details at http://www.bigorno.net/xrick/
76
77Release History
78---------------
79
80Please see the file called CHANGELOG.md.
81
82Contacts
83--------
84
85Report problems or ask questions to:
86
87* _BigOrno_ (bigorno@bigorno.net)
88* _Pierluigi Vicinanza_ (pierluigi DOT vicinanza AT gmail.com)
diff --git a/apps/plugins/xrick/README.rockbox b/apps/plugins/xrick/README.rockbox
new file mode 100644
index 0000000000..fe6bcd2fd3
--- /dev/null
+++ b/apps/plugins/xrick/README.rockbox
@@ -0,0 +1,16 @@
1xrick on Rockbox
2----------------
3
4Based on the upstream #021212 release.
5
6Currently enabled on all players that have at least 2-bit (ie greyscale)
7display, but is optimized for color displays of 320x200 or larger.
8
9You will need the data files extracted into your player's root
10directory. For example:
11
12 $ unzip xrick-data.zip -d /mnt/myMp3Player
13
14Note that most players do not have a tuned keymap for xrick, instead
15relying on the standard "pluginlib" bindings that are most likely
16sub-optimal. Patches welcome!
diff --git a/apps/plugins/xrick/SOURCES b/apps/plugins/xrick/SOURCES
new file mode 100644
index 0000000000..f3acdd541a
--- /dev/null
+++ b/apps/plugins/xrick/SOURCES
@@ -0,0 +1,40 @@
1control.c
2devtools.c
3draw.c
4e_bomb.c
5e_bonus.c
6e_box.c
7e_bullet.c
8e_rick.c
9e_sbonus.c
10e_them.c
11ents.c
12game.c
13maps.c
14rects.c
15res_magic.c
16resources.c
17scr_gameover.c
18scr_getname.c
19scr_imain.c
20scr_imap.c
21scr_pause.c
22scr_xrick.c
23scroller.c
24util.c
25
26data/img.c
27data/pics.c
28data/sounds.c
29data/sprites.c
30data/tiles.c
31
32system/main_rockbox.c
33system/sysarg_rockbox.c
34system/sysevt_rockbox.c
35system/sysfile_rockbox.c
36system/sysmem_rockbox.c
37system/sysmenu_rockbox.c
38system/syssnd_rockbox.c
39system/system_rockbox.c
40system/sysvid_rockbox.c
diff --git a/apps/plugins/xrick/config.h b/apps/plugins/xrick/config.h
new file mode 100644
index 0000000000..31ecc79bb7
--- /dev/null
+++ b/apps/plugins/xrick/config.h
@@ -0,0 +1,58 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#ifndef XRICK_CONFIG_H
26#define XRICK_CONFIG_H
27
28/* graphics (choose one) */
29#define GFXST
30#undef GFXPC
31
32/* sound support */
33#define ENABLE_SOUND
34
35/* cheats support */
36#define ENABLE_CHEATS
37
38/* development tools */
39#undef ENABLE_DEVTOOLS
40
41/* Print debug info to screen */
42#undef ENABLE_SYSPRINTF_TO_SCREEN
43
44/* enable/disable subsystem debug */
45#undef DEBUG_MEMORY
46#undef DEBUG_ENTS
47#undef DEBUG_SCROLLER
48#undef DEBUG_MAPS
49#undef DEBUG_JOYSTICK
50#undef DEBUG_EVENTS
51#undef DEBUG_AUDIO
52#undef DEBUG_AUDIO2
53#undef DEBUG_VIDEO
54#undef DEBUG_VIDEO2
55
56#endif /* ndef XRICK_CONFIG_H */
57
58/* eof */
diff --git a/apps/plugins/xrick/control.c b/apps/plugins/xrick/control.c
new file mode 100644
index 0000000000..27fbe97376
--- /dev/null
+++ b/apps/plugins/xrick/control.c
@@ -0,0 +1,24 @@
1/*
2 * xrick/control.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/control.h"
17
18unsigned control_status = 0;
19extern inline bool control_test(control_t c);
20extern inline void control_set(control_t c);
21extern inline void control_clear(control_t c);
22bool control_active = true;
23
24/* eof */
diff --git a/apps/plugins/xrick/control.h b/apps/plugins/xrick/control.h
new file mode 100644
index 0000000000..91d0a8a195
--- /dev/null
+++ b/apps/plugins/xrick/control.h
@@ -0,0 +1,41 @@
1/*
2 * xrick/control.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _CONTROL_H
17#define _CONTROL_H
18
19#include "xrick/system/basic_types.h"
20
21typedef enum
22{
23 Control_UP = (1 << 0),
24 Control_DOWN = (1 << 1),
25 Control_LEFT = (1 << 2),
26 Control_RIGHT = (1 << 3),
27 Control_PAUSE = (1 << 4),
28 Control_END = (1 << 5),
29 Control_EXIT = (1 << 6),
30 Control_FIRE = (1 << 7)
31} control_t;
32
33extern unsigned control_status;
34inline bool control_test(control_t c) { return control_status & c; }
35inline void control_set(control_t c) { control_status |= c; }
36inline void control_clear(control_t c) { control_status &= ~c; }
37extern bool control_active;
38
39#endif /* ndef _CONTROL_H */
40
41/* eof */
diff --git a/apps/plugins/xrick/data/img.c b/apps/plugins/xrick/data/img.c
new file mode 100644
index 0000000000..2226df3c48
--- /dev/null
+++ b/apps/plugins/xrick/data/img.c
@@ -0,0 +1,23 @@
1/*
2 * xrick/data/img.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/data/img.h"
15
16#include <stddef.h> /* NULL */
17
18/*
19 * globals
20 */
21img_t *img_splash = NULL;
22
23/* eof */
diff --git a/apps/plugins/xrick/data/img.h b/apps/plugins/xrick/data/img.h
new file mode 100644
index 0000000000..96952627f9
--- /dev/null
+++ b/apps/plugins/xrick/data/img.h
@@ -0,0 +1,39 @@
1/*
2 * xrick/data/img.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _IMG_H
17#define _IMG_H
18
19#include "xrick/system/basic_types.h"
20
21typedef struct {
22 U8 r, g, b, nothing;
23} img_color_t;
24
25typedef struct {
26 U16 width;
27 U16 height;
28 U16 xPos;
29 U16 yPos;
30 U16 ncolors;
31 img_color_t *colors;
32 U8 *pixels;
33} img_t;
34
35extern img_t *img_splash;
36
37#endif /* ndef _IMG_H */
38
39/* eof */
diff --git a/apps/plugins/xrick/data/pics.c b/apps/plugins/xrick/data/pics.c
new file mode 100644
index 0000000000..103bfd8344
--- /dev/null
+++ b/apps/plugins/xrick/data/pics.c
@@ -0,0 +1,29 @@
1/*
2 * xrick/data/pics.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/data/pics.h"
15
16#ifdef GFXST
17
18#include <stddef.h> /* NULL */
19
20/*
21 * globals
22 */
23pic_t *pic_haf = NULL;
24pic_t *pic_congrats = NULL;
25pic_t *pic_splash = NULL;
26
27#endif /* GFXST */
28
29/* eof */
diff --git a/apps/plugins/xrick/data/pics.h b/apps/plugins/xrick/data/pics.h
new file mode 100644
index 0000000000..0c540b125e
--- /dev/null
+++ b/apps/plugins/xrick/data/pics.h
@@ -0,0 +1,41 @@
1/*
2 * xrick/data/pics.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _PICS_H
17#define _PICS_H
18
19#include "xrick/config.h"
20
21#ifdef GFXST
22
23#include "xrick/system/basic_types.h"
24
25typedef struct {
26 U16 width;
27 U16 height;
28 U16 xPos;
29 U16 yPos;
30 U32 *pixels;
31} pic_t;
32
33extern pic_t *pic_haf;
34extern pic_t *pic_congrats;
35extern pic_t *pic_splash;
36
37#endif /* GFXST */
38
39#endif /* ndef _PICS_H */
40
41/* eof */
diff --git a/apps/plugins/xrick/data/sounds.c b/apps/plugins/xrick/data/sounds.c
new file mode 100644
index 0000000000..853e7cd198
--- /dev/null
+++ b/apps/plugins/xrick/data/sounds.c
@@ -0,0 +1,44 @@
1/*
2 * xrick/data/sounds.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/data/sounds.h"
15
16#ifdef ENABLE_SOUND
17
18#include <stddef.h> /* NULL */
19
20sound_t *soundBombshht = NULL;
21sound_t *soundBonus = NULL;
22sound_t *soundBox = NULL;
23sound_t *soundBullet = NULL;
24sound_t *soundCrawl = NULL;
25sound_t *soundDie = NULL;
26sound_t *soundEntity[SOUNDS_NBR_ENTITIES];
27sound_t *soundExplode = NULL;
28sound_t *soundGameover = NULL;
29sound_t *soundJump = NULL;
30sound_t *soundPad = NULL;
31sound_t *soundSbonus1 = NULL;
32sound_t *soundSbonus2 = NULL;
33sound_t *soundStick = NULL;
34sound_t *soundTune0 = NULL;
35sound_t *soundTune1 = NULL;
36sound_t *soundTune2 = NULL;
37sound_t *soundTune3 = NULL;
38sound_t *soundTune4 = NULL;
39sound_t *soundTune5 = NULL;
40sound_t *soundWalk = NULL;
41
42#endif /* ENABLE_SOUND */
43
44/* eof */ \ No newline at end of file
diff --git a/apps/plugins/xrick/data/sounds.h b/apps/plugins/xrick/data/sounds.h
new file mode 100644
index 0000000000..5d89b3e9ee
--- /dev/null
+++ b/apps/plugins/xrick/data/sounds.h
@@ -0,0 +1,87 @@
1/*
2 * xrick/data/sounds.h
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#ifndef _SOUNDS_H
15#define _SOUNDS_H
16
17#include "xrick/config.h"
18
19#ifdef ENABLE_SOUND
20
21#include "xrick/system/basic_types.h"
22
23typedef struct {
24 char *name;
25 U8 *buf;
26 U32 len;
27 bool dispose;
28} sound_t;
29
30enum
31{
32 /* expected format is 8-bit mono at 22050Hz */
33 Wave_SAMPLE_RATE = 22050,
34 Wave_AUDIO_FORMAT = 1, /* PCM = 1 (i.e. Linear quantization) */
35 Wave_CHANNEL_COUNT = 1,
36 Wave_BITS_PER_SAMPLE = 8,
37};
38
39typedef struct {
40 /* "RIFF" chunk descriptor */
41 U8 riffChunkId[4];
42 U8 riffChunkSize[4];
43 U8 riffType[4];
44 /* "fmt" sub-chunk */
45 U8 formatChunkId[4];
46 U8 formatChunkSize[4];
47 U8 audioFormat[2];
48 U8 channelCount[2];
49 U8 sampleRate[4];
50 U8 byteRate[4];
51 U8 blockAlign[2];
52 U8 bitsPerSample[2];
53 /* "data" sub-chunk */
54 U8 dataChunkId[4];
55 U8 dataChunkSize[4];
56} wave_header_t;
57
58/* apparently there are 10 entity sounds in original game (ref. "e_them.c" notes)? However we only have 9 so far... */
59enum { SOUNDS_NBR_ENTITIES = 10 };
60
61extern sound_t *soundBombshht;
62extern sound_t *soundBonus;
63extern sound_t *soundBox;
64extern sound_t *soundBullet;
65extern sound_t *soundCrawl;
66extern sound_t *soundDie;
67extern sound_t *soundEntity[SOUNDS_NBR_ENTITIES];
68extern sound_t *soundExplode;
69extern sound_t *soundGameover;
70extern sound_t *soundJump;
71extern sound_t *soundPad;
72extern sound_t *soundSbonus1;
73extern sound_t *soundSbonus2;
74extern sound_t *soundStick;
75extern sound_t *soundTune0;
76extern sound_t *soundTune1;
77extern sound_t *soundTune2;
78extern sound_t *soundTune3;
79extern sound_t *soundTune4;
80extern sound_t *soundTune5;
81extern sound_t *soundWalk;
82
83#endif /* ENABLE_SOUND */
84
85#endif /* ndef _SOUNDS_H */
86
87/* eof */ \ No newline at end of file
diff --git a/apps/plugins/xrick/data/sprites.c b/apps/plugins/xrick/data/sprites.c
new file mode 100644
index 0000000000..461861e85e
--- /dev/null
+++ b/apps/plugins/xrick/data/sprites.c
@@ -0,0 +1,22 @@
1/*
2 * xrick/data/sprites.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/data/sprites.h"
15
16/*
17 * globals
18 */
19size_t sprites_nbr_sprites = 0;
20sprite_t *sprites_data = NULL;
21
22/* eof */
diff --git a/apps/plugins/xrick/data/sprites.h b/apps/plugins/xrick/data/sprites.h
new file mode 100644
index 0000000000..124ad4bc85
--- /dev/null
+++ b/apps/plugins/xrick/data/sprites.h
@@ -0,0 +1,70 @@
1/*
2 * xrick/data/sprites.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16/*
17 * NOTES -- PC version
18 *
19 * A sprite consists in 4 columns and 21 rows of (U16 mask, U16 pict),
20 * each pair representing 8 pixels (cga encoding, two bits per pixels).
21 * Sprites are stored in 'sprites.bin' and are loaded by spr_init. Memory
22 * is freed by spr_shutdown.
23 *
24 * There are four sprites planes. Plane 0 is the raw content of 'sprites.bin',
25 * and planes 1, 2 and 3 contain copies of plane 0 with all sprites shifted
26 * 2, 4 and 6 pixels to the right.
27 */
28
29#ifndef _SPRITES_H_
30#define _SPRITES_H_
31
32#include "xrick/config.h"
33
34#include "xrick/system/basic_types.h"
35
36#include <stddef.h> /* size_t */
37
38#ifdef GFXPC
39
40typedef struct {
41 U16 mask;
42 U16 pict;
43} spriteX_t;
44
45enum {
46 SPRITES_NBR_ROWS = 21,
47 SPRITES_NBR_COLS = 4
48};
49typedef spriteX_t sprite_t[SPRITES_NBR_COLS][SPRITES_NBR_ROWS]; /* one sprite */
50
51#endif /* GFXPC */
52
53
54#ifdef GFXST
55
56enum {
57 SPRITES_NBR_ROWS = 21,
58 SPRITES_NBR_COLS = 4,
59 SPRITES_NBR_DATA = SPRITES_NBR_ROWS * SPRITES_NBR_COLS
60};
61typedef U32 sprite_t[SPRITES_NBR_DATA];
62
63#endif /* GFXST */
64
65extern size_t sprites_nbr_sprites;
66extern sprite_t *sprites_data;
67
68#endif /* ndef _SPRITES_H_ */
69
70/* eof */
diff --git a/apps/plugins/xrick/data/tiles.c b/apps/plugins/xrick/data/tiles.c
new file mode 100644
index 0000000000..17d702ee6b
--- /dev/null
+++ b/apps/plugins/xrick/data/tiles.c
@@ -0,0 +1,22 @@
1/*
2 * xrick/data/tiles.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/data/tiles.h"
15
16/*
17 * globals
18 */
19size_t tiles_nbr_banks = 0;
20tile_t *tiles_data = NULL;
21
22/* eof */
diff --git a/apps/plugins/xrick/data/tiles.h b/apps/plugins/xrick/data/tiles.h
new file mode 100644
index 0000000000..35bc07184e
--- /dev/null
+++ b/apps/plugins/xrick/data/tiles.h
@@ -0,0 +1,70 @@
1/*
2 * xrick/data/tiles.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16/*
17 * NOTES
18 *
19 * A tile consists in one column and 8 rows of 8 U16 (cga encoding, two
20 * bits per pixel). The tl_tiles array contains all tiles, with the
21 * following structure:
22 *
23 * 0x0000 - 0x00FF tiles for main intro
24 * 0x0100 - 0x01FF tiles for map intro
25 * 0x0200 - 0x0327 unused
26 * 0x0328 - 0x0427 game tiles, page 0
27 * 0x0428 - 0x0527 game tiles, page 1
28 * 0x0527 - 0x05FF unused
29 */
30
31#ifndef _TILES_H
32#define _TILES_H
33
34#include "xrick/system/basic_types.h"
35
36#include "xrick/config.h"
37
38#include <stddef.h> /* size_t */
39
40/*
41 * three special tile numbers
42 */
43enum {
44 TILES_BULLET = 0x01,
45 TILES_BOMB = 0x02,
46 TILES_RICK = 0x03
47};
48
49/*
50 * one single tile
51 */
52enum { TILES_NBR_LINES = 0x08 };
53
54#ifdef GFXPC
55typedef U16 tile_t[TILES_NBR_LINES];
56#endif
57#ifdef GFXST
58typedef U32 tile_t[TILES_NBR_LINES];
59#endif
60
61/*
62 * tiles banks (each bank is 0x100 tiles)
63 */
64enum { TILES_NBR_TILES = 0x100 };
65extern size_t tiles_nbr_banks;
66extern tile_t *tiles_data;
67
68#endif /* ndef _TILES_H */
69
70/* eof */
diff --git a/apps/plugins/xrick/debug.h b/apps/plugins/xrick/debug.h
new file mode 100644
index 0000000000..5bf777213b
--- /dev/null
+++ b/apps/plugins/xrick/debug.h
@@ -0,0 +1,84 @@
1/*
2 * xrick/debug.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _DEBUG_H
17#define _DEBUG_H
18
19#include "xrick/config.h"
20
21/* define IFDEBUG macros */
22#ifdef DEBUG_MEMORY
23#define IFDEBUG_MEMORY(X); X
24#else
25#define IFDEBUG_MEMORY(X);
26#endif
27
28#ifdef DEBUG_ENTS
29#define IFDEBUG_ENTS(X); X
30#else
31#define IFDEBUG_ENTS(X);
32#endif
33
34#ifdef DEBUG_SCROLLER
35#define IFDEBUG_SCROLLER(X); X
36#else
37#define IFDEBUG_SCROLLER(X);
38#endif
39
40#ifdef DEBUG_MAPS
41#define IFDEBUG_MAPS(X); X
42#else
43#define IFDEBUG_MAPS(X);
44#endif
45
46#ifdef DEBUG_JOYSTICK
47#define IFDEBUG_JOYSTICK(X); X
48#else
49#define IFDEBUG_JOYSTICK(X);
50#endif
51
52#ifdef DEBUG_EVENTS
53#define IFDEBUG_EVENTS(X); X
54#else
55#define IFDEBUG_EVENTS(X);
56#endif
57
58#ifdef DEBUG_AUDIO
59#define IFDEBUG_AUDIO(X); X
60#else
61#define IFDEBUG_AUDIO(X);
62#endif
63
64#ifdef DEBUG_AUDIO2
65#define IFDEBUG_AUDIO2(X); X
66#else
67#define IFDEBUG_AUDIO2(X);
68#endif
69
70#ifdef DEBUG_VIDEO
71#define IFDEBUG_VIDEO(X); X
72#else
73#define IFDEBUG_VIDEO(X);
74#endif
75
76#ifdef DEBUG_VIDEO2
77#define IFDEBUG_VIDEO2(X); X
78#else
79#define IFDEBUG_VIDEO2(X);
80#endif
81
82#endif /* ndef _DEBUG_H */
83
84/* eof */
diff --git a/apps/plugins/xrick/devtools.c b/apps/plugins/xrick/devtools.c
new file mode 100644
index 0000000000..56fd5a7106
--- /dev/null
+++ b/apps/plugins/xrick/devtools.c
@@ -0,0 +1,247 @@
1/*
2 * xrick/devtools.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/config.h"
17
18#ifdef ENABLE_DEVTOOLS
19
20#include "xrick/devtools.h"
21#include "xrick/game.h"
22#include "xrick/control.h"
23#include "xrick/screens.h"
24#include "xrick/draw.h"
25#include "xrick/data/sprites.h"
26#include "xrick/maps.h"
27#include "xrick/system/system.h"
28
29/*
30 * DevTools
31 */
32
33U8
34devtools_run(void)
35{
36 static U8 seq = 0;
37 static U8 pos = 0;
38 static U8 pos2 = 0;
39 U8 i, j, k, l;
40 U8 s[128];
41
42 if (seq == 0) {
43 sysvid_clear();
44 game_rects = &draw_SCREENRECT;
45#ifdef GFXPC
46 draw_filter = 0xffff;
47#endif
48 seq = 1;
49 }
50
51 switch (seq) {
52 case 1: /* draw tiles */
53 sysvid_clear();
54 draw_tilesBank = 0;
55 sys_snprintf(s, sizeof(s), "TILES@BANK@%d\376", pos);
56 draw_setfb(4, 4);
57 draw_tilesListImm(s);
58 k = 0;
59 for (i = 0; i < 0x10; i++) {
60 draw_setfb(80 + i * 0x0a, 14);
61 draw_tile((i<10?0x30:'A'-10) + i);
62 draw_setfb(64, 30 + i * 0x0a);
63 draw_tile((i<10?0x30:'A'-10) + i);
64 }
65 draw_tilesBank = pos;
66 for (i = 0; i < 0x10; i++)
67 for (j = 0; j < 0x10; j++) {
68 draw_setfb(80 + j * 0x0a, 30 + i * 0x0a);
69 draw_tile(k++);
70 }
71 seq = 10;
72 break;
73 case 10: /* wait for key pressed */
74 if (control_test(Control_FIRE))
75 seq = 98;
76 if (control_test(Control_UP))
77 seq = 12;
78 if (control_test(Control_DOWN))
79 seq = 13;
80 if (control_test(Control_RIGHT))
81 seq = 11;
82 break;
83 case 11: /* wait for key released */
84 if (!(control_test(Control_RIGHT))) {
85 pos = 0;
86 seq = 21;
87 }
88 break;
89 case 12: /* wait for key released */
90 if (!(control_test(Control_UP))) {
91 if (pos < 4) pos++;
92 seq = 1;
93 }
94 break;
95 case 13: /* wait for key released */
96 if (!(control_test(Control_DOWN))) {
97 if (pos > 0) pos--;
98 seq = 1;
99 }
100 break;
101 case 21: /* draw sprites */
102 sysvid_clear();
103 draw_tilesBank = 0;
104 sys_snprintf(s, sizeof(s), "SPRITES\376");
105 draw_setfb(4, 4);
106 draw_tilesListImm(s);
107 for (i = 0; i < 8; i++) {
108 draw_setfb(0x08 + 0x20 + i * 0x20, 0x30 - 26);
109 draw_tile((i<10?0x30:'A'-10) + i);
110 draw_setfb(0x08 + 0x20 + i * 0x20, 0x30 - 16);
111 draw_tile((i+8<10?0x30:'A'-10) + i+8);
112 }
113 for (i = 0; i < 4; i++) {
114 k = pos + i * 8;
115 draw_setfb(0x20 - 16, 0x08 + 0x30 + i * 0x20);
116 j = k%16;
117 k /= 16;
118 draw_tile((j<10?0x30:'A'-10) + j);
119 draw_setfb(0x20 - 26, 0x08 + 0x30 + i * 0x20);
120 j = k%16;
121 draw_tile((j<10?0x30:'A'-10) + j);
122 }
123 k = pos;
124 for (i = 0; i < 4; i++)
125 for (j = 0; j < 8; j++) {
126 draw_sprite(k++, 0x20 + j * 0x20, 0x30 + i * 0x20);
127 }
128 seq = 30;
129 break;
130 case 30: /* wait for key pressed */
131 if (control_test(Control_FIRE))
132 seq = 98;
133 if (control_test(Control_UP))
134 seq = 32;
135 if (control_test(Control_DOWN))
136 seq = 33;
137 if (control_test(Control_LEFT))
138 seq = 31;
139 if (control_test(Control_RIGHT))
140 seq = 40;
141 break;
142 case 31: /* wait for key released */
143 if (!(control_test(Control_LEFT))) {
144 pos = 0;
145 seq = 1;
146 }
147 break;
148 case 32: /* wait for key released */
149 if (!(control_test(Control_UP))) {
150 if (pos < sprites_nbr_sprites - 32) pos += 32;
151 seq = 21;
152 }
153 break;
154 case 33: /* wait for key released */
155 if (!(control_test(Control_DOWN))) {
156 if (pos > 0) pos -= 32;
157 seq = 21;
158 }
159 break;
160 case 40:
161 sysvid_clear();
162#ifdef GFXPC
163 if (pos2 == 0) pos2 = 2;
164#endif
165#ifdef GFXST
166 if (pos2 == 0) pos2 = 1;
167#endif
168 sys_snprintf(s, sizeof(s), "BLOCKS@%#04X@TO@%#04X@WITH@BANK@%d\376",
169 pos, pos + 4*8-1, pos2);
170 draw_setfb(4, 4);
171 draw_tilesBank = 0;
172 draw_tilesListImm(s);
173 draw_tilesBank = pos2;
174 for (l = 0; l < 8; l++)
175 for (k = 0; k < 4; k++)
176 for (i = 0; i < 4; i++)
177 for (j = 0; j < 4; j++) {
178 draw_setfb(20 + j * 8 + l * 36, 30 + i * 8 + k * 36);
179 draw_tile(map_blocks[pos + l + k * 8][i * 4 + j]);
180 }
181 seq = 41;
182 break;
183 case 41:
184 if (control_test(Control_FIRE))
185 seq = 98;
186 if (control_test(Control_UP))
187 seq = 42;
188 if (control_test(Control_DOWN))
189 seq = 43;
190 if (control_test(Control_LEFT))
191 seq = 44;
192 if (control_test(Control_PAUSE))
193 seq = 45;
194 break;
195 case 42:
196 if (!(control_test(Control_UP))) {
197 if (pos < map_nbr_blocks - 8*4) pos += 8 * 4;
198 seq = 40;
199 }
200 break;
201 case 43:
202 if (!(control_test(Control_DOWN))) {
203 if (pos > 0) pos -= 8 * 4;
204 seq = 40;
205 }
206 break;
207 case 44:
208 if (!(control_test(Control_LEFT))) {
209 pos = 0;
210 pos2 = 0;
211 seq = 21;
212 }
213 break;
214 case 45:
215 if (!(control_test(Control_PAUSE))) {
216#ifdef GFXPC
217 if (pos2 == 2) pos2 = 3;
218 else pos2 = 2;
219#endif
220#ifdef GFXST
221 if (pos2 == 1) pos2 = 2;
222 else pos2 = 1;
223#endif
224 seq = 40;
225 }
226 break;
227 case 98: /* wait for key released */
228 if (!(control_test(Control_FIRE)))
229 seq = 99;
230 break;
231 }
232
233 if (control_test(Control_EXIT)) /* check for exit request */
234 return SCREEN_EXIT;
235
236 if (seq == 99) { /* we're done */
237 sysvid_clear();
238 seq = 0;
239 return SCREEN_DONE;
240 }
241
242 return SCREEN_RUNNING;
243}
244
245#endif /* ENABLE_DEVTOOLS */
246
247/* eof */
diff --git a/apps/plugins/xrick/devtools.h b/apps/plugins/xrick/devtools.h
new file mode 100644
index 0000000000..e1021bfbcd
--- /dev/null
+++ b/apps/plugins/xrick/devtools.h
@@ -0,0 +1,25 @@
1/*
2 * xrick/devtools.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _DEVTOOLS_H
17#define _DEVTOOLS_H
18
19#include "xrick/system/basic_types.h"
20
21extern U8 devtools_run(void);
22
23#endif /* ndef _DEVTOOLS_H */
24
25/* eof */
diff --git a/apps/plugins/xrick/draw.c b/apps/plugins/xrick/draw.c
new file mode 100644
index 0000000000..a759ed5239
--- /dev/null
+++ b/apps/plugins/xrick/draw.c
@@ -0,0 +1,702 @@
1/*
2 * xrick/draw.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16/*
17 * NOTES
18 *
19 * This is the only file which accesses the video. Anything calling d_*
20 * function should be video-independant.
21 *
22 * draw.c draws into a 320x200 or 0x0140x0xc8 8-bits depth frame buffer,
23 * using the CGA 2 bits color codes. It is up to the video to figure out
24 * how to display the frame buffer. Whatever draw.c does, does not show
25 * until the screen is explicitely refreshed.
26 *
27 * The "screen" is the whole 0x0140 by 0x00c8 screen, coordinates go from
28 * 0x0000,0x0000 to 0x013f,0x00c7.
29 *
30 * The "map" is a 0x0100 by 0x0140 rectangle that represents the active
31 * game area.
32 *
33 * Relative to the screen, the "map" is located at 0x0020,-0x0040 : the
34 * "map" is composed of two hidden 0x0100 by 0x0040 rectangles (one at the
35 * top and one at the bottom) and one visible 0x0100 by 0x00c0 rectangle (in
36 * the middle).
37 *
38 * The "map screen" is the visible rectangle ; it is a 0x0100 by 0xc0
39 * rectangle located at 0x0020,0x00.
40 *
41 * Coordinates can be relative to the screen, the map, or the map screen.
42 *
43 * Coordinates can be expressed in pixels. When relative to the map or the
44 * map screen, they can also be expressed in tiles, the map being composed
45 * of rows of 0x20 tiles of 0x08 by 0x08 pixels.
46 */
47
48#include "xrick/system/system.h"
49
50#include "xrick/game.h"
51#include "xrick/draw.h"
52
53#include "xrick/data/sprites.h"
54#include "xrick/data/tiles.h"
55
56#include "xrick/maps.h"
57#include "xrick/rects.h"
58#include "xrick/data/img.h"
59
60
61/*
62 * counters positions (pixels, screen)
63 */
64#ifdef GFXPC
65#define DRAW_STATUS_SCORE_X 0x28
66#define DRAW_STATUS_LIVES_X 0xE8
67#define DRAW_STATUS_Y 0x08
68#endif
69#define DRAW_STATUS_BULLETS_X 0x68
70#define DRAW_STATUS_BOMBS_X 0xA8
71#ifdef GFXST
72#define DRAW_STATUS_SCORE_X 0x20
73#define DRAW_STATUS_LIVES_X 0xF0
74#define DRAW_STATUS_Y 0
75#endif
76
77
78/*
79 * public vars
80 */
81U8 *draw_tllst; /* pointer to tiles list */
82#ifdef GFXPC
83U16 draw_filter; /* CGA colors filter */
84#endif
85U8 draw_tilesBank; /* tile number offset */
86
87rect_t draw_STATUSRECT = {
88 DRAW_STATUS_SCORE_X, DRAW_STATUS_Y,
89 DRAW_STATUS_LIVES_X + 6 * 8 - DRAW_STATUS_SCORE_X, 8,
90 NULL
91};
92const rect_t draw_SCREENRECT = { 0, 0, SYSVID_WIDTH, SYSVID_HEIGHT, NULL };
93
94size_t game_color_count = 0;
95img_color_t *game_colors = NULL;
96
97/*
98 * private vars
99 */
100static U8 *fb; /* frame buffer pointer */
101
102
103/*
104 * Set the frame buffer pointer
105 *
106 * x, y: position (pixels, screen)
107 */
108void
109draw_setfb(U16 x, U16 y)
110{
111 fb = sysvid_fb + x + y * SYSVID_WIDTH;
112}
113
114
115/*
116 * Clip to map screen
117 *
118 * x, y: position (pixels, map) CHANGED clipped
119 * width, height: dimension CHANGED clipped
120 * return: true if fully clipped, false if still (at least partly) visible
121 */
122bool
123draw_clipms(S16 *x, S16 *y, U16 *width, U16 *height)
124{
125 if (*x < 0) {
126 if (*x + *width < 0)
127 return true;
128 else {
129 *width += *x;
130 *x = 0;
131 }
132 }
133 else {
134 if (*x > 0x0100)
135 return true;
136 else if (*x + *width > 0x0100) {
137 *width = 0x0100 - *x;
138 }
139 }
140
141 if (*y < DRAW_XYMAP_SCRTOP) {
142 if ((*y + *height) < DRAW_XYMAP_SCRTOP)
143 return true;
144 else {
145 *height += *y - DRAW_XYMAP_SCRTOP;
146 *y = DRAW_XYMAP_SCRTOP;
147 }
148 }
149 else {
150 if (*y >= DRAW_XYMAP_HBTOP)
151 return true;
152 else if (*y + *height > DRAW_XYMAP_HBTOP)
153 *height = DRAW_XYMAP_HBTOP - *y;
154 }
155
156 return false;
157}
158
159
160/*
161 * Draw a list of tiles onto the frame buffer
162 * start at position indicated by fb ; at the end of each (sub)list,
163 * perform a "carriage return + line feed" i.e. go back to the initial
164 * position then go down one tile row (8 pixels)
165 *
166 * ASM 1e33
167 * fb: CHANGED (see above)
168 * draw_tllst: CHANGED points to the element following 0xfe/0xff end code
169 */
170void
171draw_tilesList(void)
172{
173 U8 *t;
174
175 t = fb;
176 while (draw_tilesSubList() != 0xFE) { /* draw sub-list */
177 t += 8 * SYSVID_WIDTH; /* go down one tile i.e. 8 lines */
178 fb = t;
179 }
180}
181
182
183/*
184 * Draw a list of tiles onto the frame buffer -- same as draw_tilesList,
185 * but accept an immediate string as parameter. Note that the string needs
186 * to be properly terminated with 0xfe (\376) and 0xff (\377) chars.
187 */
188void
189draw_tilesListImm(U8 *list)
190{
191 draw_tllst = list;
192 draw_tilesList();
193}
194
195
196/*
197 * Draw a sub-list of tiles onto the frame buffer
198 * start at position indicated by fb ; leave fb pointing to the next
199 * tile to the right of the last tile drawn
200 *
201 * ASM 1e41
202 * fpb: CHANGED (see above)
203 * draw_tllst: CHANGED points to the element following 0xfe/0xff end code
204 * returns: end code (0xfe : end of list ; 0xff : end of sub-list)
205 */
206U8
207draw_tilesSubList()
208{
209 U8 i;
210
211 i = *(draw_tllst++);
212 while (i != 0xFF && i != 0xFE) { /* while not end */
213 draw_tile(i); /* draw tile */
214 i = *(draw_tllst++);
215 }
216 return i;
217}
218
219
220/*
221 * Draw a tile
222 * at position indicated by fb ; leave fb pointing to the next tile
223 * to the right of the tile drawn
224 *
225 * ASM 1e6c
226 * tlnbr: tile number
227 * draw_filter: CGA colors filter
228 * fb: CHANGED (see above)
229 */
230void
231draw_tile(U8 tileNumber)
232{
233 U8 i, k, *f;
234
235#ifdef GFXPC
236 U16 x;
237#endif
238
239#ifdef GFXST
240 U32 x;
241#endif
242
243 f = fb; /* frame buffer */
244 for (i = 0; i < TILES_NBR_LINES; i++) { /* for all 8 pixel lines */
245
246#ifdef GFXPC
247 x = tiles_data[draw_tilesBank * TILES_NBR_TILES + tileNumber][i] & draw_filter;
248 /*
249 * tiles / perform the transformation from CGA 2 bits
250 * per pixel to frame buffer 8 bits per pixels
251 */
252 for (k = 8; k--; x >>= 2)
253 f[k] = x & 3;
254 f += SYSVID_WIDTH; /* next line */
255#endif
256
257#ifdef GFXST
258 x = tiles_data[draw_tilesBank * TILES_NBR_TILES + tileNumber][i];
259 /*
260 * tiles / perform the transformation from ST 4 bits
261 * per pixel to frame buffer 8 bits per pixels
262 */
263 for (k = 8; k--; x >>= 4)
264 f[k] = x & 0x0F;
265 f += SYSVID_WIDTH; /* next line */
266#endif
267
268 }
269
270 fb += 8; /* next tile */
271}
272
273/*
274 * Draw a sprite
275 *
276 * ASM 1a09
277 * nbr: sprite number
278 * x, y: sprite position (pixels, screen)
279 * fb: CHANGED
280 */
281#ifdef GFXPC
282void
283draw_sprite(U8 nbr, U16 x, U16 y)
284{
285 U8 i, j, k, *f;
286 U16 xm = 0, xp = 0;
287
288 draw_setfb(x, y);
289
290 for (i = 0; i < SPRITES_NBR_COLS; i++) { /* for each tile column */
291 f = fb; /* frame buffer */
292 for (j = 0; j < SPRITES_NBR_ROWS; j++) { /* for each pixel row */
293 xm = sprites_data[nbr][i][j].mask; /* mask */
294 xp = sprites_data[nbr][i][j].pict; /* picture */
295 /*
296 * sprites / perform the transformation from CGA 2 bits
297 * per pixel to frame buffer 8 bits per pixels
298 */
299 for (k = 8; k--; xm >>= 2, xp >>= 2)
300 f[k] = (f[k] & (xm & 3)) | (xp & 3);
301 f += SYSVID_WIDTH;
302 }
303 fb += 8;
304 }
305}
306#endif
307
308
309/*
310 * Draw a sprite
311 *
312 * foobar
313 */
314#ifdef GFXST
315void
316draw_sprite(U8 number, U16 x, U16 y)
317{
318 U8 i, j, k, *f;
319 U16 g;
320 U32 d;
321
322 draw_setfb(x, y);
323 g = 0;
324 for (i = 0; i < SPRITES_NBR_ROWS; i++) { /* rows */
325 f = fb;
326 for (j = 0; j < SPRITES_NBR_COLS; j++) { /* cols */
327 d = sprites_data[number][g++];
328 for (k = 8; k--; d >>= 4)
329 if (d & 0x0F) f[k] = (f[k] & 0xF0) | (d & 0x0F);
330 f += 8;
331 }
332 fb += SYSVID_WIDTH;
333 }
334}
335#endif
336
337
338/*
339 * Draw a sprite
340 *
341 * NOTE re-using original ST graphics format
342 */
343#ifdef GFXST
344void
345draw_sprite2(U8 number, U16 x, U16 y, bool front)
346{
347 U32 d = 0; /* sprite data */
348 S16 x0, y0; /* clipped x, y */
349 U16 w, h; /* width, height */
350 S16 g, /* sprite data offset*/
351 r, c, /* row, column */
352 i, /* frame buffer shifter */
353 im; /* tile flag shifter */
354 U8 flg; /* tile flag */
355
356 x0 = x;
357 y0 = y;
358 w = SPRITES_NBR_COLS * 8; /* each tile column is 8 pixels */
359 h = SPRITES_NBR_ROWS;
360
361 if (draw_clipms(&x0, &y0, &w, &h)) /* return if not visible */
362 return;
363
364 g = 0;
365 draw_setfb(x0 - DRAW_XYMAP_SCRLEFT, y0 - DRAW_XYMAP_SCRTOP + 8);
366
367 for (r = 0; r < SPRITES_NBR_ROWS; r++) {
368 if (r >= h || y + r < y0) continue;
369
370 i = 0x1f;
371 im = x - (x & 0xfff8);
372 flg = map_eflg[map_map[(y + r) >> 3][(x + 0x1f)>> 3]];
373
374#ifdef ENABLE_CHEATS
375#define LOOP(N, C0, C1) \
376 d = sprites_data[number][g + N]; \
377 for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
378 if (im == 0) { \
379 flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
380 im = 8; \
381 } \
382 if (c >= w || x + c < x0) continue; \
383 if (!front && !game_cheat3 && (flg & MAP_EFLG_FGND)) continue; \
384 if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
385 if (game_cheat3) fb[i] |= 0x10; \
386 }
387#else
388#define LOOP(N, C0, C1) \
389 d = sprites_data[number][g + N]; \
390 for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
391 if (im == 0) { \
392 flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
393 im = 8; \
394 } \
395 if (!front && (flg & MAP_EFLG_FGND)) continue; \
396 if (c >= w || x + c < x0) continue; \
397 if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
398 }
399#endif
400 LOOP(3, 0x1f, 0x18);
401 LOOP(2, 0x17, 0x10);
402 LOOP(1, 0x0f, 0x08);
403 LOOP(0, 0x07, 0x00);
404
405#undef LOOP
406
407 fb += SYSVID_WIDTH;
408 g += SPRITES_NBR_COLS;
409 }
410}
411
412#endif
413
414
415/*
416 * Draw a sprite
417 * align to tile column, determine plane automatically, and clip
418 *
419 * nbr: sprite number
420 * x, y: sprite position (pixels, map).
421 * fb: CHANGED
422 */
423#ifdef GFXPC
424void
425draw_sprite2(U8 number, U16 x, U16 y, bool front)
426{
427 U8 k, *f, c, r, dx;
428 U16 cmax, rmax;
429 U16 xm = 0, xp = 0;
430 S16 xmap, ymap;
431
432 /* align to tile column, prepare map coordinate and clip */
433 xmap = x & 0xFFF8;
434 ymap = y;
435 cmax = SPRITES_NBR_COLS * 8; /* width, 4 tile columns, 8 pixels each */
436 rmax = SPRITES_NBR_ROWS; /* height, 15 pixels */
437 dx = (x - xmap) * 2;
438 if (draw_clipms(&xmap, &ymap, &cmax, &rmax)) /* return if not visible */
439 return;
440
441 /* get back to screen */
442 draw_setfb(xmap - DRAW_XYMAP_SCRLEFT, ymap - DRAW_XYMAP_SCRTOP);
443 xmap >>= 3;
444 cmax >>= 3;
445
446 /* draw */
447 for (c = 0; c < cmax; c++) { /* for each tile column */
448 f = fb;
449 for (r = 0; r < rmax; r++) { /* for each pixel row */
450 /* check that tile is not hidden behind foreground */
451#ifdef ENABLE_CHEATS
452 if (front || game_cheat3 ||
453 !(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
454#else
455 if (front ||
456 !(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
457#endif
458 xp = xm = 0;
459 if (c > 0) {
460 xm |= sprites_data[number][c - 1][r].mask << (16 - dx);
461 xp |= sprites_data[number][c - 1][r].pict << (16 - dx);
462 }
463 else
464 xm |= 0xFFFF << (16 - dx);
465 if (c < cmax) {
466 xm |= sprites_data[number][c][r].mask >> dx;
467 xp |= sprites_data[number][c][r].pict >> dx;
468 }
469 else
470 xm |= 0xFFFF >> dx;
471 /*
472 * sprites / perform the transformation from CGA 2 bits
473 * per pixel to frame buffer 8 bits per pixels
474 */
475 for (k = 8; k--; xm >>= 2, xp >>= 2) {
476 f[k] = ((f[k] & (xm & 3)) | (xp & 3));
477#ifdef ENABLE_CHEATS
478 if (game_cheat3) f[k] |= 4;
479#endif
480 }
481 }
482 f += SYSVID_WIDTH;
483 }
484 fb += 8;
485 }
486}
487#endif
488
489
490/*
491 * Redraw the map behind a sprite
492 * align to tile column and row, and clip
493 *
494 * x, y: sprite position (pixels, map).
495 */
496void
497draw_spriteBackground(U16 x, U16 y)
498{
499 U8 r, c;
500 U16 rmax, cmax;
501 S16 xmap, ymap;
502 U16 xs, ys;
503
504 /* aligne to column and row, prepare map coordinate, and clip */
505 xmap = x & 0xFFF8;
506 ymap = y & 0xFFF8;
507 cmax = (x - xmap == 0 ? 0x20 : 0x28); /* width, 4 tl cols, 8 pix each */
508 rmax = (y & 0x04) ? 0x20 : 0x18; /* height, 3 or 4 tile rows */
509 if (draw_clipms(&xmap, &ymap, &cmax, &rmax)) /* don't draw if fully clipped */
510 return;
511
512 /* get back to screen */
513 xs = xmap - DRAW_XYMAP_SCRLEFT;
514 ys = ymap - DRAW_XYMAP_SCRTOP;
515 xmap >>= 3;
516 ymap >>= 3;
517 cmax >>= 3;
518 rmax >>= 3;
519
520 /* draw */
521 for (r = 0; r < rmax; r++) { /* for each row */
522#ifdef GFXPC
523 draw_setfb(xs, ys + r * 8);
524#endif
525#ifdef GFXST
526 draw_setfb(xs, 8 + ys + r * 8);
527#endif
528 for (c = 0; c < cmax; c++) { /* for each column */
529 draw_tile(map_map[ymap + r][xmap + c]);
530 }
531 }
532}
533
534
535/*
536 * Draw entire map screen background tiles onto frame buffer.
537 *
538 * ASM 0af5, 0a54
539 */
540void
541draw_map(void)
542{
543 U8 i, j;
544
545 draw_tilesBank = map_tilesBank;
546
547 for (i = 0; i < 0x18; i++) /* 0x18 rows */
548 {
549#ifdef GFXPC
550 draw_setfb(-DRAW_XYMAP_SCRLEFT, (i * 8));
551#endif
552#ifdef GFXST
553 draw_setfb(-DRAW_XYMAP_SCRLEFT, 8 + (i * 8));
554#endif
555 for (j = 0; j < 0x20; j++) /* 0x20 tiles per row */
556 {
557 draw_tile(map_map[i + 8][j]);
558 }
559 }
560}
561
562
563/*
564 * Draw status indicators
565 *
566 * ASM 0309
567 */
568void
569draw_drawStatus(void)
570{
571 S8 i;
572 U32 sv;
573 static U8 s[7] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfe};
574
575 draw_tilesBank = 0;
576
577 for (i = 5, sv = game_score; i >= 0; i--) {
578 s[i] = 0x30 + (U8)(sv % 10);
579 sv /= 10;
580 }
581 draw_tllst = s;
582
583 draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
584 draw_tilesList();
585
586 draw_setfb(DRAW_STATUS_BULLETS_X, DRAW_STATUS_Y);
587 for (i = 0; i < game_bullets; i++)
588 draw_tile(TILES_BULLET);
589
590 draw_setfb(DRAW_STATUS_BOMBS_X, DRAW_STATUS_Y);
591 for (i = 0; i < game_bombs; i++)
592 draw_tile(TILES_BOMB);
593
594 draw_setfb(DRAW_STATUS_LIVES_X, DRAW_STATUS_Y);
595 for (i = 0; i < game_lives; i++)
596 draw_tile(TILES_RICK);
597}
598
599
600/*
601 * Draw info indicators
602 */
603#ifdef ENABLE_CHEATS
604void
605draw_infos(void)
606{
607 draw_tilesBank = 0;
608
609#ifdef GFXPC
610 draw_filter = 0xffff;
611#endif
612
613 draw_setfb(0x00, DRAW_STATUS_Y);
614 draw_tile(game_cheat1 ? 'T' : '@');
615 draw_setfb(0x08, DRAW_STATUS_Y);
616 draw_tile(game_cheat2 ? 'N' : '@');
617 draw_setfb(0x10, DRAW_STATUS_Y);
618 draw_tile(game_cheat3 ? 'V' : '@');
619}
620#endif
621
622
623/*
624 * Clear status indicators
625 */
626void
627draw_clearStatus(void)
628{
629 U8 i;
630
631#ifdef GFXPC
632 draw_tilesBank = map_tilesBank;
633#endif
634#ifdef GFXST
635 draw_tilesBank = 0;
636#endif
637 draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
638 for (i = 0; i < DRAW_STATUS_LIVES_X/8 + 6 - DRAW_STATUS_SCORE_X/8; i++) {
639#ifdef GFXPC
640 draw_tile(map_map[MAP_ROW_SCRTOP + (DRAW_STATUS_Y / 8)][i]);
641#endif
642#ifdef GFXST
643 draw_tile('@');
644#endif
645 }
646}
647
648/*
649 * Draw a picture
650 */
651#ifdef GFXST
652void
653draw_pic(const pic_t * picture)
654{
655 U8 *f;
656 U16 i, j, k, pp;
657 U32 v;
658
659 draw_setfb(picture->xPos, picture->yPos);
660 pp = 0;
661
662 for (i = 0; i < picture->height; i++) { /* rows */
663 f = fb;
664 for (j = 0; j < picture->width; j += 8) { /* cols */
665 v = picture->pixels[pp++];
666 for (k = 8; k--; v >>= 4)
667 f[k] = v & 0x0F;
668 f += 8;
669 }
670 fb += SYSVID_WIDTH;
671 }
672}
673#endif
674
675
676/*
677 * Draw a bitmap
678 */
679void
680draw_img(img_t *image)
681{
682 U8 *f;
683 U16 i, j, pp;
684
685 sysvid_setPalette(image->colors, image->ncolors);
686
687 draw_setfb(image->xPos, image->yPos);
688 pp = 0;
689
690 for (i = 0; i < image->height; i++) /* rows */
691 {
692 f = fb;
693 for (j = 0; j < image->width; j++) /* cols */
694 {
695 f[j] = image->pixels[pp++];
696 }
697 fb += SYSVID_WIDTH;
698 }
699}
700
701
702/* eof */
diff --git a/apps/plugins/xrick/draw.h b/apps/plugins/xrick/draw.h
new file mode 100644
index 0000000000..15520bd799
--- /dev/null
+++ b/apps/plugins/xrick/draw.h
@@ -0,0 +1,65 @@
1/*
2 * xrick/draw.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _DRAW_H
17#define _DRAW_H
18
19#include "xrick/rects.h"
20#include "xrick/data/img.h"
21#ifdef GFXST
22#include "xrick/data/pics.h"
23#endif
24
25#include <stddef.h> /* size_t */
26
27/* map coordinates of the screen */
28#define DRAW_XYMAP_SCRLEFT (-0x0020)
29#define DRAW_XYMAP_SCRTOP (0x0040)
30/* map coordinates of the top of the hidden bottom of the map */
31#define DRAW_XYMAP_HBTOP (0x0100)
32
33extern U8 *draw_tllst;
34#ifdef GFXPC
35extern U16 draw_filter;
36#endif
37extern U8 draw_tilesBank;
38
39extern rect_t draw_STATUSRECT;
40extern const rect_t draw_SCREENRECT; /* whole fb */
41
42extern size_t game_color_count;
43extern img_color_t *game_colors;
44
45extern void draw_setfb(U16, U16);
46extern bool draw_clipms(S16 *, S16 *, U16 *, U16 *);
47extern void draw_tilesList(void);
48extern void draw_tilesListImm(U8 *);
49extern U8 draw_tilesSubList(void);
50extern void draw_tile(register U8);
51extern void draw_sprite(U8, U16, U16);
52extern void draw_sprite2(U8, U16, U16, bool);
53extern void draw_spriteBackground(U16, U16);
54extern void draw_map(void);
55extern void draw_drawStatus(void);
56extern void draw_clearStatus(void);
57#ifdef GFXST
58extern void draw_pic(const pic_t *);
59#endif
60extern void draw_infos(void);
61extern void draw_img(img_t *);
62
63#endif /* ndef _DRAW_H */
64
65/* eof */
diff --git a/apps/plugins/xrick/e_bomb.c b/apps/plugins/xrick/e_bomb.c
new file mode 100644
index 0000000000..7b9f8cf309
--- /dev/null
+++ b/apps/plugins/xrick/e_bomb.c
@@ -0,0 +1,158 @@
1/*
2 * xrick/e_bomb.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_bomb.h"
17
18#include "xrick/game.h"
19#include "xrick/ents.h"
20#include "xrick/e_rick.h"
21#include "xrick/system/system.h"
22#ifdef ENABLE_SOUND
23#include "xrick/data/sounds.h"
24#endif
25
26/*
27 * public vars (for performance reasons)
28 */
29bool e_bomb_lethal;
30U8 e_bomb_xc;
31U16 e_bomb_yc;
32
33/*
34 * private vars
35 */
36U8 e_bomb_ticker;
37
38/*
39 * Bomb hit test
40 *
41 * ASM 11CD
42 * returns: true/hit, false/not
43 */
44bool e_bomb_hit(U8 e)
45{
46 if (ent_ents[e].x > (E_BOMB_ENT.x >= 0xE0 ? 0xFF : E_BOMB_ENT.x + 0x20))
47 return false;
48 if (ent_ents[e].x + ent_ents[e].w < (E_BOMB_ENT.x > 0x04 ? E_BOMB_ENT.x - 0x04 : 0))
49 return false;
50 if (ent_ents[e].y > (E_BOMB_ENT.y + 0x1D))
51 return false;
52 if (ent_ents[e].y + ent_ents[e].h < (E_BOMB_ENT.y > 0x0004 ? E_BOMB_ENT.y - 0x0004 : 0))
53 return false;
54 return true;
55}
56
57/*
58 * Initialize bomb
59 */
60void e_bomb_init(U16 x, U16 y)
61{
62 E_BOMB_ENT.n = 0x03;
63 E_BOMB_ENT.x = x;
64 E_BOMB_ENT.y = y;
65 e_bomb_ticker = E_BOMB_TICKER;
66 e_bomb_lethal = false;
67
68 /*
69 * Atari ST dynamite sprites are not centered the
70 * way IBM PC sprites were ... need to adjust things a little bit
71 */
72#ifdef GFXST
73 E_BOMB_ENT.x += 4;
74 E_BOMB_ENT.y += 5;
75#endif
76
77}
78
79
80/*
81 * Entity action
82 *
83 * ASM 18CA
84 */
85void
86e_bomb_action(U8 e/*unused*/)
87{
88 (void)e;
89
90 /* tick */
91 e_bomb_ticker--;
92
93 if (e_bomb_ticker == 0)
94 {
95 /*
96 * end: deactivate
97 */
98 E_BOMB_ENT.n = 0;
99 e_bomb_lethal = false;
100 }
101 else if (e_bomb_ticker >= 0x0A)
102 {
103 /*
104 * ticking
105 */
106#ifdef ENABLE_SOUND
107 if ((e_bomb_ticker & 0x03) == 0x02)
108 syssnd_play(soundBombshht, 1);
109#endif
110#ifdef GFXST
111 /* ST bomb sprites sequence is longer */
112 if (e_bomb_ticker < 40)
113 E_BOMB_ENT.sprite = 0x99 + 19 - (e_bomb_ticker >> 1);
114 else
115#endif
116 E_BOMB_ENT.sprite = (e_bomb_ticker & 0x01) ? 0x23 : 0x22;
117 }
118 else if (e_bomb_ticker == 0x09)
119 {
120 /*
121 * explode
122 */
123#ifdef ENABLE_SOUND
124 syssnd_play(soundExplode, 1);
125#endif
126#ifdef GFXPC
127 E_BOMB_ENT.sprite = 0x24 + 4 - (e_bomb_ticker >> 1);
128#endif
129#ifdef GFXST
130 /* See above: fixing alignment */
131 E_BOMB_ENT.x -= 4;
132 E_BOMB_ENT.y -= 5;
133 E_BOMB_ENT.sprite = 0xa8 + 4 - (e_bomb_ticker >> 1);
134#endif
135 e_bomb_xc = E_BOMB_ENT.x + 0x0C;
136 e_bomb_yc = E_BOMB_ENT.y + 0x000A;
137 e_bomb_lethal = true;
138 if (e_bomb_hit(E_RICK_NO))
139 e_rick_gozombie();
140 }
141 else
142 {
143 /*
144 * exploding
145 */
146#ifdef GFXPC
147 E_BOMB_ENT.sprite = 0x24 + 4 - (e_bomb_ticker >> 1);
148#endif
149#ifdef GFXST
150 E_BOMB_ENT.sprite = 0xa8 + 4 - (e_bomb_ticker >> 1);
151#endif
152 /* exploding, hence lethal */
153 if (e_bomb_hit(E_RICK_NO))
154 e_rick_gozombie();
155 }
156}
157
158/* eof */
diff --git a/apps/plugins/xrick/e_bomb.h b/apps/plugins/xrick/e_bomb.h
new file mode 100644
index 0000000000..93d66f7084
--- /dev/null
+++ b/apps/plugins/xrick/e_bomb.h
@@ -0,0 +1,36 @@
1/*
2 * xrick/e_bomb.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_BOMB_H
17#define _E_BOMB_H
18
19#include "xrick/system/basic_types.h"
20
21#define E_BOMB_NO 3
22#define E_BOMB_ENT ent_ents[E_BOMB_NO]
23#define E_BOMB_TICKER (0x2D)
24
25extern bool e_bomb_lethal;
26extern U8 e_bomb_ticker;
27extern U8 e_bomb_xc;
28extern U16 e_bomb_yc;
29
30extern bool e_bomb_hit(U8);
31extern void e_bomb_init(U16, U16);
32extern void e_bomb_action(U8);
33
34#endif /* ndef _E_BOMB_H */
35
36/* eof */
diff --git a/apps/plugins/xrick/e_bonus.c b/apps/plugins/xrick/e_bonus.c
new file mode 100644
index 0000000000..cc45bbfc99
--- /dev/null
+++ b/apps/plugins/xrick/e_bonus.c
@@ -0,0 +1,60 @@
1/*
2 * xrick/e_bonus.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_bonus.h"
17
18#include "xrick/game.h"
19#include "xrick/ents.h"
20
21#include "xrick/e_rick.h"
22#include "xrick/maps.h"
23
24
25/*
26 * Entity action
27 *
28 * ASM 242C
29 */
30void
31e_bonus_action(U8 e)
32{
33#define seq c1
34
35 if (ent_ents[e].seq == 0) {
36 if (e_rick_boxtest(e)) {
37 game_score += 500;
38#ifdef ENABLE_SOUND
39 syssnd_play(soundBonus, 1);
40#endif
41 map_marks[ent_ents[e].mark].ent |= MAP_MARK_NACT;
42 ent_ents[e].seq = 1;
43 ent_ents[e].sprite = 0xad;
44 ent_ents[e].front = true;
45 ent_ents[e].y -= 0x08;
46 }
47 }
48
49 else if (ent_ents[e].seq > 0 && ent_ents[e].seq < 10) {
50 ent_ents[e].seq++;
51 ent_ents[e].y -= 2;
52 }
53
54 else {
55 ent_ents[e].n = 0;
56 }
57}
58
59
60/* eof */
diff --git a/apps/plugins/xrick/e_bonus.h b/apps/plugins/xrick/e_bonus.h
new file mode 100644
index 0000000000..a2839e2a7a
--- /dev/null
+++ b/apps/plugins/xrick/e_bonus.h
@@ -0,0 +1,25 @@
1/*
2 * xrick/e_bonus.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_BONUS_H
17#define _E_BONUS_H
18
19#include "xrick/system/basic_types.h"
20
21extern void e_bonus_action(U8);
22
23#endif /* ndef _E_BONUS_H */
24
25/* eof */
diff --git a/apps/plugins/xrick/e_box.c b/apps/plugins/xrick/e_box.c
new file mode 100644
index 0000000000..25884b1a12
--- /dev/null
+++ b/apps/plugins/xrick/e_box.c
@@ -0,0 +1,109 @@
1/*
2 * xrick/e_box.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_box.h"
17
18#include "xrick/game.h"
19#include "xrick/ents.h"
20#include "xrick/e_bullet.h"
21#include "xrick/e_bomb.h"
22#include "xrick/e_rick.h"
23#include "xrick/maps.h"
24#include "xrick/util.h"
25
26/*
27 * FIXME this is because the same structure is used
28 * for all entities. Need to replace this w/ an inheritance
29 * solution.
30 */
31#define cnt c1
32
33/*
34 * Constants
35 */
36#define SEQ_INIT 0x0A
37
38/*
39 * Prototypes
40 */
41static void explode(U8);
42
43/*
44 * Entity action
45 *
46 * ASM 245A
47 */
48void
49e_box_action(U8 e)
50{
51 static U8 sp[] = {0x24, 0x25, 0x26, 0x27, 0x28}; /* explosion sprites sequence */
52
53 if (ent_ents[e].n & ENT_LETHAL) {
54 /*
55 * box is lethal i.e. exploding
56 * play sprites sequence then stop
57 */
58 ent_ents[e].sprite = sp[ent_ents[e].cnt >> 1];
59 if (--ent_ents[e].cnt == 0) {
60 ent_ents[e].n = 0;
61 map_marks[ent_ents[e].mark].ent |= MAP_MARK_NACT;
62 }
63 } else {
64 /*
65 * not lethal: check to see if triggered
66 */
67 if (e_rick_boxtest(e)) {
68 /* rick: collect bombs or bullets and stop */
69#ifdef ENABLE_SOUND
70 syssnd_play(soundBox, 1);
71#endif
72 if (ent_ents[e].n == 0x10)
73 game_bombs = GAME_BOMBS_INIT;
74 else /* 0x11 */
75 game_bullets = GAME_BULLETS_INIT;
76 ent_ents[e].n = 0;
77 map_marks[ent_ents[e].mark].ent |= MAP_MARK_NACT;
78 }
79 else if (e_rick_state_test(E_RICK_STSTOP) &&
80 u_fboxtest(e, e_rick_stop_x, e_rick_stop_y)) {
81 /* rick's stick: explode */
82 explode(e);
83 }
84 else if (E_BULLET_ENT.n && u_fboxtest(e, e_bullet_xc, e_bullet_yc)) {
85 /* bullet: explode (and stop bullet) */
86 E_BULLET_ENT.n = 0;
87 explode(e);
88 }
89 else if (e_bomb_lethal && e_bomb_hit(e)) {
90 /* bomb: explode */
91 explode(e);
92 }
93 }
94}
95
96
97/*
98 * Explode when
99 */
100static void explode(U8 e)
101{
102 ent_ents[e].cnt = SEQ_INIT;
103 ent_ents[e].n |= ENT_LETHAL;
104#ifdef ENABLE_SOUND
105 syssnd_play(soundExplode, 1);
106#endif
107}
108
109/* eof */
diff --git a/apps/plugins/xrick/e_box.h b/apps/plugins/xrick/e_box.h
new file mode 100644
index 0000000000..1271449bf5
--- /dev/null
+++ b/apps/plugins/xrick/e_box.h
@@ -0,0 +1,25 @@
1/*
2 * xrick/e_box.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_BOX_H
17#define _E_BOX_H
18
19#include "xrick/system/basic_types.h"
20
21extern void e_box_action(U8);
22
23#endif /* ndef _E_BOX_H */
24
25/* eof */
diff --git a/apps/plugins/xrick/e_bullet.c b/apps/plugins/xrick/e_bullet.c
new file mode 100644
index 0000000000..86542256e8
--- /dev/null
+++ b/apps/plugins/xrick/e_bullet.c
@@ -0,0 +1,84 @@
1/*
2 * xrick/e_bullet.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_bullet.h"
17
18#include "xrick/system/system.h"
19#include "xrick/game.h"
20#include "xrick/ents.h"
21#include "xrick/maps.h"
22
23/*
24 * public vars (for performance reasons)
25 */
26S8 e_bullet_offsx;
27S16 e_bullet_xc, e_bullet_yc;
28
29/*
30 * Initialize bullet
31 */
32void
33e_bullet_init(U16 x, U16 y)
34{
35 E_BULLET_ENT.n = 0x02;
36 E_BULLET_ENT.x = x;
37 E_BULLET_ENT.y = y + 0x0006;
38 if (game_dir == LEFT) {
39 e_bullet_offsx = -0x08;
40 E_BULLET_ENT.sprite = 0x21;
41 }
42 else {
43 e_bullet_offsx = 0x08;
44 E_BULLET_ENT.sprite = 0x20;
45 }
46#ifdef ENABLE_SOUND
47 syssnd_play(soundBullet, 1);
48#endif
49}
50
51
52/*
53 * Entity action
54 *
55 * ASM 1883, 0F97
56 */
57void
58e_bullet_action(U8 e/*unused*/)
59{
60 (void)e;
61
62 /* move bullet */
63 E_BULLET_ENT.x += e_bullet_offsx;
64
65 if (E_BULLET_ENT.x <= -0x10 || E_BULLET_ENT.x > 0xe8)
66 {
67 /* out: deactivate */
68 E_BULLET_ENT.n = 0;
69 }
70 else
71 {
72 /* update bullet center coordinates */
73 e_bullet_xc = E_BULLET_ENT.x + 0x0c;
74 e_bullet_yc = E_BULLET_ENT.y + 0x05;
75 if (map_eflg[map_map[e_bullet_yc >> 3][e_bullet_xc >> 3]] & MAP_EFLG_SOLID)
76 {
77 /* hit something: deactivate */
78 E_BULLET_ENT.n = 0;
79 }
80 }
81}
82
83
84/* eof */
diff --git a/apps/plugins/xrick/e_bullet.h b/apps/plugins/xrick/e_bullet.h
new file mode 100644
index 0000000000..5b8c78b709
--- /dev/null
+++ b/apps/plugins/xrick/e_bullet.h
@@ -0,0 +1,32 @@
1/*
2 * xrick/e_bullet.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_BULLET_H
17#define _E_BULLET_H
18
19#include "xrick/system/basic_types.h"
20
21#define E_BULLET_NO 2
22#define E_BULLET_ENT ent_ents[E_BULLET_NO]
23
24extern S8 e_bullet_offsx;
25extern S16 e_bullet_xc, e_bullet_yc;
26
27extern void e_bullet_init(U16, U16);
28extern void e_bullet_action(U8);
29
30#endif /* ndef _E_BULLET_H */
31
32/* eof */
diff --git a/apps/plugins/xrick/e_rick.c b/apps/plugins/xrick/e_rick.c
new file mode 100644
index 0000000000..548a4f9681
--- /dev/null
+++ b/apps/plugins/xrick/e_rick.c
@@ -0,0 +1,606 @@
1/*
2 * xrick/e_rick.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_rick.h"
17
18#include "xrick/system/system.h"
19#include "xrick/config.h"
20#include "xrick/game.h"
21#include "xrick/ents.h"
22#include "xrick/e_bullet.h"
23#include "xrick/e_bomb.h"
24#include "xrick/control.h"
25#include "xrick/maps.h"
26#include "xrick/util.h"
27
28/*
29 * public vars
30 */
31S16 e_rick_stop_x = 0;
32S16 e_rick_stop_y = 0;
33unsigned e_rick_state = 0;
34
35/*
36* public functions
37*/
38extern inline void e_rick_state_set(e_rick_state_t s);
39extern inline void e_rick_state_clear(e_rick_state_t s);
40extern inline bool e_rick_state_test(e_rick_state_t s);
41
42
43/*
44 * local vars
45 */
46static U8 scrawl;
47
48static bool trigger = false;
49
50static S8 offsx;
51static U8 ylow;
52static S16 offsy;
53
54static U8 seq;
55
56static U8 save_crawl, save_direction;
57static U16 save_x, save_y;
58
59
60/*
61 * Box test
62 *
63 * ASM 113E (based on)
64 *
65 * e: entity to test against (corresponds to SI in asm code -- here DI
66 * is assumed to point to rick).
67 * ret: true/intersect, false/not.
68 */
69bool
70e_rick_boxtest(U8 e)
71{
72 /*
73 * rick: x+0x05 to x+0x11, y+[0x08 if rick's crawling] to y+0x14
74 * entity: x to x+w, y to y+h
75 */
76
77 if (E_RICK_ENT.x + 0x11 < ent_ents[e].x ||
78 E_RICK_ENT.x + 0x05 > ent_ents[e].x + ent_ents[e].w ||
79 E_RICK_ENT.y + 0x14 < ent_ents[e].y ||
80 E_RICK_ENT.y + (e_rick_state_test(E_RICK_STCRAWL) ? 0x08 : 0x00) > ent_ents[e].y + ent_ents[e].h - 1)
81 return false;
82 else
83 return true;
84}
85
86
87
88
89/*
90 * Go zombie
91 *
92 * ASM 1851
93 */
94void
95e_rick_gozombie(void)
96{
97#ifdef ENABLE_CHEATS
98 if (game_cheat2) return;
99#endif
100
101 /* already zombie? */
102 if (e_rick_state_test(E_RICK_STZOMBIE)) return;
103
104#ifdef ENABLE_SOUND
105 syssnd_play(soundDie, 1);
106#endif
107
108 e_rick_state_set(E_RICK_STZOMBIE);
109 offsy = -0x0300;
110 offsx = (E_RICK_ENT.x > 0x80 ? -3 : +3);
111 ylow = 0;
112 E_RICK_ENT.front = true;
113}
114
115
116/*
117 * Action sub-function for e_rick when zombie
118 *
119 * ASM 17DC
120 */
121static void
122e_rick_z_action(void)
123{
124 U32 i;
125
126 /* sprite */
127 E_RICK_ENT.sprite = (E_RICK_ENT.x & 0x04) ? 0x1A : 0x19;
128
129 /* x */
130 E_RICK_ENT.x += offsx;
131
132 /* y */
133 i = (E_RICK_ENT.y << 8) + offsy + ylow;
134 E_RICK_ENT.y = i >> 8;
135 offsy += 0x80;
136 ylow = i;
137
138 /* dead when out of screen */
139 if (E_RICK_ENT.y < 0 || E_RICK_ENT.y > 0x0140)
140 {
141 e_rick_state_set(E_RICK_STDEAD);
142 }
143}
144
145
146/*
147 * Action sub-function for e_rick.
148 *
149 * ASM 13BE
150 */
151void
152e_rick_action2(void)
153{
154 U8 env0, env1;
155 S16 x, y;
156 U32 i;
157
158 e_rick_state_clear(E_RICK_STSTOP | E_RICK_STSHOOT);
159
160 /* if zombie, run dedicated function and return */
161 if (e_rick_state_test(E_RICK_STZOMBIE))
162 {
163 e_rick_z_action();
164 return;
165 }
166
167 /* climbing? */
168 if (e_rick_state_test(E_RICK_STCLIMB))
169 {
170 goto climbing;
171 }
172 /*
173 * NOT CLIMBING
174 */
175 e_rick_state_clear(E_RICK_STJUMP);
176 /* calc y */
177 i = (E_RICK_ENT.y << 8) + offsy + ylow;
178 y = i >> 8;
179 /* test environment */
180 u_envtest(E_RICK_ENT.x, y, e_rick_state_test(E_RICK_STCRAWL), &env0, &env1);
181 /* stand up, if possible */
182 if (e_rick_state_test(E_RICK_STCRAWL) && !env0)
183 {
184 e_rick_state_clear(E_RICK_STCRAWL);
185 }
186 /* can move vertically? */
187 if (env1 & (offsy < 0 ?
188 MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD :
189 MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))
190 goto vert_not;
191
192 /*
193 * VERTICAL MOVE
194 */
195 e_rick_state_set(E_RICK_STJUMP);
196 /* killed? */
197 if (env1 & MAP_EFLG_LETHAL) {
198 e_rick_gozombie();
199 return;
200 }
201 /* save */
202 E_RICK_ENT.y = y;
203 ylow = i;
204 /* climb? */
205 if ((env1 & MAP_EFLG_CLIMB) && (control_test(Control_UP | Control_DOWN)))
206 {
207 offsy = 0x0100;
208 e_rick_state_set(E_RICK_STCLIMB);
209 return;
210 }
211 /* fall */
212 offsy += 0x0080;
213 if (offsy > 0x0800) {
214 offsy = 0x0800;
215 ylow = 0;
216 }
217
218 /*
219 * HORIZONTAL MOVE
220 */
221 horiz:
222 /* should move? */
223 if (!(control_test(Control_LEFT | Control_RIGHT))) {
224 seq = 2; /* no: reset seq and return */
225 return;
226 }
227 if (control_test(Control_LEFT)) { /* move left */
228 x = E_RICK_ENT.x - 2;
229 game_dir = LEFT;
230 if (x < 0) { /* prev submap */
231 game_chsm = true;
232 E_RICK_ENT.x = 0xe2;
233 return;
234 }
235 } else { /* move right */
236 x = E_RICK_ENT.x + 2;
237 game_dir = RIGHT;
238 if (x >= 0xe8) { /* next submap */
239 game_chsm = true;
240 E_RICK_ENT.x = 0x04;
241 return;
242 }
243 }
244
245 /* still within this map: test environment */
246 u_envtest(x, E_RICK_ENT.y, e_rick_state_test(E_RICK_STCRAWL), &env0, &env1);
247
248 /* save x-position if it is possible to move */
249 if (!(env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))) {
250 E_RICK_ENT.x = x;
251 if (env1 & MAP_EFLG_LETHAL) e_rick_gozombie();
252 }
253
254 /* end */
255 return;
256
257 /*
258 * NO VERTICAL MOVE
259 */
260 vert_not:
261 if (offsy < 0) {
262 /* not climbing + trying to go _up_ not possible -> hit the roof */
263 e_rick_state_set(E_RICK_STJUMP); /* fall back to the ground */
264 E_RICK_ENT.y &= 0xF8;
265 offsy = 0;
266 ylow = 0;
267 goto horiz;
268 }
269 /* else: not climbing + trying to go _down_ not possible -> standing */
270 /* align to ground */
271 E_RICK_ENT.y &= 0xF8;
272 E_RICK_ENT.y |= 0x03;
273 ylow = 0;
274
275 /* standing on a super pad? */
276 if ((env1 & MAP_EFLG_SPAD) && offsy >= 0X0200) {
277 offsy = (control_test(Control_UP)) ? 0xf800 : 0x00fe - offsy;
278#ifdef ENABLE_SOUND
279 syssnd_play(soundPad, 1);
280#endif
281 goto horiz;
282 }
283
284 offsy = 0x0100; /* reset*/
285
286 /* standing. firing ? */
287 if (scrawl || !(control_test(Control_FIRE)))
288 goto firing_not;
289
290 /*
291 * FIRING
292 */
293 if (control_test(Control_LEFT | Control_RIGHT)) { /* stop */
294 if (control_test(Control_RIGHT))
295 {
296 game_dir = RIGHT;
297 e_rick_stop_x = E_RICK_ENT.x + 0x17;
298 } else {
299 game_dir = LEFT;
300 e_rick_stop_x = E_RICK_ENT.x;
301 }
302 e_rick_stop_y = E_RICK_ENT.y + 0x000E;
303 e_rick_state_set(E_RICK_STSTOP);
304 return;
305 }
306
307 if (control_test(Control_UP)) { /* bullet */
308 e_rick_state_set(E_RICK_STSHOOT);
309 /* not an automatic gun: shoot once only */
310 if (trigger)
311 return;
312 else
313 trigger = true;
314 /* already a bullet in the air ... that's enough */
315 if (E_BULLET_ENT.n)
316 return;
317 /* else use a bullet, if any available */
318 if (!game_bullets)
319 return;
320#ifdef ENABLE_CHEATS
321 if (!game_cheat1)
322#endif
323 {
324 game_bullets--;
325 }
326
327 /* initialize bullet */
328 e_bullet_init(E_RICK_ENT.x, E_RICK_ENT.y);
329 return;
330 }
331
332 trigger = false; /* not shooting means trigger is released */
333 seq = 0; /* reset */
334
335 if (control_test(Control_DOWN)) { /* bomb */
336 /* already a bomb ticking ... that's enough */
337 if (E_BOMB_ENT.n)
338 return;
339 /* else use a bomb, if any available */
340 if (!game_bombs)
341 return;
342#ifdef ENABLE_CHEATS
343 if (!game_cheat1)
344#endif
345 {
346 game_bombs--;
347 }
348
349 /* initialize bomb */
350 e_bomb_init(E_RICK_ENT.x, E_RICK_ENT.y);
351 return;
352 }
353
354 return;
355
356 /*
357 * NOT FIRING
358 */
359 firing_not:
360 if (control_test(Control_UP)) { /* jump or climb */
361 if (env1 & MAP_EFLG_CLIMB) { /* climb */
362 e_rick_state_set(E_RICK_STCLIMB);
363 return;
364 }
365 offsy = -0x0580; /* jump */
366 ylow = 0;
367#ifdef ENABLE_SOUND
368 syssnd_play(soundJump, 1);
369#endif
370 goto horiz;
371 }
372 if (control_test(Control_DOWN)) { /* crawl or climb */
373 if ((env1 & MAP_EFLG_VERT) && /* can go down */
374 !(control_test(Control_LEFT | Control_RIGHT)) && /* + not moving horizontaly */
375 (E_RICK_ENT.x & 0x1f) < 0x0a) { /* + aligned -> climb */
376 E_RICK_ENT.x &= 0xf0;
377 E_RICK_ENT.x |= 0x04;
378 e_rick_state_set(E_RICK_STCLIMB);
379 }
380 else { /* crawl */
381 e_rick_state_set(E_RICK_STCRAWL);
382 goto horiz;
383 }
384
385 }
386 goto horiz;
387
388 /*
389 * CLIMBING
390 */
391 climbing:
392 /* should move? */
393 if (!(control_test(Control_UP | Control_DOWN | Control_LEFT | Control_RIGHT))) {
394 seq = 0; /* no: reset seq and return */
395 return;
396 }
397
398 if (control_test(Control_UP | Control_DOWN)) {
399 /* up-down: calc new y and test environment */
400 y = E_RICK_ENT.y + ((control_test(Control_UP)) ? -0x02 : 0x02);
401 u_envtest(E_RICK_ENT.x, y, e_rick_state_test(E_RICK_STCRAWL), &env0, &env1);
402 if (env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP) &&
403 !(control_test(Control_UP))) {
404 /* FIXME what? */
405 e_rick_state_clear(E_RICK_STCLIMB);
406 return;
407 }
408 if (!(env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP)) ||
409 (env1 & MAP_EFLG_WAYUP)) {
410 /* ok to move, save */
411 E_RICK_ENT.y = y;
412 if (env1 & MAP_EFLG_LETHAL) {
413 e_rick_gozombie();
414 return;
415 }
416 if (!(env1 & (MAP_EFLG_VERT|MAP_EFLG_CLIMB))) {
417 /* reached end of climb zone */
418 offsy = (control_test(Control_UP)) ? -0x0300 : 0x0100;
419#ifdef ENABLE_SOUND
420 if (control_test(Control_UP))
421 syssnd_play(soundJump, 1);
422#endif
423 e_rick_state_clear(E_RICK_STCLIMB);
424 return;
425 }
426 }
427 }
428 if (control_test(Control_LEFT | Control_RIGHT)) {
429 /* left-right: calc new x and test environment */
430 if (control_test(Control_LEFT)) {
431 x = E_RICK_ENT.x - 0x02;
432 if (x < 0) { /* (i.e. negative) prev submap */
433 game_chsm = true;
434 /*6dbd = 0x00;*/
435 E_RICK_ENT.x = 0xe2;
436 return;
437 }
438 }
439 else {
440 x = E_RICK_ENT.x + 0x02;
441 if (x >= 0xe8) { /* next submap */
442 game_chsm = true;
443 /*6dbd = 0x01;*/
444 E_RICK_ENT.x = 0x04;
445 return;
446 }
447 }
448 u_envtest(x, E_RICK_ENT.y, e_rick_state_test(E_RICK_STCRAWL), &env0, &env1);
449 if (env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD)) return;
450 E_RICK_ENT.x = x;
451 if (env1 & MAP_EFLG_LETHAL) {
452 e_rick_gozombie();
453 return;
454 }
455
456 if (env1 & (MAP_EFLG_VERT|MAP_EFLG_CLIMB)) return;
457 e_rick_state_clear(E_RICK_STCLIMB);
458 if (control_test(Control_UP))
459 offsy = -0x0300;
460 }
461}
462
463
464/*
465 * Action function for e_rick
466 *
467 * ASM 12CA
468 */
469void e_rick_action(U8 e/*unused*/)
470{
471 static U8 stopped = false; /* is this the most elegant way? */
472
473 (void)e;
474
475 e_rick_action2();
476
477 scrawl = e_rick_state_test(E_RICK_STCRAWL);
478
479 if (e_rick_state_test(E_RICK_STZOMBIE))
480 {
481 return;
482 }
483 /*
484 * set sprite
485 */
486
487 if (e_rick_state_test(E_RICK_STSTOP))
488 {
489 E_RICK_ENT.sprite = (game_dir ? 0x17 : 0x0B);
490#ifdef ENABLE_SOUND
491 if (!stopped)
492 {
493 syssnd_play(soundStick, 1);
494 stopped = true;
495 }
496#endif
497 return;
498 }
499
500 stopped = false;
501
502 if (e_rick_state_test(E_RICK_STSHOOT))
503 {
504 E_RICK_ENT.sprite = (game_dir ? 0x16 : 0x0A);
505 return;
506 }
507
508 if (e_rick_state_test(E_RICK_STCLIMB))
509 {
510 E_RICK_ENT.sprite = (((E_RICK_ENT.x ^ E_RICK_ENT.y) & 0x04) ? 0x18 : 0x0c);
511#ifdef ENABLE_SOUND
512 seq = (seq + 1) & 0x03;
513 if (seq == 0) syssnd_play(soundWalk, 1);
514#endif
515 return;
516 }
517
518 if (e_rick_state_test(E_RICK_STCRAWL))
519 {
520 E_RICK_ENT.sprite = (game_dir ? 0x13 : 0x07);
521 if (E_RICK_ENT.x & 0x04) E_RICK_ENT.sprite++;
522#ifdef ENABLE_SOUND
523 seq = (seq + 1) & 0x03;
524 if (seq == 0) syssnd_play(soundCrawl, 1);
525#endif
526 return;
527 }
528
529 if (e_rick_state_test(E_RICK_STJUMP))
530 {
531 E_RICK_ENT.sprite = (game_dir ? 0x15 : 0x06);
532 return;
533 }
534
535 seq++;
536
537 if (seq >= 0x14)
538 {
539#ifdef ENABLE_SOUND
540 syssnd_play(soundWalk, 1);
541#endif
542 seq = 0x04;
543 }
544#ifdef ENABLE_SOUND
545 else
546 {
547 if (seq == 0x0C)
548 {
549 syssnd_play(soundWalk, 1);
550 }
551 }
552#endif
553
554 E_RICK_ENT.sprite = (seq >> 2) + 1 + (game_dir ? 0x0c : 0x00);
555}
556
557
558/*
559 * Save status
560 *
561 * ASM part of 0x0BBB
562 */
563void e_rick_save(void)
564{
565 save_x = E_RICK_ENT.x;
566 save_y = E_RICK_ENT.y;
567 save_crawl = e_rick_state_test(E_RICK_STCRAWL);
568 save_direction = game_dir;
569 /* FIXME
570 * save_C0 = E_RICK_ENT.b0C;
571 * plus some 6DBC stuff?
572 */
573}
574
575
576/*
577 * Restore status
578 *
579 * ASM part of 0x0BDC
580 */
581void e_rick_restore(void)
582{
583 E_RICK_ENT.x = save_x;
584 E_RICK_ENT.y = save_y;
585 if (save_crawl)
586 {
587 e_rick_state_set(E_RICK_STCRAWL);
588 }
589 else
590 {
591 e_rick_state_clear(E_RICK_STCRAWL);
592 }
593 game_dir = save_direction;
594
595 E_RICK_ENT.front = false;
596 e_rick_state_clear(E_RICK_STCLIMB); /* should we clear other states? */
597 /* FIXME
598 * E_RICK_ENT.b0C = save_C0;
599 * plus some 6DBC stuff?
600 */
601}
602
603
604
605
606/* eof */
diff --git a/apps/plugins/xrick/e_rick.h b/apps/plugins/xrick/e_rick.h
new file mode 100644
index 0000000000..57b45366d7
--- /dev/null
+++ b/apps/plugins/xrick/e_rick.h
@@ -0,0 +1,50 @@
1/*
2 * xrick/e_rick.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_RICK_H
17#define _E_RICK_H
18
19#include "xrick/system/basic_types.h"
20
21#define E_RICK_NO 1
22#define E_RICK_ENT ent_ents[E_RICK_NO]
23
24typedef enum
25{
26 E_RICK_STSTOP = (1 << 0),
27 E_RICK_STSHOOT = (1 << 1),
28 E_RICK_STCLIMB = (1 << 2),
29 E_RICK_STJUMP = (1 << 3),
30 E_RICK_STZOMBIE = (1 << 4),
31 E_RICK_STDEAD = (1 << 5),
32 E_RICK_STCRAWL = (1 << 6),
33} e_rick_state_t;
34
35extern unsigned e_rick_state;
36inline void e_rick_state_set(e_rick_state_t s) { e_rick_state |= s; }
37inline void e_rick_state_clear(e_rick_state_t s) { e_rick_state &= ~s; }
38inline bool e_rick_state_test(e_rick_state_t s) { return e_rick_state & s; }
39
40extern S16 e_rick_stop_x, e_rick_stop_y;
41
42extern void e_rick_save(void);
43extern void e_rick_restore(void);
44extern void e_rick_action(U8);
45extern void e_rick_gozombie(void);
46extern bool e_rick_boxtest(U8);
47
48#endif /* ndef _E_RICK_H */
49
50/* eof */
diff --git a/apps/plugins/xrick/e_sbonus.c b/apps/plugins/xrick/e_sbonus.c
new file mode 100644
index 0000000000..1630d419aa
--- /dev/null
+++ b/apps/plugins/xrick/e_sbonus.c
@@ -0,0 +1,88 @@
1/*
2 * xrick/e_sbonus.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_sbonus.h"
17
18#include "xrick/game.h"
19#include "xrick/ents.h"
20#include "xrick/util.h"
21#include "xrick/maps.h"
22#include "xrick/e_rick.h"
23
24
25/*
26 * public vars
27 */
28bool e_sbonus_counting = false;
29U8 e_sbonus_counter = 0;
30U16 e_sbonus_bonus = 0;
31
32
33/*
34 * Entity action / start counting
35 *
36 * ASM 2182
37 */
38void
39e_sbonus_start(U8 e)
40{
41 ent_ents[e].sprite = 0; /* invisible */
42 if (u_trigbox(e, ENT_XRICK.x + 0x0C, ENT_XRICK.y + 0x0A)) {
43 /* rick is within trigger box */
44 ent_ents[e].n = 0;
45 e_sbonus_counting = true; /* 6DD5 */
46 e_sbonus_counter = 0x1e; /* 6DDB */
47 e_sbonus_bonus = 2000; /* 291A-291D */
48#ifdef ENABLE_SOUND
49 syssnd_play(soundSbonus1, 1);
50#endif
51 }
52}
53
54
55/*
56 * Entity action / stop counting
57 *
58 * ASM 2143
59 */
60void
61e_sbonus_stop(U8 e)
62{
63 ent_ents[e].sprite = 0; /* invisible */
64
65 if (!e_sbonus_counting)
66 return;
67
68 if (u_trigbox(e, ENT_XRICK.x + 0x0C, ENT_XRICK.y + 0x0A)) {
69 /* rick is within trigger box */
70 e_sbonus_counting = false; /* stop counting */
71 ent_ents[e].n = 0; /* deactivate entity */
72 game_score += e_sbonus_bonus; /* add bonus to score */
73#ifdef ENABLE_SOUND
74 syssnd_play(soundSbonus2, 1);
75#endif
76 /* make sure the entity won't be activated again */
77 map_marks[ent_ents[e].mark].ent |= MAP_MARK_NACT;
78 }
79 else {
80 /* keep counting */
81 if (--e_sbonus_counter == 0) {
82 e_sbonus_counter = 0x1e;
83 if (e_sbonus_bonus) e_sbonus_bonus--;
84 }
85 }
86}
87
88/* eof */
diff --git a/apps/plugins/xrick/e_sbonus.h b/apps/plugins/xrick/e_sbonus.h
new file mode 100644
index 0000000000..dbb29c9c99
--- /dev/null
+++ b/apps/plugins/xrick/e_sbonus.h
@@ -0,0 +1,30 @@
1/*
2 * xrick/e_sbonus.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_SBONUS_H
17#define _E_SBONUS_H
18
19#include "xrick/system/basic_types.h"
20
21extern bool e_sbonus_counting;
22extern U8 e_sbonus_counter;
23extern U16 e_sbonus_bonus;
24
25extern void e_sbonus_start(U8);
26extern void e_sbonus_stop(U8);
27
28#endif /* ndef _E_SBONUS_H */
29
30/* eof */
diff --git a/apps/plugins/xrick/e_them.c b/apps/plugins/xrick/e_them.c
new file mode 100644
index 0000000000..3871798a57
--- /dev/null
+++ b/apps/plugins/xrick/e_them.c
@@ -0,0 +1,738 @@
1/*
2 * xrick/e_them.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/e_them.h"
17
18#include "xrick/game.h"
19#include "xrick/ents.h"
20#include "xrick/e_rick.h"
21#include "xrick/e_bomb.h"
22#include "xrick/e_bullet.h"
23#include "xrick/maps.h"
24#include "xrick/util.h"
25
26#define TYPE_1A (0x00)
27#define TYPE_1B (0xff)
28
29/*
30 * public vars
31 */
32U32 e_them_rndseed = 0;
33
34/*
35 * local vars
36 */
37static U16 e_them_rndnbr = 0;
38
39/*
40 * Check if entity boxtests with a lethal e_them i.e. something lethal
41 * in slot 0 and 4 to 8.
42 *
43 * ASM 122E
44 *
45 * e: entity slot number.
46 * ret: true/boxtests, false/not
47 */
48static bool
49u_themtest(U8 e)
50{
51 U8 i;
52
53 if ((ent_ents[0].n & ENT_LETHAL) && u_boxtest(e, 0))
54 return true;
55
56 for (i = 4; i < 9; i++)
57 if ((ent_ents[i].n & ENT_LETHAL) && u_boxtest(e, i))
58 return true;
59
60 return false;
61}
62
63
64/*
65 * Go zombie
66 *
67 * ASM 237B
68 */
69void
70e_them_gozombie(U8 e)
71{
72#define offsx c1
73 ent_ents[e].n = 0x47; /* zombie entity */
74 ent_ents[e].front = true;
75 ent_ents[e].offsy = -0x0400;
76#ifdef ENABLE_SOUND
77 syssnd_play(soundDie, 1);
78#endif
79 game_score += 50;
80 if (ent_ents[e].flags & ENT_FLG_ONCE) {
81 /* make sure entity won't be activated again */
82 map_marks[ent_ents[e].mark].ent |= MAP_MARK_NACT;
83 }
84 ent_ents[e].offsx = (ent_ents[e].x >= 0x80 ? -0x02 : 0x02);
85#undef offsx
86}
87
88
89/*
90 * Action sub-function for e_them _t1a and _t1b
91 *
92 * Those two types move horizontally, and fall if they have to.
93 * Type 1a moves horizontally over a given distance and then
94 * u-turns and repeats; type 1b is more subtle as it does u-turns
95 * in order to move horizontally towards rick.
96 *
97 * ASM 2242
98 */
99void
100e_them_t1_action2(U8 e, U8 type)
101{
102#define offsx c1
103#define step_count c2
104 U32 i;
105 S16 x, y;
106 U8 env0, env1;
107
108 /* by default, try vertical move. calculate new y */
109 i = (ent_ents[e].y << 8) + ent_ents[e].offsy + ent_ents[e].ylow;
110 y = i >> 8;
111
112 /* deactivate if outside vertical boundaries */
113 /* no need to test zero since e_them _t1a/b don't go up */
114 /* FIXME what if they got scrolled out ? */
115 if (y > 0x140) {
116 ent_ents[e].n = 0;
117 return;
118 }
119
120 /* test environment */
121 u_envtest(ent_ents[e].x, y, false, &env0, &env1);
122
123 if (!(env1 & (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))) {
124 /* vertical move possible: falling */
125 if (env1 & MAP_EFLG_LETHAL) {
126 /* lethal entities kill e_them */
127 e_them_gozombie(e);
128 return;
129 }
130 /* save, cleanup and return */
131 ent_ents[e].y = y;
132 ent_ents[e].ylow = i;
133 ent_ents[e].offsy += 0x0080;
134 if (ent_ents[e].offsy > 0x0800)
135 ent_ents[e].offsy = 0x0800;
136 return;
137 }
138
139 /* vertical move not possible. calculate new sprite */
140 ent_ents[e].sprite = ent_ents[e].sprbase
141 + ent_sprseq[(ent_ents[e].x & 0x1c) >> 3]
142 + (ent_ents[e].offsx < 0 ? 0x03 : 0x00);
143
144 /* reset offsy */
145 ent_ents[e].offsy = 0x0080;
146
147 /* align to ground */
148 ent_ents[e].y &= 0xfff8;
149 ent_ents[e].y |= 0x0003;
150
151 /* latency: if not zero then decrease and return */
152 if (ent_ents[e].latency > 0) {
153 ent_ents[e].latency--;
154 return;
155 }
156
157 /* horizontal move. calculate new x */
158 if (ent_ents[e].offsx == 0) /* not supposed to move -> don't */
159 return;
160
161 x = ent_ents[e].x + ent_ents[e].offsx;
162 if (ent_ents[e].x < 0 || ent_ents[e].x > 0xe8) {
163 /* U-turn and return if reaching horizontal boundaries */
164 ent_ents[e].step_count = 0;
165 ent_ents[e].offsx = -ent_ents[e].offsx;
166 return;
167 }
168
169 /* test environment */
170 u_envtest(x, ent_ents[e].y, false, &env0, &env1);
171
172 if (env1 & (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP)) {
173 /* horizontal move not possible: u-turn and return */
174 ent_ents[e].step_count = 0;
175 ent_ents[e].offsx = -ent_ents[e].offsx;
176 return;
177 }
178
179 /* horizontal move possible */
180 if (env1 & MAP_EFLG_LETHAL) {
181 /* lethal entities kill e_them */
182 e_them_gozombie(e);
183 return;
184 }
185
186 /* save */
187 ent_ents[e].x = x;
188
189 /* depending on type, */
190 if (type == TYPE_1B) {
191 /* set direction to move horizontally towards rick */
192 if ((ent_ents[e].x & 0x1e) != 0x10) /* prevents too frequent u-turns */
193 return;
194 ent_ents[e].offsx = (ent_ents[e].x < E_RICK_ENT.x) ? 0x02 : -0x02;
195 return;
196 }
197 else {
198 /* set direction according to step counter */
199 ent_ents[e].step_count++;
200 /* FIXME why trig_x (b16) ?? */
201 if ((ent_ents[e].trig_x >> 1) > ent_ents[e].step_count)
202 return;
203 }
204
205 /* type is 1A and step counter reached its limit: u-turn */
206 ent_ents[e].step_count = 0;
207 ent_ents[e].offsx = -ent_ents[e].offsx;
208#undef offsx
209#undef step_count
210}
211
212
213/*
214 * ASM 21CF
215 */
216void
217e_them_t1_action(U8 e, U8 type)
218{
219 e_them_t1_action2(e, type);
220
221 /* lethal entities kill them */
222 if (u_themtest(e)) {
223 e_them_gozombie(e);
224 return;
225 }
226
227 /* bullet kills them */
228 if (E_BULLET_ENT.n &&
229 u_fboxtest(e, E_BULLET_ENT.x + (e_bullet_offsx < 0 ? 0 : 0x18),
230 E_BULLET_ENT.y)) {
231 E_BULLET_ENT.n = 0;
232 e_them_gozombie(e);
233 return;
234 }
235
236 /* bomb kills them */
237 if (e_bomb_lethal && e_bomb_hit(e)) {
238 e_them_gozombie(e);
239 return;
240 }
241
242 /* rick stops them */
243 if (e_rick_state_test(E_RICK_STSTOP) &&
244 u_fboxtest(e, e_rick_stop_x, e_rick_stop_y))
245 ent_ents[e].latency = 0x14;
246
247 /* they kill rick */
248 if (e_rick_boxtest(e))
249 e_rick_gozombie();
250}
251
252
253/*
254 * Action function for e_them _t1a type (stays within boundaries)
255 *
256 * ASM 2452
257 */
258void
259e_them_t1a_action(U8 e)
260{
261 e_them_t1_action(e, TYPE_1A);
262}
263
264
265/*
266 * Action function for e_them _t1b type (runs for rick)
267 *
268 * ASM 21CA
269 */
270void
271e_them_t1b_action(U8 e)
272{
273 e_them_t1_action(e, TYPE_1B);
274}
275
276
277/*
278 * Action function for e_them _z (zombie) type
279 *
280 * ASM 23B8
281 */
282void
283e_them_z_action(U8 e)
284{
285#define offsx c1
286 U32 i;
287
288 /* calc new sprite */
289 ent_ents[e].sprite = ent_ents[e].sprbase
290 + ((ent_ents[e].x & 0x04) ? 0x07 : 0x06);
291
292 /* calc new y */
293 i = (ent_ents[e].y << 8) + ent_ents[e].offsy + ent_ents[e].ylow;
294
295 /* deactivate if out of vertical boundaries */
296 if (ent_ents[e].y < 0 || ent_ents[e].y > 0x0140) {
297 ent_ents[e].n = 0;
298 return;
299 }
300
301 /* save */
302 ent_ents[e].offsy += 0x0080;
303 ent_ents[e].ylow = i;
304 ent_ents[e].y = i >> 8;
305
306 /* calc new x */
307 ent_ents[e].x += ent_ents[e].offsx;
308
309 /* must stay within horizontal boundaries */
310 if (ent_ents[e].x < 0)
311 ent_ents[e].x = 0;
312 if (ent_ents[e].x > 0xe8)
313 ent_ents[e].x = 0xe8;
314#undef offsx
315}
316
317
318/*
319 * Action sub-function for e_them _t2.
320 *
321 * Must document what it does.
322 *
323 * ASM 2792
324 */
325void
326e_them_t2_action2(U8 e)
327{
328#define flgclmb c1
329#define offsx c2
330 U32 i;
331 S16 x, y, yd;
332 U8 env0, env1;
333
334 /*
335 * vars required by the Black Magic (tm) performance at the
336 * end of this function.
337 */
338 static U16 bx;
339 static U8 *bl = (U8 *)&bx;
340 static U8 *bh = (U8 *)&bx + 1;
341 static U16 cx;
342 static U8 *cl = (U8 *)&cx;
343 static U8 *ch = (U8 *)&cx + 1;
344 static U16 *sl = (U16 *)&e_them_rndseed;
345 static U16 *sh = ((U16 *)&e_them_rndseed) + 1;
346
347 /*sys_printf("e_them_t2 ------------------------------\n");*/
348
349 /* latency: if not zero then decrease */
350 if (ent_ents[e].latency > 0) ent_ents[e].latency--;
351
352 /* climbing? */
353 if (!ent_ents[e].flgclmb) goto climbing_not;
354
355 /* CLIMBING */
356
357 /*sys_printf("e_them_t2 climbing\n");*/
358
359 /* latency: if not zero then return */
360 if (ent_ents[e].latency > 0) return;
361
362 /* calc new sprite */
363 ent_ents[e].sprite = ent_ents[e].sprbase + 0x08 +
364 (((ent_ents[e].x ^ ent_ents[e].y) & 0x04) ? 1 : 0);
365
366 /* reached rick's level? */
367 if ((ent_ents[e].y & 0xfe) != (E_RICK_ENT.y & 0xfe)) goto ymove;
368
369 xmove:
370 /* calc new x and test environment */
371 ent_ents[e].offsx = (ent_ents[e].x < E_RICK_ENT.x) ? 0x02 : -0x02;
372 x = ent_ents[e].x + ent_ents[e].offsx;
373 u_envtest(x, ent_ents[e].y, false, &env0, &env1);
374 if (env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))
375 return;
376 if (env1 & MAP_EFLG_LETHAL) {
377 e_them_gozombie(e);
378 return;
379 }
380 ent_ents[e].x = x;
381 if (env1 & (MAP_EFLG_VERT|MAP_EFLG_CLIMB)) /* still climbing */
382 return;
383 goto climbing_not; /* not climbing anymore */
384
385 ymove:
386 /* calc new y and test environment */
387 yd = ent_ents[e].y < E_RICK_ENT.y ? 0x02 : -0x02;
388 y = ent_ents[e].y + yd;
389 if (y < 0 || y > 0x0140) {
390 ent_ents[e].n = 0;
391 return;
392 }
393 u_envtest(ent_ents[e].x, y, false, &env0, &env1);
394 if (env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP)) {
395 if (yd < 0)
396 goto xmove; /* can't go up */
397 else
398 goto climbing_not; /* can't go down */
399 }
400 /* can move */
401 ent_ents[e].y = y;
402 if (env1 & (MAP_EFLG_VERT|MAP_EFLG_CLIMB)) /* still climbing */
403 return;
404
405 /* NOT CLIMBING */
406
407 climbing_not:
408 /*sys_printf("e_them_t2 climbing NOT\n");*/
409
410 ent_ents[e].flgclmb = false; /* not climbing */
411
412 /* calc new y (falling) and test environment */
413 i = (ent_ents[e].y << 8) + ent_ents[e].offsy + ent_ents[e].ylow;
414 y = i >> 8;
415 u_envtest(ent_ents[e].x, y, false, &env0, &env1);
416 if (!(env1 & (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))) {
417 /*sys_printf("e_them_t2 y move OK\n");*/
418 /* can go there */
419 if (env1 & MAP_EFLG_LETHAL) {
420 e_them_gozombie(e);
421 return;
422 }
423 if (y > 0x0140) { /* deactivate if outside */
424 ent_ents[e].n = 0;
425 return;
426 }
427 if (!(env1 & MAP_EFLG_VERT)) {
428 /* save */
429 ent_ents[e].y = y;
430 ent_ents[e].ylow = i;
431 ent_ents[e].offsy += 0x0080;
432 if (ent_ents[e].offsy > 0x0800)
433 ent_ents[e].offsy = 0x0800;
434 return;
435 }
436 if (((ent_ents[e].x & 0x07) == 0x04) && (y < E_RICK_ENT.y)) {
437 /*sys_printf("e_them_t2 climbing00\n");*/
438 ent_ents[e].flgclmb = true; /* climbing */
439 return;
440 }
441 }
442
443 /*sys_printf("e_them_t2 ymove nok or ...\n");*/
444 /* can't go there, or ... */
445 ent_ents[e].y = (ent_ents[e].y & 0xf8) | 0x03; /* align to ground */
446 ent_ents[e].offsy = 0x0100;
447 if (ent_ents[e].latency != 00)
448 return;
449
450 if ((env1 & MAP_EFLG_CLIMB) &&
451 ((ent_ents[e].x & 0x0e) == 0x04) &&
452 (ent_ents[e].y > E_RICK_ENT.y)) {
453 /*sys_printf("e_them_t2 climbing01\n");*/
454 ent_ents[e].flgclmb = true; /* climbing */
455 return;
456 }
457
458 /* calc new sprite */
459 ent_ents[e].sprite = ent_ents[e].sprbase +
460 ent_sprseq[(ent_ents[e].offsx < 0 ? 4 : 0) +
461 ((ent_ents[e].x & 0x0e) >> 3)];
462 /*sys_printf("e_them_t2 sprite %02x\n", ent_ents[e].sprite);*/
463
464
465 /* */
466 if (ent_ents[e].offsx == 0)
467 ent_ents[e].offsx = 2;
468 x = ent_ents[e].x + ent_ents[e].offsx;
469 /*sys_printf("e_them_t2 xmove x=%02x\n", x);*/
470 if (x < 0xe8) {
471 u_envtest(x, ent_ents[e].y, false, &env0, &env1);
472 if (!(env1 & (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP))) {
473 ent_ents[e].x = x;
474 if ((x & 0x1e) != 0x08)
475 return;
476
477 /*
478 * Black Magic (tm)
479 *
480 * this is obviously some sort of randomizer to define a direction
481 * for the entity. it is an exact copy of what the assembler code
482 * does but I can't explain.
483 */
484 bx = e_them_rndnbr + *sh + *sl + 0x0d;
485 cx = *sh;
486 *bl ^= *ch;
487 *bl ^= *cl;
488 *bl ^= *bh;
489 e_them_rndnbr = bx;
490
491 ent_ents[e].offsx = (*bl & 0x01) ? -0x02 : 0x02;
492
493 /* back to normal */
494
495 return;
496
497 }
498 }
499
500 /* U-turn */
501 /*sys_printf("e_them_t2 u-turn\n");*/
502 if (ent_ents[e].offsx == 0)
503 ent_ents[e].offsx = 2;
504 else
505 ent_ents[e].offsx = -ent_ents[e].offsx;
506#undef offsx
507}
508
509/*
510 * Action function for e_them _t2 type
511 *
512 * ASM 2718
513 */
514void
515e_them_t2_action(U8 e)
516{
517 e_them_t2_action2(e);
518
519 /* they kill rick */
520 if (e_rick_boxtest(e))
521 e_rick_gozombie();
522
523 /* lethal entities kill them */
524 if (u_themtest(e)) {
525 e_them_gozombie(e);
526 return;
527 }
528
529 /* bullet kills them */
530 if (E_BULLET_ENT.n &&
531 u_fboxtest(e, E_BULLET_ENT.x + (e_bullet_offsx < 0 ? 00 : 0x18),
532 E_BULLET_ENT.y)) {
533 E_BULLET_ENT.n = 0;
534 e_them_gozombie(e);
535 return;
536 }
537
538 /* bomb kills them */
539 if (e_bomb_lethal && e_bomb_hit(e)) {
540 e_them_gozombie(e);
541 return;
542 }
543
544 /* rick stops them */
545 if (e_rick_state_test(E_RICK_STSTOP) &&
546 u_fboxtest(e, e_rick_stop_x, e_rick_stop_y))
547 ent_ents[e].latency = 0x14;
548}
549
550
551/*
552 * Action sub-function for e_them _t3
553 *
554 * FIXME always starts asleep??
555 *
556 * Waits until triggered by something, then execute move steps from
557 * ent_mvstep with sprite from ent_sprseq. When done, either restart
558 * or disappear.
559 *
560 * Not always lethal ... but if lethal, kills rick.
561 *
562 * ASM: 255A
563 */
564void
565e_them_t3_action2(U8 e)
566{
567#define sproffs c1
568#define step_count c2
569 U8 i;
570 S16 x, y;
571 int wav_index;
572
573 while (1) {
574
575 /* calc new sprite */
576 i = ent_sprseq[ent_ents[e].sprbase + ent_ents[e].sproffs];
577 if (i == 0xff)
578 i = ent_sprseq[ent_ents[e].sprbase];
579 ent_ents[e].sprite = i;
580
581 if (ent_ents[e].sproffs != 0) { /* awake */
582
583 /* rotate sprseq */
584 if (ent_sprseq[ent_ents[e].sprbase + ent_ents[e].sproffs] != 0xff)
585 ent_ents[e].sproffs++;
586 if (ent_sprseq[ent_ents[e].sprbase + ent_ents[e].sproffs] == 0xff)
587 ent_ents[e].sproffs = 1;
588
589 if (ent_ents[e].step_count < ent_mvstep[ent_ents[e].step_no].count) {
590 /*
591 * still running this step: try to increment x and y while
592 * checking that they remain within boudaries. if so, return.
593 * else switch to next step.
594 */
595 ent_ents[e].step_count++;
596 x = ent_ents[e].x + ent_mvstep[ent_ents[e].step_no].dx;
597
598 /* check'n save */
599 if (x > 0 && x < 0xe8) {
600 ent_ents[e].x = x;
601 /*FIXME*/
602 /*
603 y = ent_mvstep[ent_ents[e].step_no].dy;
604 if (y < 0)
605 y += 0xff00;
606 y += ent_ents[e].y;
607 */
608 y = ent_ents[e].y + ent_mvstep[ent_ents[e].step_no].dy;
609 if (y > 0 && y < 0x0140) {
610 ent_ents[e].y = y;
611 return;
612 }
613 }
614 }
615
616 /*
617 * step is done, or x or y is outside boundaries. try to
618 * switch to next step
619 */
620 ent_ents[e].step_no++;
621 if (ent_mvstep[ent_ents[e].step_no].count != 0xff) {
622 /* there is a next step: init and loop */
623 ent_ents[e].step_count = 0;
624 }
625 else {
626 /* there is no next step: restart or deactivate */
627 if (!e_rick_state_test(E_RICK_STZOMBIE) &&
628 !(ent_ents[e].flags & ENT_FLG_ONCE)) {
629 /* loop this entity */
630 ent_ents[e].sproffs = 0;
631 ent_ents[e].n &= ~ENT_LETHAL;
632 if (ent_ents[e].flags & ENT_FLG_LETHALR)
633 ent_ents[e].n |= ENT_LETHAL;
634 ent_ents[e].x = ent_ents[e].xsave;
635 ent_ents[e].y = ent_ents[e].ysave;
636 if (ent_ents[e].y < 0 || ent_ents[e].y > 0x140) {
637 ent_ents[e].n = 0;
638 return;
639 }
640 }
641 else {
642 /* deactivate this entity */
643 ent_ents[e].n = 0;
644 return;
645 }
646 }
647 }
648 else { /* ent_ents[e].sprseq1 == 0 -- waiting */
649
650 /* ugly GOTOs */
651
652 if (ent_ents[e].flags & ENT_FLG_TRIGRICK) { /* reacts to rick */
653 /* wake up if triggered by rick */
654 if (u_trigbox(e, E_RICK_ENT.x + 0x0C, E_RICK_ENT.y + 0x0A))
655 goto wakeup;
656 }
657
658 if (ent_ents[e].flags & ENT_FLG_TRIGSTOP) { /* reacts to rick "stop" */
659 /* wake up if triggered by rick "stop" */
660 if (e_rick_state_test(E_RICK_STSTOP) &&
661 u_trigbox(e, e_rick_stop_x, e_rick_stop_y))
662 goto wakeup;
663 }
664
665 if (ent_ents[e].flags & ENT_FLG_TRIGBULLET) { /* reacts to bullets */
666 /* wake up if triggered by bullet */
667 if (E_BULLET_ENT.n && u_trigbox(e, e_bullet_xc, e_bullet_yc)) {
668 E_BULLET_ENT.n = 0;
669 goto wakeup;
670 }
671 }
672
673 if (ent_ents[e].flags & ENT_FLG_TRIGBOMB) { /* reacts to bombs */
674 /* wake up if triggered by bomb */
675 if (e_bomb_lethal && u_trigbox(e, e_bomb_xc, e_bomb_yc))
676 goto wakeup;
677 }
678
679 /* not triggered: keep waiting */
680 return;
681
682 /* something triggered the entity: wake up */
683 /* initialize step counter */
684 wakeup:
685 if (e_rick_state_test(E_RICK_STZOMBIE))
686 {
687 return;
688 }
689#ifdef ENABLE_SOUND
690 /*
691 * FIXME the sound should come from a table, there are 10 of them
692 * but I dont have the table yet. must rip the data off the game...
693 * FIXME is it 8 of them, not 10?
694 * FIXME testing below...
695 */
696
697 /* FIXME this is defensive, need to figure out whether there
698 is simply missing sound (and possibly rip it)
699 or wrong data in sumbmap 47 (when making the switch explode)
700 and submap 13 (when touching jewel) */
701 wav_index = (ent_ents[e].trigsnd & 0x1F) - 0x14;
702 if((0 <= wav_index) && (wav_index < SOUNDS_NBR_ENTITIES - 1))
703 {
704 syssnd_play(soundEntity[wav_index], 1);
705 }
706 /*syssnd_play(WAV_ENTITY[0], 1);*/
707#endif /* ENABLE_SOUND */
708 ent_ents[e].n &= ~ENT_LETHAL;
709 if (ent_ents[e].flags & ENT_FLG_LETHALI)
710 ent_ents[e].n |= ENT_LETHAL;
711 ent_ents[e].sproffs = 1;
712 ent_ents[e].step_count = 0;
713 ent_ents[e].step_no = ent_ents[e].step_no_i;
714 return;
715 }
716 }
717#undef step_count
718}
719
720
721/*
722 * Action function for e_them _t3 type
723 *
724 * ASM 2546
725 */
726void
727e_them_t3_action(U8 e)
728{
729 e_them_t3_action2(e);
730
731 /* if lethal, can kill rick */
732 if ((ent_ents[e].n & ENT_LETHAL) &&
733 !e_rick_state_test(E_RICK_STZOMBIE) && e_rick_boxtest(e)) { /* CALL 1130 */
734 e_rick_gozombie();
735 }
736}
737
738/* eof */
diff --git a/apps/plugins/xrick/e_them.h b/apps/plugins/xrick/e_them.h
new file mode 100644
index 0000000000..7b524142f6
--- /dev/null
+++ b/apps/plugins/xrick/e_them.h
@@ -0,0 +1,31 @@
1/*
2 * xrick/e_them.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _E_THEM_H
17#define _E_THEM_H
18
19#include "xrick/system/basic_types.h"
20
21extern U32 e_them_rndseed;
22
23extern void e_them_t1a_action(U8);
24extern void e_them_t1b_action(U8);
25extern void e_them_t2_action(U8);
26extern void e_them_t3_action(U8);
27extern void e_them_z_action(U8);
28
29#endif /* ndef _E_THEM_H */
30
31/* eof */
diff --git a/apps/plugins/xrick/ents.c b/apps/plugins/xrick/ents.c
new file mode 100644
index 0000000000..5e35f2bdd1
--- /dev/null
+++ b/apps/plugins/xrick/ents.c
@@ -0,0 +1,540 @@
1/*
2 * xrick/ents.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/ents.h"
17
18#include "xrick/config.h"
19#include "xrick/control.h"
20#include "xrick/game.h"
21#include "xrick/debug.h"
22#include "xrick/e_bullet.h"
23#include "xrick/e_bomb.h"
24#include "xrick/e_rick.h"
25#include "xrick/e_them.h"
26#include "xrick/e_bonus.h"
27#include "xrick/e_box.h"
28#include "xrick/e_sbonus.h"
29#include "xrick/rects.h"
30#include "xrick/maps.h"
31#include "xrick/draw.h"
32
33#include <stdlib.h> /* abs */
34
35/*
36 * global vars
37 */
38ent_t ent_ents[ENT_ENTSNUM + 1];
39
40size_t ent_nbr_entdata = 0;
41entdata_t *ent_entdata = NULL;
42
43rect_t *ent_rects = NULL;
44
45size_t ent_nbr_sprseq = 0;
46U8 *ent_sprseq = NULL;
47
48size_t ent_nbr_mvstep = 0;
49mvstep_t *ent_mvstep = NULL;
50
51/*
52 * prototypes
53 */
54static void ent_addrect(S16, S16, U16, U16);
55static bool ent_creat1(U8 *);
56static bool ent_creat2(U8 *, U16);
57
58
59/*
60 * Reset entities
61 *
62 * ASM 2520
63 */
64void
65ent_reset(void)
66{
67 U8 i;
68
69 e_rick_state_clear(E_RICK_STSTOP);
70 e_bomb_lethal = false;
71
72 ent_ents[0].n = 0;
73 for (i = 2; ent_ents[i].n != 0xff; i++)
74 ent_ents[i].n = 0;
75}
76
77
78/*
79 * Create an entity on slots 4 to 8 by using the first slot available.
80 * Entities of type e_them on slots 4 to 8, when lethal, can kill
81 * other e_them (on slots 4 to C) as well as rick.
82 *
83 * ASM 209C
84 *
85 * e: anything, CHANGED to the allocated entity number.
86 * return: true/OK false/not
87 */
88static bool
89ent_creat1(U8 *e)
90{
91 /* look for a slot */
92 for (*e = 0x04; *e < 0x09; (*e)++)
93 if (ent_ents[*e].n == 0) { /* if slot available, use it */
94 ent_ents[*e].c1 = 0;
95 return true;
96 }
97
98 return false;
99}
100
101
102/*
103 * Create an entity on slots 9 to C by using the first slot available.
104 * Entities of type e_them on slots 9 to C can kill rick when lethal,
105 * but they can never kill other e_them.
106 *
107 * ASM 20BC
108 *
109 * e: anything, CHANGED to the allocated entity number.
110 * m: number of the mark triggering the creation of the entity.
111 * ret: true/OK false/not
112 */
113static bool
114ent_creat2(U8 *e, U16 m)
115{
116 /* make sure the entity created by this mark is not active already */
117 for (*e = 0x09; *e < 0x0c; (*e)++)
118 if (ent_ents[*e].n != 0 && ent_ents[*e].mark == m)
119 return false;
120
121 /* look for a slot */
122 for (*e = 0x09; *e < 0x0c; (*e)++)
123 if (ent_ents[*e].n == 0) { /* if slot available, use it */
124 ent_ents[*e].c1 = 2;
125 return true;
126 }
127
128 return false;
129}
130
131
132/*
133 * Process marks that are within the visible portion of the map,
134 * and create the corresponding entities.
135 *
136 * absolute map coordinate means that they are not relative to
137 * map_frow, as any other coordinates are.
138 *
139 * ASM 1F40
140 *
141 * frow: first visible row of the map -- absolute map coordinate
142 * lrow: last visible row of the map -- absolute map coordinate
143 */
144void
145ent_actvis(U8 frow, U8 lrow)
146{
147 U16 m;
148 U8 e;
149 U16 y;
150
151 /*
152 * go through the list and find the first mark that
153 * is visible, i.e. which has a row greater than the
154 * first row (marks being ordered by row number).
155 */
156 for (m = map_submaps[game_submap].mark;
157 map_marks[m].row != 0xff && map_marks[m].row < frow;
158 m++);
159
160 if (map_marks[m].row == 0xff) /* none found */
161 return;
162
163 /*
164 * go through the list and process all marks that are
165 * visible, i.e. which have a row lower than the last
166 * row (marks still being ordered by row number).
167 */
168 for (;
169 map_marks[m].row != 0xff && map_marks[m].row < lrow;
170 m++) {
171
172 /* ignore marks that are not active */
173 if (map_marks[m].ent & MAP_MARK_NACT)
174 continue;
175
176 /*
177 * allocate a slot to the new entity
178 *
179 * slot type
180 * 0 available for e_them (lethal to other e_them, and stops entities
181 * i.e. entities can't move over them. E.g. moving blocks. But they
182 * can move over entities and kill them!).
183 * 1 xrick
184 * 2 bullet
185 * 3 bomb
186 * 4-8 available for e_them, e_box, e_bonus or e_sbonus (lethal to
187 * other e_them, identified by their number being >= 0x10)
188 * 9-C available for e_them, e_box, e_bonus or e_sbonus (not lethal to
189 * other e_them, identified by their number being < 0x10)
190 *
191 * the type of an entity is determined by its .n as detailed below.
192 *
193 * 1 xrick
194 * 2 bullet
195 * 3 bomb
196 * 4, 7, a, d e_them, type 1a
197 * 5, 8, b, e e_them, type 1b
198 * 6, 9, c, f e_them, type 2
199 * 10, 11 box
200 * 12, 13, 14, 15 bonus
201 * 16, 17 speed bonus
202 * >17 e_them, type 3
203 * 47 zombie
204 */
205
206 if (!(map_marks[m].flags & ENT_FLG_STOPRICK)) {
207 if (map_marks[m].ent >= 0x10) {
208 /* boxes, bonuses and type 3 e_them go to slot 4-8 */
209 /* (c1 set to 0 -> all type 3 e_them are sleeping) */
210 if (!ent_creat1(&e)) continue;
211 }
212 else {
213 /* type 1 and 2 e_them go to slot 9-c */
214 /* (c1 set to 2) */
215 if (!ent_creat2(&e, m)) continue;
216 }
217 }
218 else {
219 /* entities stopping rick (e.g. blocks) go to slot 0 */
220 if (ent_ents[0].n) continue;
221 e = 0;
222 ent_ents[0].c1 = 0;
223 }
224
225 /*
226 * initialize the entity
227 */
228 ent_ents[e].mark = m;
229 ent_ents[e].flags = map_marks[m].flags;
230 ent_ents[e].n = map_marks[m].ent;
231
232 /*
233 * if entity is to be already running (i.e. not asleep and waiting
234 * for some trigger to move), then use LETHALR i.e. restart flag, right
235 * from the beginning
236 */
237 if (ent_ents[e].flags & ENT_FLG_LETHALR)
238 ent_ents[e].n |= ENT_LETHAL;
239
240 ent_ents[e].x = map_marks[m].xy & 0xf8;
241
242 y = (map_marks[m].xy & 0x07) + (map_marks[m].row & 0xf8) - map_frow;
243 y <<= 3;
244 if (!(ent_ents[e].flags & ENT_FLG_STOPRICK))
245 y += 3;
246 ent_ents[e].y = y;
247
248 ent_ents[e].xsave = ent_ents[e].x;
249 ent_ents[e].ysave = ent_ents[e].y;
250
251 /*ent_ents[e].w0C = 0;*/ /* in ASM code but never used */
252
253 ent_ents[e].w = ent_entdata[map_marks[m].ent].w;
254 ent_ents[e].h = ent_entdata[map_marks[m].ent].h;
255 ent_ents[e].sprbase = ent_entdata[map_marks[m].ent].spr;
256 ent_ents[e].step_no_i = ent_entdata[map_marks[m].ent].sni;
257 ent_ents[e].trigsnd = (U8)ent_entdata[map_marks[m].ent].snd;
258
259 /*
260 * FIXME what is this? when all trigger flags are up, then
261 * use .sni for sprbase. Why? What is the point? (This is
262 * for type 1 and 2 e_them, ...)
263 *
264 * This also means that as long as sprite has not been
265 * recalculated, a wrong value is used. This is normal, see
266 * what happens to the falling guy on the right on submap 3:
267 * it changes when hitting the ground.
268 *
269 * Note: sprite recalculation has been fixed, refer to the commit log.
270 */
271#define ENT_FLG_TRIGGERS \
272(ENT_FLG_TRIGBOMB|ENT_FLG_TRIGBULLET|ENT_FLG_TRIGSTOP|ENT_FLG_TRIGRICK)
273 if ((ent_ents[e].flags & ENT_FLG_TRIGGERS) == ENT_FLG_TRIGGERS
274 && e >= 0x09)
275 ent_ents[e].sprbase = (U8)(ent_entdata[map_marks[m].ent].sni & 0x00ff);
276#undef ENT_FLG_TRIGGERS
277
278 ent_ents[e].sprite = (U8)ent_ents[e].sprbase;
279 ent_ents[e].trig_x = map_marks[m].lt & 0xf8;
280 ent_ents[e].latency = (map_marks[m].lt & 0x07) << 5; /* <<5 eq *32 */
281
282 ent_ents[e].trig_y = 3 + 8 * ((map_marks[m].row & 0xf8) - map_frow +
283 (map_marks[m].lt & 0x07));
284
285 ent_ents[e].c2 = 0;
286 ent_ents[e].offsy = 0;
287 ent_ents[e].ylow = 0;
288
289 ent_ents[e].front = false;
290
291 }
292}
293
294
295/*
296 * Add a tile-aligned rectangle containing the given rectangle (indicated
297 * by its MAP coordinates) to the list of rectangles. Clip the rectangle
298 * so it fits into the display zone.
299 */
300static void
301ent_addrect(S16 x, S16 y, U16 width, U16 height)
302{
303 S16 x0, y0;
304 U16 w0, h0;
305 rect_t *r;
306
307 /*sys_printf("rect %#04x,%#04x %#04x %#04x ", x, y, width, height);*/
308
309 /* align to tiles */
310 x0 = x & 0xfff8;
311 y0 = y & 0xfff8;
312 w0 = width;
313 h0 = height;
314 if (x - x0) w0 = (w0 + (x - x0)) | 0x0007;
315 if (y - y0) h0 = (h0 + (y - y0)) | 0x0007;
316
317 /* clip */
318 if (draw_clipms(&x0, &y0, &w0, &h0)) { /* do not add if fully clipped */
319 /*sys_printf("-> [clipped]\n");*/
320 return;
321 }
322
323 /*sys_printf("-> %#04x,%#04x %#04x %#04x\n", x0, y0, w0, h0);*/
324
325#ifdef GFXST
326 y0 += 8;
327#endif
328
329 /* get to screen */
330 x0 -= DRAW_XYMAP_SCRLEFT;
331 y0 -= DRAW_XYMAP_SCRTOP;
332
333 /* add rectangle to the list */
334 r = rects_new(x0, y0, w0, h0, ent_rects);
335 if (!r)
336 {
337 control_set(Control_EXIT);
338 return;
339 }
340 ent_rects = r;
341}
342
343
344/*
345 * Draw all entities onto the frame buffer.
346 *
347 * ASM 07a4
348 *
349 * NOTE This may need to be part of draw.c. Also needs better comments,
350 * NOTE and probably better rectangles management.
351 */
352void
353ent_draw(void)
354{
355 U8 i;
356#ifdef ENABLE_CHEATS
357 static bool ch3 = false;
358#endif
359 S16 dx, dy;
360
361 draw_tilesBank = map_tilesBank;
362
363 /* reset rectangles list */
364 rects_free(ent_rects);
365 ent_rects = NULL;
366
367 /*sys_printf("\n");*/
368
369 /*
370 * background loop : erase all entities that were visible
371 */
372 for (i = 0; ent_ents[i].n != 0xff; i++) {
373#ifdef ENABLE_CHEATS
374 if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s))
375#else
376 if (ent_ents[i].prev_n && ent_ents[i].prev_s)
377#endif
378 /* if entity was active, then erase it (redraw the map) */
379 draw_spriteBackground(ent_ents[i].prev_x, ent_ents[i].prev_y);
380 }
381
382 /*
383 * foreground loop : draw all entities that are visible
384 */
385 for (i = 0; ent_ents[i].n != 0xff; i++) {
386 /*
387 * If entity is active now, draw the sprite. If entity was
388 * not active before, add a rectangle for the sprite.
389 */
390#ifdef ENABLE_CHEATS
391 if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite))
392#else
393 if (ent_ents[i].n && ent_ents[i].sprite)
394#endif
395 /* If entitiy is active, draw the sprite. */
396 draw_sprite2(ent_ents[i].sprite,
397 ent_ents[i].x, ent_ents[i].y,
398 ent_ents[i].front);
399 }
400
401 /*
402 * rectangles loop : figure out which parts of the screen have been
403 * impacted and need to be refreshed, then save state
404 */
405 for (i = 0; ent_ents[i].n != 0xff; i++) {
406#ifdef ENABLE_CHEATS
407 if (ent_ents[i].prev_n && (ch3 || ent_ents[i].prev_s)) {
408#else
409 if (ent_ents[i].prev_n && ent_ents[i].prev_s) {
410#endif
411 /* (1) if entity was active and has been drawn ... */
412#ifdef ENABLE_CHEATS
413 if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
414#else
415 if (ent_ents[i].n && ent_ents[i].sprite) {
416#endif
417 /* (1.1) ... and is still active now and still needs to be drawn, */
418 /* then check if rectangles intersect */
419 dx = abs(ent_ents[i].x - ent_ents[i].prev_x);
420 dy = abs(ent_ents[i].y - ent_ents[i].prev_y);
421 if (dx < 0x20 && dy < 0x16) {
422 /* (1.1.1) if they do, then create one rectangle */
423 ent_addrect((ent_ents[i].prev_x < ent_ents[i].x)
424 ? ent_ents[i].prev_x : ent_ents[i].x,
425 (ent_ents[i].prev_y < ent_ents[i].y)
426 ? ent_ents[i].prev_y : ent_ents[i].y,
427 dx + 0x20, dy + 0x15);
428 }
429 else {
430 /* (1.1.2) else, create two rectangles */
431 ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
432 ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
433 }
434 }
435 else
436 /* (1.2) ... and is not active anymore or does not need to be drawn */
437 /* then create one single rectangle */
438 ent_addrect(ent_ents[i].prev_x, ent_ents[i].prev_y, 0x20, 0x15);
439 }
440#ifdef ENABLE_CHEATS
441 else if (ent_ents[i].n && (game_cheat3 || ent_ents[i].sprite)) {
442#else
443 else if (ent_ents[i].n && ent_ents[i].sprite) {
444#endif
445 /* (2) if entity is active and needs to be drawn, */
446 /* then create one rectangle */
447 ent_addrect(ent_ents[i].x, ent_ents[i].y, 0x20, 0x15);
448 }
449
450 /* save state */
451 ent_ents[i].prev_x = ent_ents[i].x;
452 ent_ents[i].prev_y = ent_ents[i].y;
453 ent_ents[i].prev_n = ent_ents[i].n;
454 ent_ents[i].prev_s = ent_ents[i].sprite;
455 }
456
457#ifdef ENABLE_CHEATS
458 ch3 = game_cheat3;
459#endif
460}
461
462
463/*
464 * Clear entities previous state
465 *
466 */
467void
468ent_clprev(void)
469{
470 U8 i;
471
472 for (i = 0; ent_ents[i].n != 0xff; i++)
473 ent_ents[i].prev_n = 0;
474}
475
476/*
477 * Table containing entity action function pointers.
478 */
479void (*ent_actf[])(U8) = {
480 NULL, /* 00 - zero means that the slot is free */
481 e_rick_action, /* 01 - 12CA */
482 e_bullet_action, /* 02 - 1883 */
483 e_bomb_action, /* 03 - 18CA */
484 e_them_t1a_action, /* 04 - 2452 */
485 e_them_t1b_action, /* 05 - 21CA */
486 e_them_t2_action, /* 06 - 2718 */
487 e_them_t1a_action, /* 07 - 2452 */
488 e_them_t1b_action, /* 08 - 21CA */
489 e_them_t2_action, /* 09 - 2718 */
490 e_them_t1a_action, /* 0A - 2452 */
491 e_them_t1b_action, /* 0B - 21CA */
492 e_them_t2_action, /* 0C - 2718 */
493 e_them_t1a_action, /* 0D - 2452 */
494 e_them_t1b_action, /* 0E - 21CA */
495 e_them_t2_action, /* 0F - 2718 */
496 e_box_action, /* 10 - 245A */
497 e_box_action, /* 11 - 245A */
498 e_bonus_action, /* 12 - 242C */
499 e_bonus_action, /* 13 - 242C */
500 e_bonus_action, /* 14 - 242C */
501 e_bonus_action, /* 15 - 242C */
502 e_sbonus_start, /* 16 - 2182 */
503 e_sbonus_stop /* 17 - 2143 */
504};
505
506
507/*
508 * Run entities action function
509 *
510 */
511void
512ent_action(void)
513{
514 U8 i, k;
515
516 IFDEBUG_ENTS(
517 sys_printf("xrick/ents: --------- action ----------------\n");
518 for (i = 0; ent_ents[i].n != 0xff; i++)
519 if (ent_ents[i].n) {
520 sys_printf("xrick/ents: slot %#04x, entity %#04x", i, ent_ents[i].n);
521 sys_printf(" (%#06x, %#06x), sprite %#04x.\n",
522 ent_ents[i].x, ent_ents[i].y, ent_ents[i].sprite);
523 }
524 );
525
526 for (i = 0; ent_ents[i].n != 0xff; i++) {
527 if (ent_ents[i].n) {
528 k = ent_ents[i].n & 0x7f;
529 if (k == 0x47)
530 e_them_z_action(i);
531 else if (k >= 0x18)
532 e_them_t3_action(i);
533 else
534 ent_actf[k](i);
535 }
536 }
537}
538
539
540/* eof */
diff --git a/apps/plugins/xrick/ents.h b/apps/plugins/xrick/ents.h
new file mode 100644
index 0000000000..1014205035
--- /dev/null
+++ b/apps/plugins/xrick/ents.h
@@ -0,0 +1,118 @@
1/*
2 * xrick/ents.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _ENTS_H
17#define _ENTS_H
18
19#include "xrick/rects.h"
20
21#include <stddef.h> /* size_t */
22
23#define ENT_XRICK ent_ents[1]
24
25/*
26 * flags for ent_ents[e].n ("yes" when set)
27 *
28 * ENT_LETHAL: is entity lethal?
29 */
30#define ENT_LETHAL 0x80
31
32/*
33 * flags for ent_ents[e].flag ("yes" when set)
34 *
35 * ENT_FLG_ONCE: should the entity run once only?
36 * ENT_FLG_STOPRICK: does the entity stops rick (and goes to slot zero)?
37 * ENT_FLG_LETHALR: is entity lethal when restarting?
38 * ENT_FLG_LETHALI: is entity initially lethal?
39 * ENT_FLG_TRIGBOMB: can entity be triggered by a bomb?
40 * ENT_FLG_TRIGBULLET: can entity be triggered by a bullet?
41 * ENT_FLG_TRIGSTOP: can entity be triggered by rick stop?
42 * ENT_FLG_TRIGRICK: can entity be triggered by rick?
43 */
44#define ENT_FLG_ONCE 0x01
45#define ENT_FLG_STOPRICK 0x02
46#define ENT_FLG_LETHALR 0x04
47#define ENT_FLG_LETHALI 0x08
48#define ENT_FLG_TRIGBOMB 0x10
49#define ENT_FLG_TRIGBULLET 0x20
50#define ENT_FLG_TRIGSTOP 0x40
51#define ENT_FLG_TRIGRICK 0x80
52
53typedef struct {
54 U8 n; /* b00 */
55 /*U8 b01;*/ /* b01 in ASM code but never used */
56 S16 x; /* b02 - position */
57 S16 y; /* w04 - position */
58 U8 sprite; /* b08 - sprite number */
59 /*U16 w0C;*/ /* w0C in ASM code but never used */
60 U8 w; /* b0E - width */
61 U8 h; /* b10 - height */
62 U16 mark; /* w12 - number of the mark that created the entity */
63 U8 flags; /* b14 */
64 S16 trig_x; /* b16 - position of trigger box */
65 S16 trig_y; /* w18 - position of trigger box */
66 S16 xsave; /* b1C */
67 S16 ysave; /* w1E */
68 U16 sprbase; /* w20 */
69 U16 step_no_i; /* w22 */
70 U16 step_no; /* w24 */
71 S16 c1; /* b26 */
72 S16 c2; /* b28 */
73 U8 ylow; /* b2A */
74 S16 offsy; /* w2C */
75 U8 latency; /* b2E */
76 U8 prev_n; /* new */
77 S16 prev_x; /* new */
78 S16 prev_y; /* new */
79 U8 prev_s; /* new */
80 bool front; /* new */
81 U8 trigsnd; /* new */
82} ent_t;
83
84typedef struct {
85 U8 w, h;
86 U16 spr, sni;
87 U8 trig_w, trig_h;
88 U8 snd;
89} entdata_t;
90
91typedef struct {
92 U8 count;
93 S8 dx, dy;
94} mvstep_t;
95
96enum { ENT_ENTSNUM = 12 };
97extern ent_t ent_ents[ENT_ENTSNUM + 1];
98
99extern size_t ent_nbr_entdata;
100extern entdata_t *ent_entdata;
101
102extern rect_t *ent_rects;
103
104extern size_t ent_nbr_sprseq;
105extern U8 *ent_sprseq;
106
107extern size_t ent_nbr_mvstep;
108extern mvstep_t *ent_mvstep;
109
110extern void ent_reset(void);
111extern void ent_actvis(U8, U8);
112extern void ent_draw(void);
113extern void ent_clprev(void);
114extern void ent_action(void);
115
116#endif /* ndef _ENTS_H */
117
118/* eof */
diff --git a/apps/plugins/xrick/game.c b/apps/plugins/xrick/game.c
new file mode 100644
index 0000000000..ac99c1829e
--- /dev/null
+++ b/apps/plugins/xrick/game.c
@@ -0,0 +1,722 @@
1/*
2 * xrick/game.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/game.h"
17
18#include "xrick/draw.h"
19#include "xrick/maps.h"
20#include "xrick/ents.h"
21#include "xrick/e_rick.h"
22#include "xrick/e_sbonus.h"
23#include "xrick/e_them.h"
24#include "xrick/screens.h"
25#include "xrick/rects.h"
26#include "xrick/scroller.h"
27#include "xrick/control.h"
28#include "xrick/resources.h"
29
30#ifdef ENABLE_DEVTOOLS
31#include "xrick/devtools.h"
32#endif
33
34
35/*
36 * local typedefs
37 */
38typedef enum {
39#ifdef ENABLE_DEVTOOLS
40 DEVTOOLS,
41#endif
42 XRICK,
43 INIT_GAME, INIT_BUFFER,
44 INTRO_MAIN, INTRO_MAP,
45 PAUSE_PRESSED1, PAUSE_PRESSED1B, PAUSED, PAUSE_PRESSED2,
46 PLAY0, PLAY1, PLAY2, PLAY3,
47 CHAIN_SUBMAP, CHAIN_MAP, CHAIN_END,
48 SCROLL_UP, SCROLL_DOWN,
49 RESTART, GAMEOVER, GETNAME, EXIT
50} game_state_t;
51
52
53/*
54 * global vars
55 */
56U8 game_period = 0;
57bool game_waitevt = false;
58const rect_t *game_rects = NULL;
59
60U8 game_lives = 0;
61U8 game_bombs = 0;
62U8 game_bullets = 0;
63U32 game_score = 0;
64
65U16 game_map = 0;
66U16 game_submap = 0;
67
68U8 game_dir = RIGHT;
69bool game_chsm = false;
70
71bool game_cheat1 = false;
72bool game_cheat2 = false;
73bool game_cheat3 = false;
74
75
76/*
77 * local vars
78 */
79static U8 isave_frow;
80static game_state_t game_state;
81#ifdef ENABLE_SOUND
82static sound_t *currentMusic = NULL;
83#endif
84
85
86/*
87 * prototypes
88 */
89static void frame(void);
90static void init(void);
91static void play0(void);
92static void play3(void);
93static void restart(void);
94static void isave(void);
95static void irestore(void);
96
97
98/*
99 * Cheats
100 */
101#ifdef ENABLE_CHEATS
102void
103game_toggleCheat(cheat_t cheat)
104{
105 if (game_state != INTRO_MAIN && game_state != INTRO_MAP &&
106 game_state != GAMEOVER && game_state != GETNAME &&
107#ifdef ENABLE_DEVTOOLS
108 game_state != DEVTOOLS &&
109#endif
110 game_state != XRICK && game_state != EXIT)
111 {
112 switch (cheat)
113 {
114 case Cheat_UNLIMITED_ALL:
115 {
116 game_cheat1 = !game_cheat1;
117 game_lives = 6;
118 game_bombs = 6;
119 game_bullets = 6;
120 break;
121 }
122 case Cheat_NEVER_DIE:
123 {
124 game_cheat2 = !game_cheat2;
125 break;
126 }
127 case Cheat_EXPOSE:
128 {
129 game_cheat3 = !game_cheat3;
130 break;
131 }
132 }
133 draw_infos();
134 /* FIXME this should probably only raise a flag ... */
135 /* plus we only need to update INFORECT not the whole screen */
136 sysvid_update(&draw_SCREENRECT);
137 }
138}
139#endif
140
141#ifdef ENABLE_SOUND
142/*
143 * Music
144 */
145void
146game_setmusic(sound_t * newMusic, S8 loop)
147{
148 if (!newMusic)
149 {
150 return;
151 }
152
153 if (currentMusic)
154 {
155 game_stopmusic();
156 }
157
158 syssnd_play(newMusic, loop);
159
160 currentMusic = newMusic;
161}
162
163void
164game_stopmusic(void)
165{
166 syssnd_stop(currentMusic);
167 currentMusic = NULL;
168}
169#endif /*ENABLE_SOUND */
170
171/*
172 * Main loop
173 */
174void
175game_run(void)
176{
177 U32 currentTime,
178#ifdef ENABLE_SOUND
179 lastSoundTime = 0,
180#endif
181 lastFrameTime = 0;
182
183 if (!resources_load())
184 {
185 resources_unload();
186 return;
187 }
188
189 if (!sys_cacheData())
190 {
191 sys_uncacheData();
192 return;
193 }
194
195 game_period = sysarg_args_period ? sysarg_args_period : GAME_PERIOD;
196 game_state = XRICK;
197
198 /* main loop */
199 while (game_state != EXIT)
200 {
201 currentTime = sys_gettime();
202
203 if (currentTime - lastFrameTime >= game_period)
204 {
205 /* frame */
206 frame();
207
208 /* video */
209 /*DEBUG*//*game_rects=&draw_SCREENRECT;*//*DEBUG*/
210 sysvid_update(game_rects);
211
212 /* reset rectangles list */
213 rects_free(ent_rects);
214 ent_rects = NULL;
215 draw_STATUSRECT.next = NULL; /* FIXME freerects should handle this */
216
217 /* events */
218 if (game_waitevt)
219 {
220 sysevt_wait(); /* wait for an event */
221 }
222 else
223 {
224 sysevt_poll(); /* process events (non-blocking) */
225 }
226
227 lastFrameTime = currentTime;
228 }
229
230#ifdef ENABLE_SOUND
231 if (currentTime - lastSoundTime >= syssnd_period)
232 {
233 /* sound */
234 syssnd_update();
235
236 lastSoundTime = currentTime;
237 }
238#endif /* ENABLE_SOUND */
239
240 sys_yield();
241 }
242
243#ifdef ENABLE_SOUND
244 syssnd_stopAll();
245#endif
246
247 sys_uncacheData();
248
249 resources_unload();
250}
251
252/*
253 * Prepare frame
254 *
255 * This function loops forever: use 'return' when a frame is ready.
256 * When returning, game_rects must contain every parts of the buffer
257 * that have been modified.
258 */
259static void
260frame(void)
261{
262 while (1) {
263
264 switch (game_state) {
265
266
267
268#ifdef ENABLE_DEVTOOLS
269 case DEVTOOLS:
270 switch (devtools_run()) {
271 case SCREEN_RUNNING:
272 return;
273 case SCREEN_DONE:
274 game_state = INIT_GAME;
275 break;
276 case SCREEN_EXIT:
277 game_state = EXIT;
278 return;
279 }
280 break;
281#endif
282
283
284
285 case XRICK:
286 switch(screen_xrick()) {
287 case SCREEN_RUNNING:
288 return;
289 case SCREEN_DONE:
290#ifdef ENABLE_DEVTOOLS
291 game_state = DEVTOOLS;
292#else
293 game_state = INIT_GAME;
294#endif
295 break;
296 case SCREEN_EXIT:
297 game_state = EXIT;
298 return;
299 }
300 break;
301
302
303
304 case INIT_GAME:
305 init();
306 game_state = INTRO_MAIN;
307 break;
308
309
310
311 case INTRO_MAIN:
312 switch (screen_introMain()) {
313 case SCREEN_RUNNING:
314 return;
315 case SCREEN_DONE:
316 game_state = INTRO_MAP;
317 break;
318 case SCREEN_EXIT:
319 game_state = EXIT;
320 return;
321 }
322 break;
323
324
325
326 case INTRO_MAP:
327 switch (screen_introMap()) {
328 case SCREEN_RUNNING:
329 return;
330 case SCREEN_DONE:
331 game_waitevt = false;
332 game_state = INIT_BUFFER;
333 break;
334 case SCREEN_EXIT:
335 game_state = EXIT;
336 return;
337 }
338 break;
339
340
341
342 case INIT_BUFFER:
343 sysvid_clear(); /* clear buffer */
344 draw_map(); /* draw the map onto the buffer */
345 draw_drawStatus(); /* draw the status bar onto the buffer */
346#ifdef ENABLE_CHEATS
347 draw_infos(); /* draw the info bar onto the buffer */
348#endif
349 game_rects = &draw_SCREENRECT; /* request full buffer refresh */
350 game_state = PLAY0;
351 return;
352
353
354
355 case PAUSE_PRESSED1:
356 screen_pause(true);
357 game_state = PAUSE_PRESSED1B;
358 break;
359
360
361
362 case PAUSE_PRESSED1B:
363 if (control_test(Control_PAUSE))
364 return;
365 game_state = PAUSED;
366 break;
367
368
369
370 case PAUSED:
371 if (control_test(Control_PAUSE))
372 game_state = PAUSE_PRESSED2;
373 if (control_test(Control_EXIT))
374 game_state = EXIT;
375 return;
376
377
378
379 case PAUSE_PRESSED2:
380 if (!(control_test(Control_PAUSE))) {
381 game_waitevt = false;
382 screen_pause(false);
383#ifdef ENABLE_SOUND
384 syssnd_pauseAll(false);
385#endif
386 game_state = PLAY2;
387 }
388 return;
389
390
391
392 case PLAY0:
393 play0();
394 break;
395
396
397
398 case PLAY1:
399 if (control_test(Control_PAUSE)) {
400#ifdef ENABLE_SOUND
401 syssnd_pauseAll(true);
402#endif
403 game_waitevt = true;
404 game_state = PAUSE_PRESSED1;
405 }
406 else if (!control_active) {
407#ifdef ENABLE_SOUND
408 syssnd_pauseAll(true);
409#endif
410 game_waitevt = true;
411 screen_pause(true);
412 game_state = PAUSED;
413 }
414 else
415 game_state = PLAY2;
416 break;
417
418
419
420 case PLAY2:
421 if (e_rick_state_test(E_RICK_STDEAD)) { /* rick is dead */
422 if (game_cheat1 || --game_lives) {
423 game_state = RESTART;
424 } else {
425 game_state = GAMEOVER;
426 }
427 }
428 else if (game_chsm) /* request to chain to next submap */
429 game_state = CHAIN_SUBMAP;
430 else
431 game_state = PLAY3;
432 break;
433
434
435
436 case PLAY3:
437 play3();
438 return;
439
440
441
442 case CHAIN_SUBMAP:
443 if (map_chain())
444 game_state = CHAIN_END;
445 else {
446 game_bullets = 0x06;
447 game_bombs = 0x06;
448 game_map++;
449
450 if (game_map == map_nbr_maps - 1) {
451 /* reached end of game */
452 /* FIXME @292?*/
453 }
454
455 game_state = CHAIN_MAP;
456 }
457 break;
458
459
460
461 case CHAIN_MAP: /* CHAIN MAP */
462 switch (screen_introMap()) {
463 case SCREEN_RUNNING:
464 return;
465 case SCREEN_DONE:
466 if (game_map >= map_nbr_maps - 1) { /* reached end of game */
467 sysarg_args_map = 0;
468 sysarg_args_submap = 0;
469 game_state = GAMEOVER;
470 }
471 else { /* initialize game */
472 ent_ents[1].x = map_maps[game_map].x;
473 ent_ents[1].y = map_maps[game_map].y;
474 map_frow = (U8)map_maps[game_map].row;
475 game_submap = map_maps[game_map].submap;
476 game_state = CHAIN_END;
477 }
478 break;
479 case SCREEN_EXIT:
480 game_state = EXIT;
481 return;
482 }
483 break;
484
485
486
487 case CHAIN_END:
488 map_init(); /* initialize the map */
489 isave(); /* save data in case of a restart */
490 ent_clprev(); /* cleanup entities */
491 draw_map(); /* draw the map onto the buffer */
492 draw_drawStatus(); /* draw the status bar onto the buffer */
493 game_rects = &draw_SCREENRECT; /* request full screen refresh */
494 game_state = PLAY0;
495 return;
496
497
498
499 case SCROLL_UP:
500 switch (scroll_up()) {
501 case SCROLL_RUNNING:
502 return;
503 case SCROLL_DONE:
504 game_state = PLAY0;
505 break;
506 }
507 break;
508
509
510
511 case SCROLL_DOWN:
512 switch (scroll_down()) {
513 case SCROLL_RUNNING:
514 return;
515 case SCROLL_DONE:
516 game_state = PLAY0;
517 break;
518 }
519 break;
520
521
522
523 case RESTART:
524 restart();
525 game_state = PLAY0;
526 return;
527
528
529
530 case GAMEOVER:
531 switch (screen_gameover()) {
532 case SCREEN_RUNNING:
533 return;
534 case SCREEN_DONE:
535 game_state = GETNAME;
536 break;
537 case SCREEN_EXIT:
538 game_state = EXIT;
539 break;
540 }
541 break;
542
543
544
545 case GETNAME:
546 switch (screen_getname()) {
547 case SCREEN_RUNNING:
548 return;
549 case SCREEN_DONE:
550 game_state = INIT_GAME;
551 return;
552 case SCREEN_EXIT:
553 game_state = EXIT;
554 break;
555 }
556 break;
557
558
559
560 case EXIT:
561 return;
562
563 }
564 }
565}
566
567
568/*
569 * Initialize the game
570 */
571static void
572init(void)
573{
574 U8 i;
575
576 e_rick_state_clear(0xff);
577
578 game_lives = 6;
579 game_bombs = 6;
580 game_bullets = 6;
581 game_score = 0;
582
583 game_map = sysarg_args_map;
584
585 if (sysarg_args_submap == 0)
586 {
587 game_submap = map_maps[game_map].submap;
588 map_frow = (U8)map_maps[game_map].row;
589 }
590 else
591 {
592 /* dirty hack to determine frow */
593 game_submap = sysarg_args_submap;
594 i = 0;
595 while (i < map_nbr_connect &&
596 (map_connect[i].submap != game_submap ||
597 map_connect[i].dir != RIGHT))
598 {
599 i++;
600 }
601 map_frow = map_connect[i].rowin - 0x10;
602 ent_ents[1].y = 0x10 << 3;
603 }
604
605 ent_ents[1].x = map_maps[game_map].x;
606 ent_ents[1].y = map_maps[game_map].y;
607 ent_ents[1].w = 0x18;
608 ent_ents[1].h = 0x15;
609 ent_ents[1].n = 0x01;
610 ent_ents[1].sprite = 0x01;
611 ent_ents[1].front = false;
612 ent_ents[ENT_ENTSNUM].n = 0xFF;
613
614 map_resetMarks();
615
616 map_init();
617 isave();
618}
619
620
621/*
622 * play0
623 *
624 */
625static void
626play0(void)
627{
628 if (control_test(Control_END)) { /* request to end the game */
629 game_state = GAMEOVER;
630 return;
631 }
632
633 if (control_test(Control_EXIT)) { /* request to exit the game */
634 game_state = EXIT;
635 return;
636 }
637
638 ent_action(); /* run entities */
639 e_them_rndseed++; /* (0270) */
640
641 game_state = PLAY1;
642}
643
644
645/*
646 * play3
647 *
648 */
649static void
650play3(void)
651{
652 draw_clearStatus(); /* clear the status bar */
653 ent_draw(); /* draw all entities onto the buffer */
654 /* sound */
655 draw_drawStatus(); /* draw the status bar onto the buffer*/
656
657 game_rects = &draw_STATUSRECT; /* refresh status bar too */
658 draw_STATUSRECT.next = ent_rects; /* take care to cleanup draw_STATUSRECT->next later! */
659
660 if (!e_rick_state_test(E_RICK_STZOMBIE)) { /* need to scroll ? */
661 if (ent_ents[1].y >= 0xCC) {
662 game_state = SCROLL_UP;
663 return;
664 }
665 if (ent_ents[1].y <= 0x60) {
666 game_state = SCROLL_DOWN;
667 return;
668 }
669 }
670
671 game_state = PLAY0;
672}
673
674
675/*
676 * restart
677 *
678 */
679static void
680restart(void)
681{
682 e_rick_state_clear(E_RICK_STDEAD|E_RICK_STZOMBIE);
683
684 game_bullets = 6;
685 game_bombs = 6;
686
687 ent_ents[1].n = 1;
688
689 irestore();
690 map_init();
691 isave();
692 ent_clprev();
693 draw_map();
694 draw_drawStatus();
695 game_rects = &draw_SCREENRECT;
696}
697
698
699/*
700 * isave (0bbb)
701 *
702 */
703static void
704isave(void)
705{
706 e_rick_save();
707 isave_frow = map_frow;
708}
709
710
711/*
712 * irestore (0bdc)
713 *
714 */
715static void
716irestore(void)
717{
718 e_rick_restore();
719 map_frow = isave_frow;
720}
721
722/* eof */
diff --git a/apps/plugins/xrick/game.h b/apps/plugins/xrick/game.h
new file mode 100644
index 0000000000..025c3bdbe2
--- /dev/null
+++ b/apps/plugins/xrick/game.h
@@ -0,0 +1,73 @@
1/*
2 * xrick/game.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _GAME_H
17#define _GAME_H
18
19#include "xrick/config.h"
20#include "xrick/rects.h"
21#ifdef ENABLE_SOUND
22#include "xrick/data/sounds.h"
23#endif
24
25#include <stddef.h> /* NULL */
26
27#define LEFT 1
28#define RIGHT 0
29
30#define GAME_PERIOD 40
31
32#define GAME_BOMBS_INIT 6
33#define GAME_BULLETS_INIT 6
34
35extern U8 game_lives; /* lives counter */
36extern U8 game_bombs; /* bombs counter */
37extern U8 game_bullets; /* bullets counter */
38
39extern U32 game_score; /* score */
40
41extern U16 game_map; /* current map */
42extern U16 game_submap; /* current submap */
43
44extern U8 game_dir; /* direction (LEFT, RIGHT) */
45extern bool game_chsm; /* change submap request (true, false) */
46
47extern bool game_waitevt; /* wait for events (true, false) */
48extern U8 game_period; /* time between each frame, in millisecond */
49
50extern const rect_t *game_rects; /* rectangles to redraw at each frame */
51
52extern void game_run(void);
53#ifdef ENABLE_SOUND
54extern void game_setmusic(sound_t * sound, S8 loop);
55extern void game_stopmusic(void);
56#endif /* ENABLE_SOUND */
57
58#ifdef ENABLE_CHEATS
59typedef enum
60{
61 Cheat_UNLIMITED_ALL,
62 Cheat_NEVER_DIE,
63 Cheat_EXPOSE
64} cheat_t;
65extern bool game_cheat1; /* infinite lives, bombs and bullets */
66extern bool game_cheat2; /* never die */
67extern bool game_cheat3; /* highlight sprites */
68extern void game_toggleCheat(cheat_t);
69#endif /* ENABLE_CHEATS */
70
71#endif /* ndef _GAME_H */
72
73/* eof */
diff --git a/apps/plugins/xrick/maps.c b/apps/plugins/xrick/maps.c
new file mode 100644
index 0000000000..f85d0b9906
--- /dev/null
+++ b/apps/plugins/xrick/maps.c
@@ -0,0 +1,253 @@
1/*
2 * xrick/maps.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16/*
17 * NOTES
18 *
19 * A map is composed of submaps, which in turn are composed of rows of
20 * 0x20 tiles. map_map contains the tiles for the current portion of the
21 * current submap, i.e. a little bit more than what appear on the screen,
22 * but not the whole submap.
23 *
24 * map_frow is map_map top row within the submap.
25 *
26 * Submaps are stored as arrays of blocks, each block being a 4x4 tile
27 * array. map_submaps[].bnum points to the first block of the array.
28 *
29 * Before a submap can be played, it needs to be expanded from blocks
30 * to map_map.
31 */
32
33#include "xrick/maps.h"
34
35#include "xrick/game.h"
36#include "xrick/debug.h"
37#include "xrick/control.h"
38#include "xrick/ents.h"
39#include "xrick/draw.h"
40#include "xrick/screens.h"
41#include "xrick/e_sbonus.h"
42
43/*
44 * global vars
45 */
46U8 map_map[0x2C][0x20];
47
48size_t map_nbr_maps = 0;
49map_t *map_maps = NULL;
50
51size_t map_nbr_submaps = 0;
52submap_t *map_submaps = NULL;
53
54size_t map_nbr_connect = 0;
55connect_t *map_connect = NULL;
56
57size_t map_nbr_blocks = 0;
58block_t *map_blocks = NULL;
59
60size_t map_nbr_marks = 0;
61mark_t *map_marks = NULL;
62
63size_t map_nbr_bnums = 0;
64U8 *map_bnums = NULL;
65
66size_t map_nbr_eflgc = 0;
67U8 *map_eflg_c = NULL;
68U8 map_eflg[0x100];
69
70U8 map_frow;
71U8 map_tilesBank;
72
73
74/*
75 * prototypes
76 */
77static void map_eflg_expand(U8);
78
79
80/*
81 * Fill in map_map with tile numbers by expanding blocks.
82 *
83 * add map_submaps[].bnum to map_frow to find out where to start from.
84 * We need to /4 map_frow to convert from tile rows to block rows, then
85 * we need to *8 to convert from block rows to block numbers (there
86 * are 8 blocks per block row). This is achieved by *2 then &0xfff8.
87 */
88void
89map_expand(void)
90{
91 U8 i, j, k, l;
92 U8 row, col;
93 U16 pbnum;
94
95 pbnum = map_submaps[game_submap].bnum + ((2 * map_frow) & 0xfff8);
96 row = col = 0;
97
98 for (i = 0; i < 0x0b; i++) { /* 0x0b rows of blocks */
99 for (j = 0; j < 0x08; j++) { /* 0x08 blocks per row */
100 for (k = 0, l = 0; k < 0x04; k++) { /* expand one block */
101 map_map[row][col++] = map_blocks[map_bnums[pbnum]][l++];
102 map_map[row][col++] = map_blocks[map_bnums[pbnum]][l++];
103 map_map[row][col++] = map_blocks[map_bnums[pbnum]][l++];
104 map_map[row][col] = map_blocks[map_bnums[pbnum]][l++];
105 row += 1; col -= 3;
106 }
107 row -= 4; col += 4;
108 pbnum++;
109 }
110 row += 4; col = 0;
111 }
112}
113
114
115/*
116 * Initialize a new submap
117 *
118 * ASM 0cc3
119 */
120void
121map_init(void)
122{
123 /*sys_printf("xrick/map_init: map=%#04x submap=%#04x\n", g_map, game_submap);*/
124#ifdef GFXPC
125 draw_filter = 0xffff;
126 map_tilesBank = 2 + map_submaps[game_submap].page;
127#endif
128#ifdef GFXST
129 map_tilesBank = 1 + map_submaps[game_submap].page;
130#endif
131 map_eflg_expand(map_submaps[game_submap].page << 4);
132 map_expand();
133 ent_reset();
134 ent_actvis(map_frow + MAP_ROW_SCRTOP, map_frow + MAP_ROW_SCRBOT);
135 ent_actvis(map_frow + MAP_ROW_HTTOP, map_frow + MAP_ROW_HTBOT);
136 ent_actvis(map_frow + MAP_ROW_HBTOP, map_frow + MAP_ROW_HBBOT);
137}
138
139
140/*
141 * Expand entity flags for this map
142 *
143 * ASM 1117
144 */
145void
146map_eflg_expand(U8 offs)
147{
148 U8 i, j, k;
149
150 for (i = 0, k = 0; i < 0x10; i++) {
151 j = map_eflg_c[offs + i++];
152 while (j--) map_eflg[k++] = map_eflg_c[offs + i];
153 }
154}
155
156
157/*
158 * Chain (sub)maps
159 *
160 * ASM 0c08
161 * return: true/next submap OK, false/map finished
162 */
163bool
164map_chain(void)
165{
166 U16 c, t;
167
168 game_chsm = false;
169 e_sbonus_counting = false;
170
171 /* find connection */
172 c = map_submaps[game_submap].connect;
173 t = 3;
174
175 IFDEBUG_MAPS(
176 sys_printf("xrick/maps: chain submap=%#04x frow=%#04x .connect=%#04x %s\n",
177 game_submap, map_frow, c,
178 (game_dir == LEFT ? "-> left" : "-> right"));
179 );
180
181 /*
182 * look for the first connector with compatible row number. if none
183 * found, then panic
184 */
185 for (c = map_submaps[game_submap].connect ; ; c++)
186 {
187 if (map_connect[c].dir == 0xff)
188 {
189 sys_error("(map_chain) can not find connector\n");
190 control_set(Control_EXIT);
191 return false;
192 }
193 if (map_connect[c].dir != game_dir)
194 {
195 continue;
196 }
197 t = (ent_ents[1].y >> 3) + map_frow - map_connect[c].rowout;
198 if (t < 3)
199 {
200 break;
201 }
202 }
203
204 /* got it */
205 IFDEBUG_MAPS(
206 sys_printf("xrick/maps: chain frow=%#04x y=%#06x\n",
207 map_frow, ent_ents[1].y);
208 sys_printf("xrick/maps: chain connect=%#04x rowout=%#04x - ",
209 c, map_connect[c].rowout);
210 );
211
212 if (map_connect[c].submap == 0xff)
213 {
214 /* no next submap - request next map */
215 IFDEBUG_MAPS(
216 sys_printf("chain to next map\n");
217 );
218 return false;
219 }
220 else
221 {
222 /* next submap */
223 IFDEBUG_MAPS(
224 sys_printf("chain to submap=%#04x rowin=%#04x\n",
225 map_connect[c].submap, map_connect[c].rowin);
226 );
227 map_frow = map_frow - map_connect[c].rowout + map_connect[c].rowin;
228 game_submap = map_connect[c].submap;
229 IFDEBUG_MAPS(
230 sys_printf("xrick/maps: chain frow=%#04x\n",
231 map_frow);
232 );
233 return true;
234 }
235}
236
237
238/*
239 * Reset all marks, i.e. make them all active again.
240 *
241 * ASM 0025
242 *
243 */
244void
245map_resetMarks(void)
246{
247 U16 i;
248 for (i = 0; i < map_nbr_marks; i++)
249 map_marks[i].ent &= ~MAP_MARK_NACT;
250}
251
252
253/* eof */
diff --git a/apps/plugins/xrick/maps.h b/apps/plugins/xrick/maps.h
new file mode 100644
index 0000000000..716c6589ad
--- /dev/null
+++ b/apps/plugins/xrick/maps.h
@@ -0,0 +1,159 @@
1/*
2 * xrick/maps.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _MAPS_H
17#define _MAPS_H
18
19#include "xrick/system/basic_types.h"
20#include "xrick/system/system.h"
21#ifdef ENABLE_SOUND
22#include "xrick/data/sounds.h"
23#endif
24
25#include <stddef.h> /* size_t */
26
27/*
28 * map row definitions, for three zones : hidden top, screen, hidden bottom
29 * the three zones compose map_map, which contains the definition of the
30 * current portion of the submap.
31 */
32#define MAP_ROW_HTTOP 0x00
33#define MAP_ROW_HTBOT 0x07
34#define MAP_ROW_SCRTOP 0x08
35#define MAP_ROW_SCRBOT 0x1F
36#define MAP_ROW_HBTOP 0x20
37#define MAP_ROW_HBBOT 0x27
38
39extern U8 map_map[0x2c][0x20];
40
41/*
42 * main maps
43 */
44typedef struct {
45 U16 x, y; /* initial position for rick */
46 U16 row; /* initial map_map top row within the submap */
47 U16 submap; /* initial submap */
48#ifdef ENABLE_SOUND
49 sound_t * tune; /* map tune */
50#endif
51} map_t;
52
53extern size_t map_nbr_maps;
54extern map_t *map_maps;
55
56/*
57 * sub maps
58 */
59typedef struct {
60 U16 page; /* tiles page */
61 U16 bnum; /* first block number */
62 U16 connect; /* first connection */
63 U16 mark; /* first entity mark */
64} submap_t;
65
66extern size_t map_nbr_submaps;
67extern submap_t *map_submaps;
68
69/*
70 * connections
71 */
72typedef struct {
73 U8 dir;
74 U8 rowout;
75 U8 submap;
76 U8 rowin;
77} connect_t;
78
79extern size_t map_nbr_connect;
80extern connect_t *map_connect;
81
82/*
83 * blocks - one block is 4 by 4 tiles.
84 */
85typedef U8 block_t[0x10];
86
87extern size_t map_nbr_blocks;
88extern block_t *map_blocks;
89
90/*
91 * flags for map_marks[].ent ("yes" when set)
92 *
93 * MAP_MARK_NACT: this mark is not active anymore.
94 */
95#define MAP_MARK_NACT (0x80)
96
97/*
98 * mark structure
99 */
100typedef struct {
101 U8 row;
102 U8 ent;
103 U8 flags;
104 U8 xy; /* bits XXXX XYYY (from b03) with X->x, Y->y */
105 U8 lt; /* bits XXXX XNNN (from b04) with X->trig_x, NNN->lat & trig_y */
106} mark_t;
107
108extern size_t map_nbr_marks;
109extern mark_t *map_marks;
110
111/*
112 * block numbers, i.e. array of rows of 8 blocks
113 */
114extern size_t map_nbr_bnums;
115extern U8 *map_bnums;
116
117/*
118 * flags for map_eflg[map_map[row][col]] ("yes" when set)
119 *
120 * MAP_EFLG_VERT: vertical move only (usually on top of _CLIMB).
121 * MAP_EFLG_SOLID: solid block, can't go through.
122 * MAP_EFLG_SPAD: super pad. can't go through, but sends entities to the sky.
123 * MAP_EFLG_WAYUP: solid block, can't go through except when going up.
124 * MAP_EFLG_FGND: foreground (hides entities).
125 * MAP_EFLG_LETHAL: lethal (kill entities).
126 * MAP_EFLG_CLIMB: entities can climb here.
127 * MAP_EFLG_01:
128 */
129#define MAP_EFLG_VERT (0x80)
130#define MAP_EFLG_SOLID (0x40)
131#define MAP_EFLG_SPAD (0x20)
132#define MAP_EFLG_WAYUP (0x10)
133#define MAP_EFLG_FGND (0x08)
134#define MAP_EFLG_LETHAL (0x04)
135#define MAP_EFLG_CLIMB (0x02)
136#define MAP_EFLG_01 (0x01)
137
138extern size_t map_nbr_eflgc;
139extern U8 *map_eflg_c; /* compressed */
140extern U8 map_eflg[0x100]; /* current */
141
142/*
143 * map_map top row within the submap
144 */
145extern U8 map_frow;
146
147/*
148 * tiles offset
149 */
150extern U8 map_tilesBank;
151
152extern void map_expand(void);
153extern void map_init(void);
154extern bool map_chain(void);
155extern void map_resetMarks(void);
156
157#endif /* ndef _MAPS_H */
158
159/* eof */
diff --git a/apps/plugins/xrick/rects.c b/apps/plugins/xrick/rects.c
new file mode 100644
index 0000000000..9324426060
--- /dev/null
+++ b/apps/plugins/xrick/rects.c
@@ -0,0 +1,57 @@
1/*
2 * xrick/rects.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/rects.h"
17#include "xrick/system/system.h"
18
19/*
20 * Free a list of rectangles and set the pointer to NULL.
21 *
22 * p: rectangle list CHANGED to NULL
23 */
24void
25rects_free(rect_t *r)
26{
27 while (r)
28 {
29 rect_t * next = r->next;
30 sysmem_pop(r);
31 r = next;
32 }
33}
34
35
36/*
37 * Add a rectangle to a list of rectangles
38 */
39rect_t *
40rects_new(U16 x, U16 y, U16 width, U16 height, rect_t *next)
41{
42 rect_t *r;
43
44 r = sysmem_push(sizeof(*r));
45 if (!r)
46 {
47 return NULL;
48 }
49 r->x = x;
50 r->y = y;
51 r->width = width;
52 r->height = height;
53 r->next = next;
54 return r;
55}
56
57/* eof */
diff --git a/apps/plugins/xrick/rects.h b/apps/plugins/xrick/rects.h
new file mode 100644
index 0000000000..ed75f609aa
--- /dev/null
+++ b/apps/plugins/xrick/rects.h
@@ -0,0 +1,32 @@
1/*
2 * xrick/rects.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _RECTS_H
17#define _RECTS_H
18
19#include "xrick/system/basic_types.h"
20
21typedef struct rect_s {
22 U16 x, y;
23 U16 width, height;
24 struct rect_s *next;
25} rect_t;
26
27extern void rects_free(rect_t *);
28extern rect_t *rects_new(U16, U16, U16, U16, rect_t *);
29
30#endif /* ndef _RECTS_H */
31
32/* eof */
diff --git a/apps/plugins/xrick/res_magic.c b/apps/plugins/xrick/res_magic.c
new file mode 100644
index 0000000000..c3365bc816
--- /dev/null
+++ b/apps/plugins/xrick/res_magic.c
@@ -0,0 +1,18 @@
1/*
2 * xrick/res_magic.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/resources.h"
15
16const U8 resource_magic[4] = { 0x52, 0x49, 0x43, 0x4B }; /* 'R','I','C','K' */
17
18/* eof */
diff --git a/apps/plugins/xrick/resources.c b/apps/plugins/xrick/resources.c
new file mode 100644
index 0000000000..d7b49e8f73
--- /dev/null
+++ b/apps/plugins/xrick/resources.c
@@ -0,0 +1,1297 @@
1/*
2 * xrick/resources.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/resources.h"
15
16#include "xrick/draw.h"
17#include "xrick/ents.h"
18#include "xrick/maps.h"
19#include "xrick/util.h"
20#include "xrick/data/sprites.h"
21#include "xrick/data/tiles.h"
22#include "xrick/data/pics.h"
23#include "xrick/system/basic_funcs.h"
24
25#include "xrick/system/miniz_config.h"
26#include "miniz/miniz.c"
27
28/*
29 * prototypes
30 */
31static bool readFile(const unsigned id);
32static bool checkCrc32(const unsigned id);
33static bool readHeader(file_t fp, const unsigned id);
34static bool loadString(file_t fp, char ** str, const char terminator);
35static void unloadString(char ** buffer);
36static bool loadResourceFilelist(file_t fp);
37static void unloadResourceFilelist(void);
38static bool loadResourceEntdata(file_t fp);
39static void unloadResourceEntdata(void);
40static bool loadRawData(file_t fp, void ** buffer, const size_t size, size_t * count);
41static void unloadRawData(void ** buffer, size_t * count);
42static bool loadResourceMaps(file_t fp);
43static void unloadResourceMaps(void);
44static bool loadResourceSubmaps(file_t fp);
45static void unloadResourceSubmaps(void);
46static bool loadResourceImapsteps(file_t fp);
47static void unloadResourceImapsteps(void);
48static bool loadResourceImaptext(file_t fp);
49static void unloadResourceImaptext(void);
50static bool loadResourceHighScores(file_t fp);
51static void unloadResourceHighScores(void);
52static bool loadResourceSpritesData(file_t fp);
53static void unloadResourceSpritesData(void);
54static bool loadResourceTilesData(file_t fp);
55static void unloadResourceTilesData(void);
56static bool loadImage(file_t fp, img_t ** image);
57static void unloadImage(img_t ** image);
58#ifdef GFXST
59static bool loadPicture(file_t fp, pic_t ** picture);
60static void unloadPicture(pic_t ** picture);
61#endif /* GFXST */
62#ifdef ENABLE_SOUND
63static bool fromResourceIdToSound(const unsigned id, sound_t *** sound);
64static bool loadSound(const unsigned id);
65static void unloadSound(const unsigned id);
66#endif /* ENABLE_SOUND */
67
68/*
69 * local vars
70 */
71static char * resourceFiles[Resource_MAX_COUNT] =
72{
73 BOOTSTRAP_RESOURCE_NAME,
74 /* the rest initialised to NULL by default */
75};
76
77/*
78 * load 16b length + not-terminated string
79 */
80static bool loadString(file_t fp, char ** buffer, const char terminator)
81{
82 size_t length;
83 U16 u16Temp;
84 char * bufferTemp;
85
86 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
87 {
88 return false;
89 }
90 length = letoh16(u16Temp);
91
92 bufferTemp = sysmem_push(length + 1);
93 *buffer = bufferTemp;
94 if (!bufferTemp)
95 {
96 return false;
97 }
98
99 if (length)
100 {
101 if (sysfile_read(fp, bufferTemp, length, 1) != 1)
102 {
103 return false;
104 }
105 }
106
107 bufferTemp[length] = terminator;
108
109 return true;
110}
111
112/*
113 *
114 */
115static void unloadString(char ** buffer)
116{
117 sysmem_pop(*buffer);
118 *buffer = NULL;
119}
120
121/*
122 *
123 */
124static bool loadResourceFilelist(file_t fp)
125{
126 unsigned id;
127
128 for (id = Resource_PALETTE; id < Resource_MAX_COUNT; ++id)
129 {
130 if (!loadString(fp, &(resourceFiles[id]), 0x00))
131 {
132 return false;
133 }
134 }
135 return true;
136}
137
138/*
139 *
140 */
141static void unloadResourceFilelist()
142{
143 unsigned id;
144
145 for (id = Resource_MAX_COUNT - 1; id >= Resource_PALETTE; --id)
146 {
147 unloadString(&(resourceFiles[id]));
148 }
149}
150
151/*
152 *
153 */
154static bool loadResourceEntdata(file_t fp)
155{
156 size_t i;
157 U16 u16Temp;
158 resource_entdata_t dataTemp;
159
160 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
161 {
162 return false;
163 }
164 ent_nbr_entdata = letoh16(u16Temp);
165
166 ent_entdata = sysmem_push(ent_nbr_entdata * sizeof(*ent_entdata));
167 if (!ent_entdata)
168 {
169 return false;
170 }
171
172 for (i = 0; i < ent_nbr_entdata; ++i)
173 {
174 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
175 {
176 return false;
177 }
178 ent_entdata[i].w = dataTemp.w;
179 ent_entdata[i].h = dataTemp.h;
180 memcpy(&u16Temp, dataTemp.spr, sizeof(U16));
181 ent_entdata[i].spr = letoh16(u16Temp);
182 memcpy(&u16Temp, dataTemp.sni, sizeof(U16));
183 ent_entdata[i].sni = letoh16(u16Temp);
184 ent_entdata[i].trig_w = dataTemp.trig_w;
185 ent_entdata[i].trig_h = dataTemp.trig_h;
186 ent_entdata[i].snd = dataTemp.snd;
187 }
188 return true;
189}
190
191/*
192 *
193 */
194static void unloadResourceEntdata()
195{
196 sysmem_pop(ent_entdata);
197 ent_entdata = NULL;
198 ent_nbr_entdata = 0;
199}
200
201/*
202 *
203 */
204static bool loadRawData(file_t fp, void ** buffer, const size_t size, size_t * count)
205{
206 U16 u16Temp;
207 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
208 {
209 return false;
210 }
211 *count = letoh16(u16Temp);
212
213 *buffer = sysmem_push((*count) * size);
214 if (!(*buffer))
215 {
216 return false;
217 }
218
219 if (sysfile_read(fp, *buffer, size, *count) != (int)(*count))
220 {
221 return false;
222 }
223 return true;
224}
225
226/*
227 *
228 */
229static void unloadRawData(void ** buffer, size_t * count)
230{
231 sysmem_pop(*buffer);
232 *buffer = NULL;
233 *count = 0;
234}
235
236/*
237 *
238 */
239static bool loadResourceMaps(file_t fp)
240{
241 size_t i;
242 U16 u16Temp;
243 resource_map_t dataTemp;
244
245 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
246 {
247 return false;
248 }
249 map_nbr_maps = letoh16(u16Temp);
250
251 map_maps = sysmem_push(map_nbr_maps * sizeof(*map_maps));
252 if (!map_maps)
253 {
254 return false;
255 }
256
257 for (i = 0; i < map_nbr_maps; ++i)
258 {
259#ifdef ENABLE_SOUND
260 sound_t **soundTemp;
261#endif
262 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
263 {
264 return false;
265 }
266 memcpy(&u16Temp, dataTemp.x, sizeof(U16));
267 map_maps[i].x = letoh16(u16Temp);
268 memcpy(&u16Temp, dataTemp.y, sizeof(U16));
269 map_maps[i].y = letoh16(u16Temp);
270 memcpy(&u16Temp, dataTemp.row, sizeof(U16));
271 map_maps[i].row = letoh16(u16Temp);
272 memcpy(&u16Temp, dataTemp.submap, sizeof(U16));
273 map_maps[i].submap = letoh16(u16Temp);
274#ifdef ENABLE_SOUND
275 memcpy(&u16Temp, dataTemp.tuneId, sizeof(U16));
276 if (!fromResourceIdToSound(letoh16(u16Temp), &soundTemp))
277 {
278 return false;
279 }
280 map_maps[i].tune = *soundTemp;
281#endif /* ENABLE_SOUND */
282 }
283 return true;
284}
285
286/*
287 *
288 */
289static void unloadResourceMaps()
290{
291 sysmem_pop(map_maps);
292 map_maps = NULL;
293 map_nbr_maps = 0;
294}
295
296/*
297 *
298 */
299static bool loadResourceSubmaps(file_t fp)
300{
301 size_t i;
302 U16 u16Temp;
303 resource_submap_t dataTemp;
304
305 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
306 {
307 return false;
308 }
309 map_nbr_submaps = letoh16(u16Temp);
310
311 map_submaps = sysmem_push(map_nbr_submaps * sizeof(*map_submaps));
312 if (!map_submaps)
313 {
314 return false;
315 }
316
317 for (i = 0; i < map_nbr_submaps; ++i)
318 {
319 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
320 {
321 return false;
322 }
323 memcpy(&u16Temp, dataTemp.page, sizeof(U16));
324 map_submaps[i].page = letoh16(u16Temp);
325 memcpy(&u16Temp, dataTemp.bnum, sizeof(U16));
326 map_submaps[i].bnum = letoh16(u16Temp);
327 memcpy(&u16Temp, dataTemp.connect, sizeof(U16));
328 map_submaps[i].connect = letoh16(u16Temp);
329 memcpy(&u16Temp, dataTemp.mark, sizeof(U16));
330 map_submaps[i].mark = letoh16(u16Temp);
331 }
332 return true;
333}
334
335/*
336 *
337 */
338static void unloadResourceSubmaps()
339{
340 sysmem_pop(map_submaps);
341 map_submaps = NULL;
342 map_nbr_submaps = 0;
343}
344
345/*
346 *
347 */
348static bool loadResourceImapsteps(file_t fp)
349{
350 size_t i;
351 U16 u16Temp;
352 resource_imapsteps_t dataTemp;
353
354 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
355 {
356 return false;
357 }
358 screen_nbr_imapstesps = letoh16(u16Temp);
359
360 screen_imapsteps = sysmem_push(screen_nbr_imapstesps * sizeof(*screen_imapsteps));
361 if (!screen_imapsteps)
362 {
363 return false;
364 }
365
366 for (i = 0; i < screen_nbr_imapstesps; ++i)
367 {
368 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
369 {
370 return false;
371 }
372 memcpy(&u16Temp, dataTemp.count, sizeof(U16));
373 screen_imapsteps[i].count = letoh16(u16Temp);
374 memcpy(&u16Temp, dataTemp.dx, sizeof(U16));
375 screen_imapsteps[i].dx = letoh16(u16Temp);
376 memcpy(&u16Temp, dataTemp.dy, sizeof(U16));
377 screen_imapsteps[i].dy = letoh16(u16Temp);
378 memcpy(&u16Temp, dataTemp.base, sizeof(U16));
379 screen_imapsteps[i].base = letoh16(u16Temp);
380 }
381 return true;
382}
383
384/*
385 *
386 */
387static void unloadResourceImapsteps()
388{
389 sysmem_pop(screen_imapsteps);
390 screen_imapsteps = NULL;
391 screen_nbr_imapstesps = 0;
392}
393
394/*
395 *
396 */
397static bool loadResourceImaptext(file_t fp)
398{
399 size_t i;
400 U16 u16Temp;
401
402 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
403 {
404 return false;
405 }
406 screen_nbr_imaptext = letoh16(u16Temp);
407
408 screen_imaptext = sysmem_push(screen_nbr_imaptext * sizeof(*screen_imaptext));
409 if (!screen_imapsteps)
410 {
411 return false;
412 }
413
414 for (i = 0; i < screen_nbr_imaptext; ++i)
415 {
416 if (!loadString(fp, (char **)(&(screen_imaptext[i])), 0xFE))
417 {
418 return false;
419 }
420 }
421 return true;
422}
423
424/*
425 *
426 */
427static void unloadResourceImaptext()
428{
429 int i;
430
431 for (i = screen_nbr_imaptext - 1; i >= 0; --i)
432 {
433 unloadString((char **)(&(screen_imaptext[i])));
434 }
435
436 sysmem_pop(screen_imaptext);
437 screen_imaptext = NULL;
438 screen_nbr_imaptext = 0;
439}
440
441/*
442 *
443 */
444static bool loadResourceHighScores(file_t fp)
445{
446 size_t i;
447 U16 u16Temp;
448 U32 u32Temp;
449 resource_hiscore_t dataTemp;
450
451 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
452 {
453 return false;
454 }
455 screen_nbr_hiscores = letoh16(u16Temp);
456
457 screen_highScores = sysmem_push(screen_nbr_hiscores * sizeof(*screen_highScores));
458 if (!screen_highScores)
459 {
460 return false;
461 }
462
463 for (i = 0; i < screen_nbr_hiscores; ++i)
464 {
465 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
466 {
467 return false;
468 }
469 memcpy(&u32Temp, dataTemp.score, sizeof(U32));
470 screen_highScores[i].score = letoh32(u32Temp);
471 memcpy(screen_highScores[i].name, dataTemp.name, HISCORE_NAME_SIZE);
472 }
473 return true;
474}
475
476/*
477 *
478 */
479static void unloadResourceHighScores()
480{
481 sysmem_pop(screen_highScores);
482 screen_highScores = NULL;
483 screen_nbr_hiscores = 0;
484}
485
486/*
487 *
488 */
489static bool loadResourceSpritesData(file_t fp)
490{
491 size_t i, j;
492 U16 u16Temp;
493
494 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
495 {
496 return false;
497 }
498 sprites_nbr_sprites = letoh16(u16Temp);
499
500 sprites_data = sysmem_push(sprites_nbr_sprites * sizeof(*sprites_data));
501 if (!sprites_data)
502 {
503 return false;
504 }
505
506#ifdef GFXST
507 for (i = 0; i < sprites_nbr_sprites; ++i)
508 {
509 for (j = 0; j < SPRITES_NBR_DATA; ++j)
510 {
511 U32 u32Temp;
512 if (sysfile_read(fp, &u32Temp, sizeof(u32Temp), 1) != 1)
513 {
514 return false;
515 }
516 sprites_data[i][j] = letoh32(u32Temp);
517 }
518 }
519#endif /* GFXST */
520
521#ifdef GFXPC
522 for (i = 0; i < sprites_nbr_sprites; ++i)
523 {
524 for (j = 0; j < SPRITES_NBR_COLS; ++j)
525 {
526 size_t k;
527 for (k = 0; k < SPRITES_NBR_ROWS; ++k)
528 {
529 resource_spriteX_t dataTemp;
530 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
531 {
532 return false;
533 }
534 memcpy(&u16Temp, dataTemp.mask, sizeof(U16));
535 sprites_data[i][j][k].mask = letoh16(u16Temp);
536 memcpy(&u16Temp, dataTemp.pict, sizeof(U16));
537 sprites_data[i][j][k].pict = letoh16(u16Temp);
538 }
539 }
540 }
541#endif /* GFXPC */
542
543 return true;
544}
545
546/*
547 *
548 */
549static void unloadResourceSpritesData()
550{
551 sysmem_pop(sprites_data);
552 sprites_data = NULL;
553 sprites_nbr_sprites = 0;
554}
555
556/*
557 *
558 */
559static bool loadResourceTilesData(file_t fp)
560{
561 size_t i, j, k;
562 U16 u16Temp;
563
564 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
565 {
566 return false;
567 }
568 tiles_nbr_banks = letoh16(u16Temp);
569
570 tiles_data = sysmem_push(tiles_nbr_banks * TILES_NBR_TILES * sizeof(*tiles_data));
571 if (!tiles_data)
572 {
573 return false;
574 }
575
576 for (i = 0; i < tiles_nbr_banks ; ++i)
577 {
578 for (j = 0; j < TILES_NBR_TILES; ++j)
579 {
580 for (k = 0; k < TILES_NBR_LINES ; ++k)
581 {
582#ifdef GFXPC
583 if (sysfile_read(fp, &u16Temp, sizeof(u16Temp), 1) != 1)
584 {
585 return false;
586 }
587 tiles_data[i * TILES_NBR_TILES + j][k] = letoh16(u16Temp);
588#endif /* GFXPC */
589#ifdef GFXST
590 U32 u32Temp;
591 if (sysfile_read(fp, &u32Temp, sizeof(u32Temp), 1) != 1)
592 {
593 return false;
594 }
595 tiles_data[i * TILES_NBR_TILES + j][k] = letoh32(u32Temp);
596#endif /* GFXST */
597 }
598 }
599 }
600 return true;
601}
602
603/*
604 *
605 */
606static void unloadResourceTilesData()
607{
608 sysmem_pop(tiles_data);
609 tiles_data = NULL;
610 tiles_nbr_banks = 0;
611}
612
613/*
614 *
615 */
616static bool loadImage(file_t fp, img_t ** image)
617{
618 U16 u16Temp;
619 size_t pixelCount, colorCount;
620 resource_pic_t dataTemp;
621 img_t * imgTemp;
622 void * vp;
623 bool success;
624
625 imgTemp = sysmem_push(sizeof(*imgTemp));
626 *image = imgTemp;
627 if (!imgTemp)
628 {
629 return false;
630 }
631
632 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
633 {
634 return false;
635 }
636 memcpy(&u16Temp, dataTemp.width, sizeof(U16));
637 imgTemp->width = letoh16(u16Temp);
638 memcpy(&u16Temp, dataTemp.height, sizeof(U16));
639 imgTemp->height = letoh16(u16Temp);
640 memcpy(&u16Temp, dataTemp.xPos, sizeof(U16));
641 imgTemp->xPos = letoh16(u16Temp);
642 memcpy(&u16Temp, dataTemp.yPos, sizeof(U16));
643 imgTemp->yPos = letoh16(u16Temp);
644
645 vp = imgTemp->colors;
646 success = loadRawData(fp, &vp, sizeof(*imgTemp->colors), &colorCount);
647 imgTemp->ncolors = colorCount;
648 imgTemp->colors = vp;
649 if (!success)
650 {
651 return false;
652 }
653
654 pixelCount = (imgTemp->width * imgTemp->height); /*we use 8b per pixel*/
655
656 imgTemp->pixels = sysmem_push(pixelCount * sizeof(U8));
657 if (!imgTemp->pixels)
658 {
659 return false;
660 }
661
662 if (sysfile_read(fp, imgTemp->pixels, sizeof(U8), pixelCount) != (int)pixelCount)
663 {
664 return false;
665 }
666 return true;
667}
668
669/*
670 *
671 */
672static void unloadImage(img_t ** image)
673{
674 if (*image)
675 {
676 sysmem_pop((*image)->pixels);
677 sysmem_pop((*image)->colors);
678 }
679 sysmem_pop(*image);
680 *image = NULL;
681}
682
683/*
684 *
685 */
686#ifdef GFXST
687static bool loadPicture(file_t fp, pic_t ** picture)
688{
689 U16 u16Temp;
690 size_t i, pixelWords32b;
691 resource_pic_t dataTemp;
692 pic_t * picTemp;
693
694 picTemp = sysmem_push(sizeof(*picTemp));
695 *picture = picTemp;
696 if (!picTemp)
697 {
698 return false;
699 }
700
701 if (sysfile_read(fp, &dataTemp, sizeof(dataTemp), 1) != 1)
702 {
703 return false;
704 }
705 memcpy(&u16Temp, dataTemp.width, sizeof(U16));
706 picTemp->width = letoh16(u16Temp);
707 memcpy(&u16Temp, dataTemp.height, sizeof(U16));
708 picTemp->height = letoh16(u16Temp);
709 memcpy(&u16Temp, dataTemp.xPos, sizeof(U16));
710 picTemp->xPos = letoh16(u16Temp);
711 memcpy(&u16Temp, dataTemp.yPos, sizeof(U16));
712 picTemp->yPos = letoh16(u16Temp);
713
714 pixelWords32b = (picTemp->width * picTemp->height) / 8; /*we use 4b per pixel*/
715
716 picTemp->pixels = sysmem_push(pixelWords32b * sizeof(U32));
717 if (!picTemp->pixels)
718 {
719 return false;
720 }
721
722 for (i = 0; i < pixelWords32b; ++i)
723 {
724 U32 u32Temp;
725 if (sysfile_read(fp, &u32Temp, sizeof(u32Temp), 1) != 1)
726 {
727 return false;
728 }
729 picTemp->pixels[i] = letoh32(u32Temp);
730 }
731 return true;
732}
733
734/*
735 *
736 */
737static void unloadPicture(pic_t ** picture)
738{
739 if (*picture)
740 {
741 sysmem_pop((*picture)->pixels);
742 }
743 sysmem_pop(*picture);
744 *picture = NULL;
745}
746#endif /* GFXST */
747
748#ifdef ENABLE_SOUND
749
750/*
751 *
752 */
753static bool fromResourceIdToSound(const unsigned id, sound_t *** sound)
754{
755 switch (id)
756 {
757 case Resource_SOUNDBOMBSHHT: *sound = &soundBombshht; return true;
758 case Resource_SOUNDBONUS: *sound = &soundBonus; return true;
759 case Resource_SOUNDBOX: *sound = &soundBox; return true;
760 case Resource_SOUNDBULLET: *sound = &soundBullet; return true;
761 case Resource_SOUNDCRAWL: *sound = &soundCrawl; return true;
762 case Resource_SOUNDDIE: *sound = &soundDie; return true;
763 case Resource_SOUNDENTITY0: *sound = &(soundEntity[0]); return true;
764 case Resource_SOUNDENTITY1: *sound = &(soundEntity[1]); return true;
765 case Resource_SOUNDENTITY2: *sound = &(soundEntity[2]); return true;
766 case Resource_SOUNDENTITY3: *sound = &(soundEntity[3]); return true;
767 case Resource_SOUNDENTITY4: *sound = &(soundEntity[4]); return true;
768 case Resource_SOUNDENTITY5: *sound = &(soundEntity[5]); return true;
769 case Resource_SOUNDENTITY6: *sound = &(soundEntity[6]); return true;
770 case Resource_SOUNDENTITY7: *sound = &(soundEntity[7]); return true;
771 case Resource_SOUNDENTITY8: *sound = &(soundEntity[8]); return true;
772 case Resource_SOUNDEXPLODE: *sound = &soundExplode; return true;
773 case Resource_SOUNDGAMEOVER: *sound = &soundGameover; return true;
774 case Resource_SOUNDJUMP: *sound = &soundJump; return true;
775 case Resource_SOUNDPAD: *sound = &soundPad; return true;
776 case Resource_SOUNDSBONUS1: *sound = &soundSbonus1; return true;
777 case Resource_SOUNDSBONUS2: *sound = &soundSbonus2; return true;
778 case Resource_SOUNDSTICK: *sound = &soundStick; return true;
779 case Resource_SOUNDTUNE0: *sound = &soundTune0; return true;
780 case Resource_SOUNDTUNE1: *sound = &soundTune1; return true;
781 case Resource_SOUNDTUNE2: *sound = &soundTune2; return true;
782 case Resource_SOUNDTUNE3: *sound = &soundTune3; return true;
783 case Resource_SOUNDTUNE4: *sound = &soundTune4; return true;
784 case Resource_SOUNDTUNE5: *sound = &soundTune5; return true;
785 case Resource_SOUNDWALK: *sound = &soundWalk; return true;
786 default:
787 {
788 sys_error("(resources) no associated sound found for ID %d", id);
789 return false;
790 }
791 }
792}
793
794/*
795 *
796 */
797static bool loadSound(const unsigned id)
798{
799 sound_t ** sound;
800 file_t fp;
801 wave_header_t header;
802 U16 u16Temp;
803 U32 u32Temp;
804 int bytesRead;
805 bool isHeaderValid;
806
807 if (!fromResourceIdToSound(id, &sound))
808 {
809 return false;
810 }
811
812 *sound = sysmem_push(sizeof(**sound));
813 if (!*sound)
814 {
815 return false;
816 }
817
818 (*sound)->buf = NULL;
819 (*sound)->dispose = true; /* sounds are "fire and forget" by default */
820
821 (*sound)->name = u_strdup(resourceFiles[id]);
822 if (!(*sound)->name)
823 {
824 return false;
825 }
826
827 fp = sysfile_open(resourceFiles[id]);
828 if (!fp)
829 {
830 sys_error("(resources) unable to open \"%s\"", resourceFiles[id]);
831 return false;
832 }
833
834 bytesRead = sysfile_read(fp, &header, sizeof(header), 1);
835 sysfile_close(fp);
836 if (bytesRead != 1)
837 {
838 sys_error("(resources) unable to read WAVE header from \"%s\"", resourceFiles[id]);
839 return false;
840 }
841
842 isHeaderValid = false;
843 for (;;)
844 {
845 if (memcmp(header.riffChunkId, "RIFF", 4) ||
846 memcmp(header.riffType, "WAVE", 4) ||
847 memcmp(header.formatChunkId, "fmt ", 4) ||
848 memcmp(header.dataChunkId, "data", 4))
849 {
850 break;
851 }
852 memcpy(&u16Temp, header.audioFormat, sizeof(u16Temp));
853 if (letoh16(u16Temp) != Wave_AUDIO_FORMAT)
854 {
855 break;
856 }
857 memcpy(&u16Temp, header.channelCount, sizeof(u16Temp));
858 if (letoh16(u16Temp) != Wave_CHANNEL_COUNT)
859 {
860 break;
861 }
862 memcpy(&u32Temp, header.sampleRate, sizeof(u32Temp));
863 if (letoh32(u32Temp) != Wave_SAMPLE_RATE)
864 {
865 isHeaderValid = false;
866 break;
867 }
868 memcpy(&u16Temp, header.bitsPerSample, sizeof(u16Temp));
869 if (letoh16(u16Temp) != Wave_BITS_PER_SAMPLE)
870 {
871 isHeaderValid = false;
872 break;
873 }
874
875 memcpy(&u32Temp, header.dataChunkSize, sizeof(u32Temp));
876 (*sound)->len = letoh32(u32Temp);
877
878 isHeaderValid = true;
879 break;
880 }
881 if (!isHeaderValid)
882 {
883 sys_error("(resources) incompatible WAVE header for \"%s\"", resourceFiles[id]);
884 return false;
885 }
886 return true;
887}
888
889/*
890 *
891 */
892static void unloadSound(const unsigned id)
893{
894 sound_t ** sound;
895
896 if (!fromResourceIdToSound(id, &sound))
897 {
898 return;
899 }
900
901 if (!*sound)
902 {
903 return;
904 }
905
906 sysmem_pop((*sound)->name);
907 sysmem_pop(*sound);
908 *sound = NULL;
909}
910#endif /* ENABLE_SOUND */
911
912
913/*
914 *
915 */
916static bool readHeader(file_t fp, const unsigned id)
917{
918 resource_header_t header;
919 U16 u16Temp;
920
921 if (sysfile_read(fp, &header, sizeof(header), 1) != 1)
922 {
923 sys_error("(resources) unable to read header from \"%s\"", resourceFiles[id]);
924 return false;
925 }
926
927 if (memcmp(header.magic, resource_magic, sizeof(header.magic)) != 0)
928 {
929 sys_error("(resources) wrong header for \"%s\"", resourceFiles[id]);
930 return false;
931 }
932
933 memcpy(&u16Temp, header.version, sizeof(u16Temp));
934 u16Temp = htole16(u16Temp);
935 if (u16Temp != DATA_VERSION)
936 {
937 sys_error("(resources) incompatible version for \"%s\"", resourceFiles[id]);
938 return false;
939 }
940
941 memcpy(&u16Temp, header.resourceId, sizeof(u16Temp));
942 u16Temp = htole16(u16Temp);
943 if (u16Temp != id)
944 {
945 sys_error("(resources) mismatching ID for \"%s\"", resourceFiles[id]);
946 return false;
947 }
948 return true;
949}
950
951/*
952 *
953 */
954static bool checkCrc32(const unsigned id)
955{
956 int bytesRead;
957 U8 tempBuffer[1024];
958 U32 expectedCrc32, calculatedCrc32 = MZ_CRC32_INIT;
959
960 file_t fp = sysfile_open(resourceFiles[id]);
961 if (fp == NULL)
962 {
963 sys_error("(resources) unable to open \"%s\"", resourceFiles[id]);
964 return false;
965 }
966
967 bytesRead = sysfile_read(fp, tempBuffer, sizeof(U32), 1); /* prepare beginning of buffer for the following loop */
968 if (bytesRead != 1)
969 {
970 sys_error("(resources) not enough data for \"%s\"", resourceFiles[id]);
971 sysfile_close(fp);
972 return false;
973 }
974 do
975 {
976 bytesRead = sysfile_read(fp, tempBuffer + sizeof(U32), sizeof(U8), sizeof(tempBuffer) - sizeof(U32));
977
978 calculatedCrc32 = mz_crc32(calculatedCrc32, tempBuffer, bytesRead);
979
980 memcpy(tempBuffer, tempBuffer + bytesRead, sizeof(U32));
981 } while (bytesRead == sizeof(tempBuffer) - sizeof(U32));
982
983 sysfile_close(fp);
984
985 memcpy(&expectedCrc32, tempBuffer, sizeof(U32));
986 expectedCrc32 = letoh32(expectedCrc32);
987 if (expectedCrc32 != calculatedCrc32)
988 {
989 sys_error("(resources) crc check failed for \"%s\"", resourceFiles[id]);
990 return false;
991 }
992 return true;
993}
994
995/*
996 *
997 */
998static bool readFile(const unsigned id)
999{
1000 bool success;
1001 file_t fp;
1002 void * vp;
1003
1004 switch (id)
1005 {
1006#ifndef GFXST
1007 case Resource_PICHAF: /* fallthrough */
1008 case Resource_PICCONGRATS: /* fallthrough */
1009 case Resource_PICSPLASH: return true;
1010#endif /* ndef GFXST */
1011#ifndef GFXPC
1012 case Resource_IMAINHOFT: /* fallthrough */
1013 case Resource_IMAINRDT: /* fallthrough */
1014 case Resource_IMAINCDC: /* fallthrough */
1015 case Resource_SCREENCONGRATS: return true;
1016#endif /* ndef GFXPC */
1017 default: break;
1018 }
1019
1020 if (resourceFiles[id] == NULL)
1021 {
1022 sys_error("(resources) resource ID %d not available", id);
1023 return false;
1024 }
1025
1026 if (!checkCrc32(id))
1027 {
1028 return false;
1029 }
1030
1031 fp = sysfile_open(resourceFiles[id]);
1032 if (fp == NULL)
1033 {
1034 if (id == Resource_FILELIST)
1035 sys_error("Please install resource files!");
1036 else
1037 sys_error("(resources) unable to open \"%s\"", resourceFiles[id]);
1038 return false;
1039 }
1040
1041 success = readHeader(fp, id);
1042
1043 if (success)
1044 {
1045 switch (id)
1046 {
1047 case Resource_FILELIST: success = loadResourceFilelist(fp); break;
1048 case Resource_PALETTE:
1049 {
1050 vp = game_colors;
1051 success = loadRawData(fp, &vp, sizeof(*game_colors), &game_color_count);
1052 game_colors = vp;
1053 break;
1054 }
1055 case Resource_ENTDATA: success = loadResourceEntdata(fp); break;
1056 case Resource_SPRSEQ:
1057 {
1058 vp = ent_sprseq;
1059 success = loadRawData(fp, &vp, sizeof(*ent_sprseq), &ent_nbr_sprseq);
1060 ent_sprseq = vp;
1061 break;
1062 }
1063 case Resource_MVSTEP:
1064 {
1065 vp = ent_mvstep;
1066 success = loadRawData(fp, &vp, sizeof(*ent_mvstep), &ent_nbr_mvstep);
1067 ent_mvstep = vp;
1068 break;
1069 }
1070 case Resource_MAPS: success = loadResourceMaps(fp); break;
1071 case Resource_SUBMAPS: success = loadResourceSubmaps(fp); break;
1072 case Resource_CONNECT:
1073 {
1074 vp = map_connect;
1075 success = loadRawData(fp, &vp, sizeof(*map_connect), &map_nbr_connect);
1076 map_connect = vp;
1077 break;
1078 }
1079 case Resource_BNUMS:
1080 {
1081 vp = map_bnums;
1082 success = loadRawData(fp, &vp, sizeof(*map_bnums), &map_nbr_bnums);
1083 map_bnums = vp;
1084 break;
1085 }
1086 case Resource_BLOCKS:
1087 {
1088 vp = map_blocks;
1089 success = loadRawData(fp, &vp, sizeof(*map_blocks), &map_nbr_blocks);
1090 map_blocks = vp;
1091 break;
1092 }
1093 case Resource_MARKS:
1094 {
1095 vp = map_marks;
1096 success = loadRawData(fp, &vp, sizeof(*map_marks), &map_nbr_marks);
1097 map_marks = vp;
1098 break;
1099 }
1100 case Resource_EFLGC:
1101 {
1102 vp = map_eflg_c;
1103 success = loadRawData(fp, &vp, sizeof(*map_eflg_c), &map_nbr_eflgc);
1104 map_eflg_c = vp;
1105 break;
1106 }
1107 case Resource_IMAPSL:
1108 {
1109 vp = screen_imapsl;
1110 success = loadRawData(fp, &vp, sizeof(*screen_imapsl), &screen_nbr_imapsl);
1111 screen_imapsl = vp;
1112 break;
1113 }
1114 case Resource_IMAPSTEPS: success = loadResourceImapsteps(fp); break;
1115 case Resource_IMAPSOFS:
1116 {
1117 vp = screen_imapsofs;
1118 success = loadRawData(fp, &vp, sizeof(*screen_imapsofs), &screen_nbr_imapsofs);
1119 screen_imapsofs = vp;
1120 break;
1121 }
1122 case Resource_IMAPTEXT: success = loadResourceImaptext(fp); break;
1123 case Resource_GAMEOVERTXT: success = loadString(fp, (char **)(&screen_gameovertxt), 0xFE); break;
1124 case Resource_PAUSEDTXT: success = loadString(fp, (char **)(&screen_pausedtxt), 0xFE); break;
1125 case Resource_SPRITESDATA: success = loadResourceSpritesData(fp); break;
1126 case Resource_TILESDATA: success = loadResourceTilesData(fp); break;
1127 case Resource_HIGHSCORES: success = loadResourceHighScores(fp); break;
1128 case Resource_IMGSPLASH: success = loadImage(fp, &img_splash); break;
1129#ifdef GFXST
1130 case Resource_PICHAF: success = loadPicture(fp, &pic_haf); break;
1131 case Resource_PICCONGRATS: success = loadPicture(fp, &pic_congrats); break;
1132 case Resource_PICSPLASH: success = loadPicture(fp, &pic_splash); break;
1133#endif /* GFXST */
1134#ifdef GFXPC
1135 case Resource_IMAINHOFT: success = loadString(fp, (char **)(&screen_imainhoft), 0xFE); break;
1136 case Resource_IMAINRDT: success = loadString(fp, (char **)(&screen_imainrdt), 0xFE); break;
1137 case Resource_IMAINCDC: success = loadString(fp, (char **)(&screen_imaincdc), 0xFE); break;
1138 case Resource_SCREENCONGRATS: success = loadString(fp, (char **)(&screen_congrats), 0xFE); break;
1139#endif /* GFXPC */
1140 default: success = false; break;
1141 }
1142 }
1143
1144 if (!success)
1145 {
1146 sys_error("(resources) error when parsing \"%s\"", resourceFiles[id]);
1147 }
1148
1149 sysfile_close(fp);
1150 return success;
1151}
1152
1153/*
1154 *
1155 */
1156bool resources_load()
1157{
1158 bool success;
1159 unsigned id;
1160
1161 /* note: loading order is important: file list first, then sounds, then the rest */
1162
1163 success = readFile(Resource_FILELIST);
1164
1165#ifdef ENABLE_SOUND
1166 for (id = Resource_SOUNDBOMBSHHT; (id <= Resource_SOUNDWALK) && success; ++id)
1167 {
1168 success = loadSound(id);
1169 }
1170#endif /* ENABLE_SOUND */
1171
1172 for (id = Resource_PALETTE; (id <= Resource_SCREENCONGRATS) && success; ++id)
1173 {
1174 success = readFile(id);
1175 }
1176 return success;
1177}
1178
1179/*
1180 *
1181 */
1182void resources_unload()
1183{
1184 int id;
1185 void * vp;
1186
1187 for (id = Resource_SCREENCONGRATS; id >= Resource_PALETTE; --id)
1188 {
1189 switch (id)
1190 {
1191 case Resource_PALETTE:
1192 {
1193 vp = game_colors;
1194 unloadRawData(&vp, &game_color_count);
1195 game_colors = vp;
1196 break;
1197 }
1198 case Resource_ENTDATA: unloadResourceEntdata(); break;
1199 case Resource_SPRSEQ:
1200 {
1201 vp = ent_sprseq;
1202 unloadRawData(&vp, &ent_nbr_sprseq);
1203 ent_sprseq = vp;
1204 break;
1205 }
1206 case Resource_MVSTEP:
1207 {
1208 vp = ent_mvstep;
1209 unloadRawData(&vp, &ent_nbr_mvstep);
1210 ent_mvstep = vp;
1211 break;
1212 }
1213 case Resource_MAPS: unloadResourceMaps(); break;
1214 case Resource_SUBMAPS: unloadResourceSubmaps(); break;
1215 case Resource_CONNECT:
1216 {
1217 vp = map_connect;
1218 unloadRawData(&vp, &map_nbr_connect);
1219 map_connect = vp;
1220 break;
1221 }
1222 case Resource_BNUMS:
1223 {
1224 vp = map_bnums;
1225 unloadRawData(&vp, &map_nbr_bnums);
1226 map_bnums = vp;
1227 break;
1228 }
1229 case Resource_BLOCKS:
1230 {
1231 vp = map_blocks;
1232 unloadRawData(&vp, &map_nbr_blocks);
1233 map_blocks = vp;
1234 break;
1235 }
1236 case Resource_MARKS:
1237 {
1238 vp = map_marks;
1239 unloadRawData(&vp, &map_nbr_marks);
1240 map_marks = vp;
1241 break;
1242 }
1243 case Resource_EFLGC:
1244 {
1245 vp = map_eflg_c;
1246 unloadRawData(&vp, &map_nbr_eflgc);
1247 map_eflg_c = vp;
1248 break;
1249 }
1250 case Resource_IMAPSL:
1251 {
1252 vp = screen_imapsl;
1253 unloadRawData(&vp, &screen_nbr_imapsl);
1254 screen_imapsl = vp;
1255 break;
1256 }
1257 case Resource_IMAPSTEPS: unloadResourceImapsteps(); break;
1258 case Resource_IMAPSOFS:
1259 {
1260 vp = screen_imapsofs;
1261 unloadRawData(&vp, &screen_nbr_imapsofs);
1262 screen_imapsofs = vp;
1263 break;
1264 }
1265 case Resource_IMAPTEXT: unloadResourceImaptext(); break;
1266 case Resource_GAMEOVERTXT: unloadString((char **)(&screen_gameovertxt)); break;
1267 case Resource_PAUSEDTXT: unloadString((char **)(&screen_pausedtxt)); break;
1268 case Resource_SPRITESDATA: unloadResourceSpritesData(); break;
1269 case Resource_TILESDATA: unloadResourceTilesData(); break;
1270 case Resource_HIGHSCORES: unloadResourceHighScores(); break;
1271 case Resource_IMGSPLASH: unloadImage(&img_splash); break;
1272#ifdef GFXST
1273 case Resource_PICHAF: unloadPicture(&pic_haf); break;
1274 case Resource_PICCONGRATS: unloadPicture(&pic_congrats); break;
1275 case Resource_PICSPLASH: unloadPicture(&pic_splash); break;
1276#endif /* GFXST */
1277#ifdef GFXPC
1278 case Resource_IMAINHOFT: unloadString((char **)(&screen_imainhoft)); break;
1279 case Resource_IMAINRDT: unloadString((char **)(&screen_imainrdt)); break;
1280 case Resource_IMAINCDC: unloadString((char **)(&screen_imaincdc)); break;
1281 case Resource_SCREENCONGRATS: unloadString((char **)(&screen_congrats)); break;
1282#endif /* GFXPC */
1283 default: break;
1284 }
1285 }
1286
1287#ifdef ENABLE_SOUND
1288 for (id = Resource_SOUNDWALK; id >= Resource_SOUNDBOMBSHHT; --id)
1289 {
1290 unloadSound(id);
1291 }
1292#endif /* ENABLE_SOUND */
1293
1294 unloadResourceFilelist();
1295}
1296
1297/* eof */
diff --git a/apps/plugins/xrick/resources.h b/apps/plugins/xrick/resources.h
new file mode 100644
index 0000000000..e919ed081a
--- /dev/null
+++ b/apps/plugins/xrick/resources.h
@@ -0,0 +1,165 @@
1/*
2 * xrick/resources.h
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#ifndef _RESOURCES_H
15#define _RESOURCES_H
16
17#include "xrick/config.h"
18#include "xrick/screens.h"
19#include "xrick/system/basic_types.h"
20
21/*
22 * All data is assumed to be Little Endian
23 */
24typedef struct
25{
26 U8 magic[4];
27 U8 version[2];
28 U8 resourceId[2];
29} resource_header_t;
30
31typedef struct
32{
33 U8 w;
34 U8 h;
35 U8 spr[2];
36 U8 sni[2];
37 U8 trig_w;
38 U8 trig_h;
39 U8 snd;
40} resource_entdata_t;
41
42typedef struct
43{
44 U8 x[2];
45 U8 y[2];
46 U8 row[2];
47 U8 submap[2];
48 U8 tuneId[2];
49} resource_map_t;
50
51typedef struct
52{
53 U8 page[2];
54 U8 bnum[2];
55 U8 connect[2];
56 U8 mark[2];
57} resource_submap_t;
58
59typedef struct {
60 U8 count[2];
61 U8 dx[2];
62 U8 dy[2];
63 U8 base[2];
64} resource_imapsteps_t;
65
66typedef struct {
67 U8 width[2];
68 U8 height[2];
69 U8 xPos[2];
70 U8 yPos[2];
71} resource_pic_t;
72
73typedef struct {
74 U8 score[4];
75 U8 name[HISCORE_NAME_SIZE];
76} resource_hiscore_t;
77
78#ifdef GFXPC
79typedef struct {
80 U8 mask[2];
81 U8 pict[2];
82} resource_spriteX_t;
83#endif /* GFXPC */
84
85extern const U8 resource_magic[4];
86
87enum
88{
89 DATA_VERSION = 3,
90
91 /* "bootstrap" file */
92 Resource_FILELIST = 0,
93
94 /* graphics, misc, texts */
95 Resource_PALETTE,
96 Resource_ENTDATA,
97 Resource_SPRSEQ,
98 Resource_MVSTEP,
99 Resource_MAPS,
100 Resource_SUBMAPS,
101 Resource_CONNECT,
102 Resource_BNUMS,
103 Resource_BLOCKS,
104 Resource_MARKS,
105 Resource_EFLGC,
106 Resource_IMAPSL,
107 Resource_IMAPSTEPS,
108 Resource_IMAPSOFS,
109 Resource_IMAPTEXT,
110 Resource_GAMEOVERTXT,
111 Resource_PAUSEDTXT,
112 Resource_SPRITESDATA,
113 Resource_TILESDATA,
114 Resource_HIGHSCORES,
115 Resource_IMGSPLASH,
116 Resource_PICHAF, /* ST version only */
117 Resource_PICCONGRATS, /* ST version only */
118 Resource_PICSPLASH, /* ST version only */
119 Resource_IMAINHOFT, /* PC version only */
120 Resource_IMAINRDT, /* PC version only */
121 Resource_IMAINCDC, /* PC version only */
122 Resource_SCREENCONGRATS, /* PC version only */
123
124 /* sounds */
125 Resource_SOUNDBOMBSHHT,
126 Resource_SOUNDBONUS,
127 Resource_SOUNDBOX,
128 Resource_SOUNDBULLET,
129 Resource_SOUNDCRAWL,
130 Resource_SOUNDDIE,
131 Resource_SOUNDENTITY0,
132 Resource_SOUNDENTITY1,
133 Resource_SOUNDENTITY2,
134 Resource_SOUNDENTITY3,
135 Resource_SOUNDENTITY4,
136 Resource_SOUNDENTITY5,
137 Resource_SOUNDENTITY6,
138 Resource_SOUNDENTITY7,
139 Resource_SOUNDENTITY8,
140 Resource_SOUNDEXPLODE,
141 Resource_SOUNDGAMEOVER,
142 Resource_SOUNDJUMP,
143 Resource_SOUNDPAD,
144 Resource_SOUNDSBONUS1,
145 Resource_SOUNDSBONUS2,
146 Resource_SOUNDSTICK,
147 Resource_SOUNDTUNE0,
148 Resource_SOUNDTUNE1,
149 Resource_SOUNDTUNE2,
150 Resource_SOUNDTUNE3,
151 Resource_SOUNDTUNE4,
152 Resource_SOUNDTUNE5,
153 Resource_SOUNDWALK,
154
155 Resource_MAX_COUNT,
156};
157
158#define BOOTSTRAP_RESOURCE_NAME "filelist.dat"
159
160bool resources_load(void);
161void resources_unload(void);
162
163#endif /* ndef _RESOURCES_H */
164
165/* eof */
diff --git a/apps/plugins/xrick/scr_gameover.c b/apps/plugins/xrick/scr_gameover.c
new file mode 100644
index 0000000000..7467ce3cbe
--- /dev/null
+++ b/apps/plugins/xrick/scr_gameover.c
@@ -0,0 +1,92 @@
1/*
2 * xrick/scr_gameover.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/draw.h"
20#include "xrick/control.h"
21#include "xrick/system/system.h"
22
23/*
24 * Display the game over screen
25 *
26 * return: SCREEN_RUNNING, SCREEN_DONE, SCREEN_EXIT
27 */
28U8
29screen_gameover(void)
30{
31 static U8 seq = 0;
32 static U8 period = 0;
33#ifdef GFXST
34 static U32 tm = 0;
35#endif
36 if (seq == 0) {
37 draw_tilesBank = 0;
38 seq = 1;
39 period = game_period; /* save period, */
40 game_period = 50; /* and use our own */
41#ifdef ENABLE_SOUND
42 game_setmusic(soundGameover, 1);
43#endif
44 }
45
46 switch (seq) {
47 case 1: /* display banner */
48#ifdef GFXST
49 sysvid_clear();
50 tm = sys_gettime();
51#endif
52 draw_tllst = screen_gameovertxt;
53 draw_setfb(120, 80);
54#ifdef GFXPC
55 draw_filter = 0xAAAA;
56#endif
57 draw_tilesList();
58 draw_drawStatus();
59
60 game_rects = &draw_SCREENRECT;
61 seq = 2;
62 break;
63
64 case 2: /* wait for key pressed */
65 if (control_test(Control_FIRE))
66 seq = 3;
67#ifdef GFXST
68 else if (sys_gettime() - tm > SCREEN_TIMEOUT)
69 seq = 4;
70#endif
71 break;
72
73 case 3: /* wait for key released */
74 if (!(control_test(Control_FIRE)))
75 seq = 4;
76 break;
77 }
78
79 if (control_test(Control_EXIT)) /* check for exit request */
80 return SCREEN_EXIT;
81
82 if (seq == 4) { /* we're done */
83 sysvid_clear();
84 seq = 0;
85 game_period = period;
86 return SCREEN_DONE;
87 }
88
89 return SCREEN_RUNNING;
90}
91
92/* eof */
diff --git a/apps/plugins/xrick/scr_getname.c b/apps/plugins/xrick/scr_getname.c
new file mode 100644
index 0000000000..eb1afffde9
--- /dev/null
+++ b/apps/plugins/xrick/scr_getname.c
@@ -0,0 +1,290 @@
1/*
2 * xrick/scr_getname.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/draw.h"
20#include "xrick/control.h"
21#include "xrick/data/pics.h"
22#include "xrick/system/system.h"
23
24/*
25 * local vars
26 */
27static U8 seq = 0;
28static U8 x, y, p;
29static U8 player_name[HISCORE_NAME_SIZE];
30
31#define TILE_POINTER '\072'
32#define TILE_CURSOR '\073'
33#define TOPLEFT_X 116
34#define TOPLEFT_Y 64
35#define NAMEPOS_X 120
36#define NAMEPOS_Y 160
37#define AUTOREPEAT_TMOUT 100
38
39
40/*
41 * prototypes
42 */
43static void pointer_show(bool);
44static void name_update(void);
45static void name_draw(void);
46
47
48/*
49 * Get name
50 *
51 * return: 0 while running, 1 when finished.
52 */
53U8
54screen_getname(void)
55{
56 static U32 tm = 0;
57 U8 i, j;
58
59 if (seq == 0)
60 {
61 /* figure out if this is a high score */
62 if (game_score < screen_highScores[screen_nbr_hiscores - 1].score)
63 return SCREEN_DONE;
64
65 /* prepare */
66 draw_tilesBank = 0;
67#ifdef GFXPC
68 draw_filter = 0xffff;
69#endif
70 for (i = 0; i < HISCORE_NAME_SIZE; i++)
71 {
72 player_name[i] = '@';
73 }
74 x = 5, y = 4, p = 0;
75 game_rects = &draw_SCREENRECT;
76 seq = 1;
77 }
78
79 switch (seq)
80 {
81 case 1: /* prepare screen */
82 {
83 sysvid_clear();
84#ifdef GFXPC
85 draw_setfb(32, 8);
86 draw_filter = 0xaaaa; /* red */
87 draw_tilesListImm(screen_congrats);
88#endif
89#ifdef GFXST
90 draw_pic(pic_congrats);
91#endif
92 draw_setfb(72, 40);
93#ifdef GFXPC
94 draw_filter = 0xffff; /* yellow */
95#endif
96 draw_tilesListImm((U8 *)"PLEASE@ENTER@YOUR@NAME\376");
97#ifdef GFXPC
98 draw_filter = 0x5555; /* green */
99#endif
100 for (i = 0; i < 6; i++)
101 {
102 for (j = 0; j < 4; j++)
103 {
104 draw_setfb(TOPLEFT_X + i * 8 * 2, TOPLEFT_Y + j * 8 * 2);
105 draw_tile('A' + i + j * 6);
106 }
107 }
108 draw_setfb(TOPLEFT_X, TOPLEFT_Y + 64);
109#ifdef GFXST
110 draw_tilesListImm((U8 *)"Y@Z@.@@@\074\373\374\375\376");
111#endif
112#ifdef GFXPC
113 draw_tilesListImm((U8 *)"Y@Z@.@@@\074@\075@\376");
114#endif
115 name_draw();
116 pointer_show(true);
117 seq = 2;
118 break;
119 }
120 case 2: /* wait for key pressed */
121 {
122 if (control_test(Control_FIRE))
123 seq = 3;
124 if (control_test(Control_UP)) {
125 if (y > 0) {
126 pointer_show(false);
127 y--;
128 pointer_show(true);
129 tm = sys_gettime();
130 }
131 seq = 4;
132 }
133 if (control_test(Control_DOWN)) {
134 if (y < 4) {
135 pointer_show(false);
136 y++;
137 pointer_show(true);
138 tm = sys_gettime();
139 }
140 seq = 5;
141 }
142 if (control_test(Control_LEFT)) {
143 if (x > 0) {
144 pointer_show(false);
145 x--;
146 pointer_show(true);
147 tm = sys_gettime();
148 }
149 seq = 6;
150 }
151 if (control_test(Control_RIGHT)) {
152 if (x < 5) {
153 pointer_show(false);
154 x++;
155 pointer_show(true);
156 tm = sys_gettime();
157 }
158 seq = 7;
159 }
160 break;
161 }
162 case 3: /* wait for FIRE released */
163 {
164 if (!(control_test(Control_FIRE)))
165 {
166 if (x == 5 && y == 4)
167 { /* end */
168 i = 0;
169 while (game_score < screen_highScores[i].score) i++;
170 j = 7;
171 while (j > i)
172 {
173 screen_highScores[j].score = screen_highScores[j - 1].score;
174 for (x = 0; x < HISCORE_NAME_SIZE; x++)
175 {
176 screen_highScores[j].name[x] = screen_highScores[j - 1].name[x];
177 }
178 j--;
179 }
180 screen_highScores[i].score = game_score;
181 for (x = 0; x < HISCORE_NAME_SIZE; x++)
182 {
183 screen_highScores[i].name[x] = player_name[x];
184 }
185 seq = 99;
186 }
187 else
188 {
189 name_update();
190 name_draw();
191 seq = 2;
192 }
193 }
194 break;
195 }
196 case 4: /* wait for UP released */
197 {
198 if (!(control_test(Control_UP)) ||
199 sys_gettime() - tm > AUTOREPEAT_TMOUT)
200 seq = 2;
201 break;
202 }
203 case 5: /* wait for DOWN released */
204 {
205 if (!(control_test(Control_DOWN)) ||
206 sys_gettime() - tm > AUTOREPEAT_TMOUT)
207 seq = 2;
208 break;
209 }
210 case 6: /* wait for LEFT released */
211 {
212 if (!(control_test(Control_LEFT)) ||
213 sys_gettime() - tm > AUTOREPEAT_TMOUT)
214 seq = 2;
215 break;
216 }
217 case 7: /* wait for RIGHT released */
218 {
219 if (!(control_test(Control_RIGHT)) ||
220 sys_gettime() - tm > AUTOREPEAT_TMOUT)
221 seq = 2;
222 break;
223 }
224 }
225
226 if (control_test(Control_EXIT)) /* check for exit request */
227 return SCREEN_EXIT;
228
229 if (seq == 99) { /* seq 99, we're done */
230 sysvid_clear();
231 seq = 0;
232 return SCREEN_DONE;
233 }
234 else
235 return SCREEN_RUNNING;
236}
237
238
239static void
240pointer_show(bool show)
241{
242 draw_setfb(TOPLEFT_X + x * 8 * 2, TOPLEFT_Y + y * 8 * 2 + 8);
243#ifdef GFXPC
244 draw_filter = 0xaaaa; /* red */
245#endif
246 draw_tile(show? TILE_POINTER:'@');
247}
248
249static void
250name_update(void)
251{
252 U8 i;
253
254 i = x + y * 6;
255 if (i < 26 && p < 10)
256 player_name[p++] = 'A' + i;
257 if (i == 26 && p < 10)
258 player_name[p++] = '.';
259 if (i == 27 && p < 10)
260 player_name[p++] = '@';
261 if (i == 28 && p > 0) {
262 p--;
263 }
264}
265
266static void
267name_draw(void)
268{
269 U8 i;
270
271 draw_setfb(NAMEPOS_X, NAMEPOS_Y);
272#ifdef GFXPC
273 draw_filter = 0xaaaa; /* red */
274#endif
275 for (i = 0; i < p; i++)
276 draw_tile(player_name[i]);
277 for (i = p; i < 10; i++)
278 draw_tile(TILE_CURSOR);
279
280#ifdef GFXST
281 draw_setfb(NAMEPOS_X, NAMEPOS_Y + 8);
282 for (i = 0; i < 10; i++)
283 draw_tile('@');
284 draw_setfb(NAMEPOS_X + 8 * (p < 9 ? p : 9), NAMEPOS_Y + 8);
285 draw_tile(TILE_POINTER);
286#endif
287}
288
289
290/* eof */
diff --git a/apps/plugins/xrick/scr_imain.c b/apps/plugins/xrick/scr_imain.c
new file mode 100644
index 0000000000..6851cd0cca
--- /dev/null
+++ b/apps/plugins/xrick/scr_imain.c
@@ -0,0 +1,170 @@
1/*
2 * xrick/scr_imain.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/draw.h"
20#include "xrick/control.h"
21#ifdef GFXST
22#include "xrick/data/pics.h"
23#endif
24#include "xrick/system/system.h"
25
26/*
27 * Main introduction
28 *
29 * return: SCREEN_RUNNING, SCREEN_DONE, SCREEN_EXIT
30 */
31U8
32screen_introMain(void)
33{
34 static U8 seq = 0;
35 static U8 seen = 0;
36 static bool first = true;
37 static U8 period = 0;
38 static U32 tm = 0;
39
40 if (seq == 0) {
41 draw_tilesBank = 0;
42 if (first)
43 seq = 1;
44 else
45 seq = 4;
46 period = game_period;
47 game_period = 50;
48 game_rects = &draw_SCREENRECT;
49#ifdef ENABLE_SOUND
50 game_setmusic(soundTune5, -1);
51#endif
52 }
53
54 switch (seq)
55 {
56 case 1: /* display Rick Dangerous title and Core Design copyright */
57 {
58 sysvid_clear();
59 tm = sys_gettime();
60#ifdef GFXPC
61 /* Rick Dangerous title */
62 draw_tllst = (U8 *)screen_imainrdt;
63 draw_setfb(32, 16);
64 draw_filter = 0xaaaa;
65 draw_tilesList();
66
67 /* Core Design copyright + press space to start */
68 draw_tllst = (U8 *)screen_imaincdc;
69 draw_setfb(64, 80);
70 draw_filter = 0x5555;
71 draw_tilesList();
72#endif
73#ifdef GFXST
74 draw_pic(pic_splash);
75#endif
76 seq = 2;
77 break;
78 }
79 case 2: /* wait for key pressed or timeout */
80 {
81 if (control_test(Control_FIRE))
82 seq = 3;
83 else if (sys_gettime() - tm > SCREEN_TIMEOUT) {
84 seen++;
85 seq = 4;
86 }
87 break;
88 }
89 case 3: /* wait for key released */
90 {
91 if (!(control_test(Control_FIRE))) {
92 if (seen++ == 0)
93 seq = 4;
94 else
95 seq = 7;
96 }
97 break;
98 }
99 case 4: /* dispay hall of fame */
100 {
101 U8 s[32];
102 size_t i;
103
104 sysvid_clear();
105 tm = sys_gettime();
106 /* hall of fame title */
107#ifdef GFXPC
108 draw_tllst = (U8 *)screen_imainhoft;
109 draw_setfb(32, 0);
110 draw_filter = 0xaaaa;
111 draw_tilesList();
112#endif
113#ifdef GFXST
114 draw_pic(pic_haf);
115#endif
116 /* hall of fame content */
117 draw_setfb(56, 48);
118#ifdef GFXPC
119 draw_filter = 0x5555;
120#endif
121 for (i = 0; i < screen_nbr_hiscores; i++) {
122 sys_snprintf((char *)s, sizeof(s), "%06d@@@====@@@%s",
123 screen_highScores[i].score, screen_highScores[i].name);
124 s[26] = (U8)'\377';
125 s[27] = (U8)'\377';
126 s[28] = (U8)'\376';
127 draw_tllst = s;
128 draw_tilesList();
129 }
130 seq = 5;
131 break;
132 }
133 case 5: /* wait for key pressed or timeout */
134 {
135 if (control_test(Control_FIRE))
136 seq = 6;
137 else if (sys_gettime() - tm > SCREEN_TIMEOUT) {
138 seen++;
139 seq = 1;
140 }
141 break;
142 }
143 case 6: /* wait for key released */
144 {
145 if (!(control_test(Control_FIRE))) {
146 if (seen++ == 0)
147 seq = 1;
148 else
149 seq = 7;
150 }
151 break;
152 }
153 }
154
155 if (control_test(Control_EXIT)) /* check for exit request */
156 return SCREEN_EXIT;
157
158 if (seq == 7) { /* we're done */
159 sysvid_clear();
160 seq = 0;
161 seen = 0;
162 first = false;
163 game_period = period;
164 return SCREEN_DONE;
165 }
166 else
167 return SCREEN_RUNNING;
168}
169
170/* eof */
diff --git a/apps/plugins/xrick/scr_imap.c b/apps/plugins/xrick/scr_imap.c
new file mode 100644
index 0000000000..5be80a24e4
--- /dev/null
+++ b/apps/plugins/xrick/scr_imap.c
@@ -0,0 +1,293 @@
1/*
2 * xrick/scr_imap.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/rects.h"
20#include "xrick/draw.h"
21#include "xrick/control.h"
22#include "xrick/maps.h"
23
24/*
25 * local vars
26 */
27static U16 step; /* current step */
28static U16 count; /* number of loops for current step */
29static U16 run; /* 1 = run, 0 = no more step */
30static U8 flipflop; /* flipflop for top, bottom, left, right */
31static U8 spnum; /* sprite number */
32static U16 spx, spdx; /* sprite x position and delta */
33static U16 spy, spdy; /* sprite y position and delta */
34static U16 spbase, spoffs; /* base, offset for sprite numbers table */
35static U8 seq = 0; /* anim sequence */
36
37static rect_t anim_rect = { 128, 16 + 16, 64, 64, NULL }; /* anim rectangle */
38
39/*
40 * prototypes
41 */
42static void drawtb(void);
43static void drawlr(void);
44static void drawsprite(void);
45static void drawcenter(void);
46static void nextstep(void);
47static void anim(void);
48static void init(void);
49
50/*
51 * Map introduction
52 *
53 * ASM: 1948
54 *
55 * return: SCREEN_RUNNING, SCREEN_DONE, SCREEN_EXIT
56 */
57U8
58screen_introMap(void)
59{
60 switch (seq) {
61 case 0:
62 sysvid_clear();
63
64#ifdef GFXPC
65 draw_tilesBank = 1;
66 draw_filter = 0xAAAA;
67#endif
68#ifdef GFXST
69 draw_tilesBank = 0;
70#endif
71 draw_tllst = screen_imaptext[game_map];
72 draw_setfb(40, 16);
73 draw_tilesSubList();
74
75 draw_setfb(40, 104);
76#ifdef GFXPC
77 draw_filter = 0x5555;
78#endif
79 draw_tilesList();
80
81 game_rects = NULL;
82
83#ifdef GFXPC
84 draw_filter = 0xFFFF;
85#endif
86
87 init();
88 nextstep();
89 drawcenter();
90 drawtb();
91 drawlr();
92 drawsprite();
93 draw_drawStatus();
94
95 game_rects = &draw_SCREENRECT;
96
97#ifdef ENABLE_SOUND
98 game_setmusic(map_maps[game_map].tune, 1);
99#endif
100
101 seq = 1;
102 break;
103 case 1: /* top and bottom borders */
104 drawtb();
105 game_rects = &anim_rect;
106 seq = 2;
107 break;
108 case 2: /* background and sprite */
109 anim();
110 drawcenter();
111 drawsprite();
112 game_rects = &anim_rect;
113 seq = 3;
114 break;
115 case 3: /* all borders */
116 drawtb();
117 drawlr();
118 game_rects = &anim_rect;
119 seq = 1;
120 break;
121 case 4: /* wait for key release */
122 if (!(control_test(Control_FIRE)))
123 seq = 5;
124 break;
125 }
126
127 if (control_test(Control_FIRE)) { /* end as soon as key pressed */
128 seq = 4;
129 }
130
131 if (control_test(Control_EXIT)) /* check for exit request */
132 return SCREEN_EXIT;
133
134 if (seq == 5) { /* end as soon as key pressed */
135 sysvid_clear();
136 seq = 0;
137 return SCREEN_DONE;
138 }
139 else
140 return SCREEN_RUNNING;
141}
142
143
144/*
145 * Display top and bottom borders (0x1B1F)
146 *
147 */
148static void
149drawtb(void)
150{
151 U8 i;
152
153 flipflop++;
154 if (flipflop & 0x01) {
155 draw_setfb(136, 16 + 16);
156 for (i = 0; i < 6; i++)
157 draw_tile(0x40);
158 draw_setfb(136, 72 + 16);
159 for (i = 0; i < 6; i++)
160 draw_tile(0x06);
161 }
162 else {
163 draw_setfb(136, 16 + 16);
164 for (i = 0; i < 6; i++)
165 draw_tile(0x05);
166 draw_setfb(136, 72 + 16);
167 for (i = 0; i < 6; i++)
168 draw_tile(0x40);
169 }
170}
171
172
173/*
174 * Display left and right borders (0x1B7C)
175 *
176 */
177static void
178drawlr(void)
179{
180 U8 i;
181
182 if (flipflop & 0x02) {
183 for (i = 0; i < 8; i++) {
184 draw_setfb(128, 16 + i * 8 + 16);
185 draw_tile(0x04);
186 draw_setfb(184, 16 + i * 8 + 16);
187 draw_tile(0x04);
188 }
189 }
190 else {
191 for (i = 0; i < 8; i++) {
192 draw_setfb(128, 16 + i * 8 + 16);
193 draw_tile(0x2B);
194 draw_setfb(184, 16 + i * 8 + 16);
195 draw_tile(0x2B);
196 }
197 }
198}
199
200
201/*
202 * Draw the sprite (0x19C6)
203 *
204 */
205static void
206drawsprite(void)
207{
208 draw_sprite(spnum, 136 + ((spx << 1) & 0x1C), 24 + (spy << 1) + 16);
209}
210
211
212/*
213 * Draw the background (0x1AF1)
214 *
215 */
216static void
217drawcenter(void)
218{
219 static U8 tn0[] = { 0x07, 0x5B, 0x7F, 0xA3, 0xC7 };
220 U8 i, j, tn;
221
222 tn = tn0[game_map];
223 for (i = 0; i < 6; i++) {
224 draw_setfb(136, (24 + 8 * i) + 16);
225 for (j = 0; j < 6; j++)
226 draw_tile(tn++);
227 }
228}
229
230
231/*
232 * Next Step (0x1A74)
233 *
234 */
235static void
236nextstep(void)
237{
238 if (screen_imapsteps[step].count) {
239 count = screen_imapsteps[step].count;
240 spdx = screen_imapsteps[step].dx;
241 spdy = screen_imapsteps[step].dy;
242 spbase = screen_imapsteps[step].base;
243 spoffs = 0;
244 step++;
245 }
246 else {
247 run = 0;
248 }
249}
250
251
252/*
253 * Anim (0x1AA8)
254 *
255 */
256static void
257anim(void)
258{
259 U8 i;
260
261 if (run) {
262 i = screen_imapsl[spbase + spoffs];
263 if (i == 0) {
264 spoffs = 0;
265 i = screen_imapsl[spbase];
266 }
267 spnum = i;
268 spoffs++;
269 spx += spdx;
270 spy += spdy;
271 count--;
272 if (count == 0)
273 nextstep();
274 }
275}
276
277
278/*
279 * Initialize (0x1A43)
280 *
281 */
282static void
283init(void)
284{
285 run = 0; run--;
286 step = screen_imapsofs[game_map];
287 spx = screen_imapsteps[step].dx;
288 spy = screen_imapsteps[step].dy;
289 step++;
290 spnum = 0; /* NOTE spnum in [8728] is never initialized ? */
291}
292
293/* eof */
diff --git a/apps/plugins/xrick/scr_pause.c b/apps/plugins/xrick/scr_pause.c
new file mode 100644
index 0000000000..04c3368d21
--- /dev/null
+++ b/apps/plugins/xrick/scr_pause.c
@@ -0,0 +1,50 @@
1/*
2 * xrick/scr_pause.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/draw.h"
20#include "xrick/control.h"
21#include "xrick/ents.h"
22
23/*
24 * Display the pause indicator
25 */
26void
27screen_pause(bool pause)
28{
29 if (pause) {
30 draw_tilesBank = 0;
31 draw_tllst = screen_pausedtxt;
32 draw_setfb(120, 80);
33#ifdef GFXPC
34 draw_filter = 0xAAAA;
35#endif
36 draw_tilesList();
37 }
38 else {
39#ifdef GFXPC
40 draw_filter = 0xFFFF;
41#endif
42 draw_map();
43 ent_draw();
44 draw_drawStatus();
45 }
46 game_rects = &draw_SCREENRECT;
47}
48
49
50/* eof */
diff --git a/apps/plugins/xrick/scr_xrick.c b/apps/plugins/xrick/scr_xrick.c
new file mode 100644
index 0000000000..00798ec123
--- /dev/null
+++ b/apps/plugins/xrick/scr_xrick.c
@@ -0,0 +1,101 @@
1/*
2 * xrick/scr_xrick.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/screens.h"
17
18#include "xrick/game.h"
19#include "xrick/draw.h"
20#include "xrick/control.h"
21#include "xrick/data/img.h"
22#include "xrick/system/system.h"
23
24/*
25 * global vars
26 */
27size_t screen_nbr_imapsl = 0;
28U8 *screen_imapsl = NULL;
29
30size_t screen_nbr_imapstesps = 0;
31screen_imapsteps_t *screen_imapsteps = NULL;
32
33size_t screen_nbr_imapsofs = 0;
34U8 *screen_imapsofs = NULL;
35
36size_t screen_nbr_imaptext = 0;
37U8 **screen_imaptext = NULL;
38
39size_t screen_nbr_hiscores = 0;
40hiscore_t *screen_highScores = NULL;
41
42#ifdef GFXPC
43U8 *screen_imainhoft = NULL;
44U8 *screen_imainrdt = NULL;
45U8 *screen_imaincdc = NULL;
46U8 *screen_congrats = NULL;
47#endif
48U8 *screen_gameovertxt = NULL;
49U8 *screen_pausedtxt = NULL;
50
51
52/*
53 * Display XRICK splash screen
54 *
55 * return: SCREEN_RUNNING, SCREEN_DONE, SCREEN_EXIT
56 */
57U8
58screen_xrick(void)
59{
60 static U8 seq = 0;
61 static U8 wait = 0;
62
63 if (seq == 0) {
64 sysvid_clear();
65 draw_img(img_splash);
66 game_rects = &draw_SCREENRECT;
67 seq = 1;
68 }
69
70 switch (seq) {
71 case 1: /* wait */
72 if (wait++ > 0x2) {
73#ifdef ENABLE_SOUND
74 game_setmusic(soundBullet, 1);
75#endif
76 seq = 2;
77 wait = 0;
78 }
79 break;
80
81 case 2: /* wait */
82 if (wait++ > 0x20) {
83 seq = 99;
84 wait = 0;
85 }
86 }
87
88 if (control_test(Control_EXIT)) /* check for exit request */
89 return SCREEN_EXIT;
90
91 if (seq == 99) { /* we're done */
92 sysvid_clear();
93 sysvid_setGamePalette();
94 seq = 0;
95 return SCREEN_DONE;
96 }
97
98 return SCREEN_RUNNING;
99}
100
101/* eof */
diff --git a/apps/plugins/xrick/screens.h b/apps/plugins/xrick/screens.h
new file mode 100644
index 0000000000..60564a99d2
--- /dev/null
+++ b/apps/plugins/xrick/screens.h
@@ -0,0 +1,74 @@
1/*
2 * xrick/screens.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _SCREENS_H
17#define _SCREENS_H
18
19#include "xrick/system/basic_types.h"
20#include "xrick/config.h"
21
22#include <stddef.h> /* size_t */
23
24#define SCREEN_TIMEOUT 4000
25#define SCREEN_RUNNING 0
26#define SCREEN_DONE 1
27#define SCREEN_EXIT 2
28
29typedef struct {
30 U16 count; /* number of loops */
31 U16 dx, dy; /* sprite x and y deltas */
32 U16 base; /* base for sprite numbers table */
33} screen_imapsteps_t; /* description of one step */
34
35enum { HISCORE_NAME_SIZE = 10 };
36typedef struct {
37 U32 score;
38 U8 name[HISCORE_NAME_SIZE];
39} hiscore_t;
40
41extern size_t screen_nbr_imapsl;
42extern U8 *screen_imapsl; /* sprite lists */
43
44extern size_t screen_nbr_imapstesps;
45extern screen_imapsteps_t *screen_imapsteps; /* map intro steps */
46
47extern size_t screen_nbr_imapsofs;
48extern U8 *screen_imapsofs; /* first step for each map */
49
50extern size_t screen_nbr_imaptext;
51extern U8 **screen_imaptext; /* map intro texts */
52
53extern size_t screen_nbr_hiscores;
54extern hiscore_t *screen_highScores; /* highest scores (hall of fame) */
55
56#ifdef GFXPC
57extern U8 *screen_imainhoft; /* hall of fame title */
58extern U8 *screen_imainrdt; /* rick dangerous title */
59extern U8 *screen_imaincdc; /* core design copyright text */
60extern U8 *screen_congrats; /* congratulations */
61#endif /* GFXPC */
62extern U8 *screen_gameovertxt; /* game over */
63extern U8 *screen_pausedtxt; /* paused */
64
65extern U8 screen_xrick(void); /* splash */
66extern U8 screen_introMain(void); /* main intro */
67extern U8 screen_introMap(void); /* map intro */
68extern U8 screen_gameover(void); /* gameover */
69extern U8 screen_getname(void); /* enter you name */
70extern void screen_pause(bool); /* pause indicator */
71
72#endif /* ndef _SCREENS_H */
73
74/* eof */
diff --git a/apps/plugins/xrick/scroller.c b/apps/plugins/xrick/scroller.c
new file mode 100644
index 0000000000..824fe9a6a9
--- /dev/null
+++ b/apps/plugins/xrick/scroller.c
@@ -0,0 +1,165 @@
1/*
2 * xrick/scroller.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/scroller.h"
17
18#include "xrick/game.h"
19#include "xrick/debug.h"
20#include "xrick/draw.h"
21#include "xrick/maps.h"
22#include "xrick/ents.h"
23
24/*
25 * Local variables
26 */
27static U8 period;
28
29/*
30 * Scroll up
31 *
32 */
33U8
34scroll_up(void)
35{
36 U8 i, j;
37 static U8 n = 0;
38
39 /* last call: restore */
40 if (n == 8) {
41 n = 0;
42 game_period = period;
43 return SCROLL_DONE;
44 }
45
46 /* first call: prepare */
47 if (n == 0) {
48 period = game_period;
49 game_period = SCROLL_PERIOD;
50 }
51
52 /* translate map */
53 for (i = MAP_ROW_SCRTOP; i < MAP_ROW_HBBOT; i++)
54 for (j = 0x00; j < 0x20; j++)
55 map_map[i][j] = map_map[i + 1][j];
56
57 /* translate entities */
58 for (i = 0; ent_ents[i].n != 0xFF; i++) {
59 if (ent_ents[i].n) {
60 ent_ents[i].ysave -= 8;
61 ent_ents[i].trig_y -= 8;
62 ent_ents[i].y -= 8;
63 if (ent_ents[i].y & 0x8000) { /* map coord. from 0x0000 to 0x0140 */
64 IFDEBUG_SCROLLER(
65 sys_printf("xrick/scroller: entity %#04X is gone\n", i);
66 );
67 ent_ents[i].n = 0;
68 }
69 }
70 }
71
72 /* display */
73 draw_map();
74 ent_draw();
75 draw_drawStatus();
76 map_frow++;
77
78 /* loop */
79 if (n++ == 7) {
80 /* activate visible entities */
81 ent_actvis(map_frow + MAP_ROW_HBTOP, map_frow + MAP_ROW_HBBOT);
82
83 /* prepare map */
84 map_expand();
85
86 /* display */
87 draw_map();
88 ent_draw();
89 draw_drawStatus();
90 }
91
92 game_rects = &draw_SCREENRECT;
93
94 return SCROLL_RUNNING;
95}
96
97/*
98 * Scroll down
99 *
100 */
101U8
102scroll_down(void)
103{
104 U8 i, j;
105 static U8 n = 0;
106
107 /* last call: restore */
108 if (n == 8) {
109 n = 0;
110 game_period = period;
111 return SCROLL_DONE;
112 }
113
114 /* first call: prepare */
115 if (n == 0) {
116 period = game_period;
117 game_period = SCROLL_PERIOD;
118 }
119
120 /* translate map */
121 for (i = MAP_ROW_SCRBOT; i > MAP_ROW_HTTOP; i--)
122 for (j = 0x00; j < 0x20; j++)
123 map_map[i][j] = map_map[i - 1][j];
124
125 /* translate entities */
126 for (i = 0; ent_ents[i].n != 0xFF; i++) {
127 if (ent_ents[i].n) {
128 ent_ents[i].ysave += 8;
129 ent_ents[i].trig_y += 8;
130 ent_ents[i].y += 8;
131 if (ent_ents[i].y > 0x0140) { /* map coord. from 0x0000 to 0x0140 */
132 IFDEBUG_SCROLLER(
133 sys_printf("xrick/scroller: entity %#04X is gone\n", i);
134 );
135 ent_ents[i].n = 0;
136 }
137 }
138 }
139
140 /* display */
141 draw_map();
142 ent_draw();
143 draw_drawStatus();
144 map_frow--;
145
146 /* loop */
147 if (n++ == 7) {
148 /* activate visible entities */
149 ent_actvis(map_frow + MAP_ROW_HTTOP, map_frow + MAP_ROW_HTBOT);
150
151 /* prepare map */
152 map_expand();
153
154 /* display */
155 draw_map();
156 ent_draw();
157 draw_drawStatus();
158 }
159
160 game_rects = &draw_SCREENRECT;
161
162 return SCROLL_RUNNING;
163}
164
165/* eof */
diff --git a/apps/plugins/xrick/scroller.h b/apps/plugins/xrick/scroller.h
new file mode 100644
index 0000000000..50c2190547
--- /dev/null
+++ b/apps/plugins/xrick/scroller.h
@@ -0,0 +1,31 @@
1/*
2 * xrick/scroller.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _SCROLLER_H
17#define _SCROLLER_H
18
19#define SCROLL_RUNNING 1
20#define SCROLL_DONE 0
21
22#define SCROLL_PERIOD 24
23
24#include "xrick/system/basic_types.h"
25
26extern U8 scroll_up(void);
27extern U8 scroll_down(void);
28
29#endif /* ndef _SCROLLER_H */
30
31/* eof */
diff --git a/apps/plugins/xrick/system/basic_funcs.c b/apps/plugins/xrick/system/basic_funcs.c
new file mode 100644
index 0000000000..fbc025a6ef
--- /dev/null
+++ b/apps/plugins/xrick/system/basic_funcs.c
@@ -0,0 +1,33 @@
1/*
2 * xrick/system/basic_funcs.c
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#include "xrick/system/basic_funcs.h"
15
16#ifdef USE_DEFAULT_ENDIANNESS_FUNCTIONS
17
18extern inline uint16_t swap16(uint16_t x);
19extern inline uint32_t swap32(uint32_t x);
20
21extern inline uint16_t htobe16(uint16_t host);
22extern inline uint16_t htole16(uint16_t host);
23extern inline uint16_t betoh16(uint16_t big_endian);
24extern inline uint16_t letoh16(uint16_t little_endian);
25
26extern inline uint32_t htobe32(uint32_t host);
27extern inline uint32_t htole32(uint32_t host);
28extern inline uint32_t betoh32(uint32_t big_endian);
29extern inline uint32_t letoh32(uint32_t little_endian);
30
31#endif /* USE_DEFAULT_ENDIANNESS_FUNCTIONS */
32
33/* eof */
diff --git a/apps/plugins/xrick/system/basic_funcs.h b/apps/plugins/xrick/system/basic_funcs.h
new file mode 100644
index 0000000000..1ac5c58d32
--- /dev/null
+++ b/apps/plugins/xrick/system/basic_funcs.h
@@ -0,0 +1,141 @@
1/*
2 * xrick/system/basic_funcs.h
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#ifndef _BASIC_FUNCS_H
15#define _BASIC_FUNCS_H
16
17#include "xrick/system/basic_types.h"
18#include "xrick/system/system.h"
19
20#ifdef __WIN32__
21/* Windows is little endian only */
22# define __ORDER_LITTLE_ENDIAN__ 1234
23# define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
24# define USE_DEFAULT_ENDIANNESS_FUNCTIONS
25# include <stdlib.h> /* _byteswap_XXX */
26
27#elif defined(ROCKBOX)
28/* Rockbox*/
29# include "plugin.h"
30# define __ORDER_LITTLE_ENDIAN__ 1234
31# define __ORDER_BIG_ENDIAN__ 4321
32# ifdef ROCKBOX_BIG_ENDIAN
33# define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
34# else
35# define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
36# endif
37
38#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || defined(__OpenBSD__) || defined(__NetBSD__)
39/* *BSD */
40# include <sys/endian.h>
41# define __ORDER_BIG_ENDIAN__ BIG_ENDIAN
42# define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN
43# define __BYTE_ORDER__ BYTE_ORDER
44
45#elif (defined(BSD) && (BSD >= 199103)) || defined(__MacOSX__)
46/* more BSD */
47# include <machine/endian.h>
48# define __ORDER_BIG_ENDIAN__ BIG_ENDIAN
49# define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN
50# define __BYTE_ORDER__ BYTE_ORDER
51
52#elif defined(__linux__) /*|| defined (__BEOS__)*/
53/* Linux, BeOS */
54# include <endian.h>
55# define betoh16(x) be16toh(x)
56# define letoh16(x) le16toh(x)
57# define betoh32(x) be32toh(x)
58# define letoh32(x) le32toh(x)
59
60#else
61/* shall we just '#include <endian.h>'? */
62# define USE_DEFAULT_ENDIANNESS_FUNCTIONS
63
64#endif /* __WIN32__ */
65
66/* define default endianness */
67#ifndef __ORDER_LITTLE_ENDIAN__
68# define __ORDER_LITTLE_ENDIAN__ 1234
69#endif
70
71#ifndef __ORDER_BIG_ENDIAN__
72# define __ORDER_BIG_ENDIAN__ 4321
73#endif
74
75#ifndef __BYTE_ORDER__
76# warning "Byte order not defined on your system, assuming little endian!"
77# define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
78#endif
79
80/* provide default endianness functions */
81#ifdef USE_DEFAULT_ENDIANNESS_FUNCTIONS
82
83# define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
84
85inline uint32_t swap32(uint32_t x)
86{
87# ifdef _MSC_VER
88 return _byteswap_ulong(x);
89# elif (GCC_VERSION > 40300) || defined(__clang__)
90 return __builtin_bswap32(x);
91# else
92 return (x >> 24) |
93 ((x >> 8) & 0x0000FF00) |
94 ((x << 8) & 0x00FF0000) |
95 (x << 24);
96# endif /* _MSC_VER */
97}
98
99inline uint16_t swap16(uint16_t x)
100{
101# ifdef _MSC_VER
102 return _byteswap_ushort(x);
103# elif (GCC_VERSION > 40800) || defined(__clang__)
104 return __builtin_bswap16(x);
105# else
106 return (x << 8)|(x >> 8);
107# endif /* _MSC_VER */
108}
109
110# if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)
111inline uint16_t htobe16(uint16_t host) { return swap16(host); }
112inline uint16_t htole16(uint16_t host) { return host; }
113inline uint16_t betoh16(uint16_t big_endian) { return swap16(big_endian); }
114inline uint16_t letoh16(uint16_t little_endian) { return little_endian; }
115
116inline uint32_t htobe32(uint32_t host) { return swap32(host); }
117inline uint32_t htole32(uint32_t host) { return host; }
118inline uint32_t betoh32(uint32_t big_endian) { return swap32(big_endian); }
119inline uint32_t letoh32(uint32_t little_endian) { return little_endian; }
120
121# elif (__BYTE_ORDER__==__ORDER_BIG_ENDIAN__)
122inline uint16_t htobe16(uint16_t host) { return host; }
123inline uint16_t htole16(uint16_t host) { return swap16(host); }
124inline uint16_t betoh16(uint16_t big_endian) { return big_endian; }
125inline uint16_t letoh16(uint16_t little_endian) { return swap16(little_endian); }
126
127inline uint32_t htobe32(uint32_t host) { return host; }
128inline uint32_t htole32(uint32_t host) { return swap32(host); }
129inline uint32_t betoh32(uint32_t big_endian) { return big_endian; }
130inline uint32_t letoh32(uint32_t little_endian) { return swap32(little_endian); }
131
132# else
133# error "Unknown/unsupported byte order!"
134
135# endif
136
137#endif /* USE_DEFAULT_ENDIANNESS_FUNCTIONS */
138
139#endif /* ndef _BASIC_FUNCS_H */
140
141/* eof */
diff --git a/apps/plugins/xrick/system/basic_types.h b/apps/plugins/xrick/system/basic_types.h
new file mode 100644
index 0000000000..e05fd477f3
--- /dev/null
+++ b/apps/plugins/xrick/system/basic_types.h
@@ -0,0 +1,48 @@
1/*
2 * xrick/system/basic_types.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _BASIC_TYPES_H
17#define _BASIC_TYPES_H
18
19#ifdef _MSC_VER
20
21typedef enum { false, true } bool;
22
23#define inline __inline
24
25typedef unsigned __int8 uint8_t;
26typedef unsigned __int16 uint16_t;
27typedef unsigned __int32 uint32_t;
28typedef __int8 int8_t;
29typedef __int16 int16_t;
30typedef __int32 int32_t;
31
32#else /* ndef _MSC_VER */
33
34#include <stdbool.h>
35#include <stdint.h>
36
37#endif /* _MSC_VER */
38
39typedef uint8_t U8; /* 8 bits unsigned */
40typedef uint16_t U16; /* 16 bits unsigned */
41typedef uint32_t U32; /* 32 bits unsigned */
42typedef int8_t S8; /* 8 bits signed */
43typedef int16_t S16; /* 16 bits signed */
44typedef int32_t S32; /* 32 bits signed */
45
46#endif /* ndef _BASIC_TYPES_H */
47
48/* eof */
diff --git a/apps/plugins/xrick/system/main_rockbox.c b/apps/plugins/xrick/system/main_rockbox.c
new file mode 100644
index 0000000000..e273e1dc8d
--- /dev/null
+++ b/apps/plugins/xrick/system/main_rockbox.c
@@ -0,0 +1,43 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26#include "xrick/game.h"
27
28#include "plugin.h"
29
30/* Plug-in entry point */
31enum plugin_status plugin_start(const void* parameter)
32{
33 char *filename = (char*)parameter;
34 bool success = sys_init(1, &filename);
35 if (success)
36 {
37 game_run();
38 }
39 sys_shutdown();
40 return (success? PLUGIN_OK : PLUGIN_ERROR);
41}
42
43/* eof */
diff --git a/apps/plugins/xrick/system/miniz_config.h b/apps/plugins/xrick/system/miniz_config.h
new file mode 100644
index 0000000000..65899b0c23
--- /dev/null
+++ b/apps/plugins/xrick/system/miniz_config.h
@@ -0,0 +1,38 @@
1/*
2 * xrick/system/miniz_config.h
3 *
4 * Copyright (C) 2008-2014 Pierluigi Vicinanza. All rights reserved.
5 *
6 * The use and distribution terms for this software are contained in the file
7 * named README, which can be found in the root of this distribution. By
8 * using this software in any fashion, you are agreeing to be bound by the
9 * terms of this license.
10 *
11 * You must not remove this notice, or any other, from this software.
12 */
13
14#ifndef _MINIZ_CONFIG_H
15#define _MINIZ_CONFIG_H
16
17/*
18 * miniz used only for crc32 calculation
19 */
20#define MINIZ_NO_STDIO
21#define MINIZ_NO_TIME
22#define MINIZ_NO_ARCHIVE_APIS
23#define MINIZ_NO_ARCHIVE_WRITING_APIS
24#define MINIZ_NO_ZLIB_APIS
25#define MINIZ_NO_MALLOC
26#ifdef ROCKBOX
27# define MINIZ_NO_ASSERT
28# ifndef SIMULATOR
29# define MINIZ_X86_OR_X64_CPU 0
30# define MINIZ_HAS_64BIT_REGISTERS 0
31# define TINFL_USE_64BIT_BITBUF 0
32# define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
33# endif /* ndef SIMULATOR */
34#endif
35
36#endif /* ndef _MINIZ_CONFIG_H */
37
38/* eof */
diff --git a/apps/plugins/xrick/system/rockboxcodes.h b/apps/plugins/xrick/system/rockboxcodes.h
new file mode 100644
index 0000000000..ca56c338b6
--- /dev/null
+++ b/apps/plugins/xrick/system/rockboxcodes.h
@@ -0,0 +1,110 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#ifndef _ROCKBOXCODES_H
26#define _ROCKBOXCODES_H
27
28/* keypad mappings */
29#include "plugin.h"
30
31#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
32 (CONFIG_KEYPAD == IRIVER_H300_PAD)
33#define XRICK_BTN_UP BUTTON_UP | BUTTON_REC
34#define XRICK_BTN_DOWN BUTTON_DOWN | BUTTON_MODE
35#define XRICK_BTN_LEFT BUTTON_LEFT
36#define XRICK_BTN_RIGHT BUTTON_RIGHT
37#define XRICK_BTN_FIRE BUTTON_ON
38#define XRICK_BTN_PAUSE BUTTON_SELECT
39#define XRICK_BTN_MENU BUTTON_OFF
40
41#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
42#define XRICK_BTN_MENU BUTTON_POWER
43#define XRICK_BTN_FIRE BUTTON_PLAY
44#define XRICK_BTN_PAUSE BUTTON_REW
45#define XRICK_BTN_UP BUTTON_SCROLL_UP
46#define XRICK_BTN_DOWN BUTTON_SCROLL_DOWN
47#define XRICK_BTN_LEFT BUTTON_LEFT
48#define XRICK_BTN_RIGHT BUTTON_RIGHT
49
50#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
51 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
52 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
53#define XRICK_BTN_UP BUTTON_MENU
54#define XRICK_BTN_DOWN BUTTON_PLAY
55#define XRICK_BTN_LEFT BUTTON_LEFT
56#define XRICK_BTN_RIGHT BUTTON_RIGHT
57#define XRICK_BTN_FIRE BUTTON_SELECT
58#define XRICK_BTN_PAUSE BUTTON_SCROLL_BACK
59#define XRICK_BTN_MENU BUTTON_SCROLL_FWD
60
61#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
62#define XRICK_BTN_UP BUTTON_UP
63#define XRICK_BTN_UPLEFT BUTTON_BACK
64#define XRICK_BTN_UPRIGHT BUTTON_PLAYPAUSE
65#define XRICK_BTN_DOWN BUTTON_DOWN
66#define XRICK_BTN_DOWNLEFT BUTTON_BOTTOMLEFT
67#define XRICK_BTN_DOWNRIGHT BUTTON_BOTTOMRIGHT
68#define XRICK_BTN_LEFT BUTTON_LEFT
69#define XRICK_BTN_RIGHT BUTTON_RIGHT
70#define XRICK_BTN_FIRE BUTTON_VOL_DOWN
71#define XRICK_BTN_PAUSE BUTTON_VOL_UP
72#define XRICK_BTN_MENU BUTTON_POWER
73
74#elif (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
75#define XRICK_BTN_UP BUTTON_UP
76#define XRICK_BTN_DOWN BUTTON_DOWN
77#define XRICK_BTN_LEFT BUTTON_LEFT
78#define XRICK_BTN_RIGHT BUTTON_RIGHT
79#define XRICK_BTN_FIRE BUTTON_PLAY
80#define XRICK_BTN_PAUSE BUTTON_FFWD
81#define XRICK_BTN_MENU BUTTON_REW
82
83#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD)
84#define XRICK_BTN_UP BUTTON_UP
85#define XRICK_BTN_DOWN BUTTON_DOWN
86#define XRICK_BTN_LEFT BUTTON_LEFT
87#define XRICK_BTN_RIGHT BUTTON_RIGHT
88#define XRICK_BTN_FIRE BUTTON_PLAY
89#define XRICK_BTN_PAUSE BUTTON_FFWD
90#define XRICK_BTN_MENU BUTTON_REW
91
92/* place other keypad mappings here
93#elif CONFIG_KEYPAD ==...
94#define XRICK_BTN...
95*/
96
97#else
98# include "lib/pluginlib_actions.h"
99#define XRICK_BTN_UP PLA_UP
100#define XRICK_BTN_DOWN PLA_DOWN
101#define XRICK_BTN_LEFT PLA_LEFT
102#define XRICK_BTN_RIGHT PLA_RIGHT
103#define XRICK_BTN_FIRE PLA_SELECT
104#define XRICK_BTN_PAUSE PLA_CANCEL
105#define XRICK_BTN_MENU PLA_EXIT
106#endif
107
108#endif /* ndef _ROCKBOXCODES_H */
109
110/* eof */
diff --git a/apps/plugins/xrick/system/sysarg_rockbox.c b/apps/plugins/xrick/system/sysarg_rockbox.c
new file mode 100644
index 0000000000..fa502ff4b0
--- /dev/null
+++ b/apps/plugins/xrick/system/sysarg_rockbox.c
@@ -0,0 +1,49 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27/*
28 * globals
29 */
30int sysarg_args_period = 0; /* time between each frame, in milliseconds. The default is 40. */
31int sysarg_args_map = 0;
32int sysarg_args_submap = 0;
33bool sysarg_args_nosound = false;
34const char *sysarg_args_data = NULL;
35
36/*
37 * Read and process arguments
38 */
39bool sysarg_init(int argc/*unused*/, char **argv)
40{
41 (void)argc;
42
43 /* note: "*argv" is truly a "const *" */
44 sysarg_args_data = *argv;
45
46 return true;
47}
48
49/* eof */
diff --git a/apps/plugins/xrick/system/sysevt_rockbox.c b/apps/plugins/xrick/system/sysevt_rockbox.c
new file mode 100644
index 0000000000..f5314712e8
--- /dev/null
+++ b/apps/plugins/xrick/system/sysevt_rockbox.c
@@ -0,0 +1,156 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27#include "xrick/config.h"
28#include "xrick/control.h"
29#include "xrick/game.h"
30#include "xrick/system/sysmenu_rockbox.h"
31#include "xrick/system/rockboxcodes.h"
32
33/*
34 * Helper function to set/clear controls according to key press
35 */
36static inline void checkKey(int key, unsigned button, control_t control)
37{
38 if (key & button)
39 {
40 control_set(control);
41 }
42 else
43 {
44 control_clear(control);
45 }
46}
47
48/*
49 * Process events, if any, then return
50 */
51void sysevt_poll(void)
52{
53 static int previousKey, currentKey;
54
55 /* this is because "Restart Game" is handled via menu */
56 if (control_test(Control_END))
57 {
58 control_clear(Control_END);
59 }
60
61 for (;;)
62 {
63 /* check for USB connection */
64 if ((rb->default_event_handler(rb->button_get(false)) == SYS_USB_CONNECTED)
65#if defined(HAS_BUTTON_HOLD)
66 || rb->button_hold()
67#endif
68 )
69 {
70 sysmenu_exec();
71 }
72
73 currentKey = rb->button_status();
74 if (currentKey != previousKey)
75 {
76 break;
77 }
78 else if (game_waitevt)
79 {
80 rb->yield();
81 }
82 else /* (currentKey == previousKey) && !game_waitevt */
83 {
84 return;
85 }
86 }
87
88#ifdef XRICK_BTN_MENU
89 if (currentKey & XRICK_BTN_MENU)
90 {
91 sysmenu_exec();
92 }
93#endif
94
95#ifdef XRICK_BTN_PAUSE
96 checkKey(currentKey, XRICK_BTN_PAUSE, Control_PAUSE);
97#endif
98
99 checkKey(currentKey, XRICK_BTN_UP, Control_UP);
100
101 checkKey(currentKey, XRICK_BTN_DOWN, Control_DOWN);
102
103 checkKey(currentKey, XRICK_BTN_LEFT, Control_LEFT);
104
105 checkKey(currentKey, XRICK_BTN_RIGHT, Control_RIGHT);
106
107 checkKey(currentKey, XRICK_BTN_FIRE, Control_FIRE);
108
109#ifdef XRICK_BTN_UPLEFT
110 if (!control_test(Control_UP | Control_LEFT))
111 {
112 checkKey(currentKey, XRICK_BTN_UPLEFT, Control_UP | Control_LEFT);
113 }
114#endif /* XRICK_BTN_UPLEFT */
115
116#ifdef XRICK_BTN_UPRIGHT
117 if (!control_test(Control_UP | Control_RIGHT))
118 {
119 checkKey(currentKey, XRICK_BTN_UPRIGHT, Control_UP | Control_RIGHT);
120 }
121#endif /* XRICK_BTN_UPRIGHT */
122
123#ifdef XRICK_BTN_DOWNLEFT
124 if (!control_test(Control_DOWN | Control_LEFT))
125 {
126 checkKey(currentKey, XRICK_BTN_DOWNLEFT, Control_DOWN | Control_LEFT);
127 }
128#endif /* XRICK_BTN_DOWNLEFT */
129
130#ifdef XRICK_BTN_DOWNRIGHT
131 if (!control_test(Control_DOWN | Control_RIGHT))
132 {
133 checkKey(currentKey, XRICK_BTN_DOWNRIGHT, Control_DOWN | Control_RIGHT);
134 }
135#endif /* XRICK_BTN_DOWNRIGHT */
136
137 previousKey = currentKey;
138}
139
140/*
141 * Wait for an event, then process it and return
142 */
143void sysevt_wait(void)
144{
145#ifdef HAVE_ADJUSTABLE_CPU_FREQ
146 rb->cpu_boost(false);
147#endif
148
149 sysevt_poll(); /* sysevt_poll deals with blocking case as well */
150
151#ifdef HAVE_ADJUSTABLE_CPU_FREQ
152 rb->cpu_boost(true);
153#endif
154}
155
156/* eof */
diff --git a/apps/plugins/xrick/system/sysfile_rockbox.c b/apps/plugins/xrick/system/sysfile_rockbox.c
new file mode 100644
index 0000000000..06227caec1
--- /dev/null
+++ b/apps/plugins/xrick/system/sysfile_rockbox.c
@@ -0,0 +1,122 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27#include "xrick/config.h"
28#include "xrick/util.h"
29
30#include "plugin.h"
31
32#define XRICK_GAME_DIR ROCKBOX_DIR "/xrick/"
33
34/*
35 * Global variables
36 */
37const char *sysfile_defaultPath = XRICK_GAME_DIR;
38
39/*
40 * Local variables
41 */
42static char *rootPath = NULL;
43
44/*
45 *
46 */
47bool sysfile_setRootPath(const char *name)
48{
49 rootPath = u_strdup(name);
50 return (rootPath != NULL);
51}
52
53/*
54 *
55 */
56void sysfile_clearRootPath()
57{
58 sysmem_pop(rootPath);
59 rootPath = NULL;
60}
61
62/*
63 * Open a data file.
64 */
65file_t sysfile_open(const char *name)
66{
67 long fd;
68
69 size_t fullPathLength = rb->strlen(rootPath) + rb->strlen(name) + 2;
70 char *fullPath = sysmem_push(fullPathLength);
71 if (!fullPath)
72 {
73 return NULL;
74 }
75 rb->snprintf(fullPath, fullPathLength, "%s/%s", rootPath, name);
76 fd = rb->open(fullPath, O_RDONLY);
77 sysmem_pop(fullPath);
78
79 /*
80 * note: I've never seen zero/NULL being used as a file descriptor under Rockbox.
81 * Putting a check here in case this will ever happen (will need a fix).
82 */
83 if (fd == 0)
84 {
85 sys_error("(file) unsupported file descriptor (zero/NULL) being used");
86 }
87 if (fd < 0)
88 {
89 return NULL;
90 }
91
92 return (file_t)fd;
93}
94
95/*
96 * Read a file within a data archive.
97 */
98int sysfile_read(file_t file, void *buf, size_t size, size_t count)
99{
100 long fd = (long)file;
101 return (rb->read(fd, buf, size * count) / size);
102}
103
104/*
105 * Seek.
106 */
107int sysfile_seek(file_t file, long offset, int origin)
108{
109 long fd = (long)file;
110 return rb->lseek(fd, offset, origin);
111}
112
113/*
114 * Close a file within a data archive.
115 */
116void sysfile_close(file_t file)
117{
118 long fd = (long)file;
119 rb->close(fd);
120}
121
122/* eof */
diff --git a/apps/plugins/xrick/system/sysmem_rockbox.c b/apps/plugins/xrick/system/sysmem_rockbox.c
new file mode 100644
index 0000000000..06a683a463
--- /dev/null
+++ b/apps/plugins/xrick/system/sysmem_rockbox.c
@@ -0,0 +1,156 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27#include "xrick/debug.h"
28
29#include "plugin.h"
30
31/*
32 * Local variables
33 */
34enum
35{
36 ALIGNMENT = sizeof(void*) /* this is more of an educated guess; might want to adjust for your specific architecture */
37};
38static U8 * stackBuffer;
39static U8 * stackTop;
40static size_t stackSize;
41static size_t stackMaxSize;
42static bool isMemoryInitialised = false;
43IFDEBUG_MEMORY( static size_t maxUsedMemory = 0; );
44
45/*
46 * Initialise memory stack
47 */
48bool sysmem_init(void)
49{
50 if (isMemoryInitialised)
51 {
52 return true;
53 }
54
55 if (rb->audio_status())
56 {
57 /* Playback must be stopped the entire time the sound buffer is used.*/
58 rb->audio_stop();
59 }
60
61 stackBuffer = rb->plugin_get_audio_buffer(&stackMaxSize);
62 stackTop = stackBuffer;
63 stackSize = 0;
64 isMemoryInitialised = true;
65 return true;
66}
67
68/*
69 * Cleanup memory stack
70 */
71void sysmem_shutdown(void)
72{
73 if (!isMemoryInitialised)
74 {
75 return;
76 }
77
78 if (stackTop != stackBuffer || stackSize != 0)
79 {
80 sys_error("(memory) improper deallocation detected");
81 }
82
83 IFDEBUG_MEMORY(
84 sys_printf("xrick/memory: max memory usage was %u bytes\n", maxUsedMemory);
85 );
86
87 isMemoryInitialised = false;
88}
89
90/*
91 * Allocate a memory-aligned block on top of the memory stack
92 */
93void *sysmem_push(size_t size)
94{
95 uintptr_t alignedPtr;
96 size_t * allocatedSizePtr;
97
98 size_t neededSize = sizeof(size_t) + size + (ALIGNMENT - 1);
99 if (stackSize + neededSize > stackMaxSize)
100 {
101 sys_error("(memory) tried to allocate a block when memory full");
102 return NULL;
103 }
104
105 alignedPtr = (((uintptr_t)stackTop) + sizeof(size_t) + ALIGNMENT) & ~((uintptr_t)(ALIGNMENT - 1));
106
107 allocatedSizePtr = (size_t *)(alignedPtr);
108 allocatedSizePtr[-1] = neededSize;
109
110 stackTop += neededSize;
111 stackSize += neededSize;
112
113 IFDEBUG_MEMORY(
114 sys_printf("xrick/memory: allocated %u bytes\n", neededSize);
115 if (stackSize > maxUsedMemory) maxUsedMemory = stackSize;
116 );
117
118 return (void *)alignedPtr;
119}
120
121/*
122 * Release block from the top of the memory stack
123 */
124void sysmem_pop(void * alignedPtr)
125{
126 size_t allocatedSize;
127
128 if (!alignedPtr)
129 {
130 return;
131 }
132
133 if (stackSize == 0)
134 {
135 sys_error("(memory) tried to release a block when memory empty");
136 return;
137 }
138
139 allocatedSize = ((size_t *)(alignedPtr))[-1];
140 stackTop -= allocatedSize;
141 stackSize -= allocatedSize;
142
143 IFDEBUG_MEMORY(
144 if ((uintptr_t)alignedPtr != ((((uintptr_t)stackTop) + sizeof(size_t) + ALIGNMENT) & ~((uintptr_t)(ALIGNMENT - 1))))
145 {
146 sys_error("(memory) tried to release a wrong block");
147 return;
148 }
149 );
150
151 IFDEBUG_MEMORY(
152 sys_printf("xrick/memory: released %u bytes\n", allocatedSize);
153 );
154}
155
156/* eof */
diff --git a/apps/plugins/xrick/system/sysmenu_rockbox.c b/apps/plugins/xrick/system/sysmenu_rockbox.c
new file mode 100644
index 0000000000..fb80881749
--- /dev/null
+++ b/apps/plugins/xrick/system/sysmenu_rockbox.c
@@ -0,0 +1,200 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/sysmenu_rockbox.h"
26
27#include "xrick/config.h"
28#include "xrick/control.h"
29#include "xrick/draw.h"
30#include "xrick/game.h"
31#include "xrick/system/system.h"
32#include "xrick/system/syssnd_rockbox.h"
33
34#include "plugin.h"
35#ifndef HAVE_LCD_COLOR
36#include "lib/grey.h"
37#endif
38
39#ifdef HAVE_LCD_COLOR
40static fb_data *lcd_fb=NULL;
41#endif
42
43#ifdef ENABLE_CHEATS
44/*
45 * Cheat settings menu
46 */
47static char * sysmenu_cheatItemText(int selected_item, void *data, char *buffer, size_t buffer_len)
48{
49 (void)selected_item;
50 cheat_t cheat = (cheat_t)data;
51 (void)buffer;
52 (void)buffer_len;
53 char * messages[] =
54 {
55 "Disable Unlimited Lives/Ammo Mode",
56 "Enable Unlimited Lives/Ammo Mode",
57 "Disable Never Die Mode",
58 "Enable Never Die Mode",
59 "Disable Expose Mode",
60 "Enable Expose Mode"
61 };
62
63 switch (cheat)
64 {
65 case Cheat_UNLIMITED_ALL:
66 {
67 return game_cheat1? messages[0] : messages[1];
68 }
69 case Cheat_NEVER_DIE:
70 {
71 return game_cheat2? messages[2] : messages[3];
72 }
73 case Cheat_EXPOSE:
74 {
75 return game_cheat3? messages[4] : messages[5];
76 }
77 default: break;
78 }
79 return "";
80}
81
82/*
83 * Callback invoked by cheat menu item
84 */
85static int sysmenu_doToggleCheat(void *param)
86{
87 cheat_t cheat = (cheat_t)param;
88 game_toggleCheat(cheat);
89 return 0;
90}
91
92MENUITEM_FUNCTION_DYNTEXT(sysmenu_unlimitedAllItem, MENU_FUNC_USEPARAM, sysmenu_doToggleCheat,
93 sysmenu_cheatItemText, NULL, (void *)Cheat_UNLIMITED_ALL,
94 NULL, Icon_NOICON);
95
96MENUITEM_FUNCTION_DYNTEXT(sysmenu_neverDieItem, MENU_FUNC_USEPARAM, sysmenu_doToggleCheat,
97 sysmenu_cheatItemText, NULL, (void *)Cheat_NEVER_DIE,
98 NULL, Icon_NOICON);
99
100MENUITEM_FUNCTION_DYNTEXT(sysmenu_exposeItem, MENU_FUNC_USEPARAM, sysmenu_doToggleCheat,
101 sysmenu_cheatItemText, NULL, (void *)Cheat_EXPOSE,
102 NULL, Icon_NOICON);
103
104MAKE_MENU(sysmenu_cheatItems, "Cheat Settings", NULL, Icon_NOICON,
105 &sysmenu_unlimitedAllItem, &sysmenu_neverDieItem, &sysmenu_exposeItem);
106
107#endif /* ENABLE_CHEATS */
108
109/*
110 * Display main menu
111 */
112void sysmenu_exec(void)
113{
114 int result;
115 bool done;
116
117 enum
118 {
119 Menu_RESUME,
120 Menu_RESTART,
121#ifdef ENABLE_CHEATS
122 Menu_CHEAT_SETTINGS,
123#endif
124 Menu_QUIT
125 };
126
127 MENUITEM_STRINGLIST(sysmenu_mainItems, "xrick Menu", NULL,
128 "Resume Game",
129 "Restart Game",
130#ifdef ENABLE_CHEATS
131 "Cheat Settings",
132#endif
133 ID2P(LANG_MENU_QUIT));
134
135#ifdef ENABLE_SOUND
136 syssnd_pauseAll(true);
137#endif
138
139#ifndef HAVE_LCD_COLOR
140 grey_show(false);
141#endif
142
143 done = false;
144 do
145 {
146 rb->button_clear_queue();
147
148 result = rb->do_menu(&sysmenu_mainItems, NULL, NULL, false);
149 switch(result)
150 {
151 case Menu_RESUME:
152 {
153 done = true;
154 break;
155 }
156 case Menu_RESTART:
157 {
158 control_set(Control_END);
159 done = true;
160 break;
161 }
162#ifdef ENABLE_CHEATS
163 case Menu_CHEAT_SETTINGS:
164 {
165 rb->do_menu(&sysmenu_cheatItems, NULL, NULL, false);
166 break;
167 }
168#endif
169 case Menu_QUIT:
170 {
171 control_set(Control_EXIT);
172 done = true;
173 break;
174 }
175 default: break;
176 }
177 } while (!done);
178
179#ifdef HAVE_LCD_COLOR
180 if (!(control_test(Control_EXIT)))
181 {
182 if(!lcd_fb)
183 {
184 struct viewport *vp_main = rb->lcd_set_viewport(NULL);
185 lcd_fb = vp_main->buffer->fb_ptr;
186 }
187 rb->memset(lcd_fb, 0, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
188 sysvid_update(&draw_SCREENRECT);
189 rb->lcd_update();
190 }
191#else
192 grey_show(true);
193#endif
194
195#ifdef ENABLE_SOUND
196 syssnd_pauseAll(false);
197#endif
198}
199
200/* eof */
diff --git a/apps/plugins/xrick/system/sysmenu_rockbox.h b/apps/plugins/xrick/system/sysmenu_rockbox.h
new file mode 100644
index 0000000000..fcd13606fa
--- /dev/null
+++ b/apps/plugins/xrick/system/sysmenu_rockbox.h
@@ -0,0 +1,32 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#ifndef _SYSMENU_ROCKBOX_H
26#define _SYSMENU_ROCKBOX_H
27
28extern void sysmenu_exec(void);
29
30#endif /* ndef _SYSMENU_ROCKBOX_H */
31
32/* eof */
diff --git a/apps/plugins/xrick/system/syssnd_rockbox.c b/apps/plugins/xrick/system/syssnd_rockbox.c
new file mode 100644
index 0000000000..97ed5474f1
--- /dev/null
+++ b/apps/plugins/xrick/system/syssnd_rockbox.c
@@ -0,0 +1,483 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/config.h"
26
27#ifdef ENABLE_SOUND
28
29#include "xrick/system/system.h"
30
31#include "xrick/game.h"
32#include "xrick/debug.h"
33#include "xrick/system/syssnd_rockbox.h"
34
35#include "plugin.h"
36
37/*
38 * Global variables
39 */
40const U8 syssnd_period = 20;
41
42/*
43 * Local variables
44 */
45enum
46{
47 SYSSND_MIX_CHANNELS = 5,
48 SYSSND_MIX_SAMPLES = 1024, /* try changing this value if sound mixing is too slow or choppy */
49 SYSSND_SOURCE_SAMPLES = SYSSND_MIX_SAMPLES / 2
50};
51
52/* channels to be mixed */
53static channel_t channels[SYSSND_MIX_CHANNELS];
54/* buffer used to mix sounds sent to pcm playback, stores 16b stereo 44Khz audio samples */
55enum { AUDIO_BUFFER_COUNT = 4 };
56typedef struct
57{
58 U32 data[SYSSND_MIX_SAMPLES];
59 size_t length; /* in 8 bit mono samples */
60} mix_buffer_t;
61static mix_buffer_t mixBuffers[AUDIO_BUFFER_COUNT];
62static size_t writeIndex;
63static size_t readIndex;
64static size_t fillCount;
65static bool isAudioPlaying;
66static bool isAudioInitialised = false;
67
68/*
69 * Prototypes
70 */
71static void endChannel(size_t c);
72static void get_more(const void **start, size_t *size);
73
74/*
75 * Deactivate channel
76 */
77static void endChannel(size_t c)
78{
79 channels[c].loop = 0;
80 channels[c].sound = NULL;
81}
82
83/*
84 * Audio callback
85 */
86static void get_more(const void **start, size_t *size)
87{
88 if (fillCount > 0)
89 {
90 /* Store output data address and size. */
91 *start = mixBuffers[readIndex].data;
92 *size = mixBuffers[readIndex].length * 8;
93
94 /* Free this part of output buffer. */
95 mixBuffers[readIndex].length = 0;
96
97 /* Advance to the next part of output buffer. */
98 readIndex = (readIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
99 fillCount--;
100 }
101 else
102 {
103 /* Nothing to play. */
104 isAudioPlaying = false;
105 }
106}
107
108/*
109 * Mix audio samples and fill playback buffer
110 */
111void syssnd_update(void)
112{
113 if (!isAudioInitialised)
114 {
115 return;
116 }
117
118 for (;;)
119 {
120 size_t c;
121 size_t sampleOffset;
122 size_t maxSampleCount;
123 bool isFirstSound;
124 U8 *sourceBuf, *sourceBufEnd;
125 U32 *destBuf;
126
127 /* Cancel if whole buffer filled. */
128 if (fillCount >= (AUDIO_BUFFER_COUNT - 1))
129 {
130 return;
131 }
132
133 maxSampleCount = 0;
134
135 sampleOffset = mixBuffers[writeIndex].length;
136 destBuf = mixBuffers[writeIndex].data + sampleOffset * 2;
137
138 isFirstSound = true;
139 for (c = 0; c < SYSSND_MIX_CHANNELS ; ++c)
140 {
141 U32 * mixBuffer;
142 size_t sampleCount;
143 channel_t * channel = &channels[c];
144
145 if (!channel->sound /* no sound to play on this channel */
146 || (channel->loop == 0)) /* channel is inactive */
147 {
148 continue;
149 }
150
151 if (isFirstSound)
152 {
153 /* clear mixing buffer */
154 rb->memset(destBuf, 0, (SYSSND_MIX_SAMPLES - (sampleOffset * 2)) * sizeof(U32));
155 isFirstSound = false;
156 }
157
158 sampleCount = MIN(SYSSND_SOURCE_SAMPLES - sampleOffset, channel->len);
159 if (maxSampleCount < sampleCount)
160 {
161 maxSampleCount = sampleCount;
162 }
163
164 /* mix sound samples */
165 mixBuffer = destBuf;
166 sourceBuf = channel->buf;
167 sourceBufEnd = channel->buf + sampleCount;
168 while (sourceBuf < sourceBufEnd)
169 {
170 /* Convert from unsigned 8 bit mono 22khz to signed 16 bit stereo 44khz */
171 const int sourceSample = *sourceBuf++;
172 int monoSample = (sourceSample - 0x80) << 8;
173 U32 stereoSample = *mixBuffer;
174 monoSample += (S32)(stereoSample) >> 16;
175 if (monoSample >= 0x8000)
176 {
177 monoSample = 0x7FFF;
178 }
179 else if (monoSample < -0x8000)
180 {
181 monoSample = -0x8000;
182 }
183 stereoSample = (U16)monoSample | ((U16)monoSample << 16);
184 *mixBuffer++ = stereoSample;
185 *mixBuffer++ = stereoSample;
186 }
187 channel->buf = sourceBufEnd;
188
189 channel->len -= sampleCount;
190 if (channel->len == 0) /* ending ? */
191 {
192 if (channel->loop > 0)
193 {
194 channel->loop--;
195 }
196 if (channel->loop)
197 {
198 /* just loop */
199 IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - loop\n", c););
200 channel->buf = channel->sound->buf;
201 channel->len = channel->sound->len;
202 }
203 else
204 {
205 /* end for real */
206 IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - end\n", c););
207 endChannel(c);
208 }
209 }
210 }
211
212 if (maxSampleCount == 0)
213 {
214 return;
215 }
216
217 mixBuffers[writeIndex].length += maxSampleCount;
218
219 /* Advance one part of audio buffer. */
220 writeIndex = (writeIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
221 fillCount++;
222
223 if (!isAudioPlaying && fillCount > 0)
224 {
225 rb->pcm_play_data(&get_more, NULL, NULL, 0);
226 isAudioPlaying = true;
227 }
228 }
229}
230
231/*
232 * Initialise audio
233 */
234bool syssnd_init(void)
235{
236 if (isAudioInitialised)
237 {
238 return true;
239 }
240
241 IFDEBUG_AUDIO(sys_printf("xrick/audio: start\n"););
242
243 rb->talk_disable(true);
244
245 /* Stop playback to reconfigure audio settings and acquire audio buffer */
246 rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
247
248#if INPUT_SRC_CAPS != 0
249 /* Select playback */
250 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
251 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
252#endif
253
254 rb->pcm_set_frequency(HW_FREQ_44);
255 rb->pcm_apply_settings();
256
257 rb->memset(channels, 0, sizeof(channels));
258 rb->memset(mixBuffers, 0, sizeof(mixBuffers));
259
260 writeIndex = 0;
261 readIndex = 0;
262 fillCount = 0;
263 isAudioPlaying = false;
264
265 isAudioInitialised = true;
266 IFDEBUG_AUDIO(sys_printf("xrick/audio: ready\n"););
267 return true;
268}
269
270/*
271 * Shutdown
272 */
273void syssnd_shutdown(void)
274{
275 if (!isAudioInitialised)
276 {
277 return;
278 }
279
280 /* Stop playback. */
281 rb->pcm_play_stop();
282
283 /* Reset playing status. */
284 isAudioPlaying = false;
285
286 /* Restore default sampling rate. */
287 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
288 rb->pcm_apply_settings();
289
290 rb->talk_disable(false);
291
292 isAudioInitialised = false;
293 IFDEBUG_AUDIO(sys_printf("xrick/audio: stop\n"););
294}
295
296/*
297 * Play a sound
298 *
299 * loop: number of times the sound should be played, -1 to loop forever
300 *
301 * NOTE if sound is already playing, simply reset it (i.e. can not have
302 * twice the same sound playing -- tends to become noisy when too many
303 * bad guys die at the same time).
304 */
305void syssnd_play(sound_t *sound, S8 loop)
306{
307 size_t c;
308
309 if (!isAudioInitialised || !sound)
310 {
311 return;
312 }
313
314 c = 0;
315 while (channels[c].sound != sound &&
316 channels[c].loop != 0 &&
317 c < SYSSND_MIX_CHANNELS)
318 {
319 c++;
320 }
321 if (c >= SYSSND_MIX_CHANNELS)
322 {
323 return;
324 }
325
326 if (!sound->buf)
327 {
328 syssnd_load(sound);
329 if (!sound->buf)
330 {
331 sys_error("(audio) can not load %s", sound->name);
332 return;
333 }
334 }
335
336 IFDEBUG_AUDIO(
337 if (channels[c].sound == sound)
338 {
339 sys_printf("xrick/audio: already playing %s on channel %d - resetting\n",
340 sound->name, c);
341 }
342 else
343 {
344 sys_printf("xrick/audio: playing %s on channel %d\n", sound->name, c);
345 }
346 );
347
348 channels[c].loop = loop;
349 channels[c].sound = sound;
350 channels[c].buf = sound->buf;
351 channels[c].len = sound->len;
352}
353
354/*
355 * Pause all sounds
356 */
357void syssnd_pauseAll(bool pause)
358{
359 if (!isAudioInitialised)
360 {
361 return;
362 }
363
364 rb->pcm_play_lock();
365 rb->mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
366 rb->pcm_play_unlock();
367}
368
369/*
370 * Stop a sound
371 */
372void syssnd_stop(sound_t *sound)
373{
374 size_t c;
375
376 if (!isAudioInitialised || !sound)
377 {
378 return;
379 }
380
381 for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
382 {
383 if (channels[c].sound == sound)
384 {
385 endChannel(c);
386 }
387 }
388}
389
390/*
391 * Stops all channels.
392 */
393void syssnd_stopAll(void)
394{
395 size_t c;
396
397 if (!isAudioInitialised)
398 {
399 return;
400 }
401
402 for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
403 {
404 if (channels[c].sound)
405 {
406 endChannel(c);
407 }
408 }
409}
410
411/*
412 * Load a sound.
413 */
414void syssnd_load(sound_t *sound)
415{
416 int bytesRead;
417 file_t fp;
418 bool success;
419
420 if (!isAudioInitialised || !sound)
421 {
422 return;
423 }
424
425 success = false;
426 do
427 {
428 sound->buf = sysmem_push(sound->len);
429 if (!sound->buf)
430 {
431 sys_error("(audio) not enough memory for \"%s\", %d bytes needed", sound->name, sound->len);
432 break;
433 }
434
435 fp = sysfile_open(sound->name);
436 if (!fp)
437 {
438 sys_error("(audio) unable to open \"%s\"", sound->name);
439 break;
440 }
441
442 sysfile_seek(fp, sizeof(wave_header_t), SEEK_SET); /* skip WAVE header */
443
444 bytesRead = sysfile_read(fp, sound->buf, sound->len, 1);
445 sysfile_close(fp);
446 if (bytesRead != 1)
447 {
448 sys_error("(audio) unable to read from \"%s\"", sound->name);
449 break;
450 }
451
452 success = true;
453 } while (false);
454
455 if (!success)
456 {
457 sysmem_pop(sound->buf);
458 sound->buf = NULL;
459 sound->len = 0;
460 return;
461 }
462
463 IFDEBUG_AUDIO(sys_printf("xrick/audio: successfully loaded \"%s\"\n", sound->name););
464}
465
466/*
467 * Unload a sound.
468 */
469void syssnd_unload(sound_t *sound)
470{
471 if (!isAudioInitialised || !sound || !sound->buf)
472 {
473 return;
474 }
475
476 sysmem_pop(sound->buf);
477 sound->buf = NULL;
478 sound->len = 0;
479}
480
481#endif /* ENABLE_SOUND */
482
483/* eof */
diff --git a/apps/plugins/xrick/system/syssnd_rockbox.h b/apps/plugins/xrick/system/syssnd_rockbox.h
new file mode 100644
index 0000000000..41bd7d8454
--- /dev/null
+++ b/apps/plugins/xrick/system/syssnd_rockbox.h
@@ -0,0 +1,48 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#ifndef _SYSSND_ROCKBOX_H
26#define _SYSSND_ROCKBOX_H
27
28#include "xrick/config.h"
29
30#ifdef ENABLE_SOUND
31
32#include "xrick/system/system.h"
33
34typedef struct {
35 sound_t *sound;
36 U8 *buf;
37 U32 len;
38 S8 loop;
39} channel_t;
40
41extern void syssnd_load(sound_t *);
42extern void syssnd_unload(sound_t *);
43
44#endif /* ENABLE_SOUND */
45
46#endif /* ndef _SYSSND_ROCKBOX_H */
47
48/* eof */
diff --git a/apps/plugins/xrick/system/system.h b/apps/plugins/xrick/system/system.h
new file mode 100644
index 0000000000..d4dda3d5d4
--- /dev/null
+++ b/apps/plugins/xrick/system/system.h
@@ -0,0 +1,178 @@
1/*
2 * xrick/system/system.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _SYSTEM_H
17#define _SYSTEM_H
18
19/*
20 * Detect GCC
21 */
22#ifdef __GNUC__
23/*
24 * make POSIX functions available
25 */
26# ifndef _POSIX_SOURCE
27# define _POSIX_SOURCE
28# endif
29#endif
30
31/*
32 * Detect Microsoft Visual C
33 */
34#ifdef _MSC_VER
35/*
36 * FIXME disable "integral size mismatch in argument; conversion supplied" warning
37 * as long as the code has not been cleared -- there are so many of them...
38 */
39#pragma warning( disable : 4761 )
40#endif
41
42/*
43 * Detect Microsoft Windows
44 */
45#if !defined( __WIN32__ ) && ( defined( WIN32 ) || defined( _WIN32 ) )
46#define __WIN32__
47#endif
48
49#include "xrick/config.h"
50#include "xrick/rects.h"
51#include "xrick/data/img.h"
52#ifdef ENABLE_SOUND
53#include "xrick/data/sounds.h"
54#endif
55
56#include <stddef.h> /* size_t */
57#include <sys/types.h> /* off_t */
58
59/*
60 * main section
61 */
62extern bool sys_init(int, char **);
63extern void sys_shutdown(void);
64extern void sys_error(const char *, ...);
65extern void sys_printf(const char *, ...);
66extern void sys_snprintf(char *, size_t, const char *, ...);
67extern size_t sys_strlen(const char *);
68extern U32 sys_gettime(void);
69extern void sys_yield(void);
70extern bool sys_cacheData(void);
71extern void sys_uncacheData(void);
72
73/*
74 * memory section
75 */
76extern bool sysmem_init(void);
77extern void sysmem_shutdown(void);
78extern void *sysmem_push(size_t);
79extern void sysmem_pop(void *);
80
81/*
82 * video section
83 */
84#define SYSVID_ZOOM 2
85#define SYSVID_MAXZOOM 4
86#define SYSVID_WIDTH 320
87#define SYSVID_HEIGHT 200
88
89extern U8 *sysvid_fb; /* frame buffer */
90
91extern bool sysvid_init(void);
92extern void sysvid_shutdown(void);
93extern void sysvid_update(const rect_t *);
94extern void sysvid_clear(void);
95extern void sysvid_zoom(S8);
96extern void sysvid_toggleFullscreen(void);
97extern void sysvid_setGamePalette(void);
98extern void sysvid_setPalette(img_color_t *, U16);
99
100/*
101 * file management section
102 */
103typedef void *file_t;
104
105extern const char *sysfile_defaultPath;
106
107extern bool sysfile_setRootPath(const char *);
108extern void sysfile_clearRootPath(void);
109
110extern file_t sysfile_open(const char *);
111extern int sysfile_seek(file_t file, long offset, int origin);
112extern int sysfile_tell(file_t);
113extern off_t sysfile_size(file_t);
114extern int sysfile_read(file_t, void *, size_t, size_t);
115extern void sysfile_close(file_t);
116
117/*
118 * events section
119 */
120extern void sysevt_poll(void);
121extern void sysevt_wait(void);
122
123/*
124 * keyboard section
125 */
126extern U8 syskbd_up;
127extern U8 syskbd_down;
128extern U8 syskbd_left;
129extern U8 syskbd_right;
130extern U8 syskbd_pause;
131extern U8 syskbd_end;
132extern U8 syskbd_xtra;
133extern U8 syskbd_fire;
134
135/*
136 * sound section
137 */
138#ifdef ENABLE_SOUND
139extern const U8 syssnd_period; /* time between each sound update, in millisecond */
140
141extern bool syssnd_init(void);
142extern void syssnd_shutdown(void);
143extern void syssnd_update(void);
144extern void syssnd_vol(S8);
145extern void syssnd_toggleMute(void);
146extern void syssnd_play(sound_t *, S8);
147extern void syssnd_pauseAll(bool);
148extern void syssnd_stop(sound_t *);
149extern void syssnd_stopAll(void);
150#endif /* ENABLE_ SOUND */
151
152/*
153 * args section
154 */
155extern int sysarg_args_period;
156extern int sysarg_args_map;
157extern int sysarg_args_submap;
158extern int sysarg_args_fullscreen;
159extern int sysarg_args_zoom;
160#ifdef ENABLE_SOUND
161extern bool sysarg_args_nosound;
162extern int sysarg_args_vol;
163#endif /* ENABLE_ SOUND */
164extern const char *sysarg_args_data;
165
166extern bool sysarg_init(int, char **);
167
168/*
169 * joystick section
170 */
171#ifdef ENABLE_JOYSTICK
172extern bool sysjoy_init(void);
173extern void sysjoy_shutdown(void);
174#endif
175
176#endif /* ndef _SYSTEM_H */
177
178/* eof */
diff --git a/apps/plugins/xrick/system/system_rockbox.c b/apps/plugins/xrick/system/system_rockbox.c
new file mode 100644
index 0000000000..3b5f96a4ed
--- /dev/null
+++ b/apps/plugins/xrick/system/system_rockbox.c
@@ -0,0 +1,262 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27#include "xrick/config.h"
28#ifdef ENABLE_SOUND
29#include "xrick/system/syssnd_rockbox.h"
30#endif
31
32#include "plugin.h"
33
34enum { LINE_LENGTH = 80 };
35
36/*
37* Error
38*/
39void sys_error(const char *err, ...)
40{
41 va_list argptr;
42 char s[LINE_LENGTH];
43
44 /* prepare message */
45 va_start(argptr, err);
46 rb->vsnprintf(s, sizeof(s), err, argptr);
47 va_end(argptr);
48
49 /* print error message */
50 rb->splashf(HZ*3, ID2P(LANG_ERROR_FORMATSTR), s);
51 DEBUGF("Error: %s\n", s);
52}
53
54/*
55* Print a message to standard output
56*/
57void sys_printf(const char *msg, ...)
58{
59 va_list argptr;
60 char s[LINE_LENGTH];
61
62 /* prepare message */
63 va_start(argptr, msg);
64 rb->vsnprintf(s, sizeof(s), msg, argptr);
65 va_end(argptr);
66
67 /* print message */
68 DEBUGF("%s",s);
69
70#ifdef ENABLE_SYSPRINTF_TO_SCREEN
71 {
72 static int currentYPos = 0;
73 size_t i;
74
75 /* Device LCDs display newlines funny. */
76 for(i = 0; s[i] != '\0'; ++i)
77 {
78 if(s[i] == '\n')
79 {
80 s[i] = ' ';
81 }
82 }
83
84 rb->lcd_putsxy(1, currentYPos, (unsigned char *)s);
85 rb->lcd_update();
86
87 currentYPos += 12;
88 if(currentYPos > LCD_HEIGHT-12)
89 {
90 currentYPos = 0;
91 rb->lcd_clear_display();
92 }
93 }
94#endif /* ENABLE_SYSPRINTF_TO_SCREEN */
95}
96
97/*
98* Print a message to string buffer
99*/
100void sys_snprintf(char *buf, size_t size, const char *msg, ...)
101{
102 va_list argptr;
103
104 va_start(argptr, msg);
105 rb->vsnprintf(buf, size, msg, argptr);
106 va_end(argptr);
107}
108
109/*
110* Returns string length
111*/
112size_t sys_strlen(const char * str)
113{
114 return rb->strlen(str);
115}
116
117/*
118* Return number of milliseconds elapsed since first call
119*/
120U32 sys_gettime(void)
121{
122 long ticks = *(rb->current_tick);
123 return (U32)((ticks * 1000) / HZ);
124}
125
126/*
127* Yield execution to another thread
128*/
129void sys_yield(void)
130{
131 rb->yield();
132}
133
134/*
135* Initialize system
136*/
137bool sys_init(int argc, char **argv)
138{
139#ifdef HAVE_ADJUSTABLE_CPU_FREQ
140 rb->cpu_boost(true);
141#endif
142
143 if (!sysarg_init(argc, argv))
144 {
145 return false;
146 }
147 if (!sysmem_init())
148 {
149 return false;
150 }
151 if (!sysvid_init())
152 {
153 return false;
154 }
155#ifdef ENABLE_SOUND
156 if (!sysarg_args_nosound && !syssnd_init())
157 {
158 return false;
159 }
160#endif
161 if (!sysfile_setRootPath(sysarg_args_data? sysarg_args_data : sysfile_defaultPath))
162 {
163 return false;
164 }
165 return true;
166}
167
168/*
169* Shutdown system
170*/
171void sys_shutdown(void)
172{
173 sysfile_clearRootPath();
174#ifdef ENABLE_SOUND
175 syssnd_shutdown();
176#endif
177 sysvid_shutdown();
178 sysmem_shutdown();
179
180#ifdef HAVE_ADJUSTABLE_CPU_FREQ
181 rb->cpu_boost(false);
182#endif
183}
184
185/*
186* Preload data before entering main loop
187*/
188bool sys_cacheData(void)
189{
190#ifdef ENABLE_SOUND
191 syssnd_load(soundGameover);
192 syssnd_load(soundSbonus2);
193 syssnd_load(soundBullet);
194 syssnd_load(soundBombshht);
195 syssnd_load(soundExplode);
196 syssnd_load(soundStick);
197 syssnd_load(soundWalk);
198 syssnd_load(soundCrawl);
199 syssnd_load(soundJump);
200 syssnd_load(soundPad);
201 syssnd_load(soundBox);
202 syssnd_load(soundBonus);
203 syssnd_load(soundSbonus1);
204 syssnd_load(soundDie);
205 syssnd_load(soundEntity[0]);
206 syssnd_load(soundEntity[1]);
207 syssnd_load(soundEntity[2]);
208 syssnd_load(soundEntity[3]);
209 syssnd_load(soundEntity[4]);
210 syssnd_load(soundEntity[5]);
211 syssnd_load(soundEntity[6]);
212 syssnd_load(soundEntity[7]);
213 syssnd_load(soundEntity[8]);
214 syssnd_load(soundTune0);
215 syssnd_load(soundTune1);
216 syssnd_load(soundTune2);
217 syssnd_load(soundTune3);
218 syssnd_load(soundTune4);
219 syssnd_load(soundTune5);
220#endif /* ENABLE_SOUND */
221 return true;
222}
223
224/*
225* Clear preloaded data before shutdown
226*/
227void sys_uncacheData(void)
228{
229#ifdef ENABLE_SOUND
230 syssnd_unload(soundTune5);
231 syssnd_unload(soundTune4);
232 syssnd_unload(soundTune3);
233 syssnd_unload(soundTune2);
234 syssnd_unload(soundTune1);
235 syssnd_unload(soundTune0);
236 syssnd_unload(soundEntity[8]);
237 syssnd_unload(soundEntity[7]);
238 syssnd_unload(soundEntity[6]);
239 syssnd_unload(soundEntity[5]);
240 syssnd_unload(soundEntity[4]);
241 syssnd_unload(soundEntity[3]);
242 syssnd_unload(soundEntity[2]);
243 syssnd_unload(soundEntity[1]);
244 syssnd_unload(soundEntity[0]);
245 syssnd_unload(soundDie);
246 syssnd_unload(soundSbonus1);
247 syssnd_unload(soundBonus);
248 syssnd_unload(soundBox);
249 syssnd_unload(soundPad);
250 syssnd_unload(soundJump);
251 syssnd_unload(soundCrawl);
252 syssnd_unload(soundWalk);
253 syssnd_unload(soundStick);
254 syssnd_unload(soundExplode);
255 syssnd_unload(soundBombshht);
256 syssnd_unload(soundBullet);
257 syssnd_unload(soundSbonus2);
258 syssnd_unload(soundGameover);
259#endif /* ENABLE_SOUND */
260}
261
262/* eof */
diff --git a/apps/plugins/xrick/system/sysvid_rockbox.c b/apps/plugins/xrick/system/sysvid_rockbox.c
new file mode 100644
index 0000000000..e93522f6c9
--- /dev/null
+++ b/apps/plugins/xrick/system/sysvid_rockbox.c
@@ -0,0 +1,409 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/system/system.h"
26
27#include "xrick/config.h"
28#include "xrick/draw.h"
29#include "xrick/game.h"
30#include "xrick/data/img.h"
31#include "xrick/debug.h"
32
33#include "plugin.h"
34#include "lib/helper.h"
35
36/*
37 * Global variables
38 */
39U8 *sysvid_fb = NULL; /* xRick generic 320x200 8bpp frame buffer */
40
41/*
42 * Local variables
43 */
44static fb_data palette[256] IBSS_ATTR;
45static bool isVideoInitialised = false;
46#ifndef HAVE_LCD_COLOR
47# include "lib/grey.h"
48GREY_INFO_STRUCT_IRAM
49static unsigned char greybuffer[LCD_HEIGHT * LCD_WIDTH] IBSS_ATTR; /* off screen buffer */
50static unsigned char *gbuf;
51# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
52enum { GREYBUFSIZE = (((LCD_WIDTH+7)/8)*LCD_HEIGHT*16+200) };
53# else
54enum { GREYBUFSIZE = (LCD_WIDTH*((LCD_HEIGHT+7)/8)*16+200) };
55# endif
56#endif /* ndef HAVE_LCD_COLOR */
57
58#ifdef HAVE_LCD_COLOR
59static fb_data *lcd_fb = NULL;
60#endif
61
62#if (LCD_HEIGHT < SYSVID_HEIGHT)
63enum { ROW_RESIZE_STEP = (LCD_HEIGHT << 16) / SYSVID_HEIGHT };
64
65static bool rowsToSkip[SYSVID_HEIGHT];
66
67/*
68 *
69 */
70static void calculateRowsToSkip(void)
71{
72 U32 currentRow, prevResizedRow;
73
74 prevResizedRow = 0;
75 rowsToSkip[0] = false;
76
77 for (currentRow = 1; currentRow < SYSVID_HEIGHT; ++currentRow)
78 {
79 U32 resizedRow = (currentRow * ROW_RESIZE_STEP) >> 16;
80 if (resizedRow == prevResizedRow)
81 {
82 rowsToSkip[currentRow] = true;
83 }
84 prevResizedRow = resizedRow;
85 }
86}
87#endif /* (LCD_HEIGHT < SYSVID_HEIGHT) */
88
89#if (LCD_WIDTH < SYSVID_WIDTH)
90enum { COLUMN_RESIZE_STEP = (LCD_WIDTH << 16) / (SYSVID_WIDTH + (DRAW_XYMAP_SCRLEFT*2)) };
91
92static bool columnsToSkip[SYSVID_WIDTH + (DRAW_XYMAP_SCRLEFT*2)];
93
94/*
95 *
96 */
97static void calculateColumnsToSkip(void)
98{
99 U32 currentColumn, prevResizedColumn;
100
101 prevResizedColumn = 0;
102 columnsToSkip[0] = false;
103
104 for (currentColumn = 1; currentColumn < (SYSVID_WIDTH + (DRAW_XYMAP_SCRLEFT*2)); ++currentColumn)
105 {
106 U32 resizedColumn = (currentColumn * COLUMN_RESIZE_STEP) >> 16;
107 if (resizedColumn == prevResizedColumn)
108 {
109 columnsToSkip[currentColumn] = true;
110 }
111 prevResizedColumn = resizedColumn;
112 }
113}
114#endif /* (LCD_WIDTH < SYSVID_WIDTH) */
115
116/*
117 *
118 */
119void sysvid_setPalette(img_color_t *pal, U16 n)
120{
121 U16 i;
122
123 for (i = 0; i < n; i++)
124 {
125#ifdef HAVE_LCD_COLOR
126#if (LCD_PIXELFORMAT == RGB888) || (LCD_PIXELFORMAT == XRGB8888)
127 unsigned long x = LCD_RGBPACK(pal[i].r, pal[i].g, pal[i].b);
128 palette[i] = FB_SCALARPACK(x);
129#else
130 palette[i] = LCD_RGBPACK(pal[i].r, pal[i].g, pal[i].b);
131#endif
132#else
133 palette[i] = ((3 * pal[i].r) + (6 * pal[i].g) + pal[i].b) / 10;
134#endif
135 }
136}
137
138/*
139 *
140 */
141void sysvid_setGamePalette()
142{
143 sysvid_setPalette(game_colors, game_color_count);
144}
145
146/*
147 * Update screen
148 */
149void sysvid_update(const rect_t *rects)
150{
151 unsigned sourceRow, sourceLastRow;
152 unsigned sourceColumn, sourceLastColumn;
153 unsigned resizedRow, resizedColumn;
154 unsigned resizedWidth, resizedHeight;
155 unsigned x, y;
156 U8 *sourceBuf, *sourceTemp;
157 fb_data *destBuf, *destTemp;
158
159 if (!rects)
160 {
161 return;
162 }
163
164 while (rects)
165 {
166 sourceRow = rects->y;
167 sourceLastRow = sourceRow + rects->height;
168 sourceColumn = rects->x;
169 sourceLastColumn = sourceColumn + rects->width;
170
171#if (LCD_WIDTH < SYSVID_WIDTH)
172 /* skip black borders */
173 if (sourceColumn < -DRAW_XYMAP_SCRLEFT)
174 {
175 sourceColumn = -DRAW_XYMAP_SCRLEFT;
176 }
177 if (sourceLastColumn > (SYSVID_WIDTH + DRAW_XYMAP_SCRLEFT))
178 {
179 sourceLastColumn = SYSVID_WIDTH + DRAW_XYMAP_SCRLEFT;
180 }
181
182 /* skip unwanted columns */
183 while (columnsToSkip[sourceColumn + DRAW_XYMAP_SCRLEFT] /* && sourceColumn < (SYSVID_WIDTH + DRAW_XYMAP_SCRLEFT) */)
184 {
185 ++sourceColumn;
186 }
187
188 resizedColumn = ((sourceColumn + DRAW_XYMAP_SCRLEFT) * COLUMN_RESIZE_STEP) >> 16;
189 resizedWidth = 0;
190#else
191 resizedColumn = sourceColumn;
192 resizedWidth = rects->width;
193#endif /* (LCD_WIDTH < SYSVID_WIDTH) */
194
195#if (LCD_HEIGHT < SYSVID_HEIGHT)
196 /* skip unwanted rows */
197 while (rowsToSkip[sourceRow] /* && sourceRow < SYSVID_HEIGHT */)
198 {
199 ++sourceRow;
200 }
201
202 resizedRow = (sourceRow * ROW_RESIZE_STEP) >> 16;
203 resizedHeight = 0;
204#else
205 resizedRow = sourceRow;
206 resizedHeight = rects->height;
207#endif /* (LCD_HEIGHT < SYSVID_HEIGHT) */
208
209 sourceBuf = sysvid_fb;
210 sourceBuf += sourceColumn + sourceRow * SYSVID_WIDTH;
211
212#ifdef HAVE_LCD_COLOR
213 if(!lcd_fb)
214 {
215 struct viewport *vp_main = rb->lcd_set_viewport(NULL);
216 lcd_fb = vp_main->buffer->fb_ptr;
217 }
218 destBuf = lcd_fb;
219#else
220 destBuf = (fb_data*) greybuffer;
221#endif /* HAVE_LCD_COLOR */
222 destBuf += resizedColumn + resizedRow * LCD_WIDTH;
223
224#if (LCD_WIDTH < SYSVID_WIDTH)
225 sourceColumn += DRAW_XYMAP_SCRLEFT;
226 sourceLastColumn += DRAW_XYMAP_SCRLEFT;
227#endif /* (LCD_WIDTH < SYSVID_WIDTH) */
228
229 for (y = sourceRow; y < sourceLastRow; ++y)
230 {
231#if (LCD_HEIGHT < SYSVID_HEIGHT)
232 if (rowsToSkip[y])
233 {
234 sourceBuf += SYSVID_WIDTH;
235 continue;
236 }
237
238 ++resizedHeight;
239#endif /* (LCD_HEIGHT < SYSVID_HEIGHT) */
240
241 sourceTemp = sourceBuf;
242 destTemp = destBuf;
243 for (x = sourceColumn; x < sourceLastColumn; ++x)
244 {
245#if (LCD_WIDTH < SYSVID_WIDTH)
246 if (columnsToSkip[x])
247 {
248 ++sourceTemp;
249 continue;
250 }
251
252 if (y == sourceRow)
253 {
254 ++resizedWidth;
255 }
256#endif /* (LCD_WIDTH < SYSVID_WIDTH) */
257
258 *destTemp = palette[*sourceTemp];
259
260 ++sourceTemp;
261 ++destTemp;
262 }
263
264 sourceBuf += SYSVID_WIDTH;
265 destBuf += LCD_WIDTH;
266 }
267
268#ifdef HAVE_LCD_COLOR
269 IFDEBUG_VIDEO2(
270 for (y = resizedRow; y < resizedRow + resizedHeight; ++y)
271 {
272 destBuf = lcd_fb + resizedColumn + y * LCD_WIDTH;
273 *destBuf = palette[0x01];
274 *(destBuf + resizedWidth - 1) = palette[0x01];
275 }
276
277 for (x = resizedColumn; x < resizedColumn + resizedWidth; ++x)
278 {
279 destBuf = rb->lcd_fb + x + resizedRow * LCD_WIDTH;
280 *destBuf = palette[0x01];
281 *(destBuf + (resizedHeight - 1) * LCD_WIDTH) = palette[0x01];
282 }
283 );
284
285 rb->lcd_update_rect(resizedColumn, resizedRow, resizedWidth, resizedHeight);
286#else
287 grey_ub_gray_bitmap_part(greybuffer, resizedColumn, resizedRow, LCD_WIDTH, resizedColumn, resizedRow, resizedWidth, resizedHeight);
288#endif /* HAVE_LCD_COLOR */
289
290 rects = rects->next;
291 }
292}
293
294/*
295 * Clear screen
296 * (077C)
297 */
298void sysvid_clear(void)
299{
300 rb->memset(sysvid_fb, 0, sizeof(U8) * SYSVID_WIDTH * SYSVID_HEIGHT);
301}
302
303/*
304 * Initialise video
305 */
306bool sysvid_init()
307{
308 bool success;
309
310 if (isVideoInitialised)
311 {
312 return true;
313 }
314
315 IFDEBUG_VIDEO(sys_printf("xrick/video: start\n"););
316
317 success = false;
318 do
319 {
320 /* allocate xRick generic frame buffer into memory */
321 sysvid_fb = sysmem_push(sizeof(U8) * SYSVID_WIDTH * SYSVID_HEIGHT);
322 if (!sysvid_fb)
323 {
324 sys_error("(video) unable to allocate frame buffer");
325 break;
326 }
327
328#ifndef HAVE_LCD_COLOR
329 gbuf = sysmem_push(GREYBUFSIZE);
330 if (!gbuf)
331 {
332 sys_error("(video) unable to allocate buffer for greyscale functions");
333 break;
334 }
335
336 if (!grey_init(gbuf, GREYBUFSIZE, GREY_ON_COP, LCD_WIDTH, LCD_HEIGHT, NULL))
337 {
338 sys_error("(video) not enough memory to initialise greyscale functions");
339 break;
340 }
341#endif /* ndef HAVE_LCD_COLOR */
342
343 success = true;
344 } while (false);
345
346 if (!success)
347 {
348#ifndef HAVE_LCD_COLOR
349 sysmem_pop(gbuf);
350#endif
351 sysmem_pop(sysvid_fb);
352 return false;
353 }
354
355#if (LCD_HEIGHT < SYSVID_HEIGHT)
356 calculateRowsToSkip();
357#endif
358#if (LCD_WIDTH < SYSVID_WIDTH)
359 calculateColumnsToSkip();
360#endif
361
362#if LCD_DEPTH > 1
363 rb->lcd_set_backdrop(NULL);
364#endif
365 /* Turn off backlight timeout */
366 backlight_ignore_timeout();
367
368 rb->lcd_set_foreground(LCD_WHITE);
369 rb->lcd_set_background(LCD_BLACK);
370 rb->lcd_clear_display();
371
372#ifdef HAVE_LCD_COLOR
373 rb->lcd_update();
374#else
375 /* switch on greyscale overlay */
376 grey_show(true);
377#endif /* HAVE_LCD_COLOR */
378
379 isVideoInitialised = true;
380 IFDEBUG_VIDEO(sys_printf("xrick/video: ready\n"););
381 return true;
382}
383
384/*
385 * Shutdown video
386 */
387void sysvid_shutdown(void)
388{
389 if (!isVideoInitialised)
390 {
391 return;
392 }
393
394#ifndef HAVE_LCD_COLOR
395 grey_show(false);
396 grey_release();
397
398 sysmem_pop(gbuf);
399#endif /* ndef HAVE_LCD_COLOR */
400 sysmem_pop(sysvid_fb);
401
402 /* Turn on backlight timeout (revert to settings) */
403 backlight_use_settings();
404
405 isVideoInitialised = false;
406 IFDEBUG_VIDEO(sys_printf("xrick/video: stop\n"););
407}
408
409/* eof */
diff --git a/apps/plugins/xrick/util.c b/apps/plugins/xrick/util.c
new file mode 100644
index 0000000000..8926a00e5d
--- /dev/null
+++ b/apps/plugins/xrick/util.c
@@ -0,0 +1,230 @@
1/*
2 * xrick/util.c
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#include "xrick/util.h"
17
18#include "xrick/config.h"
19#include "xrick/game.h"
20#include "xrick/ents.h"
21#include "xrick/e_rick.h"
22#include "xrick/maps.h"
23#include "xrick/system/system.h"
24
25#include <string.h> /* memcpy */
26
27/*
28 * Full box test.
29 *
30 * ASM 1199
31 *
32 * e: entity to test against.
33 * x,y: coordinates to test.
34 * ret: true/(x,y) is within e's space, false/not.
35 */
36bool
37u_fboxtest(U8 e, S16 x, S16 y)
38{
39 if (ent_ents[e].x >= x ||
40 ent_ents[e].x + ent_ents[e].w < x ||
41 ent_ents[e].y >= y ||
42 ent_ents[e].y + ent_ents[e].h < y)
43 return false;
44 else
45 return true;
46}
47
48
49
50
51/*
52 * Box test (then whole e2 is checked agains the center of e1).
53 *
54 * ASM 113E
55 *
56 * e1: entity to test against (corresponds to DI in asm code).
57 * e2: entity to test (corresponds to SI in asm code).
58 * ret: true/intersect, false/not.
59 */
60bool
61u_boxtest(U8 e1, U8 e2)
62{
63 /* rick is special (may be crawling) */
64 if (e1 == E_RICK_NO)
65 return e_rick_boxtest(e2);
66
67 /*
68 * entity 1: x+0x05 to x+0x011, y to y+0x14
69 * entity 2: x to x+ .w, y to y+ .h
70 */
71 if (ent_ents[e1].x + 0x11 < ent_ents[e2].x ||
72 ent_ents[e1].x + 0x05 > ent_ents[e2].x + ent_ents[e2].w ||
73 ent_ents[e1].y + 0x14 < ent_ents[e2].y ||
74 ent_ents[e1].y > ent_ents[e2].y + ent_ents[e2].h - 1)
75 return false;
76 else
77 return true;
78}
79
80
81/*
82 * Compute the environment flag.
83 *
84 * ASM 0FBC if !crawl, else 103E
85 *
86 * x, y: coordinates where to compute the environment flag
87 * crawl: is rick crawling?
88 * rc0: anything CHANGED to the environment flag for crawling (6DBA)
89 * rc1: anything CHANGED to the environment flag (6DAD)
90 */
91void
92u_envtest(S16 x, S16 y, bool crawl, U8 *rc0, U8 *rc1)
93{
94 U8 i, xx;
95
96 /* prepare for ent #0 test */
97 ent_ents[ENT_ENTSNUM].x = x;
98 ent_ents[ENT_ENTSNUM].y = y;
99
100 i = 1;
101 if (!crawl) i++;
102 if (y & 0x0004) i++;
103
104 x += 4;
105 xx = (U8)x; /* FIXME? */
106
107 x = x >> 3; /* from pixels to tiles */
108 y = y >> 3; /* from pixels to tiles */
109
110 *rc0 = *rc1 = 0;
111
112 if (xx & 0x07) { /* tiles columns alignment */
113 if (crawl) {
114 *rc0 |= (map_eflg[map_map[y][x]] &
115 (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP));
116 *rc0 |= (map_eflg[map_map[y][x + 1]] &
117 (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP));
118 *rc0 |= (map_eflg[map_map[y][x + 2]] &
119 (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP));
120 y++;
121 }
122 do {
123 *rc1 |= (map_eflg[map_map[y][x]] &
124 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_FGND|
125 MAP_EFLG_LETHAL|MAP_EFLG_01));
126 *rc1 |= (map_eflg[map_map[y][x + 1]] &
127 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_FGND|
128 MAP_EFLG_LETHAL|MAP_EFLG_CLIMB|MAP_EFLG_01));
129 *rc1 |= (map_eflg[map_map[y][x + 2]] &
130 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_FGND|
131 MAP_EFLG_LETHAL|MAP_EFLG_01));
132 y++;
133 } while (--i > 0);
134
135 *rc1 |= (map_eflg[map_map[y][x]] &
136 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP|MAP_EFLG_FGND|
137 MAP_EFLG_LETHAL|MAP_EFLG_01));
138 *rc1 |= (map_eflg[map_map[y][x + 1]]);
139 *rc1 |= (map_eflg[map_map[y][x + 2]] &
140 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP|MAP_EFLG_FGND|
141 MAP_EFLG_LETHAL|MAP_EFLG_01));
142 }
143 else {
144 if (crawl) {
145 *rc0 |= (map_eflg[map_map[y][x]] &
146 (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP));
147 *rc0 |= (map_eflg[map_map[y][x + 1]] &
148 (MAP_EFLG_VERT|MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_WAYUP));
149 y++;
150 }
151 do {
152 *rc1 |= (map_eflg[map_map[y][x]] &
153 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_FGND|
154 MAP_EFLG_LETHAL|MAP_EFLG_CLIMB|MAP_EFLG_01));
155 *rc1 |= (map_eflg[map_map[y][x + 1]] &
156 (MAP_EFLG_SOLID|MAP_EFLG_SPAD|MAP_EFLG_FGND|
157 MAP_EFLG_LETHAL|MAP_EFLG_CLIMB|MAP_EFLG_01));
158 y++;
159 } while (--i > 0);
160
161 *rc1 |= (map_eflg[map_map[y][x]]);
162 *rc1 |= (map_eflg[map_map[y][x + 1]]);
163 }
164
165 /*
166 * If not lethal yet, and there's an entity on slot zero, and (x,y)
167 * boxtests this entity, then raise SOLID flag. This is how we make
168 * sure that no entity can move over the entity that is on slot zero.
169 *
170 * Beware! When game_cheat2 is set, this means that a block can
171 * move over rick without killing him -- but then rick is trapped
172 * because the block is solid.
173 */
174 if (!(*rc1 & MAP_EFLG_LETHAL)
175 && ent_ents[0].n
176 && u_boxtest(ENT_ENTSNUM, 0)) {
177 *rc1 |= MAP_EFLG_SOLID;
178 }
179
180 /* When game_cheat2 is set, the environment can not be lethal. */
181#ifdef ENABLE_CHEATS
182 if (game_cheat2) *rc1 &= ~MAP_EFLG_LETHAL;
183#endif
184}
185
186
187/*
188 * Check if x,y is within e trigger box.
189 *
190 * ASM 126F
191 * return: false if not in box, true if in box.
192 */
193bool
194u_trigbox(U8 e, S16 x, S16 y)
195{
196 U16 xmax, ymax;
197
198 xmax = ent_ents[e].trig_x + (ent_entdata[ent_ents[e].n & 0x7F].trig_w << 3);
199 ymax = ent_ents[e].trig_y + (ent_entdata[ent_ents[e].n & 0x7F].trig_h << 3);
200
201 if (xmax > 0xFF) xmax = 0xFF;
202
203 if (x <= ent_ents[e].trig_x || x > xmax ||
204 y <= ent_ents[e].trig_y || y > ymax)
205 return false;
206 else
207 return true;
208}
209
210/*
211 * Custom implementation of strdup function
212 */
213char *
214u_strdup(const char *sourceStr)
215{
216 char *destStr;
217 size_t length;
218
219 length = sys_strlen(sourceStr) + 1;
220 destStr = sysmem_push(length);
221 if (!destStr)
222 {
223 return NULL;
224 }
225 memcpy(destStr, sourceStr, length);
226 return destStr;
227}
228
229
230/* eof */
diff --git a/apps/plugins/xrick/util.h b/apps/plugins/xrick/util.h
new file mode 100644
index 0000000000..6a455ba0f6
--- /dev/null
+++ b/apps/plugins/xrick/util.h
@@ -0,0 +1,29 @@
1/*
2 * xrick/util.h
3 *
4 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net).
5 * Copyright (C) 2008-2014 Pierluigi Vicinanza.
6 * All rights reserved.
7 *
8 * The use and distribution terms for this software are contained in the file
9 * named README, which can be found in the root of this distribution. By
10 * using this software in any fashion, you are agreeing to be bound by the
11 * terms of this license.
12 *
13 * You must not remove this notice, or any other, from this software.
14 */
15
16#ifndef _UTIL_H
17#define _UTIL_H
18
19#include "xrick/system/basic_types.h"
20
21extern void u_envtest(S16, S16, bool, U8 *, U8 *);
22extern bool u_boxtest(U8, U8);
23extern bool u_fboxtest(U8, S16, S16);
24extern bool u_trigbox(U8, S16, S16);
25extern char * u_strdup(const char *);
26
27#endif /* ndef _UTIL_H */
28
29/* eof */
diff --git a/apps/plugins/xrick/xrick.make b/apps/plugins/xrick/xrick.make
new file mode 100644
index 0000000000..26406a957d
--- /dev/null
+++ b/apps/plugins/xrick/xrick.make
@@ -0,0 +1,31 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10XRICKSRCDIR := $(APPSDIR)/plugins/xrick
11XRICKBUILDDIR := $(BUILDDIR)/apps/plugins/xrick
12
13INCLUDES += -I$(XRICKSRCDIR)../ \
14 -I$(XRICKSRCDIR)/3rd_party
15
16ROCKS += $(XRICKBUILDDIR)/xrick.rock
17
18XRICK_SRC := $(call preprocess, $(XRICKSRCDIR)/SOURCES)
19XRICK_OBJ := $(call c2obj, $(XRICK_SRC))
20
21# add source files to OTHER_SRC to get automatic dependencies
22OTHER_SRC += $(XRICK_SRC)
23
24XRICKCFLAGS = $(PLUGINFLAGS) -std=gnu99 -O2
25
26$(XRICKBUILDDIR)/xrick.rock: $(XRICK_OBJ)
27
28# new rule needed to use extra compile flags
29$(XRICKBUILDDIR)/%.o: $(XRICKSRCDIR)/%.c
30 $(SILENT)mkdir -p $(dir $@)
31 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(XRICKCFLAGS) -c $< -o $@
diff --git a/bootloader/rocker_linux.c b/bootloader/rocker_linux.c
index c0cba2776c..4d095b8885 100644
--- a/bootloader/rocker_linux.c
+++ b/bootloader/rocker_linux.c
@@ -145,6 +145,7 @@ enum boot_mode
145 BOOT_OF, 145 BOOT_OF,
146 BOOT_COUNT, 146 BOOT_COUNT,
147 BOOT_STOP, /* power down/suspend */ 147 BOOT_STOP, /* power down/suspend */
148 BOOT_CANARY,
148}; 149};
149 150
150static void display_text_center(int y, const char *text) 151static void display_text_center(int y, const char *text)
@@ -165,16 +166,17 @@ static void display_text_centerf(int y, const char *format, ...)
165} 166}
166 167
167/* get timeout before taking action if the user doesn't touch the device */ 168/* get timeout before taking action if the user doesn't touch the device */
168static int get_inactivity_tmo(void) 169static int get_inactivity_tmo(int same_as_last)
169{ 170{
170#if defined(HAS_BUTTON_HOLD) 171#if defined(HAS_BUTTON_HOLD)
171 if(button_hold()) 172 if(button_hold())
172 return 5 * HZ; /* Inactivity timeout when on hold */ 173 return 5 * HZ; /* Inactivity timeout when on hold */
173 else 174 else
174#endif 175#endif
175 return 10 * HZ; /* Inactivity timeout when not on hold */ 176 if (same_as_last)
176 177 return 1 * HZ; /* Timeout when mode is the same as the previous mode */
177 // XXX if booting the last selection, use a short timeout? 178 else
179 return 10 * HZ; /* Default timeout */
178} 180}
179 181
180/* return action on idle timeout */ 182/* return action on idle timeout */
@@ -188,34 +190,38 @@ static enum boot_mode inactivity_action(enum boot_mode cur_selection)
188 return cur_selection; /* return last choice */ 190 return cur_selection; /* return last choice */
189} 191}
190 192
191/* we store the boot mode in a file in /tmp so we can reload it between 'boots' 193static int mounted = 0;
192 * (since the mostly suspends instead of powering down) */
193static enum boot_mode load_boot_mode(enum boot_mode mode)
194{
195 int fd = open(BASE_DIR "/.rockbox/rb_bl_mode.txt", O_RDONLY);
196 if(fd >= 0)
197 {
198 read(fd, &mode, sizeof(mode));
199 close(fd);
200 }
201 return mode;
202}
203 194
204static void mount_storage(int enable) 195static void mount_storage(int enable)
205{ 196{
206 if (enable) { 197 if (enable && !mounted) {
207 system("/bin/mkdir -p " BASE_DIR); 198 system("/bin/mkdir -p " BASE_DIR);
208 if (system("/bin/mount /dev/mmcblk0 " BASE_DIR)) 199 if (system("/bin/mount /dev/mmcblk0 " BASE_DIR))
209 system("/bin/mount /dev/mmcblk0p1 " BASE_DIR); 200 system("/bin/mount /dev/mmcblk0p1 " BASE_DIR);
210 // XXX possibly invoke sys_serv -> "MOUNT:MOUNT:%s %s", blkdev, mntpoint 201 // XXX possibly invoke sys_serv -> "MOUNT:MOUNT:%s %s", blkdev, mntpoint
211 } else { 202 } else if (!enable && mounted) {
212 system("/bin/unmount " BASE_DIR); 203 system("/bin/unmount " BASE_DIR);
213 // XXX possibly invoke sys_serv -> "MOUNT:UNMOUNT:%s %s", mntpoint 204 // XXX possibly invoke sys_serv -> "MOUNT:UNMOUNT:%s %s", mntpoint
214 } 205 }
206 mounted = enable;
207}
208
209/* we store the boot mode in a file so we can reload it between 'boots' */
210static enum boot_mode load_boot_mode(enum boot_mode mode)
211{
212 mount_storage(true);
213 int fd = open(BASE_DIR "/.rockbox/rb_bl_mode.txt", O_RDONLY);
214 if(fd >= 0)
215 {
216 read(fd, &mode, sizeof(mode));
217 close(fd);
218 }
219 return mode;
215} 220}
216 221
217static void save_boot_mode(enum boot_mode mode) 222static void save_boot_mode(enum boot_mode mode)
218{ 223{
224 mount_storage(true);
219 int fd = open(BASE_DIR "/.rockbox/rb_bl_mode.txt", O_RDWR | O_CREAT | O_TRUNC); 225 int fd = open(BASE_DIR "/.rockbox/rb_bl_mode.txt", O_RDWR | O_CREAT | O_TRUNC);
220 if(fd >= 0) 226 if(fd >= 0)
221 { 227 {
@@ -227,9 +233,9 @@ static void save_boot_mode(enum boot_mode mode)
227static enum boot_mode get_boot_mode(void) 233static enum boot_mode get_boot_mode(void)
228{ 234{
229 /* load previous mode, or start with rockbox if none */ 235 /* load previous mode, or start with rockbox if none */
230 enum boot_mode init_mode = load_boot_mode(BOOT_ROCKBOX); 236 enum boot_mode init_mode = load_boot_mode(BOOT_CANARY);
231 /* wait for user action */ 237 /* wait for user action */
232 enum boot_mode mode = init_mode; 238 enum boot_mode mode = (init_mode == BOOT_CANARY) ? BOOT_ROCKBOX : init_mode;
233 int last_activity = current_tick; 239 int last_activity = current_tick;
234#if defined(HAS_BUTTON_HOLD) 240#if defined(HAS_BUTTON_HOLD)
235 bool hold_status = button_hold(); 241 bool hold_status = button_hold();
@@ -244,7 +250,7 @@ static enum boot_mode get_boot_mode(void)
244 return mode; 250 return mode;
245 } 251 }
246 /* inactivity detection */ 252 /* inactivity detection */
247 int timeout = last_activity + get_inactivity_tmo(); 253 int timeout = last_activity + get_inactivity_tmo(init_mode == mode);
248 if(TIME_AFTER(current_tick, timeout)) 254 if(TIME_AFTER(current_tick, timeout))
249 { 255 {
250 /* save last choice */ 256 /* save last choice */
@@ -314,10 +320,14 @@ static enum boot_mode get_boot_mode(void)
314 if(btn == BUTTON_SELECT) 320 if(btn == BUTTON_SELECT)
315 break; 321 break;
316 /* left/right/up/down: change mode */ 322 /* left/right/up/down: change mode */
317 if(btn == BUTTON_LEFT || btn == BUTTON_DOWN) 323 if(btn == BUTTON_LEFT || btn == BUTTON_DOWN) {
318 mode = (mode + BOOT_COUNT - 1) % BOOT_COUNT; 324 mode = (mode + BOOT_COUNT - 1) % BOOT_COUNT;
319 if(btn == BUTTON_RIGHT || btn == BUTTON_UP) 325 init_mode = BOOT_CANARY;
326 }
327 if(btn == BUTTON_RIGHT || btn == BUTTON_UP) {
320 mode = (mode + 1) % BOOT_COUNT; 328 mode = (mode + 1) % BOOT_COUNT;
329 init_mode = BOOT_CANARY;
330 }
321 } 331 }
322 332
323 /* save mode */ 333 /* save mode */
@@ -642,8 +652,8 @@ int main(int argc, char **argv)
642 system("/bin/chmod +x /tmp/" BOOTFILE); 652 system("/bin/chmod +x /tmp/" BOOTFILE);
643 execl("/tmp/" BOOTFILE, BOOTFILE, NULL); 653 execl("/tmp/" BOOTFILE, BOOTFILE, NULL);
644 printf("execvp failed: %s\n", strerror(errno)); 654 printf("execvp failed: %s\n", strerror(errno));
645 /* fallback to OF in case of failure */ 655 error_screen("Cannot boot Rockbox!");
646 error_screen("Cannot boot Rockbox"); 656 mode = BOOT_TOOLS;
647 sleep(2 * HZ); 657 sleep(2 * HZ);
648 } 658 }
649 else 659 else
diff --git a/docs/CREDITS b/docs/CREDITS
index fba9aed0d3..a6620c720d 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -720,6 +720,8 @@ JJ Style
720Jerry Chapman 720Jerry Chapman
721Leander Lismond 721Leander Lismond
722Eren Akpolat 722Eren Akpolat
723Pierluigi Vicinanza
724Matteo Italia
723 725
724The libmad team 726The libmad team
725The wavpack team 727The wavpack team
@@ -761,3 +763,4 @@ The bsdiff team
761The libtomcrypt team 763The libtomcrypt team
762The microtar team (rxi and others) 764The microtar team (rxi and others)
763The UnifontEX and GNU Unifont teams 765The UnifontEX and GNU Unifont teams
766The xrick team
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index b245cbc09e..d82fb173cc 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -962,8 +962,8 @@ static int perform_soft_reset(void)
962 if (identify()) 962 if (identify())
963 return -5; 963 return -5;
964 964
965 if (set_features()) 965 if ((ret = set_features()))
966 return -2; 966 return -60 + ret;
967 967
968 if (set_multiple_mode(multisectors)) 968 if (set_multiple_mode(multisectors))
969 return -3; 969 return -3;
@@ -1013,7 +1013,7 @@ static int ata_power_on(void)
1013 1013
1014 rc = set_features(); 1014 rc = set_features();
1015 if (rc) 1015 if (rc)
1016 return rc * 10 - 2; 1016 return -60 + rc;
1017 1017
1018 if (set_multiple_mode(multisectors)) 1018 if (set_multiple_mode(multisectors))
1019 return -3; 1019 return -3;
@@ -1284,7 +1284,7 @@ int STORAGE_INIT_ATTR ata_init(void)
1284 goto error; 1284 goto error;
1285 } 1285 }
1286 1286
1287 rc = set_features(); 1287 rc = set_features(); // rror codes are between -1 and -49
1288 if (rc) { 1288 if (rc) {
1289 rc = -60 + rc; 1289 rc = -60 + rc;
1290 goto error; 1290 goto error;
@@ -1321,7 +1321,7 @@ int STORAGE_INIT_ATTR ata_init(void)
1321 } 1321 }
1322 rc = set_multiple_mode(multisectors); 1322 rc = set_multiple_mode(multisectors);
1323 if (rc) 1323 if (rc)
1324 rc = -70 + rc; 1324 rc = -100 + rc;
1325 1325
1326error: 1326error:
1327 mutex_unlock(&ata_mtx); 1327 mutex_unlock(&ata_mtx);
diff --git a/firmware/target/hosted/lcd-linuxfb.c b/firmware/target/hosted/lcd-linuxfb.c
index 14c8c30f89..5dda5cf1cc 100644
--- a/firmware/target/hosted/lcd-linuxfb.c
+++ b/firmware/target/hosted/lcd-linuxfb.c
@@ -59,14 +59,21 @@ void lcd_init_device(void)
59 panicf("Cannot read framebuffer fixed information"); 59 panicf("Cannot read framebuffer fixed information");
60 } 60 }
61 61
62#if 0 62 if(ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
63 /* check resolution and framebuffer size */
64 if(vinfo.xres != LCD_WIDTH || vinfo.yres != LCD_HEIGHT || vinfo.bits_per_pixel != LCD_DEPTH)
65 { 63 {
66 panicf("Unexpected framebuffer resolution: %dx%dx%d\n", vinfo.xres, 64 panicf("Cannot read framebuffer variable information");
67 vinfo.yres, vinfo.bits_per_pixel); 65 }
66
67 /* Make sure we match our desired bitdepth */
68 if (vinfo.bits_per_pixel != LCD_DEPTH || vinfo.xres != LCD_WIDTH || vinfo.yres != LCD_HEIGHT) {
69 vinfo.bits_per_pixel = LCD_DEPTH;
70 vinfo.xres = LCD_WIDTH;
71 vinfo.yres = LCD_HEIGHT;
72 if (ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo)) {
73 panicf("Cannot set framebuffer to %dx%dx%d",
74 vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
75 }
68 } 76 }
69#endif
70 /* Note: we use a framebuffer size of width*height*bbp. We cannot trust the 77 /* Note: we use a framebuffer size of width*height*bbp. We cannot trust the
71 * values returned by the driver for line_length */ 78 * values returned by the driver for line_length */
72 79
@@ -77,11 +84,6 @@ void lcd_init_device(void)
77 panicf("Cannot map framebuffer"); 84 panicf("Cannot map framebuffer");
78 } 85 }
79 86
80 if(ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
81 {
82 panicf("Cannot read framebuffer variable information");
83 }
84
85 memset(framebuffer, 0, finfo.smem_len); 87 memset(framebuffer, 0, finfo.smem_len);
86 88
87#ifdef HAVE_LCD_ENABLE 89#ifdef HAVE_LCD_ENABLE
diff --git a/firmware/target/hosted/samsungypr/lcd-ypr.c b/firmware/target/hosted/samsungypr/lcd-ypr.c
index b8db2eedd1..035abaeba7 100644
--- a/firmware/target/hosted/samsungypr/lcd-ypr.c
+++ b/firmware/target/hosted/samsungypr/lcd-ypr.c
@@ -66,17 +66,17 @@ void lcd_init_device(void)
66 exit(2); 66 exit(2);
67 } 67 }
68 68
69 /* Now we get the settable settings, and we set 16 bit bpp */ 69 /* Now we get the settable settings */
70 if (ioctl(dev_fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { 70 if (ioctl(dev_fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
71 perror("Error reading variable information"); 71 perror("Error reading variable information");
72 exit(3); 72 exit(3);
73 } 73 }
74 74
75 vinfo.bits_per_pixel = LCD_DEPTH; 75 vinfo.bits_per_pixel = LCD_DEPTH; /* Explicitly set our desired depth */
76 76
77 if (ioctl(dev_fd, FBIOPUT_VSCREENINFO, &vinfo)) { 77 if (ioctl(dev_fd, FBIOPUT_VSCREENINFO, &vinfo)) {
78 perror("fbset(ioctl)"); 78 perror("fbset(ioctl)");
79 exit(4); 79 exit(4);
80 } 80 }
81 81
82 printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); 82 printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
@@ -84,17 +84,17 @@ void lcd_init_device(void)
84 /* Figure out the size of the screen in bytes */ 84 /* Figure out the size of the screen in bytes */
85 screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; 85 screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
86 if (screensize != FRAMEBUFFER_SIZE) { 86 if (screensize != FRAMEBUFFER_SIZE) {
87 exit(4);
88 perror("Display and framebuffer mismatch!\n"); 87 perror("Display and framebuffer mismatch!\n");
88 exit(5);
89 } 89 }
90 90
91 /* Map the device to memory */ 91 /* Map the device to memory */
92 dev_fb = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, 0); 92 dev_fb = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, 0);
93 if ((int)dev_fb == -1) { 93 if ((int)dev_fb == -1) {
94 perror("Error: failed to map framebuffer device to memory"); 94 perror("Error: failed to map framebuffer device to memory");
95 exit(4); 95 exit(6);
96 } 96 }
97 printf("The framebuffer device was mapped to memory successfully.\n"); 97 printf("Framebuffer device successfully mapped into memory.\n");
98 98
99 /* Be sure to turn on display at startup */ 99 /* Be sure to turn on display at startup */
100 ioctl(dev_fd, FBIOBLANK, VESA_NO_BLANKING); 100 ioctl(dev_fd, FBIOBLANK, VESA_NO_BLANKING);
diff --git a/manual/plugins/images/ss-xrick-220x176x16.png b/manual/plugins/images/ss-xrick-220x176x16.png
new file mode 100755
index 0000000000..5fb3a74a2f
--- /dev/null
+++ b/manual/plugins/images/ss-xrick-220x176x16.png
Binary files differ
diff --git a/manual/plugins/images/ss-xrick-240x320x16.png b/manual/plugins/images/ss-xrick-240x320x16.png
new file mode 100755
index 0000000000..2d86387b15
--- /dev/null
+++ b/manual/plugins/images/ss-xrick-240x320x16.png
Binary files differ
diff --git a/manual/plugins/images/ss-xrick-320x240x16.png b/manual/plugins/images/ss-xrick-320x240x16.png
new file mode 100755
index 0000000000..7739445c35
--- /dev/null
+++ b/manual/plugins/images/ss-xrick-320x240x16.png
Binary files differ
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index c04299ca49..b2274f18af 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -111,8 +111,13 @@ text files%
111 111
112\input{plugins/xobox.tex} 112\input{plugins/xobox.tex}
113 113
114\opt{lcd_non-mono}{\input{plugins/xrick.tex}}
115
114\opt{lcd_color}{\input{plugins/xworld.tex}} 116\opt{lcd_color}{\input{plugins/xworld.tex}}
115 117
118
119
120
116\section{Demos} 121\section{Demos}
117 122
118\input{plugins/bounce.tex} 123\input{plugins/bounce.tex}
diff --git a/manual/plugins/xrick.tex b/manual/plugins/xrick.tex
new file mode 100755
index 0000000000..9f64349517
--- /dev/null
+++ b/manual/plugins/xrick.tex
@@ -0,0 +1,102 @@
1\subsection{xrick}
2\screenshot{plugins/images/ss-xrick}{xrick}{img:xrick}
3
4Xrick is a clone of the platform game \emph{'Rick Dangerous'}
5(\url{http://en.wikipedia.org/wiki/Rick_Dangerous}),
6originally written by 'BigOrno'
7(\url{http://www.bigorno.net/xrick/}),
8modified and ported to Rockbox by Pierluigi Vicinanza
9(\url{https://github.com/pierluigi-vicinanza/xrick}).
10
11Walk through the maps, avoid traps, kill the Bad Guys... and
12try to stay alive long enough to accomplish your mission!
13
14Rick can fire bullets, lay sticks of dynamite, walk, crawl,
15climb, and also poke walls or Bad Guys with his stick.
16Poking walls can trigger traps, open doors, etc. although
17sometimes a dynamite stick or a bullet is needed.
18Go figure.
19
20\subsubsection{Getting started}
21For the game to run you need the data files at \url{https://download.rockbox.org/useful/xrick-data.zip}. Extract the \fname{xrick-data.zip} archive into the top-level directory of your \dap.
22
23\subsubsection{Controls}
24By holding down \emph{Fire} and pressing \emph{Left} or \emph{Right},
25you can poke a wall (or anything else) with your stick.
26By holding down \emph{Fire} and pressing \emph{Down}, you can lay a stick of dynamite.
27By holding down \emph{Fire} and pressing \emph{Up}, you can fire a bullet.
28
29Throughout the game, use these buttons to control Rick:
30
31\begin{btnmap}
32%
33 \opt{iriverh300}{\ButtonUp/\ButtonRec}
34 \opt{fuzeplus}{\ButtonUp}
35 \opt{ipodvideo}{\ButtonMenu}
36 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginUp}
37 \opt{HAVEREMOTEKEYMAP}{& }
38 & Jump or Climb Up\\
39%
40 \opt{fuzeplus}{\ButtonPlay & Jump Right or Climb Up and Right\\}
41%
42 \opt{fuzeplus}{\ButtonBack & Jump Left or Climb Up and Left\\}
43%
44 \opt{iriverh300}{\ButtonDown/\ButtonMode}
45 \opt{fuzeplus}{\ButtonDown}
46 \opt{ipodvideo}{\ButtonPlay}
47 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginDown}
48 \opt{HAVEREMOTEKEYMAP}{& }
49 & Crouch or Climb Down\\
50%
51 \opt{fuzeplus}{\ButtonBottomRight & Crouch and Move Right or Climb Down and Right\\}
52%
53 \opt{fuzeplus}{\ButtonBottomLeft & Crouch and Move Left or Climb Down and Left\\}
54%
55 \opt{iriverh300,fuzeplus,ipodvideo}{\ButtonLeft}
56 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginLeft}
57 \opt{HAVEREMOTEKEYMAP}{& }
58 & Move Left\\
59%
60 \opt{iriverh300,fuzeplus,ipodvideo}{\ButtonRight}
61 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginRight}
62 \opt{HAVEREMOTEKEYMAP}{& }
63 & Move Right\\
64%
65 \opt{iriverh300}{\ButtonOn}
66 \opt{fuzeplus}{\ButtonVolDown}
67 \opt{ipodvideo}{\ButtonSelect}
68 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginSelect}
69
70 \opt{HAVEREMOTEKEYMAP}{& }
71 & Fire\\
72%
73 \opt{iriverh300}{\ButtonSelect}
74 \opt{fuzeplus}{\ButtonVolUp}
75 \opt{ipodvideo}{\ButtonScrollBack}
76 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginCancel}
77 \opt{HAVEREMOTEKEYMAP}{& }
78 & Pause\\
79%
80 \opt{iriverh300}{\ButtonOff}
81 \opt{fuzeplus}{\ButtonPower}
82 \opt{ipodvideo}{\ButtonScrollFwd}
83 \nopt{iriverh300,fuzeplus,ipodvideo}{\PluginExit}
84 \opt{hold_button}{/\ButtonHold}
85 \opt{HAVEREMOTEKEYMAP}{& }
86 & Menu\\
87%
88\end{btnmap}
89
90\subsubsection{Menu}
91\begin{description}
92 \item[Resume Game. ] Resume the game
93 \item[Restart Game. ] Restart the game
94 \item[Cheat Settings. ] This menu has the following options:
95
96 \emph{Enable Unlimited Lives/Ammo Mode. } Toggle trainer mode (always six bullets, six sticks, six Rick).\\
97 \emph{Enable Never Die Mode. } Toggle 'never die' mode (nothing can kill Rick). Use with care.
98 Although it lets you walk through Bad Guys unharmed, it can produce strange results.\\
99 \emph{Enable Expose Mode. } Toggle 'expose' mode (see all entities).%\\
100
101 \item[Quit. ] Quit the game
102\end{description}
diff --git a/tools/builds.pm b/tools/builds.pm
index 518fb01e49..75d34dc079 100644
--- a/tools/builds.pm
+++ b/tools/builds.pm
@@ -10,6 +10,7 @@ $releasenotes="/wiki/ReleaseNotes315";
10# 'modelname' => { 10# 'modelname' => {
11# name => 'Full Name', 11# name => 'Full Name',
12# status => 1, # 0=retired, 1=unusable, 2=unstable, 3=stable 12# status => 1, # 0=retired, 1=unusable, 2=unstable, 3=stable
13# sim = 1, # optional (defaults 1 for status 2/3 and 0 for status 1)
13# ram => 2, # optional (used?) 14# ram => 2, # optional (used?)
14# manual => 'modelname2', # optional (uses modelname2's manual) 15# manual => 'modelname2', # optional (uses modelname2's manual)
15# icon => 'modelname3', # optional (uses modelname3's icon) 16# icon => 'modelname3', # optional (uses modelname3's icon)
@@ -82,26 +83,32 @@ $releasenotes="/wiki/ReleaseNotes315";
82 }, 83 },
83 'hifietma9' => { 84 'hifietma9' => {
84 name => 'HiFi E.T MA9', 85 name => 'HiFi E.T MA9',
86 sim => 0,
85 status => 2, 87 status => 2,
86 }, 88 },
87 'hifietma9c' => { 89 'hifietma9c' => {
88 name => 'HiFi E.T MA9C', 90 name => 'HiFi E.T MA9C',
91 sim => 0,
89 status => 2, 92 status => 2,
90 }, 93 },
91 'hifietma8' => { 94 'hifietma8' => {
92 name => 'HiFi E.T MA8', 95 name => 'HiFi E.T MA8',
93 status => 2, 96 status => 2,
97 sim => 0,
94 }, 98 },
95 'hifietma8c' => { 99 'hifietma8c' => {
96 name => 'HiFi E.T MA8C', 100 name => 'HiFi E.T MA8C',
97 status => 2, 101 status => 2,
102 sim => 0,
98 }, 103 },
99 'hifimanhm60x' => { 104 'hifimanhm60x' => {
100 name => 'HiFiMAN HM-60x', 105 name => 'HiFiMAN HM-60x',
106 sim => 0,
101 status => 2, 107 status => 2,
102 }, 108 },
103 'hifimanhm801' => { 109 'hifimanhm801' => {
104 name => 'HiFiMAN HM-801', 110 name => 'HiFiMAN HM-801',
111 sim => 0,
105 status => 2, 112 status => 2,
106 }, 113 },
107 'iaudiom3' => { 114 'iaudiom3' => {
@@ -118,10 +125,12 @@ $releasenotes="/wiki/ReleaseNotes315";
118 }, 125 },
119 'ibassodx50' => { 126 'ibassodx50' => {
120 name => 'iBasso DX50', 127 name => 'iBasso DX50',
128 sim => 0,
121 status => 2, 129 status => 2,
122 }, 130 },
123 'ibassodx90' => { 131 'ibassodx90' => {
124 name => 'iBasso DX90', 132 name => 'iBasso DX90',
133 sim => 0,
125 status => 2, 134 status => 2,
126 }, 135 },
127 'ipod1g2g' => { 136 'ipod1g2g' => {
@@ -219,10 +228,12 @@ $releasenotes="/wiki/ReleaseNotes315";
219 'ondavx747' => { 228 'ondavx747' => {
220 name => 'Onda VX747', 229 name => 'Onda VX747',
221 status => 1, 230 status => 1,
231 sim => 1,
222 }, 232 },
223 'ondavx747p' => { 233 'ondavx747p' => {
224 name => 'Onda VX747+', 234 name => 'Onda VX747+',
225 status => 1, 235 status => 1,
236 sim => 1,
226 }, 237 },
227 'ondavx767' => { 238 'ondavx767' => {
228 name => 'Onda VX767', 239 name => 'Onda VX767',
@@ -231,6 +242,7 @@ $releasenotes="/wiki/ReleaseNotes315";
231 'ondavx777' => { 242 'ondavx777' => {
232 name => 'Onda VX777', 243 name => 'Onda VX777',
233 status => 1, 244 status => 1,
245 sim => 1,
234 }, 246 },
235 'rk27generic' => { 247 'rk27generic' => {
236 name => 'Rockchip rk27xx', 248 name => 'Rockchip rk27xx',
@@ -254,6 +266,7 @@ $releasenotes="/wiki/ReleaseNotes315";
254 }, 266 },
255 'samsungypr1' => { 267 'samsungypr1' => {
256 name => 'Samsung YP-R1', 268 name => 'Samsung YP-R1',
269 sim => 0,
257 status => 2, 270 status => 2,
258 }, 271 },
259 'samsungyps3' => { 272 'samsungyps3' => {
@@ -316,6 +329,7 @@ $releasenotes="/wiki/ReleaseNotes315";
316 'sansam200v4' => { 329 'sansam200v4' => {
317 name => 'SanDisk Sansa m200 v4', 330 name => 'SanDisk Sansa m200 v4',
318 status => 1, 331 status => 1,
332 sim => 1,
319 }, 333 },
320 'sansaview' => { 334 'sansaview' => {
321 name => 'SanDisk Sansa View', 335 name => 'SanDisk Sansa View',
@@ -328,14 +342,17 @@ $releasenotes="/wiki/ReleaseNotes315";
328 'zenvision' => { 342 'zenvision' => {
329 name => 'Creative Zen Vision', 343 name => 'Creative Zen Vision',
330 status => 1, 344 status => 1,
345 sim => 1,
331 }, 346 },
332 'zenvisionm30gb' => { 347 'zenvisionm30gb' => {
333 name => 'Creative Zen Vision:M 30GB', 348 name => 'Creative Zen Vision:M 30GB',
334 status => 1, 349 status => 1,
350 sim => 1,
335 }, 351 },
336 'zenvisionm60gb' => { 352 'zenvisionm60gb' => {
337 name => 'Creative Zen Vision:M 60GB', 353 name => 'Creative Zen Vision:M 60GB',
338 status => 1, 354 status => 1,
355 sim => 1,
339 }, 356 },
340 'mpiohd200' => { 357 'mpiohd200' => {
341 name => 'MPIO HD200', 358 name => 'MPIO HD200',
@@ -355,6 +372,7 @@ $releasenotes="/wiki/ReleaseNotes315";
355 }, 372 },
356 'sonynwze350' => { 373 'sonynwze350' => {
357 name => 'Sony NWZ-E350', 374 name => 'Sony NWZ-E350',
375 sim => 0,
358 status => 2, 376 status => 2,
359 }, 377 },
360 'sonynwze360' => { 378 'sonynwze360' => {
@@ -367,26 +385,32 @@ $releasenotes="/wiki/ReleaseNotes315";
367 }, 385 },
368 'sonynwze450' => { 386 'sonynwze450' => {
369 name => 'Sony NWZ-E450', 387 name => 'Sony NWZ-E450',
388 sim => 0,
370 status => 2, 389 status => 2,
371 }, 390 },
372 'sonynwze460' => { 391 'sonynwze460' => {
373 name => 'Sony NWZ-E460', 392 name => 'Sony NWZ-E460',
393 sim => 0,
374 status => 2, 394 status => 2,
375 }, 395 },
376 'sonynwze470' => { 396 'sonynwze470' => {
377 name => 'Sony NWZ-E470', 397 name => 'Sony NWZ-E470',
398 sim => 0,
378 status => 2, 399 status => 2,
379 }, 400 },
380 'sonynwze580' => { 401 'sonynwze580' => {
381 name => 'Sony NWZ-E580', 402 name => 'Sony NWZ-E580',
403 sim => 0,
382 status => 2, 404 status => 2,
383 }, 405 },
384 'sonynwza10' => { 406 'sonynwza10' => {
385 name => 'Sony NWZ-A10', 407 name => 'Sony NWZ-A10',
408 sim => 0,
386 status => 2, 409 status => 2,
387 }, 410 },
388 'sonynwa20' => { 411 'sonynwa20' => {
389 name => 'Sony NW-A20', 412 name => 'Sony NW-A20',
413 sim => 0,
390 status => 2, 414 status => 2,
391 }, 415 },
392 'sonynwza860' => { 416 'sonynwza860' => {
@@ -396,6 +420,7 @@ $releasenotes="/wiki/ReleaseNotes315";
396 'sonynwzs750' => { 420 'sonynwzs750' => {
397 name => 'Sony NWZ-S750', 421 name => 'Sony NWZ-S750',
398 status => 2, 422 status => 2,
423 sim => 0,
399 }, 424 },
400 'creativezenxfi' => { 425 'creativezenxfi' => {
401 name => 'Creative Zen X-Fi', 426 name => 'Creative Zen X-Fi',
@@ -512,6 +537,17 @@ sub allbuilds {
512 return @list; 537 return @list;
513} 538}
514 539
540sub simbuilds {
541 my @list;
542
543 for my $b (&allbuilds) {
544 push @list, $b if (defined($builds{$b}{sim}) and $builds{$b}{sim});
545 push @list, $b if (!defined($builds{$b}{sim}) and $builds{$b}{status} > 1);
546 }
547
548 return @list;
549}
550
515################################################################ 551################################################################
516 552
517# 'voicename' => { 553# 'voicename' => {
diff --git a/tools/list_targets.pl b/tools/list_targets.pl
index 039c14f0db..9785bb5f22 100755
--- a/tools/list_targets.pl
+++ b/tools/list_targets.pl
@@ -10,18 +10,29 @@
10 10
11require "./builds.pm"; 11require "./builds.pm";
12 12
13print "Retired:\n";
14
15for my $b (&stablebuilds) {
16 print " " , $builds{$b}{name} , "\n" if($builds{$b}{status} == 0);
17}
18
13print "Stable:\n"; 19print "Stable:\n";
14 20
15for my $b (&stablebuilds) { 21for my $b (&stablebuilds) {
16 print $builds{$b}{name} , "\n"; 22 print " " , $builds{$b}{name} , "\n" if($builds{$b}{status} != 0);
17} 23}
18 24
19print "Unstable:\n"; 25print "Unstable:\n";
20for my $b (&usablebuilds) { 26for my $b (&usablebuilds) {
21 print $builds{$b}{name} , "\n"; 27 print " " , $builds{$b}{name} , "\n";
22} 28}
23 29
24print "Unusable:\n"; 30print "Unusable:\n";
25for my $b (&allbuilds) { 31for my $b (&allbuilds) {
26 print $builds{$b}{name} , "\n" if($builds{$b}{status} == 1); 32 print " " , $builds{$b}{name} , "\n" if($builds{$b}{status} == 1);
33}
34
35print "Simulators:\n";
36for my $b (&simbuilds) {
37 print " " , $builds{$b}{name} , "\n";
27} 38}
diff --git a/tools/release/sims.pl b/tools/release/sims.pl
index 2cf5c4f45a..10cf950c53 100755
--- a/tools/release/sims.pl
+++ b/tools/release/sims.pl
@@ -188,26 +188,14 @@ sub buildit {
188 `make install 2>/dev/null`; 188 `make install 2>/dev/null`;
189} 189}
190 190
191for my $b (sort byname keys %builds) { 191for my $b (sort &simbuilds) {
192 if ($builds{$b}{status} > 0 && $builds{$b}{status} >= 2) 192 if ($builds{$b}{ram} ne '')
193 { 193 {
194 if ($builds{$b}{ram} ne '') 194 # These builds need the ram size sent to configure
195 { 195 runone($b, $builds{$b}{ram} . '\n');
196 # These builds need the ram size sent to configure 196 }
197 runone($b, $builds{$b}{ram} . '\n'); 197 else
198 } 198 {
199 else 199 runone($b);
200 {
201 runone($b);
202 }
203 } 200 }
204} 201}
205
206#The following ports are in the unusable category, but the simulator does build
207runone("ondavx747");
208runone("ondavx747p");
209runone("ondavx777");
210runone("sansam200v4");
211runone("zenvision");
212runone("zenvisionm30gb");
213runone("zenvisionm60gb");
diff --git a/utils/rbutilqt/base/playerbuildinfo.cpp b/utils/rbutilqt/base/playerbuildinfo.cpp
index c76abc4ffe..7edb7f750c 100644
--- a/utils/rbutilqt/base/playerbuildinfo.cpp
+++ b/utils/rbutilqt/base/playerbuildinfo.cpp
@@ -49,6 +49,7 @@ const static struct {
49 { PlayerBuildInfo::PuzzFontsUrl, "other/puzzfonts_url" }, 49 { PlayerBuildInfo::PuzzFontsUrl, "other/puzzfonts_url" },
50 { PlayerBuildInfo::QuakeUrl, "other/quake_url" }, 50 { PlayerBuildInfo::QuakeUrl, "other/quake_url" },
51 { PlayerBuildInfo::Wolf3DUrl, "other/wolf3d_url" }, 51 { PlayerBuildInfo::Wolf3DUrl, "other/wolf3d_url" },
52 { PlayerBuildInfo::XRickUrl, "other/xrick_url" },
52 { PlayerBuildInfo::XWorldUrl, "other/xworld_url" }, 53 { PlayerBuildInfo::XWorldUrl, "other/xworld_url" },
53 { PlayerBuildInfo::MidiPatchsetUrl, "other/patcheset_url" }, 54 { PlayerBuildInfo::MidiPatchsetUrl, "other/patcheset_url" },
54}; 55};
diff --git a/utils/rbutilqt/base/playerbuildinfo.h b/utils/rbutilqt/base/playerbuildinfo.h
index 85fc2ac6dc..38f6662e09 100644
--- a/utils/rbutilqt/base/playerbuildinfo.h
+++ b/utils/rbutilqt/base/playerbuildinfo.h
@@ -53,6 +53,7 @@ public:
53 QuakeUrl, 53 QuakeUrl,
54 Wolf3DUrl, 54 Wolf3DUrl,
55 XWorldUrl, 55 XWorldUrl,
56 XRickUrl,
56 MidiPatchsetUrl, 57 MidiPatchsetUrl,
57 }; 58 };
58 enum DeviceInfo { 59 enum DeviceInfo {
diff --git a/utils/rbutilqt/gui/selectiveinstallwidget.cpp b/utils/rbutilqt/gui/selectiveinstallwidget.cpp
index 28dd50d482..639e03308a 100644
--- a/utils/rbutilqt/gui/selectiveinstallwidget.cpp
+++ b/utils/rbutilqt/gui/selectiveinstallwidget.cpp
@@ -610,6 +610,7 @@ static const struct {
610 { "Quake", "games/quake.rock", PlayerBuildInfo::QuakeUrl }, 610 { "Quake", "games/quake.rock", PlayerBuildInfo::QuakeUrl },
611 { "Puzzles fonts", "games/sgt-blackbox.rock", PlayerBuildInfo::PuzzFontsUrl }, 611 { "Puzzles fonts", "games/sgt-blackbox.rock", PlayerBuildInfo::PuzzFontsUrl },
612 { "Wolf3D", "games/wolf3d.rock", PlayerBuildInfo::Wolf3DUrl }, 612 { "Wolf3D", "games/wolf3d.rock", PlayerBuildInfo::Wolf3DUrl },
613 { "XRick", "games/xrick.rock", PlayerBuildInfo::XRickUrl },
613 { "XWorld", "games/xworld.rock", PlayerBuildInfo::XWorldUrl }, 614 { "XWorld", "games/xworld.rock", PlayerBuildInfo::XWorldUrl },
614 { "MIDI Patchset", "viewers/midi.rock", PlayerBuildInfo::MidiPatchsetUrl }, 615 { "MIDI Patchset", "viewers/midi.rock", PlayerBuildInfo::MidiPatchsetUrl },
615}; 616};
diff --git a/utils/rbutilqt/rbutil.ini b/utils/rbutilqt/rbutil.ini
index f75cfe015e..bc41ff48b0 100644
--- a/utils/rbutilqt/rbutil.ini
+++ b/utils/rbutilqt/rbutil.ini
@@ -14,6 +14,7 @@ quake_url=https://download.rockbox.org/useful/quake.zip
14puzzfonts_url=https://download.rockbox.org/useful/sgt-fonts.zip 14puzzfonts_url=https://download.rockbox.org/useful/sgt-fonts.zip
15wolf3d_url=https://download.rockbox.org/useful/wolf3d.zip 15wolf3d_url=https://download.rockbox.org/useful/wolf3d.zip
16xworld_url=https://download.rockbox.org/useful/xworld.zip 16xworld_url=https://download.rockbox.org/useful/xworld.zip
17xrick_url=https://download.rockbox.org/useful/xrick-data.zip
17rbutil_url=https://download.rockbox.org/rbutil/ 18rbutil_url=https://download.rockbox.org/rbutil/
18 19
19[bootloader] 20[bootloader]