From 55d29a682a34befa574a9e2241231048634a83ba Mon Sep 17 00:00:00 2001 From: Egan Ford Date: Sun, 23 Nov 2014 17:26:22 -0700 Subject: [PATCH] init commit --- MANIFEST | 12 + Makefile | 14 + README.1st | 22 + README.md | 267 +++ asm/Makefile | 27 + asm/autoload.s | 117 ++ asm/diskload2.s | 545 ++++++ asm/diskload3.s | 524 +++++ asm/diskload8000.s | 168 ++ asm/diskload9600.s | 174 ++ asm/fastload8000.s | 194 ++ asm/fastload9600.s | 200 ++ asm/fastloadcd.s | 202 ++ asm/inflate.s | 520 +++++ c2t.c | 1707 ++++++++++++++++ c2t.h | 4352 ++++++++++++++++++++++++++++++++++++++++ c2t.h.0 | 72 + c2t.h.2 | 1134 +++++++++++ fake6502.h | 998 ++++++++++ makeheader | 53 + miniz.h | 4679 ++++++++++++++++++++++++++++++++++++++++++++ windows/c2t.exe | Bin 0 -> 167756 bytes windows/miniz.h | 4679 ++++++++++++++++++++++++++++++++++++++++++++ 23 files changed, 20660 insertions(+) create mode 100644 MANIFEST create mode 100644 Makefile create mode 100644 README.1st create mode 100644 README.md create mode 100644 asm/Makefile create mode 100644 asm/autoload.s create mode 100644 asm/diskload2.s create mode 100644 asm/diskload3.s create mode 100644 asm/diskload8000.s create mode 100644 asm/diskload9600.s create mode 100644 asm/fastload8000.s create mode 100644 asm/fastload9600.s create mode 100644 asm/fastloadcd.s create mode 100644 asm/inflate.s create mode 100644 c2t.c create mode 100644 c2t.h create mode 100644 c2t.h.0 create mode 100644 c2t.h.2 create mode 100644 fake6502.h create mode 100755 makeheader create mode 100644 miniz.h create mode 100755 windows/c2t.exe create mode 100644 windows/miniz.h diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..6f9de98 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,12 @@ +README +README.1st +MANIFEST +c2t +c2t.c +c2t.h +fake6502.h +miniz.h +windows/c2t.exe +windows/miniz.h +Makefile +makeheader diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b067e8 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ + + +all: c2t + +clean: + rm c2t.h c2t + cd asm; make clean + + +c2t: c2t.h + gcc -Wall -O3 -o c2t c2t.c + +c2t.h: + ./makeheader diff --git a/README.1st b/README.1st new file mode 100644 index 0000000..6840b43 --- /dev/null +++ b/README.1st @@ -0,0 +1,22 @@ +Read the README for help. + +Read c2t.c for build instructions. + +Description of files: + +README Documentation +README.1st This file +MANIFEST Files that should have been in this archive +c2t OS/X Intel binary +c2t.c c2t main +c2t.h c2t header including all 6502 ASM source and binaries +fake6502.h 6502 simulator source +miniz.h compression code source +windows/c2t.exe Windows Intel binary +windows/miniz.h compression code source (for windows) + +To install c2t on OS/X type: + +sudo cp c2t /usr/local/bin +sudo chmod 755 /usr/local/bin/c2t + diff --git a/README.md b/README.md new file mode 100644 index 0000000..41d8c86 --- /dev/null +++ b/README.md @@ -0,0 +1,267 @@ +c2t Documentation + + +AUTHOR + +Egan Ford (egan@sense.net, datajerk@gmail.com) + + +DESCRIPTION + +c2t is a command line tool that can convert binary code/data and/or +Apple-1/II Monitor text, as well as 140K disk images, into audio files +suitable for use with the Apple-1 and II (II, II+, //e) cassette interface. + +c2t offers three high-speed options for the 64K Apple II+ and Apple //e: +8000 bps, 8820 bps, and 9600 bps. The c2t compression option may be used to +speedup the delivery of data with all three as well as the native 1333 bps +cassette interface ROM routines. + +8820 bps (used to burn CDs) and 9600 bps are not compatible with all II+s +and //es. If you plan to distribute your audio files, then use 8000 bps. +8820 bps and 1333 bps is not an option for disk images. + +High-speed and compress options require c2t's custom loader, and at this +time that limits you to a single segment. You can overcome this limitation +by concatenating all your code together and creating your own code to +shuffle your data around, or, pad each segment with enough zeros to align +subsequent segments with their target address and then use the compress +option to minimize this overhead. + +Multi-segment audio files can be created for the Apple-1, II, II+, and //e +that can be loaded using the standard cassette interface ROM routines. + + +WHY? + +I created this because I needed a convenient way to get data loaded into my +//e without dragging my computer out of my office (2nd floor) to my mancave +(basement). IOW, I needed an iPhone/iPad/mobile solution. That, and +CFFA3000 was sold out. + + +SYNOPSIS + +Output of "c2t -h": + +usage: c2t [-vh?] + c2t [-elp] input[.mon],[addr] ... [output.mon] + c2t {-1} [-cepr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]] + c2t {-2} [-abcdef8pmqr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]] + c2t [-n8] input.dsk ... [output.[aif[f]|wav[e]]] + + -1 or -2 for Apple I or II tape format + -8 use 48k/8bit 8000 bps transfer (Apple II/II+/IIe 64K only) + Implies -2a. Negates -f and -d. + -a assembly autoload and run (Apple II/II+/IIe 64K only) + -b basic autoload and run (Apple II+/IIe 64K only) + Implies -2a. + -c compress data + -d use fast 44.1k/16bit transfer (Apple II/II+/IIe 64K only) + Implies -2a. Negates -f and -8. Use for burning CDs. + -e pad with $00 to end on page boundary + -f use faster 48k/8bit (9600 bps) transfer (Apple II/II+/IIe 64K only) + Implies -2a. Negates -8 and -d. Unreliable on some systems. + -h|? this help + -l long monitor format (24 bytes/line) + -m jump to monitor after autoload + -n do not format disks + -p pipe to stdout + -q parameters and data only (for use with custom client) + -r #, where # overrides the sample rate (e.g. -r 48000) + -t 10 second preamble (default 4) for real tape use + -v print version number and exit + +input(s) without a .mon or .dsk extension is assumed to be a binary with a 4 +byte header. If the header is missing then you must append ,load_address to +each binary input missing a header, e.g. filename,800. The load address +will be read as hex. + +input(s) with a .mon extension expected input format: + + 0280: A2 FF 9A 20 8C 02 20 4F + 0288: 03 4C 00 FF 20 9E 02 A9 + +A single input with a .dsk extension expected to be a 140K disk image. + +output must have aiff, aif, wav, wave, or mon extention. + + +EXAMPLES + +------------------------------------------------------------------------------ + +Input: Apple 1 monitor file with two segments. First 4 lines: + +0: 00 05 00 10 00 00 00 00 +8: 00 00 00 00 00 00 00 00 +280: A9 00 85 07 A9 00 A8 AA +288: 85 06 A5 00 85 04 A5 01 + +Command: + +c2t -1 a1mt.mon a1mt.aif + +Output: + +Reading a1mt.mon, type monitor, segment 1, start: 0x0000, length: 16 +Reading a1mt.mon, type monitor, segment 2, start: 0x0280, length: 290 + +Writing a1mt.aif as Apple I formatted aiff. + +To load up and run on your Apple I, type: + + C100R + 0.FR 280.3A1R + +------------------------------------------------------------------------------ + +Input: cc65/ca65 Apple II binary with DOS 4-byte header. The DOS header + contains the starting address of the program. + +Command: + +c2t -2 hello hello.wav + +Output: + +Reading hello, type binary, segment 1, start: 0x0803, length: 2958 + +Writing hello.wav as Apple II formatted wave. + +To load up and run on your Apple II, type: + + CALL -151 + 803.1390R + 803G + +------------------------------------------------------------------------------ + +Input: cc65/ca65 Apple II binary with DOS 4-byte header. The DOS header + contains the starting address of the program. + +Command: + +c2t hello hello.mon + +Output: + +Reading hello, type binary, segment 1, start: 0x0803, length: 2958 + +Writing hello.mon as Apple formatted monitor. + +Example hello.mon output: + +0803: A2 FF 9A 2C 81 +0808: C0 2C 81 C0 A9 91 A0 13 +0810: 85 9B 84 9C A9 91 A0 13 +0818: 85 96 84 97 A9 00 A0 D4 + +------------------------------------------------------------------------------ + +Input: Binary game without DOS header that should be loaded at $801. + +Command: + +c2t -2 moon.patrol,801 moon.patrol.aif + +Output: + +Reading moon.patrol, type binary, segment 1, start: 0x0801, length: 18460 + +Writing moon.patrol.aif as Apple II formatted aiff. + +To load up and run on your Apple II, type: + + CALL -151 + 801.501CR + 801G + +------------------------------------------------------------------------------ + +Input: Binary game without DOS header that should be loaded at $801 as fast + as possible while being compatible with all Apple IIs. + +Command: + +c2t -8c moon.patrol,801 moon.patrol.aif + +Reading moon.patrol, type binary, segment 1, start: 0x0801, length: 18460 + +Writing moon.patrol.aif as Apple II formatted aiff. + +start: 0x7226, length: 18393, deflated: 0.36%, data time:18.95, inflate time:6.83 +WARNING: compression disabled: no significant gain (18.11) + +To load up and run on your Apple II, type: + + LOAD + +NOTE: Compression was disabled because it didn't help. + +------------------------------------------------------------------------------ + +Input: Binary game without DOS header that should be loaded at $800 as fast + as possible while being compatible with all Apple IIs. + +Command: + +c2t -8c super_puckman,800 super_puckman.wav + +Reading super_puckman, type binary, segment 1, start: 0x0800, length: 30719 + +Writing super_puckman.wav as Apple II formatted wave. + +start: 0x886C, length: 12691, deflated: 58.69%, data time:13.25, inflate time:5.79 + +To load up and run on your Apple II, type: + + LOAD + +------------------------------------------------------------------------------ + +Input: Three binary files to be loaded at three different addresses. + +c2t -2 foo,801 foo.obj,3ffd foo.pic,2000 foo.aif + +Reading foo, type binary, segment 1, start: 0x0801, length: 91 +Reading foo.obj, type binary, segment 2, start: 0x3FFD, length: 18947 +Reading foo.pic, type binary, segment 3, start: 0x2000, length: 8192 + +Writing foo.aif as Apple II formatted aiff. + +To load up and run on your Apple II, type: + + CALL -151 + 801.85BR 3FFD.89FFR 2000.3FFFR + +------------------------------------------------------------------------------ + +Input: DOS 3.3 140K diskette image to be loaded with maximum II + compatibility. Disk will be formatted first. + +Command: + +c2t -8 dos33.dsk dos33.wav + +Output: + +Reading dos33.dsk, type disk, segment 1, start: 0x1000, length: 28672 +Reading dos33.dsk, type disk, segment 2, start: 0x1000, length: 28672 +Reading dos33.dsk, type disk, segment 3, start: 0x1000, length: 28672 +Reading dos33.dsk, type disk, segment 4, start: 0x1000, length: 28672 +Reading dos33.dsk, type disk, segment 5, start: 0x1000, length: 28672 + +Writing dos33.wav as Apple II formatted wave. + +Segment: 0, start: 0x459B, length: 19044, deflated: 33.58%, data time:19, inflate time:7.68 +Segment: 1, start: 0x74A5, length: 7002, deflated: 75.58%, data time:7, inflate time:3.70 +Segment: 2, start: 0x8514, length: 2795, deflated: 90.25%, data time:3, inflate time:2.28 +Segment: 3, start: 0x6CD4, length: 9003, deflated: 68.60%, data time:9, inflate time:4.33 +Segment: 4, start: 0x6DE6, length: 8729, deflated: 69.56%, data time:9, inflate time:4.27 + +To load up and run on your Apple II, type: + + LOAD + +------------------------------------------------------------------------------ diff --git a/asm/Makefile b/asm/Makefile new file mode 100644 index 0000000..14559cc --- /dev/null +++ b/asm/Makefile @@ -0,0 +1,27 @@ +CL = cl65 +CL_FLAGS = -t none --listing --list-bytes 100 +#CL_FLAGS = -t apple1 -C apple1-16k.cfg --listing --list-bytes 100 +CC = cl65 +CC_FLAGS = --static-locals -t apple1 -C apple1-16k.cfg +C2T = c2t + +ASRC = $(shell echo *.s) +AOBJ = $(ASRC:%.s=%.o) +ALST = $(ASRC:%.s=%.lst) +AMON = $(ASRC:%.s=%.mon) +ABIN = $(ASRC:%.s=%) + +all: $(ABIN) + +clean: + -rm -f $(ABIN) $(AOBJ) $(ALST) $(AMON) + +%: %.s + $(CL) $(CL_FLAGS) $< + +%: %.c + $(CC) $(CC_FLAGS) $< + +%.mon: % + $(C2T) $< $@ + diff --git a/asm/autoload.s b/asm/autoload.s new file mode 100644 index 0000000..732729f --- /dev/null +++ b/asm/autoload.s @@ -0,0 +1,117 @@ +;autoload.s + +org = $BF00 ; should be $BF00 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +readblk = $FEFD +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta load,x + inx + bne move1 ; move 256 bytes + jmp load +moved: + .org org +load: + lda #loadm + jsr print + + lda ld_beg + sta $3C ; starting tape address low + lda ld_beg+1 + sta $3D ; starting tape address high + lda ld_end + sta $3E ; ending tape address low + lda ld_end+1 + sta $3F ; ending tape address high + jsr readblk ; read block from tape + + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #chkm + jsr print +error: + lda #errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + diff --git a/asm/diskload2.s b/asm/diskload2.s new file mode 100644 index 0000000..72d8a74 --- /dev/null +++ b/asm/diskload2.s @@ -0,0 +1,545 @@ +;diskload2.s + +; apple vectors + +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +movecur = $FB5B ; move cursor to ch,a +dos = $9D84 +asrom = $9D72 +locrpl = $3E3 ; locate RWTS paramlist jsr +rwts = $3D9 ; RWTS jsr +cleos = $FC42 ; clear to end of screen +init = $A54F +motoroff= $C088 ; Turn drive motor off +motoron = $C089 ; Turn drive motor on +reboot = $FAA6 ; reboot machine +bell = $FBDD ; ding +rdkey = $FD0C ; read key + +; my vectors + +;print = $90CE ; from diskload.s +readtape= $9000 +inflate = $9B00 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +secnum = $05 ; loop var +trknum = $06 ; loop var +segcnt = $07 ; loop var +buffer = $08 ; MSB of RWTS buffer +trkcnt = $09 ; track counter (0-6) +pointer = $0A ; pointer LSB/MSB +prtptr = $0C ; pointer LSB/MSB +fmptr = $0E ; file manager pointer +inf_zp = $10 ; inflate vars (10) +temp = $1E ; temp var +ch = $24 ; cursor horizontal +preg = $48 ; mon p reg + +; other vars + +org = $9700 ; should be $9700 +invsp = $60 ; inverse space for draw +data = $1000 ; 7 track dump from inflate +boot1o = $96D0 ; tape loaded boot 1 location +boot1 = $3D0 ; target boot 1 location +cmpbuf = $9200 ; buffer for sector check +count = $900 + + .org org + + ldx #0 ; move 9cd0 to 3d0 +move1: + lda boot1o,x + sta boot1,x + inx + cpx #$48 + bne move1 ; branch on positive (0-127) +patch: + lda #$B3 ; hack since chksum could not be written to C000 + sta $BFFF ; chksum was written do BFFF +start: + jsr clear ; clear screen + lda #title + jsr inv + ; TRACK + lda #19 ; col 20 + sta ch + lda #0 ; row 0 + jsr movecur + lda #<track ; print track + ldy #>track + jsr print + + lda #<header ; print header + ldy #>header + jsr print + ldx #35 ; length of line + jsr line + + lda #<left ; print left side of grid + ldy #>left + jsr print + +setupiob: + jsr locrpl ; locate rwts paramlist + sty pointer ; and save pointer + sta pointer+1 + + lda #1 ; table type + ldy #0 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #6 * 16 ; slot 6 + ldy #1 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #1 ; drive number + ldy #2 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #254 ; volume number + ldy #3 ; offset in RWTS + sta (pointer),y ; write it to RWTS + +format: ; format the diskette + lda infdata+20 ; check noformat flag + bne endformat ; if not 0 jump to endformat + + jsr status + lda #<formatm ; print formatting + ldy #>formatm + jsr print + +;;; RWTS format (issues) +; lda #4 ; format(4) command +; ldy #$0C ; offset in RWTS +; sta (pointer),y ; write it to RWTS + +; jsr locrpl ; locate rwts paramlist +; jsr rwts ; do it! +; bcs formaterror +; lda #0 +; sta preg ; fix p reg so mon is happy +; jmp endformat + +;;; file manager format (works!) + jsr $3DC ; load up Y and A + sty fmptr + sta fmptr+1 + + lda #$0B ; init command + ldy #0 + sta (fmptr),y + + lda #$9D ; DOS location + ldy #1 + sta (fmptr),y + + lda #254 ; volume number + ldy #4 + sta (fmptr),y + + lda #$01 ; drive number + ldy #5 + sta (fmptr),y + + lda #$06 ; slot number + ldy #6 + sta (fmptr),y + + lda #$00 ; scratch area LSB + ldy #$0C + sta (fmptr),y + + lda #$92 ; scratch area MSB + ldy #$0D + sta (fmptr),y + + jsr $3D6 ; doit! + + ldy #$0A ; return code + lda (fmptr),y + beq endformat +formaterror: + jmp diskerror +endformat: + +;;;begin segment loop (5) + lda #0 ; 256 bytes/sector + ldy #$0b ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #0 ; buffer LSB + ldy #8 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #0 + sta trknum ; start with track 0 + lda #5 + sta segcnt +segloop: + +;;; fancy status here +; jsr status +; lda #<waitm ; print waiting for data +; ldy #>waitm +; jsr print +;countdown: +; lda #<count ; store begin location LSB +; sta begload +; lda #>count ; store begin location MSB +; sta begload+1 +; lda #<count+4 ; store end location LSB +; sta endload +; lda #>count ; store end location MSB +; sta endload+1 +;;;; hack readtape, fix later, POC for now +; lda #$60 ; return without check +; sta $9091 +; jsr readtape ; get the code +; lda #$8A ; put TXA back +; sta $9091 +;;;; end hack +; lda #18 +; sta $24 ; horiz +; lda #22 ; vert +; jsr movecur ; move cursor to $24,a; 0 base +; jsr cleos +; lda #<count ; print count down +; ldy #>count +; jsr print +; lda count +; cmp #$B0 +; bne countdown +; lda count+1 +; cmp #$B0 +; bne countdown +;;; end fancy stuff + +;;; get 7 tracks from tape +load: + jsr status + lda #<loadm ; print loading data + ldy #>loadm + jsr flash + lda #<loadm2 ; print loading data + ldy #>loadm2 + jsr print + + sec + lda #5 + sbc segcnt + asl + asl + tax + stx temp + + lda infdata+2,x ; get sec + jsr cout + lda infdata+3,x ; get sec + beq second + jsr cout +second: + lda #<secm ; print sec + ldy #>secm + jsr print + + ldx temp + lda infdata+0,x ; store begin location LSB + sta begload + lda infdata+1,x ; store begin location MSB + sta begload+1 + + lda #$00 ; store end location LSB + sta endload + lda #$90 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code +inf: + ; turn motor on to save 1-2 sec + ldx #$60 ; slot #6 + lda motoron,x ; turn it on + + jsr status + lda #<inflatem ; print inflating + ldy #>inflatem + jsr print + + ldx temp + lda infdata+0,x ;src lsb + sta inf_zp+0 + lda infdata+1,x ;src msb + sta inf_zp+1 + lda #<data ;dst lsb + sta inf_zp+2 + lda #>data ;dst msb + sta inf_zp+3 + + jsr inflate + + lda #$00 ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda #$80 ;dst end +1 msb + cmp inf_zp+3 + bne error + +;;;begin track loop (7) + jsr status + lda #<writem ; print writing + ldy #>writem + jsr print + + lda #>data + sta buffer + lda #7 + sta trkcnt ; do 7 tracks/segment +trkloop: + lda trknum ; track number + ldy #4 ; offset in RWTS + sta (pointer),y ; write it to RWTS + +; lda #0 ; seek(0) command +; ldy #$0C ; offset in RWTS +; sta (pointer),y ; write it to RWTS + +; jsr locrpl ; locate rwts paramlist +; jsr rwts ; do it! +; bcs diskerror + +;;;begin sector loop (16), backwards is faster, much faster + lda #$F + sta secnum +secloop: + ;jsr draw_w ; write sector from buffer to disk + jsr draw_s ; write sector from buffer to disk + lda secnum ; sector number + ldy #5 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda buffer ; buffer MSB + clc + adc secnum + ldy #9 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #2 ; read(1)/write(2) command + ldy #$0C ; offset in RWTS + sta (pointer),y ; write it to RWTS + + jsr locrpl ; locate rwts paramlist + jsr rwts ; do it! + bcs diskerror + lda #0 + sta preg ; fix p reg so mon is happy + + ;jsr draw_r ; read sector from disk to compare addr + ;lda #>cmpbuf ; compare MSB + ;ldy #9 ; offset in RWTS + ;sta (pointer),y ; write it to RWTS + + ;lda #1 ; read(1)/write(2) command + ;ldy #$0C ; offset in RWTS + ;sta (pointer),y ; write it to RWTS + + ;jsr locrpl ; locate rwts paramlist + ;jsr rwts ; do it! + ;bcs diskerror + ;lda #0 + ;sta preg ; fix p reg so mon is happy + + ;;; compare code + + ;jsr draw_s ; draw a space in the grid if OK + + dec secnum + bpl secloop +;;;end sector loop + + lda buffer ; buffer += $10 + clc + adc #$10 + sta buffer + + inc trknum ; next track + dec trkcnt ; + bne trkloop ; 0, all done with 7 tracks +;;;end track loop + + dec segcnt ; + beq done ; 0, all done with 5 segments + jmp segloop +;;;end segment loop + +;;; prompt for data only load? +done: + jsr status + lda #<donem ; print done + ldy #>donem + jsr print + jsr bell + jsr rdkey + jmp reboot +error: + ; turn motor off, just in case left on + ldx #$60 ; slot #6 + lda motoroff,x ; turn it off + + lda #<errorm ; print error + ldy #>errorm + jsr print + jmp warm +diskerror: + lda #0 + sta preg ; fix p reg so mon is happy + jsr status + lda #<diskerrorm ; print error + ldy #>diskerrorm + jsr print + jmp warm +status: + lda #0 + sta $24 ; horiz + lda #22 ; vert + jsr movecur ; move cursor to $24,a; 0 base + jmp cleos +draw_w: ; print a 'W' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #'W' + jmp draw +draw_r: ; print a 'R' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #'R' + jmp draw +draw_s: ; print a ' ' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #invsp +draw: ; a=horiz, y=vert, x=letter + sta $24 ; horiz + tya + jsr movecur + txa + eor #$40 + jsr cout + rts +line: + lda #'-' + ora #$80 +loop0: + jsr cout + dex + bne loop0 + jsr crout + rts +inv: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +inv1: and #$3F + jsr cout + iny + lda (prtptr),y + bne inv1 + rts +flash: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +flash1: ora #$40 + jsr cout + iny + lda (prtptr),y + bne flash1 + rts +print: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (prtptr),y + bne print1 + rts +title: + .asciiz "INSTA-DISK" +errorm: + .asciiz "ERROR" +diskerrorm: + .asciiz "DISK ERROR" +donem: + .asciiz "DONE. PRESS [RETURN] TO REBOOT." +inflatem: + .asciiz "INFLATING DATA " +loadm: + .asciiz "LOADING DATA" +loadm2: + .asciiz ", ETA " +secm: + .asciiz " SEC. " +formatm: + .asciiz "FORMATTING DISK " +waitm: + .asciiz "WAITING FOR DATA: " +writem: + .asciiz "WRITING DATA " +track: + .byte "TRACK",$0D,0 +header: + .byte " 1111111111222222222233333",$0D + .byte " 01234567890123456789012345678901234",$0D + .byte " ",0 +left: + .byte " 0|",$0D + .byte " 1|",$0D + .byte " 2|",$0D + .byte " 3|",$0D + .byte " 4|",$0D + .byte "S 5|",$0D + .byte "E 6|",$0D + .byte "C 7|",$0D + .byte "T 8|",$0D + .byte "O 9|",$0D + .byte "R A|",$0D + .byte " B|",$0D + .byte " C|",$0D + .byte " D|",$0D + .byte " E|",$0D + .byte " F|",$0D,0 +infdata: + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0 ; format flag, 1 = no format diff --git a/asm/diskload3.s b/asm/diskload3.s new file mode 100644 index 0000000..9202a9b --- /dev/null +++ b/asm/diskload3.s @@ -0,0 +1,524 @@ +;diskload3.s + +; inflate - uncompress data stored in the DEFLATE format +; by Piotr Fusik <fox@scene.pl> +; Last modified: 2007-06-17 + +; Compile with xasm (http://xasm.atari.org/), for example: +; xasm inflate.asx /l /d:inflate=$b700 /d:inflate_data=$b900 /d:inflate_zp=$f0 +; inflate is 509 bytes of code and initialized data +; inflate_data is 764 bytes of uninitialized data +; inflate_zp is 10 bytes on page zero + +; hacked for apple ii and ca65 by Egan Ford <egan@sense.net> +; dates + +; compile nodes + +; changes + +;EFF +.define equ = + +inflate = $9B00 +inflate_zp = $10 +inflate_data = $9200 + +; Pointer to compressed data +inputPointer equ inflate_zp ; 2 bytes + +; Pointer to uncompressed data +outputPointer equ inflate_zp+2 ; 2 bytes + +; Local variables + +getBit_buffer equ inflate_zp+4 ; 1 byte + +getBits_base equ inflate_zp+5 ; 1 byte +inflateStoredBlock_pageCounter equ inflate_zp+5 ; 1 byte + +inflateCodes_sourcePointer equ inflate_zp+6 ; 2 bytes +inflateDynamicBlock_lengthIndex equ inflate_zp+6 ; 1 byte +inflateDynamicBlock_lastLength equ inflate_zp+7 ; 1 byte +inflateDynamicBlock_tempCodes equ inflate_zp+7 ; 1 byte + +inflateCodes_lengthMinus2 equ inflate_zp+8 ; 1 byte +inflateDynamicBlock_allCodes equ inflate_zp+8 ; 1 byte + +inflateCodes_primaryCodes equ inflate_zp+9 ; 1 byte + + +; Argument values for getBits +GET_1_BIT equ $81 +GET_2_BITS equ $82 +GET_3_BITS equ $84 +GET_4_BITS equ $88 +GET_5_BITS equ $90 +GET_6_BITS equ $a0 +GET_7_BITS equ $c0 + +; Maximum length of a Huffman code +MAX_CODE_LENGTH equ 15 + +; Huffman trees +TREE_SIZE equ MAX_CODE_LENGTH+1 +PRIMARY_TREE equ 0 +DISTANCE_TREE equ TREE_SIZE + +; Alphabet +LENGTH_SYMBOLS equ 1+29+2 +DISTANCE_SYMBOLS equ 30 +CONTROL_SYMBOLS equ LENGTH_SYMBOLS+DISTANCE_SYMBOLS +TOTAL_SYMBOLS equ 256+CONTROL_SYMBOLS + + ; Optional (recommend for c2t or DOS) + ; DOS header location and size LSB/MSB +; .byte <START,>START,<(END-START),>(END-START) + +; Uncompress DEFLATE stream starting from the address stored in inputPointer +; to the memory starting from the address stored in outputPointer +;; org inflate + .org inflate +START: +;; mvy #0 getBit_buffer + LDY #0 + STY getBit_buffer + + +inflate_blockLoop: +; Get a bit of EOF and two bits of block type +; ldy #0 + sty getBits_base + lda #GET_3_BITS + jsr getBits +;; lsr @ + lsr A + php + tax + bne inflateCompressedBlock + +; Copy uncompressed block +; ldy #0 + sty getBit_buffer + jsr getWord + jsr getWord + sta inflateStoredBlock_pageCounter +; jmp inflateStoredBlock_firstByte + bcs inflateStoredBlock_firstByte +inflateStoredBlock_copyByte: + jsr getByte +inflateStoreByte: + jsr storeByte + bcc inflateCodes_loop +inflateStoredBlock_firstByte: + inx + bne inflateStoredBlock_copyByte + inc inflateStoredBlock_pageCounter + bne inflateStoredBlock_copyByte + +inflate_nextBlock: + plp + bcc inflate_blockLoop + rts + +inflateCompressedBlock: + +; Decompress a block with fixed Huffman trees +; :144 dta 8 +; :112 dta 9 +; :24 dta 7 +; :6 dta 8 +; :2 dta 8 ; codes with no meaning +; :30 dta 5+DISTANCE_TREE +; ldy #0 +inflateFixedBlock_setCodeLengths: + lda #4 + cpy #144 +;; rol @ + rol A + sta literalSymbolCodeLength,y + cpy #CONTROL_SYMBOLS + bcs inflateFixedBlock_noControlSymbol + lda #5+DISTANCE_TREE + cpy #LENGTH_SYMBOLS + bcs inflateFixedBlock_setControlCodeLength + cpy #24 + adc #2-DISTANCE_TREE +inflateFixedBlock_setControlCodeLength: + sta controlSymbolCodeLength,y +inflateFixedBlock_noControlSymbol: + iny + bne inflateFixedBlock_setCodeLengths +; mva #LENGTH_SYMBOLS inflateCodes_primaryCodes + LDA #LENGTH_SYMBOLS + STA inflateCodes_primaryCodes + + dex + beq inflateCodes + +; Decompress a block reading Huffman trees first + +; Build the tree for temporary codes + jsr buildTempHuffmanTree + +; Use temporary codes to get lengths of literal/length and distance codes + ldx #0 +; sec +inflateDynamicBlock_decodeLength: + php + stx inflateDynamicBlock_lengthIndex +; Fetch a temporary code + jsr fetchPrimaryCode +; Temporary code 0..15: put this length + tax + bpl inflateDynamicBlock_verbatimLength +; Temporary code 16: repeat last length 3 + getBits(2) times +; Temporary code 17: put zero length 3 + getBits(3) times +; Temporary code 18: put zero length 11 + getBits(7) times + jsr getBits +; sec + adc #1 + cpx #GET_7_BITS +;; scc:adc #7 + BCC S1 + adc #7 +S1: + tay + lda #0 + cpx #GET_3_BITS +;; scs:lda inflateDynamicBlock_lastLength + BCS S2 + lda inflateDynamicBlock_lastLength +S2: +inflateDynamicBlock_verbatimLength: + iny + ldx inflateDynamicBlock_lengthIndex + plp +inflateDynamicBlock_storeLength: + bcc inflateDynamicBlock_controlSymbolCodeLength +;; sta literalSymbolCodeLength,x+ + sta literalSymbolCodeLength,x + INX + cpx #1 +inflateDynamicBlock_storeNext: + dey + bne inflateDynamicBlock_storeLength + sta inflateDynamicBlock_lastLength +; jmp inflateDynamicBlock_decodeLength + beq inflateDynamicBlock_decodeLength +inflateDynamicBlock_controlSymbolCodeLength: + cpx inflateCodes_primaryCodes +;; scc:ora #DISTANCE_TREE + BCC S3 + ora #DISTANCE_TREE +S3: +;; sta controlSymbolCodeLength,x+ + sta controlSymbolCodeLength,x + INX + + cpx inflateDynamicBlock_allCodes + bcc inflateDynamicBlock_storeNext + dey +; ldy #0 +; jmp inflateCodes + +; Decompress a block +inflateCodes: + jsr buildHuffmanTree +inflateCodes_loop: + jsr fetchPrimaryCode + bcc inflateStoreByte + tax + beq inflate_nextBlock +; Copy sequence from look-behind buffer +; ldy #0 + sty getBits_base + cmp #9 + bcc inflateCodes_setSequenceLength + tya +; lda #0 + cpx #1+28 + bcs inflateCodes_setSequenceLength + dex + txa +;; lsr @ + lsr A + ror getBits_base + inc getBits_base +;; lsr @ + lsr A + rol getBits_base + jsr getAMinus1BitsMax8 +; sec + adc #0 +inflateCodes_setSequenceLength: + sta inflateCodes_lengthMinus2 + ldx #DISTANCE_TREE + jsr fetchCode +; sec + sbc inflateCodes_primaryCodes + tax + cmp #4 + bcc inflateCodes_setOffsetLowByte + inc getBits_base +;; lsr @ + lsr A + jsr getAMinus1BitsMax8 +inflateCodes_setOffsetLowByte: + eor #$ff + sta inflateCodes_sourcePointer + lda getBits_base + cpx #10 + bcc inflateCodes_setOffsetHighByte + lda getNPlus1Bits_mask-10,x + jsr getBits + clc +inflateCodes_setOffsetHighByte: + eor #$ff +; clc + adc outputPointer+1 + sta inflateCodes_sourcePointer+1 + jsr copyByte + jsr copyByte +inflateCodes_copyByte: + jsr copyByte + dec inflateCodes_lengthMinus2 + bne inflateCodes_copyByte +; jmp inflateCodes_loop + beq inflateCodes_loop + +buildTempHuffmanTree: +; ldy #0 + tya +inflateDynamicBlock_clearCodeLengths: + sta literalSymbolCodeLength,y + sta literalSymbolCodeLength+TOTAL_SYMBOLS-256,y + iny + bne inflateDynamicBlock_clearCodeLengths +; numberOfPrimaryCodes = 257 + getBits(5) +; numberOfDistanceCodes = 1 + getBits(5) +; numberOfTemporaryCodes = 4 + getBits(4) + ldx #3 +inflateDynamicBlock_getHeader: + lda inflateDynamicBlock_headerBits-1,x + jsr getBits +; sec + adc inflateDynamicBlock_headerBase-1,x + sta inflateDynamicBlock_tempCodes-1,x + sta inflateDynamicBlock_headerBase+1 + dex + bne inflateDynamicBlock_getHeader + +; Get lengths of temporary codes in the order stored in tempCodeLengthOrder +; ldx #0 +inflateDynamicBlock_getTempCodeLengths: + lda #GET_3_BITS + jsr getBits + ldy tempCodeLengthOrder,x + sta literalSymbolCodeLength,y + ldy #0 + inx + cpx inflateDynamicBlock_tempCodes + bcc inflateDynamicBlock_getTempCodeLengths + +; Build Huffman trees basing on code lengths (in bits) +; stored in the *SymbolCodeLength arrays +buildHuffmanTree: +; Clear nBitCode_totalCount, nBitCode_literalCount, nBitCode_controlCount + tya +; lda #0 +;; sta:rne nBitCode_clearFrom,y+ +R1: + sta nBitCode_clearFrom,y + INY + BNE R1 +; Count number of codes of each length +; ldy #0 +buildHuffmanTree_countCodeLengths: + ldx literalSymbolCodeLength,y + inc nBitCode_literalCount,x + inc nBitCode_totalCount,x + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol + ldx controlSymbolCodeLength,y + inc nBitCode_controlCount,x + inc nBitCode_totalCount,x +buildHuffmanTree_noControlSymbol: + iny + bne buildHuffmanTree_countCodeLengths +; Calculate offsets of symbols sorted by code length +; lda #0 + ldx #-3*TREE_SIZE +buildHuffmanTree_calculateOffsets: + sta nBitCode_literalOffset+3*TREE_SIZE-$100,x +;; add nBitCode_literalCount+3*TREE_SIZE-$100,x + CLC + ADC nBitCode_literalCount+3*TREE_SIZE-$100,x + inx + bne buildHuffmanTree_calculateOffsets +; Put symbols in their place in the sorted array +; ldy #0 +buildHuffmanTree_assignCode: + tya + ldx literalSymbolCodeLength,y +;; ldy:inc nBitCode_literalOffset,x + ldy nBitCode_literalOffset,x + inc nBitCode_literalOffset,x + sta codeToLiteralSymbol,y + tay + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol2 + ldx controlSymbolCodeLength,y +;; ldy:inc nBitCode_controlOffset,x + ldy nBitCode_controlOffset,x + inc nBitCode_controlOffset,x + sta codeToControlSymbol,y + tay +buildHuffmanTree_noControlSymbol2: + iny + bne buildHuffmanTree_assignCode + rts + +; Read Huffman code using the primary tree +fetchPrimaryCode: + ldx #PRIMARY_TREE +; Read a code from input basing on the tree specified in X, +; return low byte of this code in A, +; return C flag reset for literal code, set for length code +fetchCode: +; ldy #0 + tya +fetchCode_nextBit: + jsr getBit +;; rol @ + rol A + inx +;; sub nBitCode_totalCount,x + SEC + SBC nBitCode_totalCount,x + bcs fetchCode_nextBit +; clc + adc nBitCode_controlCount,x + bcs fetchCode_control +; clc + adc nBitCode_literalOffset,x + tax + lda codeToLiteralSymbol,x + clc + rts +fetchCode_control: +;; add nBitCode_controlOffset-1,x + CLC + ADC nBitCode_controlOffset-1,x + tax + lda codeToControlSymbol,x + sec + rts + +; Read A minus 1 bits, but no more than 8 +getAMinus1BitsMax8: + rol getBits_base + tax + cmp #9 + bcs getByte + lda getNPlus1Bits_mask-2,x +getBits: + jsr getBits_loop +getBits_normalizeLoop: + lsr getBits_base +;; ror @ + ror A + bcc getBits_normalizeLoop + rts + +; Read 16 bits +getWord: + jsr getByte + tax +; Read 8 bits +getByte: + lda #$80 +getBits_loop: + jsr getBit +;; ror @ + ror A + bcc getBits_loop + rts + +; Read one bit, return in the C flag +getBit: + lsr getBit_buffer + bne getBit_return + pha +; ldy #0 + lda (inputPointer),y +;; inw inputPointer + INC inputPointer + BNE S4 + INC inputPointer+1 +S4: + sec +;; ror @ + ror A + sta getBit_buffer + pla +getBit_return: + rts + +; Copy a previously written byte +copyByte: + ldy outputPointer + lda (inflateCodes_sourcePointer),y + ldy #0 +; Write a byte +storeByte: + sta (outputPointer),y + inc outputPointer + bne storeByte_return + inc outputPointer+1 + inc inflateCodes_sourcePointer+1 +storeByte_return: + rts + +getNPlus1Bits_mask: + .byte GET_1_BIT,GET_2_BITS,GET_3_BITS,GET_4_BITS,GET_5_BITS,GET_6_BITS,GET_7_BITS + +tempCodeLengthOrder: + .byte GET_2_BITS,GET_3_BITS,GET_7_BITS,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 + +inflateDynamicBlock_headerBits: + .byte GET_4_BITS,GET_5_BITS,GET_5_BITS +inflateDynamicBlock_headerBase: + .byte 3,0,0 ; second byte is modified at runtime! + + .org inflate_data + +; Data for building trees + +literalSymbolCodeLength: + .org *+256 +controlSymbolCodeLength: + .org *+CONTROL_SYMBOLS + +; Huffman trees + +nBitCode_clearFrom: +nBitCode_totalCount: + .org *+2*TREE_SIZE +nBitCode_literalCount: + .org *+TREE_SIZE +nBitCode_controlCount: + .org *+2*TREE_SIZE +nBitCode_literalOffset: + .org *+TREE_SIZE +nBitCode_controlOffset: + .org *+2*TREE_SIZE + +codeToLiteralSymbol: + .org *+256 +codeToControlSymbol: + .org *+CONTROL_SYMBOLS + + .byte 0,0,0 ; round out block + +END: diff --git a/asm/diskload8000.s b/asm/diskload8000.s new file mode 100644 index 0000000..925bda0 --- /dev/null +++ b/asm/diskload8000.s @@ -0,0 +1,168 @@ +;diskload8000.s + +org = $9000 ; should be $9000 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +endbas = $80C +target = $1000 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +pointer = $0C ; LSB/MSB pointer + +start: + .org endbas +move: ; end of BASIC, move code to readtape addr + ldx #0 +move1: + lda moved,x + sta readtape,x +; lda moved+256,x +; sta readtape+256,x + inx + bne move1 +phase1: + jsr crout ; print LOADING... + lda #<loadm + ldy #>loadm + jsr print + ; diskload2 ORG + lda #$D0 ; store begin location LSB + sta begload + lda #$96 ; store begin location MSB + sta begload+1 + ; end of DOS + 1 for comparison + lda #$00 ; store end location LSB + sta endload + lda #$C0 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code + jmp $9700 ; run it +loadm: + .byte "LOADING INSTA-DI8K, ETA " +loadsec: ; 10 bytes for "XX SEC. ",$00 + .byte 0,0,0,0,0,0,0,0,0,0 +moved: + .org org ; $9000 +readtape: + lda begload ; load begin LSB location + sta store+1 ; store it + lda begload+1 ; load begin MSB location + sta store+2 ; store it + + ldx #0 ; set X to 0 + lda #1 ; set A to 0 + +nsync: bit tapein ; 4 cycles, sync bit ; first pulse + bpl nsync ; 2 + 1 cycles + +main: ldy #0 ; 2 set Y to 0 + +psync: bit tapein ; + bmi psync + +ploop: iny ; 2 cycles + bit tapein ; 4 cycles + bpl ploop ; 2 +1 if branch, +1 if in another page + ; total ~9 cycles + + cpy #$40 ; 2 cycles if Y - $40 > 0 endcode (770Hz) + bpl endcode ; 2(3) + + cpy #$15 ; 2 cycles if Y - $15 > 0 main (2000Hz) + bpl main ; 2(3) + + cpy #$07 ; 2, if Y<, then clear carry, if Y>= set carry +store: rol store+1,x ; 7, roll carry bit into store + ldy #0 ; 2 + asl ; 2 A*=2 + bne main ; 2(3) + lda #1 ; 2 + inx ; 2 cycles + bne main ; 2(3) + inc store+2 ; 6 cycles + jmp main ; 3 cycles + ; 34 subtotal max + ; 36 subtotal max +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda endload + cmp store+1 + bne error + lda endload+1 + cmp store+2 + bne error + jsr ok +sumcheck: + jsr crout + lda #<chkm + ldy #>chkm + jsr print + + lda #0 + sta pointer + lda begload+1 + sta pointer+1 + lda #$ff ; init checksum + ldy begload +sumloop: + eor (pointer),y + + ;last page? + + ldx pointer+1 + cpx endload+1 + beq last + iny + bne sumloop + inc pointer+1 + bne sumloop +last: + iny + cpy endload + bcc sumloop + + ldy #0 + eor (endload),y +; sta chksum +; lda chksum + bne error + jmp ok ; return to caller +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +ok: + lda #<okm + ldy #>okm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts + +chkm: .asciiz "CHKSUM " +okm: .asciiz "OK" +errm: .asciiz "ERROR" +end: diff --git a/asm/diskload9600.s b/asm/diskload9600.s new file mode 100644 index 0000000..ca01639 --- /dev/null +++ b/asm/diskload9600.s @@ -0,0 +1,174 @@ +;diskload9600.s + +org = $9000 ; should be $9000 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +endbas = $80C +target = $1000 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +pointer = $0C ; LSB/MSB pointer + +start: + .org endbas +move: ; end of BASIC, move code to readtape addr + ldx #0 +move1: + lda moved,x + sta readtape,x +; lda moved+256,x +; sta readtape+256,x + inx + bne move1 +phase1: + jsr crout ; print LOADING... + lda #<loadm + ldy #>loadm + jsr print + ; diskload2 ORG + lda #$D0 ; store begin location LSB + sta begload + lda #$96 ; store begin location MSB + sta begload+1 + ; end of DOS + 1 for comparison + lda #$00 ; store end location LSB + sta endload + lda #$C0 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code + jmp $9700 ; run it +loadm: + .byte "LOADING INSTA-DISK, ETA " +loadsec: ; 10 bytes for "XX SEC. ",$00 + .byte 0,0,0,0,0,0,0,0,0,0 +moved: + .org org ; $9000 +readtape: + lda begload ; load begin LSB location + sta store+1 ; store it + lda begload+1 ; load begin MSB location + sta store+2 ; store it + + lda #$ff ; init checksum + sta chksum + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta target,x ; Store data byte + + eor chksum ; compute checksum + sta chksum ; store checksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; one pulse (81-87 cycles) + bit tapein + bpl pre ; pre pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + + ; low freq signals end of data +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda endload + cmp store+1 + bne error + lda endload+1 + cmp store+2 + bne error + jsr ok +sumcheck: + jsr crout + lda #<chkm + ldy #>chkm + jsr print + lda chksum + bne error + jmp ok ; return to caller +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +ok: + lda #<okm + ldy #>okm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts + +chkm: .asciiz "CHKSUM " +okm: .asciiz "OK" +errm: .asciiz "ERROR" +end: diff --git a/asm/fastload8000.s b/asm/fastload8000.s new file mode 100644 index 0000000..58768c1 --- /dev/null +++ b/asm/fastload8000.s @@ -0,0 +1,194 @@ +;fastload8000.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda ld_beg ; load begin LSB location + sta store+1 ; store it + lda ld_beg+1 ; load begin MSB location + sta store+2 ; store it + + ldx #0 ; set X to 0 + lda #1 ; set A to 0 + +nsync: bit tapein ; 4 cycles, sync bit ; first pulse + bpl nsync ; 2 + 1 cycles + +main: ldy #0 ; 2 set Y to 0 + +psync: bit tapein ; + bmi psync + +ploop: iny ; 2 cycles + bit tapein ; 4 cycles + bpl ploop ; 2 +1 if branch, +1 if in another page + ; total ~9 cycles + + cpy #$40 ; 2 cycles if Y - $40 > 0 endcode (770Hz) + bpl endcode ; 2(3) + + cpy #$15 ; 2 cycles if Y - $15 > 0 main (2000Hz) + bpl main ; 2(3) + + cpy #$07 ; 2, if Y<, then clear carry, if Y>= set carry +store: rol store+1,x ; 7, roll carry bit into store + ldy #0 ; 2 + asl ; 2 A*=2 + bne main ; 2(3) + lda #1 ; 2 + inx ; 2 cycles + bne main ; 2(3) + inc store+2 ; 6 cycles + jmp main ; 3 cycles + ; 34 subtotal max + ; 36 subtotal max +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda #0 + sta pointer + lda ld_beg+1 + sta pointer+1 + lda #$ff ; init checksum + ldy ld_beg +sumloop: + eor (pointer),y + + ;last page? + + ldx pointer+1 + cpx ld_end+1 + beq last + iny + bne sumloop + inc pointer+1 + bne sumloop +last: + iny + cpy ld_end + bcc sumloop + +; sty $01 + sta chksum + lda chksum + bne sumerror + + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + diff --git a/asm/fastload9600.s b/asm/fastload9600.s new file mode 100644 index 0000000..5b7cc1c --- /dev/null +++ b/asm/fastload9600.s @@ -0,0 +1,200 @@ +;fastload9600.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda #$ff + sta chksum + + lda ld_beg ; setup point to target + sta store+1 + lda ld_beg+1 + sta store+2 + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta store,x ; Store data byte + + eor chksum + sta chksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; pre pulse (81-87 cycles) + bit tapein + bpl pre ; pre pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck + inc store+2 +endcheck: + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda chksum + bne sumerror + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + diff --git a/asm/fastloadcd.s b/asm/fastloadcd.s new file mode 100644 index 0000000..9d4db18 --- /dev/null +++ b/asm/fastloadcd.s @@ -0,0 +1,202 @@ +;fastloadcd.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda #$ff + sta chksum + + lda ld_beg ; setup point to target + sta store+1 + lda ld_beg+1 + sta store+2 + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta store,x ; Store data byte + + eor chksum + sta chksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; one pulse (81-87 cycles) + bit tapein + bpl one ; one pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + bit tapein + bpl pre ; pre pulse (99-111 cycles) + +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck + inc store+2 +endcheck: + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda chksum + bne sumerror + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + diff --git a/asm/inflate.s b/asm/inflate.s new file mode 100644 index 0000000..fe81ff4 --- /dev/null +++ b/asm/inflate.s @@ -0,0 +1,520 @@ +; inflate - uncompress data stored in the DEFLATE format +; by Piotr Fusik <fox@scene.pl> +; Last modified: 2007-06-17 + +; Compile with xasm (http://xasm.atari.org/), for example: +; xasm inflate.asx /l /d:inflate=$b700 /d:inflate_data=$b900 /d:inflate_zp=$f0 +; inflate is 509 bytes of code and initialized data +; inflate_data is 764 bytes of uninitialized data +; inflate_zp is 10 bytes on page zero + +; hacked for apple ii and ca65 by Egan Ford <egan@sense.net> +; dates + +; compile nodes + +; changes + +;EFF +.define equ = + +inflate = $BA00 +inflate_zp = $0 +inflate_data = $BC00 + +; Pointer to compressed data +inputPointer equ inflate_zp ; 2 bytes + +; Pointer to uncompressed data +outputPointer equ inflate_zp+2 ; 2 bytes + +; Local variables + +getBit_buffer equ inflate_zp+4 ; 1 byte + +getBits_base equ inflate_zp+5 ; 1 byte +inflateStoredBlock_pageCounter equ inflate_zp+5 ; 1 byte + +inflateCodes_sourcePointer equ inflate_zp+6 ; 2 bytes +inflateDynamicBlock_lengthIndex equ inflate_zp+6 ; 1 byte +inflateDynamicBlock_lastLength equ inflate_zp+7 ; 1 byte +inflateDynamicBlock_tempCodes equ inflate_zp+7 ; 1 byte + +inflateCodes_lengthMinus2 equ inflate_zp+8 ; 1 byte +inflateDynamicBlock_allCodes equ inflate_zp+8 ; 1 byte + +inflateCodes_primaryCodes equ inflate_zp+9 ; 1 byte + + +; Argument values for getBits +GET_1_BIT equ $81 +GET_2_BITS equ $82 +GET_3_BITS equ $84 +GET_4_BITS equ $88 +GET_5_BITS equ $90 +GET_6_BITS equ $a0 +GET_7_BITS equ $c0 + +; Maximum length of a Huffman code +MAX_CODE_LENGTH equ 15 + +; Huffman trees +TREE_SIZE equ MAX_CODE_LENGTH+1 +PRIMARY_TREE equ 0 +DISTANCE_TREE equ TREE_SIZE + +; Alphabet +LENGTH_SYMBOLS equ 1+29+2 +DISTANCE_SYMBOLS equ 30 +CONTROL_SYMBOLS equ LENGTH_SYMBOLS+DISTANCE_SYMBOLS +TOTAL_SYMBOLS equ 256+CONTROL_SYMBOLS + +; Optional (recommend for c2t or DOS) +; DOS header location and size LSB/MSB +; .byte <START,>START,<(END-START),>(END-START) + +; Uncompress DEFLATE stream starting from the address stored in inputPointer +; to the memory starting from the address stored in outputPointer +;; org inflate + .org inflate +START: +;; mvy #0 getBit_buffer + LDY #0 + STY getBit_buffer + + +inflate_blockLoop: +; Get a bit of EOF and two bits of block type +; ldy #0 + sty getBits_base + lda #GET_3_BITS + jsr getBits +;; lsr @ + lsr A + php + tax + bne inflateCompressedBlock + +; Copy uncompressed block +; ldy #0 + sty getBit_buffer + jsr getWord + jsr getWord + sta inflateStoredBlock_pageCounter +; jmp inflateStoredBlock_firstByte + bcs inflateStoredBlock_firstByte +inflateStoredBlock_copyByte: + jsr getByte +inflateStoreByte: + jsr storeByte + bcc inflateCodes_loop +inflateStoredBlock_firstByte: + inx + bne inflateStoredBlock_copyByte + inc inflateStoredBlock_pageCounter + bne inflateStoredBlock_copyByte + +inflate_nextBlock: + plp + bcc inflate_blockLoop + rts + +inflateCompressedBlock: + +; Decompress a block with fixed Huffman trees +; :144 dta 8 +; :112 dta 9 +; :24 dta 7 +; :6 dta 8 +; :2 dta 8 ; codes with no meaning +; :30 dta 5+DISTANCE_TREE +; ldy #0 +inflateFixedBlock_setCodeLengths: + lda #4 + cpy #144 +;; rol @ + rol A + sta literalSymbolCodeLength,y + cpy #CONTROL_SYMBOLS + bcs inflateFixedBlock_noControlSymbol + lda #5+DISTANCE_TREE + cpy #LENGTH_SYMBOLS + bcs inflateFixedBlock_setControlCodeLength + cpy #24 + adc #2-DISTANCE_TREE +inflateFixedBlock_setControlCodeLength: + sta controlSymbolCodeLength,y +inflateFixedBlock_noControlSymbol: + iny + bne inflateFixedBlock_setCodeLengths +; mva #LENGTH_SYMBOLS inflateCodes_primaryCodes + LDA #LENGTH_SYMBOLS + STA inflateCodes_primaryCodes + + dex + beq inflateCodes + +; Decompress a block reading Huffman trees first + +; Build the tree for temporary codes + jsr buildTempHuffmanTree + +; Use temporary codes to get lengths of literal/length and distance codes + ldx #0 +; sec +inflateDynamicBlock_decodeLength: + php + stx inflateDynamicBlock_lengthIndex +; Fetch a temporary code + jsr fetchPrimaryCode +; Temporary code 0..15: put this length + tax + bpl inflateDynamicBlock_verbatimLength +; Temporary code 16: repeat last length 3 + getBits(2) times +; Temporary code 17: put zero length 3 + getBits(3) times +; Temporary code 18: put zero length 11 + getBits(7) times + jsr getBits +; sec + adc #1 + cpx #GET_7_BITS +;; scc:adc #7 + BCC S1 + adc #7 +S1: + tay + lda #0 + cpx #GET_3_BITS +;; scs:lda inflateDynamicBlock_lastLength + BCS S2 + lda inflateDynamicBlock_lastLength +S2: +inflateDynamicBlock_verbatimLength: + iny + ldx inflateDynamicBlock_lengthIndex + plp +inflateDynamicBlock_storeLength: + bcc inflateDynamicBlock_controlSymbolCodeLength +;; sta literalSymbolCodeLength,x+ + sta literalSymbolCodeLength,x + INX + cpx #1 +inflateDynamicBlock_storeNext: + dey + bne inflateDynamicBlock_storeLength + sta inflateDynamicBlock_lastLength +; jmp inflateDynamicBlock_decodeLength + beq inflateDynamicBlock_decodeLength +inflateDynamicBlock_controlSymbolCodeLength: + cpx inflateCodes_primaryCodes +;; scc:ora #DISTANCE_TREE + BCC S3 + ora #DISTANCE_TREE +S3: +;; sta controlSymbolCodeLength,x+ + sta controlSymbolCodeLength,x + INX + + cpx inflateDynamicBlock_allCodes + bcc inflateDynamicBlock_storeNext + dey +; ldy #0 +; jmp inflateCodes + +; Decompress a block +inflateCodes: + jsr buildHuffmanTree +inflateCodes_loop: + jsr fetchPrimaryCode + bcc inflateStoreByte + tax + beq inflate_nextBlock +; Copy sequence from look-behind buffer +; ldy #0 + sty getBits_base + cmp #9 + bcc inflateCodes_setSequenceLength + tya +; lda #0 + cpx #1+28 + bcs inflateCodes_setSequenceLength + dex + txa +;; lsr @ + lsr A + ror getBits_base + inc getBits_base +;; lsr @ + lsr A + rol getBits_base + jsr getAMinus1BitsMax8 +; sec + adc #0 +inflateCodes_setSequenceLength: + sta inflateCodes_lengthMinus2 + ldx #DISTANCE_TREE + jsr fetchCode +; sec + sbc inflateCodes_primaryCodes + tax + cmp #4 + bcc inflateCodes_setOffsetLowByte + inc getBits_base +;; lsr @ + lsr A + jsr getAMinus1BitsMax8 +inflateCodes_setOffsetLowByte: + eor #$ff + sta inflateCodes_sourcePointer + lda getBits_base + cpx #10 + bcc inflateCodes_setOffsetHighByte + lda getNPlus1Bits_mask-10,x + jsr getBits + clc +inflateCodes_setOffsetHighByte: + eor #$ff +; clc + adc outputPointer+1 + sta inflateCodes_sourcePointer+1 + jsr copyByte + jsr copyByte +inflateCodes_copyByte: + jsr copyByte + dec inflateCodes_lengthMinus2 + bne inflateCodes_copyByte +; jmp inflateCodes_loop + beq inflateCodes_loop + +buildTempHuffmanTree: +; ldy #0 + tya +inflateDynamicBlock_clearCodeLengths: + sta literalSymbolCodeLength,y + sta literalSymbolCodeLength+TOTAL_SYMBOLS-256,y + iny + bne inflateDynamicBlock_clearCodeLengths +; numberOfPrimaryCodes = 257 + getBits(5) +; numberOfDistanceCodes = 1 + getBits(5) +; numberOfTemporaryCodes = 4 + getBits(4) + ldx #3 +inflateDynamicBlock_getHeader: + lda inflateDynamicBlock_headerBits-1,x + jsr getBits +; sec + adc inflateDynamicBlock_headerBase-1,x + sta inflateDynamicBlock_tempCodes-1,x + sta inflateDynamicBlock_headerBase+1 + dex + bne inflateDynamicBlock_getHeader + +; Get lengths of temporary codes in the order stored in tempCodeLengthOrder +; ldx #0 +inflateDynamicBlock_getTempCodeLengths: + lda #GET_3_BITS + jsr getBits + ldy tempCodeLengthOrder,x + sta literalSymbolCodeLength,y + ldy #0 + inx + cpx inflateDynamicBlock_tempCodes + bcc inflateDynamicBlock_getTempCodeLengths + +; Build Huffman trees basing on code lengths (in bits) +; stored in the *SymbolCodeLength arrays +buildHuffmanTree: +; Clear nBitCode_totalCount, nBitCode_literalCount, nBitCode_controlCount + tya +; lda #0 +;; sta:rne nBitCode_clearFrom,y+ +R1: + sta nBitCode_clearFrom,y + INY + BNE R1 +; Count number of codes of each length +; ldy #0 +buildHuffmanTree_countCodeLengths: + ldx literalSymbolCodeLength,y + inc nBitCode_literalCount,x + inc nBitCode_totalCount,x + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol + ldx controlSymbolCodeLength,y + inc nBitCode_controlCount,x + inc nBitCode_totalCount,x +buildHuffmanTree_noControlSymbol: + iny + bne buildHuffmanTree_countCodeLengths +; Calculate offsets of symbols sorted by code length +; lda #0 + ldx #-3*TREE_SIZE +buildHuffmanTree_calculateOffsets: + sta nBitCode_literalOffset+3*TREE_SIZE-$100,x +;; add nBitCode_literalCount+3*TREE_SIZE-$100,x + CLC + ADC nBitCode_literalCount+3*TREE_SIZE-$100,x + inx + bne buildHuffmanTree_calculateOffsets +; Put symbols in their place in the sorted array +; ldy #0 +buildHuffmanTree_assignCode: + tya + ldx literalSymbolCodeLength,y +;; ldy:inc nBitCode_literalOffset,x + ldy nBitCode_literalOffset,x + inc nBitCode_literalOffset,x + sta codeToLiteralSymbol,y + tay + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol2 + ldx controlSymbolCodeLength,y +;; ldy:inc nBitCode_controlOffset,x + ldy nBitCode_controlOffset,x + inc nBitCode_controlOffset,x + sta codeToControlSymbol,y + tay +buildHuffmanTree_noControlSymbol2: + iny + bne buildHuffmanTree_assignCode + rts + +; Read Huffman code using the primary tree +fetchPrimaryCode: + ldx #PRIMARY_TREE +; Read a code from input basing on the tree specified in X, +; return low byte of this code in A, +; return C flag reset for literal code, set for length code +fetchCode: +; ldy #0 + tya +fetchCode_nextBit: + jsr getBit +;; rol @ + rol A + inx +;; sub nBitCode_totalCount,x + SEC + SBC nBitCode_totalCount,x + bcs fetchCode_nextBit +; clc + adc nBitCode_controlCount,x + bcs fetchCode_control +; clc + adc nBitCode_literalOffset,x + tax + lda codeToLiteralSymbol,x + clc + rts +fetchCode_control: +;; add nBitCode_controlOffset-1,x + CLC + ADC nBitCode_controlOffset-1,x + tax + lda codeToControlSymbol,x + sec + rts + +; Read A minus 1 bits, but no more than 8 +getAMinus1BitsMax8: + rol getBits_base + tax + cmp #9 + bcs getByte + lda getNPlus1Bits_mask-2,x +getBits: + jsr getBits_loop +getBits_normalizeLoop: + lsr getBits_base +;; ror @ + ror A + bcc getBits_normalizeLoop + rts + +; Read 16 bits +getWord: + jsr getByte + tax +; Read 8 bits +getByte: + lda #$80 +getBits_loop: + jsr getBit +;; ror @ + ror A + bcc getBits_loop + rts + +; Read one bit, return in the C flag +getBit: + lsr getBit_buffer + bne getBit_return + pha +; ldy #0 + lda (inputPointer),y +;; inw inputPointer + INC inputPointer + BNE S4 + INC inputPointer+1 +S4: + sec +;; ror @ + ror A + sta getBit_buffer + pla +getBit_return: + rts + +; Copy a previously written byte +copyByte: + ldy outputPointer + lda (inflateCodes_sourcePointer),y + ldy #0 +; Write a byte +storeByte: + sta (outputPointer),y + inc outputPointer + bne storeByte_return + inc outputPointer+1 + inc inflateCodes_sourcePointer+1 +storeByte_return: + rts + +getNPlus1Bits_mask: + .byte GET_1_BIT,GET_2_BITS,GET_3_BITS,GET_4_BITS,GET_5_BITS,GET_6_BITS,GET_7_BITS + +tempCodeLengthOrder: + .byte GET_2_BITS,GET_3_BITS,GET_7_BITS,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 + +inflateDynamicBlock_headerBits: + .byte GET_4_BITS,GET_5_BITS,GET_5_BITS +inflateDynamicBlock_headerBase: + .byte 3,0,0 ; second byte is modified at runtime! + + .org inflate_data + +; Data for building trees + +literalSymbolCodeLength: + .org *+256 +controlSymbolCodeLength: + .org *+CONTROL_SYMBOLS + +; Huffman trees + +nBitCode_clearFrom: +nBitCode_totalCount: + .org *+2*TREE_SIZE +nBitCode_literalCount: + .org *+TREE_SIZE +nBitCode_controlCount: + .org *+2*TREE_SIZE +nBitCode_literalOffset: + .org *+TREE_SIZE +nBitCode_controlOffset: + .org *+2*TREE_SIZE + +codeToLiteralSymbol: + .org *+256 +codeToControlSymbol: + .org *+CONTROL_SYMBOLS + +END: diff --git a/c2t.c b/c2t.c new file mode 100644 index 0000000..13c54b4 --- /dev/null +++ b/c2t.c @@ -0,0 +1,1707 @@ +/* + +c2t, Code to Tape|Text, Version 0.995, Tue May 22 22:11:12 GMT 2012 + +Parts copyright (c) 2011, 2012 All Rights Reserved, Egan Ford (egan@sense.net) + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +PARTICULAR PURPOSE. + +Built on work by: + * Mike Willegal (http://www.willegal.net/appleii/toaiff.c) + * Paul Bourke (http://paulbourke.net/dataformats/audio/, AIFF and WAVE output code) + * Malcolm Slaney and Ken Turkowski (Integer to IEEE 80-bit float code) + * Lance Leventhal and Winthrop Saville (6502 Assembly Language Subroutines, CRC 6502 code) + * Piotr Fusik (http://atariarea.krap.pl/x-asm/inflate.html, inflate 6502 code) + * Rich Geldreich (http://code.google.com/p/miniz/, deflate C code) + * Mike Chambers (http://rubbermallet.org/fake6502.c, 6502 simulator) + +License: + * Do what you like, remember to credit all sources when using. + +Description: + This small utility will read Apple I/II binary and + monitor text files and output Apple I or II AIFF and WAV + audio files for use with the Apple I and II cassette + interface. + +Features: + * Apple I, II, II+, IIe support. + * Big and little-endian machine support. + o Little-endian tested. + * AIFF and WAVE output (both tested). + * Platforms tested: + o 32-bit/64-bit x86 OS/X. + o 32-bit/64-bit x86 Linux. + o 32-bit x86 Windows/Cygwin. + o 32-bit x86 Windows/MinGW. + * Multi-segment tapes. + +Compile: + OS/X: + gcc -Wall -O -o c2t c2t.c + Linux: + gcc -Wall -O -o c2t c2t.c -lm + Windows/Cygwin: + gcc -Wall -O -o c2t c2t.c + Windows/MinGW: + PATH=C:\MinGW\bin;%PATH% + gcc -Wall -O -static -o c2t c2t.c + +Notes: + * Virtual ][ only supports .aif (or .cass) + * Dropbox only supports .wav and .aiff (do not use .wave or .aif) + +Not yet done: + * Test big-endian. + * gnuindent + * Redo malloc code in appendtone + +Thinking about: + * Check for existing file and abort, or warn, or prompt. + * -q quiet option for Makefiles + * autoload support for basic programs + +Bugs: + * Probably + +*/ + +#include "miniz.h" +#include "fake6502.h" +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <math.h> +#include "c2t.h" + +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +#define VERSION "Version 0.995" +#define OUTFILE argv[argc-1] +#define BINARY 0 +#define MONITOR 1 +#define AIFF 2 +#define WAVE 3 +#define DSK 4 + +#define WRITEBYTE(x) { \ + unsigned char wb_j, wb_temp=(x); \ + for(wb_j=0;wb_j<8;wb_j++) { \ + if(wb_temp & 0x80) \ + appendtone(&output,&outputlength,freq1,rate,0,1,&offset); \ + else \ + appendtone(&output,&outputlength,freq0,rate,0,1,&offset); \ + wb_temp<<=1; \ + } \ +} + +void usage(); +char *getext(char *filename); +void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset); +void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void ConvertToIeeeExtended(double num, unsigned char *bytes); +uint8_t read6502(uint16_t address); +void write6502(uint16_t address, uint8_t value); + +unsigned char ram[65536]; +int square = 0; + +typedef struct seg { + int start; + int length; + int codelength; + unsigned char *data; + char filename[256]; +} segment; + +int main(int argc, char **argv) +{ + FILE *ofp; + double *output = NULL, amp=0.75; + long outputlength=0; + int i, c, model=0, outputtype, offset=0, fileoutput=1, warm=0, dsk=0, noformat=0, k8=0, qr=0; + int autoload=0, basicload=0, compress=0, fast=0, cd=0, tape=0, endpage=0, longmon=0, rate=11025, bits=8, freq0=2000, freq1=1000, freq_pre=770, freq_end=770; + char *filetypes[] = {"binary","monitor","aiff","wave","disk"}; + char *modeltypes[] = {"\b","I","II"}; + char *ext; + unsigned int numseg = 0; + segment *segments = NULL; + + opterr = 1; + while((c = getopt(argc, argv, "12vabcftdpn8meh?lqr:")) != -1) + switch(c) { + case '1': // apple 1 + rate = 8000; + model = 1; + break; + case '2': // apple 2 + model = 2; + break; + case 'v': // version + fprintf(stderr,"\n%s\n\n",VERSION); + return 1; + break; + case 'a': // assembly autoloader + model = 2; + autoload = 1; + break; + case 'b': // basic autoloader + model = 2; + basicload = autoload = 1; + break; + case 'c': // compression + model = 2; + autoload = compress = 1; + break; + case 'f': // hifreq + rate = 48000; + model = 2; + autoload = fast = 1; + cd = k8 = 0; + break; + case 'd': // hifreq CD + rate = 44100; + bits = 16; + amp = 1.0; + model = 2; + cd = autoload = 1; + fast = k8 = 0; + break; + case 't': // 10 sec leader + tape = 6; + amp = 1.0; + break; + case 'm': // drop to monitor after load + warm = 1; + break; + case 'e': // end on page boundary + endpage = 1; + break; + case 'p': // stdout + fileoutput = 0; + break; + case 'n': + noformat = 1; + break; + case '8': // 8k + rate = 48000; + model = 2; + autoload = k8 = 1; + fast = cd = 0; + break; + case 'h': // help + case '?': + usage(); + return 1; + case 'q': // qr code support + rate = 48000; + model = 2; + autoload = k8 = qr = 1; + fast = cd = 0; + break; + case 'l': // long mon lines + longmon = 1; + break; + case 'r': // override rate for -1/-2 only + rate = atoi(optarg); + autoload = basicload = k8 = qr = fast = cd = 0; + break; + } + + if(argc - optind < 1 + fileoutput) { + usage(); + return 1; + } + + // read intput files + + fprintf(stderr,"\n"); + for(i=optind;i<argc-fileoutput;i++) { + char start[5]; + unsigned char b, *data; + int j, k, inputtype=BINARY; + segment *tmp; + FILE *ifp; + + if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { + fprintf(stderr,"could not allocate segment %d\n",numseg+1); + abort(); + } + segments = tmp; + + k=0; + for(j=0;j<strlen(argv[i]);j++) { + if(argv[i][j] == ',') + break; + segments[numseg].filename[k++]=argv[i][j]; + } + segments[numseg].filename[k] = '\0'; + // TODO: store as basename, check for MINGW compat + + k=0;j++; + for(;j<strlen(argv[i]);j++) + start[k++]=argv[i][j]; + start[k] = '\0'; + if(k == 0) + segments[numseg].start = -1; + else + segments[numseg].start = (int)strtol(start, (char **)NULL, 16); + + if((ext = getext(segments[numseg].filename)) != NULL) + if(strcmp(ext,"mon") == 0) + inputtype = MONITOR; + + if((ext = getext(segments[numseg].filename)) != NULL) + if(strcmp(ext,"dsk") == 0) + inputtype = DSK; + +//TODO: Windows needs "rb", check UNIX/Linux + + if ((ifp = fopen(segments[numseg].filename, "rb")) == NULL) { + fprintf(stderr,"Cannot read: %s\n\n",segments[numseg].filename); + return 1; + } + + fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); + +//hack to support dumping disks for testing, should be 48, not 140 (really should be dynamic) + + if((data = malloc(140*1024*sizeof(char))) == NULL) { + fprintf(stderr,"could not allocate 140K data\n"); + abort(); + } + + if(inputtype == DSK) { + dsk = 1; + segments[numseg].length = 0; + for(i=0;i<5;i++) { + //segments[numseg].start=i*(140 * 1024 / 5); + segments[numseg].start=0x1000; + + while(fread(&b, 1, 1, ifp) == 1 && segments[numseg].length < (140 * 1024 / 5)) + data[segments[numseg].length++]=b; + + segments[numseg].data = data; + fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); + + if(segments[numseg].length != (140 * 1024 / 5)) { + fprintf(stderr,"\n%s segment too short (< %d) for file type DISK\n\n",segments[numseg].filename,140*1024/5); + return 1; + } + + if(i==4) + break; + + numseg++; + if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { + fprintf(stderr,"could not allocate segment %d\n",numseg+1); + abort(); + } + segments = tmp; + strcpy(segments[numseg].filename,segments[numseg-1].filename); + segments[numseg].length = 0; + if((data = malloc(48*1024*sizeof(char))) == NULL) { + fprintf(stderr,"could not allocate 48K data\n"); + abort(); + } + data[segments[numseg].length++]=b; + + fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); + } + } + + if(inputtype == BINARY) { + if(segments[numseg].start == -1) { + fread(&b, 1, 1, ifp); + segments[numseg].start = b; + fread(&b, 1, 1, ifp); + segments[numseg].start |= b << 8; + fread(&b, 1, 1, ifp); + segments[numseg].length = b; + fread(&b, 1, 1, ifp); + segments[numseg].length |= b << 8; + } + + segments[numseg].length=0; + while(fread(&b, 1, 1, ifp) == 1) + data[segments[numseg].length++]=b; + + segments[numseg].data = data; + fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); + } + + if(inputtype == MONITOR) { + int byte, naddr; + char addrs[8], s; + + segments[numseg].start = -1; + segments[numseg].length = 0; + + while(fscanf(ifp,"%s ",addrs) != EOF) { + naddr = (int)strtol(addrs, (char **)NULL, 16); + if(segments[numseg].start == -1) + segments[numseg].start = naddr; + + if(naddr != segments[numseg].start + segments[numseg].length) { // multi segment + segments[numseg].data = data; + fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); + numseg++; + if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { + fprintf(stderr,"could not allocate segment %d\n",numseg+1); + abort(); + } + segments = tmp; + if((data = malloc(48*1024*sizeof(char))) == NULL) { + fprintf(stderr,"could not allocate 48K data\n"); + abort(); + } + segments[numseg].start = naddr; + segments[numseg].length = 0; + strcpy(segments[numseg].filename,segments[numseg-1].filename); + fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); + } + + while (fscanf(ifp, "%x%c", &byte, &s) != EOF) { + data[segments[numseg].length++]=byte; + if (s == '\n' || s == '\r') + break; + } + } + segments[numseg].data = data; + fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); + } + + fclose(ifp); + numseg++; + } + fprintf(stderr,"\n"); + + if(dsk) { + fast=autoload=cd=tape=0; + model=2; + + if(numseg != 5) { + fprintf(stderr,"Number of segments != 5 and/or not of length %d\n\n",140*1024/5); + return 1; + } + else { + for(i=0;i<5;i++) { + if(segments[i].length != 140*1024/5) { + fprintf(stderr,"Number of segments != 5 and/or not of length %d\n\n",140*1024/5); + return 1; + } + } + } + } + + if(endpage) + for(i=0;i<numseg;i++) { + int pad = (0xFF - ((segments[i].length + segments[i].start - 1) & 0xFF)); + + segments[i].length += pad; + while(pad--) + segments[i].data[segments[i].length - pad - 1] = 0; + } + + if(numseg > 1 || model == 1) { + if(autoload) + fprintf(stderr,"WARNING: number of segments > 1 or model = 1: autoload and fast disabled.\n\n"); + autoload = fast = 0; + } + + if(fileoutput) { + if((ext = getext(OUTFILE)) == NULL) { + usage(); + return 1; + } + else { + if(strcmp(ext,"aiff") == 0 || strcmp(ext,"aif") == 0) + outputtype = AIFF; + else if(strcmp(ext,"wave") == 0 || strcmp(ext,"wav") == 0) + outputtype = WAVE; + else if(strcmp(ext,"mon") == 0) + outputtype = MONITOR; + else { + usage(); + return 1; + } + } + } + else { +/* + if(!model) + outputtype = MONITOR; + else + outputtype = AIFF; +*/ + outputtype = MONITOR; + } + + if(outputtype != MONITOR && !model) { + fprintf(stderr,"\nYou must specify -1 or -2 for Apple I or II tape format, exiting.\n\n"); + return 1; + } + + // TODO: check for existing file and abort, or warn, or prompt + + ofp=stdout; + if(fileoutput) { + if ((ofp = fopen(OUTFILE, "w")) == NULL) { + fprintf(stderr,"\nCannot write: %s\n\n",OUTFILE); + return 1; + } + fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n",OUTFILE,modeltypes[model],filetypes[outputtype]); + } + else + fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n","STDOUT",modeltypes[model],filetypes[outputtype]); + + if(outputtype == MONITOR) { + int i, j, saddr; + unsigned long cmp_len; + unsigned char *cmp_data; + + for(i=0;i<numseg;i++) { + if(compress) { + cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); + free(segments[i].data); + segments[i].data = cmp_data; + segments[i].length = cmp_len; + } + saddr = segments[i].start; + fprintf(ofp,"%04X:", saddr); + for(j=0;j<segments[i].length;j++) { + fprintf(ofp," %02X", segments[i].data[j]); + if(++saddr % (8+(24*longmon)) == 0 && j < segments[i].length - 1) + fprintf(ofp,"\n%04X:",saddr); + } + fprintf(ofp,"\n"); + } + + fclose(ofp); + return 0; + } + + // write out code + if(!autoload && !dsk) { + int i, j; + unsigned long cmp_len; + unsigned char *cmp_data; + char checksum; + + for(i=0;i<numseg;i++) { + // header + if(model == 1) { + appendtone(&output,&outputlength,1000,rate,4.0+tape,0,&offset); + appendtone(&output,&outputlength,2000,rate,0,1,&offset); + } + else { + appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + } + checksum = 0xff; + + if(compress) { + cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); + free(segments[i].data); + segments[i].data = cmp_data; + segments[i].length = cmp_len; + } + for(j=0;j<segments[i].length;j++) { + WRITEBYTE(segments[i].data[j]); + checksum ^= segments[i].data[j]; + } + + // checksum/endbits + if(model == 2) + WRITEBYTE(checksum); + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + } + + // friendly help + fprintf(stderr,"To load up and run on your Apple %s, type:\n\n",modeltypes[model]); + if(model == 1) + fprintf(stderr,"\tC100R\n\t"); + else + fprintf(stderr,"\tCALL -151\n\t"); + + for(i=0;i<numseg;i++) + fprintf(stderr,"%X.%XR ",segments[i].start,segments[i].start+segments[i].length-1); + fprintf(stderr,"\n"); + + if(numseg == 1) { + if(model == 1) + fprintf(stderr,"\t%XR\n",segments[0].start); + else + fprintf(stderr,"\t%XG\n",segments[0].start); + } + fprintf(stderr,"\n"); + } + + if(autoload) { + char eta[40], loading[]=" LOADING "; + unsigned char byte, checksum, *cmp_data, table[12]; + unsigned long ones=0, zeros=0, cmp_len; + unsigned int length, move_len; + int i, j; + + appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + // compute uncompressed ETA + for(j=0;j<segments[0].length;j++) { + byte=segments[0].data[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + + if(fast) { + freq0 = 12000; + freq1 = 8000; + freq_pre = 6000; + freq_end = 2000; + } + if(k8) { + freq0 = 12000; + freq1 = 6000; + freq_pre = 2000; + freq_end = 770; + } + if(cd) { + freq0 = 11025; + freq1 = 7350; + freq_pre = 5512; + freq_end = 2000; + } + + if(compress) { + unsigned long cmp_ones=0, cmp_zeros=0; + double inflate_time = 0; + unsigned int endj; + + cmp_data = tdefl_compress_mem_to_heap(segments[0].data, segments[0].length, &cmp_len, TDEFL_MAX_PROBES_MASK); + + for(j=0;j<cmp_len;j++) { + byte=cmp_data[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + cmp_ones++; + else + cmp_zeros++; + byte <<= 1; + } + } + + // we need to append inflate/decompress code to end of data + for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { + byte=inflatecode[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + cmp_ones++; + else + cmp_zeros++; + byte <<= 1; + } + } + + //compute inflate time + + //load up inflate data + checksum = 0xff; + for(j=0;j<cmp_len;j++) { + ram[0xBA00 - cmp_len + j] = cmp_data[j]; + checksum ^= cmp_data[j]; + } + //load up inflate code + for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { + ram[0xBA00 + j] = inflatecode[j]; + checksum ^= inflatecode[j]; + } + ram[0xBA00 + j] = checksum; + endj = 0xBA00 + j + 1; + + if(k8) { + for(j=(0x823 - 0x80C);j<sizeof(fastload8000)/sizeof(char);j++) + ram[0xBE80 - (0x823 - 0x80C) + j] = fastload8000[j]; + ram[0xBE80 - (0x823 - 0x80C) + j++] = (0xBA00 - cmp_len) & 0xFF; + ram[0xBE80 - (0x823 - 0x80C) + j++] = (0xBA00 - cmp_len) >> 8; + ram[0xBE80 - (0x823 - 0x80C) + j++] = endj & 0xFF; + ram[0xBE80 - (0x823 - 0x80C) + j++] = endj >> 8; + ram[0x00] = 0xFF; + ram[0xBF09] = 0x00; //BRK + + reset6502(); + exec6502(0xBEE3); + + if(ram[0x00] != 0) + fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x00]); + + inflate_time += clockticks6502/1023000.0; + } + + //zero page src + ram[0x0] = (0xBA00 - cmp_len) & 0xFF; + ram[0x1] = (0xBA00 - cmp_len) >> 8; + //zero page dst + ram[0x2] = (segments[0].start) & 0xFF; + ram[0x3] = (segments[0].start) >> 8; + //setup JSR + ram[0xBF00] = 0x20; // JSR $9B00 + ram[0xBF01] = 0x00; + ram[0xBF02] = 0xBA; + ram[0xBF03] = 0x00; //BRK to stop simulation + //run it + reset6502(); + exec6502(0xBF00); + //compare (just to be safe) + for(j=0;j<segments[0].length;j++) + if(ram[segments[0].start + j] != segments[0].data[j]) { + fprintf(stderr,"WARNING: simulated inflate failed at %04X\n",j+0x1000); + break; + } + inflate_time += clockticks6502/1023000.0; + + fprintf(stderr,"start: 0x%04X, length: %5d, deflated: %.02f%%, data time:%.02f, inflate time:%.02f\n",(unsigned int)(0xB9FF - cmp_len),(unsigned int)cmp_len,100.0*(1-cmp_len/(float)segments[0].length),cmp_ones/(float)freq1 + cmp_zeros/(float)freq0,inflate_time); + + if((ones/(float)freq1 + zeros/(float)freq0) < inflate_time + (cmp_ones/(float)freq1 + cmp_zeros/(float)freq0)) { + fprintf(stderr,"WARNING: compression disabled: no significant gain (%.02f)\n",ones/(float)freq1 + zeros/(float)freq0); + compress = 0; + } + else { + free(segments[0].data); + segments[0].data = cmp_data; + segments[0].codelength = segments[0].length; + segments[0].length = cmp_len; + ones=cmp_ones; + zeros=cmp_zeros; + } + fprintf(stderr,"\n"); + } + + sprintf(eta,", ETA %d SEC. ",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25 + (3.75 * ((k8|cd|fast) == 0))) ); + + length = sizeof(basic)/sizeof(char) + sizeof(table)/sizeof(char) + strlen(loading) + strlen(segments[0].filename) + strlen(eta) + 1; + + move_len = (0x823 - 0x80C); + if(fast) + length += sizeof(fastload9600)/sizeof(char); + else + if(k8) + length += sizeof(fastload8000)/sizeof(char); + else + if(cd) + length += sizeof(fastloadcd)/sizeof(char); + else { + length += sizeof(autoloadcode)/sizeof(char); + move_len = (0x81A - 0x80C); + } + + if(fast | k8 | cd) { + if(length - sizeof(basic)/sizeof(char) - move_len > 384) { + segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 384)] = '\0'; + fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); + length = 384 + sizeof(basic)/sizeof(char) + move_len; + } + } + else { + if(length - sizeof(basic)/sizeof(char) - move_len > 256) { + segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 256)] = '\0'; + fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); + length = 256 + sizeof(basic)/sizeof(char) + move_len; + } + } + + freq0 = 2000; + freq1 = 1000; + checksum = 0xff; + + if(basicload) { // write basic stub + header[0] = length & 0xFF; + header[1] = length >> 8; + for(i=0;i<3;i++) { + WRITEBYTE(header[i]); + checksum ^= header[i]; + } + WRITEBYTE(checksum); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + appendtone(&output,&outputlength,770,rate,4.0,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + // write out basic program + checksum = 0xff; + for(i=0;i<sizeof(basic)/sizeof(char);i++) { + WRITEBYTE(basic[i]); + checksum ^= basic[i]; + } + } + else { // write out JMP 80C NOP NOP ... + unsigned char patch[] = {0x4C,0x0C,0x08,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA}; + for(i=0;i<sizeof(patch)/sizeof(char);i++) { + WRITEBYTE(patch[i]); + checksum ^= patch[i]; + } + } + + // write out move and load code + if(compress) { + unsigned int cmp_start = 0xBA00 - segments[0].length; + + //load start + table[0] = cmp_start & 0xff; + table[1] = cmp_start >> 8; + + //load end + table[2] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) & 0xff; + table[3] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) >> 8; + + //inflate src + table[4] = cmp_start & 0xff; + table[5] = cmp_start >> 8; + + //inflate end + table[8] = (segments[0].start + segments[0].codelength) & 0xff; + table[9] = (segments[0].start + segments[0].codelength) >> 8; + } + else { + //load start + table[0] = segments[0].start & 0xff; + table[1] = segments[0].start >> 8; + + //load end + table[2] = (segments[0].start + segments[0].length + 1) & 0xff; + table[3] = (segments[0].start + segments[0].length + 1) >> 8; + } + //JMP to code, inflate dst + table[6] = segments[0].start & 0xff; + table[7] = segments[0].start >> 8; + table[10] = compress; + table[11] = warm; + + if(fast) + for(i=0;i<sizeof(fastload9600)/sizeof(char);i++) { + WRITEBYTE(fastload9600[i]); + checksum ^= fastload9600[i]; + } + else + if(k8) + for(i=0;i<sizeof(fastload8000)/sizeof(char);i++) { + WRITEBYTE(fastload8000[i]); + checksum ^= fastload8000[i]; + } + else + if(cd) + for(i=0;i<sizeof(fastloadcd)/sizeof(char);i++) { + WRITEBYTE(fastloadcd[i]); + checksum ^= fastloadcd[i]; + } + else + for(i=0;i<sizeof(autoloadcode)/sizeof(char);i++) { + WRITEBYTE(autoloadcode[i]); + checksum ^= autoloadcode[i]; + } + + // append table + for(i=0;i<sizeof(table)/sizeof(char);i++) { + WRITEBYTE(table[i]); + checksum ^= table[i]; + } + + // append LOADING... + loading[0] = 0x0D; + for(i=0;i<strlen(loading);i++) { + byte = toupper(loading[i]) + 0x80; + if(loading[i] == '_') + byte = toupper(' ') + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + // append to loader the name of the file + for(i=0;i<strlen(segments[0].filename);i++) { + byte = toupper(segments[0].filename[i]) + 0x80; + if(segments[0].filename[i] == '_') + byte = toupper(' ') + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + // append to loader the ETA + for(i=0;i<strlen(eta);i++) { + byte = toupper(eta[i]) + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + // append to NULL to LOADING string + WRITEBYTE(0x00); + checksum ^= 0x00; + + // it's a wrap! + WRITEBYTE(0xff); + checksum ^= 0xff; + + if(!basicload) { + int pad = (0xFF - (length & 0xFF)); + + if(!(fast|cd|k8)) + pad += 0x100; + + length += pad; + while(pad--) + WRITEBYTE(0x00); + } + + WRITEBYTE(checksum); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + if(fast || cd || k8) + appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); + else { + appendtone(&output,&outputlength,770,rate,4.0,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + } + + // now the code + if(fast) { + freq0 = 12000; + freq1 = 8000; + } + if(cd) { + freq0 = 11025; + freq1 = 7350; + } + if(k8) { + freq0 = 12000; + freq1 = 6000; + } + + if(qr) { + char loading[]="LOADING "; + outputlength = 0; + + // 0.25 sec + appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); + + checksum = 0xff; + + // parameters, 12 bytes + for(i=0;i<sizeof(table)/sizeof(char);i++) { + WRITEBYTE(table[i]); + checksum ^= table[i]; + } + + // LOADING + for(i=0;i<strlen(loading);i++) { + byte = loading[i] + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + // append to loader the name of the file + for(i=0;i<strlen(segments[0].filename);i++) { + byte = toupper(segments[0].filename[i]) + 0x80; + if(segments[0].filename[i] == '_') + byte = toupper(' ') + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + // append to loader the ETA + for(i=0;i<strlen(eta);i++) { + byte = toupper(eta[i]) + 0x80; + WRITEBYTE(byte); + checksum ^= byte; + } + + for(i=0;i<60-strlen(segments[0].filename)-strlen(eta)-strlen(loading);i++) { + WRITEBYTE(0x00); + checksum ^= 0x00; + } + + WRITEBYTE(checksum); + + // end of parameters + appendtone(&output,&outputlength,freq_end,rate,0,2,&offset); + + // time to processes + appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); + } + + checksum = 0xff; + for(j=0;j<segments[0].length;j++) { + WRITEBYTE(segments[0].data[j]); + checksum ^= segments[0].data[j]; + } + + if(compress) { + for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { + WRITEBYTE(inflatecode[j]); + checksum ^= inflatecode[j]; + } + } + + if(fast + cd + k8 == 0) { // hack so that standard method matches others + WRITEBYTE(0x00); + WRITEBYTE(0x00); + } + + WRITEBYTE(checksum); + + if(fast || cd || k8) + //appendtone(&output,&outputlength,freq_end,rate,0,1,&offset); + appendtone(&output,&outputlength,freq_end,rate,0,10,&offset); + else + //appendtone(&output,&outputlength,1000,rate,0,1,&offset); + appendtone(&output,&outputlength,1000,rate,0,10,&offset); + + if(!qr) { + if(basicload) { + fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\tLOAD\n",modeltypes[model]); + if(warm) + fprintf(stderr,"\t%XG\n",segments[0].start); + } + else { + fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\t800.%XR 800G\n",modeltypes[model],0x800 + length + 1); + } + } + else { + fprintf(stderr,"To load up and run on your Apple %s, use the client disk.\n",modeltypes[model]); + } + fprintf(stderr,"\n"); + } + + if(dsk) { + char eta[40]; + unsigned char byte, checksum=0xff, *cmp_data, start_table[21], *diskloadcode; + unsigned long ones=0, zeros=0, cmp_len, diskloadcode_len; + unsigned int length, start_table_len = 0; + int i, j; + double inflate_times[5]; + + if(k8) { + diskloadcode = diskload8000; + diskloadcode_len = sizeof(diskload8000)/sizeof(char); + } + else { + diskloadcode = diskload9600; + diskloadcode_len = sizeof(diskload9600)/sizeof(char); + } + + rate = 48000; + appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + for(j=0;j<sizeof(diskloadcode2)/sizeof(char);j++) { + byte=diskloadcode2[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + + // compute pad length, assuming 4 pages max for code + zeros += 8*(4 * 256 - sizeof(diskloadcode2)/sizeof(char)); + + for(j=0;j<sizeof(diskloadcode3)/sizeof(char);j++) { + byte=diskloadcode3[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + + for(j=0;j<sizeof(dosboot1)/sizeof(char);j++) { + byte=dosboot1[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + + for(j=0;j<sizeof(dosboot2)/sizeof(char);j++) { + byte=dosboot2[j]; + for(i=0;i<8;i++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + + freq0 = 12000; + freq1 = 8000; + if(k8) + freq1 = 6000; + sprintf(eta,"%d SEC. ",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25)); + + //length = sizeof(basic)/sizeof(char) + sizeof(diskloadcode)/sizeof(char); + length = sizeof(basic)/sizeof(char) + diskloadcode_len; + header[0] = length & 0xFF; + header[1] = length >> 8; + + freq0 = 2000; + freq1 = 1000; + for(i=0;i<3;i++) { + WRITEBYTE(header[i]); + checksum ^= header[i]; + } + WRITEBYTE(checksum); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + appendtone(&output,&outputlength,770,rate,4.0,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + // write out basic program + checksum = 0xff; + for(i=0;i<sizeof(basic)/sizeof(char);i++) { + WRITEBYTE(basic[i]); + checksum ^= basic[i]; + } + + // patch in ETA + for(i=0;i<strlen(eta);i++) + diskloadcode[0x84F - 0x80C + i] = eta[i] + 0x80; + + // write out move and load code + //for(i=0;i<sizeof(diskloadcode)/sizeof(char);i++) { + for(i=0;i<diskloadcode_len;i++) { + WRITEBYTE(diskloadcode[i]); + checksum ^= diskloadcode[i]; + } + + // end of basic and diskloadcode + WRITEBYTE(0xff); + checksum ^= 0xff; + + WRITEBYTE(checksum); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + square=0; + freq0 = 12000; + if(k8) { + freq1 = 6000; + appendtone(&output,&outputlength,2000,rate,0.25,0,&offset); + } + else { + freq1 = 8000; + appendtone(&output,&outputlength,6000,rate,0.25,0,&offset); + } + + checksum = 0xff; + for(i=0;i<sizeof(dosboot1)/sizeof(char);i++) { + WRITEBYTE(dosboot1[i]); + checksum ^= dosboot1[i]; + } + + // time to compress and compute start location and length + // patch loadcode2 with start locations and ETA + for(i=0;i<numseg;i++) { + int k, err; + double orig_len; + unsigned char checksum=0xff; + + inflate_times[i] = 0; + + cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); + + //compute inflate time + //load up inflate code + for(j=0;j<sizeof(diskloadcode3)/sizeof(char);j++) + ram[0x9B00 + j] = diskloadcode3[j]; + //load up inflate data + for(j=0;j<cmp_len;j++) { + ram[0x8FFF - cmp_len + j] = cmp_data[j]; + checksum ^= cmp_data[j]; + } + ram[0x8FFF] = checksum; + + //compute chksum time + if(k8) { + for(j=(0x859 - 0x80C);j<diskloadcode_len;j++) + ram[0x9000 - (0x859 - 0x80C) + j] = diskloadcode[j]; + ram[0x00] = (0x8FFF - cmp_len) & 0xFF; + ram[0x01] = (0x8FFF - cmp_len) >> 8; + ram[0x02] = 0x00; + ram[0x03] = 0x90; + ram[0x04] = 0xFF; + ram[0x9089] = 0x85; //STA + ram[0x908A] = 0x04; //zero page $04 + ram[0x908B] = 0x00; //BRK + + reset6502(); + exec6502(0x9065); + + if(ram[0x04] != 0) + fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x04]); + + inflate_times[i] += clockticks6502/1023000.0; + } + + //zero page src + ram[0x10] = (0x8FFF - cmp_len) & 0xFF; + ram[0x11] = (0x8FFF - cmp_len) >> 8; + //zero page dst + ram[0x12] = 0x00; + ram[0x13] = 0x10; + //setup JSR + ram[0x9000] = 0x20; // JSR $9B00 + ram[0x9001] = 0x00; + ram[0x9002] = 0x9B; + ram[0x9003] = 0x00; //BRK to stop simulation + //run it + reset6502(); + exec6502(0x9000); + //compare (just to be safe) + err=0; + for(j=0;j<7 * 4096;j++) + if(ram[0x1000 + j] != segments[i].data[j]) { + err = 1; + break; + } + if(err) + fprintf(stderr,"WARNING: simulated inflate failed at %04X\n",j+0x1000); + inflate_times[i] += clockticks6502/1023000.0; + + free(segments[i].data); + segments[i].data = cmp_data; + orig_len = segments[i].length; + segments[i].length = cmp_len; + segments[i].start = 0x8FFF - segments[i].length; + + // compress ? + // need to see what is faster, defaulting to compress for now + // if not compressed do not set start location, change asm code to check for 0,0 + // and not use inflate code + + // where to load data + start_table[start_table_len++] = segments[i].start & 0xFF; + start_table[start_table_len++] = segments[i].start >> 8; + + ones = zeros = 0; + for(j=0;j<segments[i].length;j++) { + byte=segments[i].data[j]; + for(k=0;k<8;k++) { + if(byte & 0x80) + ones++; + else + zeros++; + byte <<= 1; + } + } + sprintf(eta,"%d",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25)); + + // ETA + start_table[start_table_len++] = eta[0] + 0x80; + if(eta[1] != 0) + start_table[start_table_len++] = eta[1] + 0x80; + else + start_table[start_table_len++] = 0; + + fprintf(stderr,"Segment: %d, start: 0x%04X, length: %5d, deflated: %.02f%%, data time:%s, inflate time:%.02f\n",i,segments[i].start,segments[i].length,100.0*(1-segments[i].length/orig_len),eta,inflate_times[i]); + } + fprintf(stderr,"\n"); + + for(i=0;i<sizeof(diskloadcode2)/sizeof(char);i++) { + WRITEBYTE(diskloadcode2[i]); + checksum ^= diskloadcode2[i]; + } + + start_table[start_table_len++] = noformat; + + for(i=0;i<start_table_len;i++) { + WRITEBYTE(start_table[i]); + checksum ^= start_table[i]; + } + + for(i=0;i<4*256 - sizeof(diskloadcode2)/sizeof(char) - start_table_len;i++) { + WRITEBYTE(0x00); + checksum ^= 0x00; + } + + for(i=0;i<sizeof(diskloadcode3)/sizeof(char);i++) { + WRITEBYTE(diskloadcode3[i]); + checksum ^= diskloadcode3[i]; + } + + for(i=0;i<sizeof(dosboot2)/sizeof(char);i++) { + WRITEBYTE(dosboot2[i]); + checksum ^= dosboot2[i]; + } + + WRITEBYTE(checksum); + if(k8) { + appendtone(&output,&outputlength,770,rate,0,2,&offset); + appendtone(&output,&outputlength,2000,rate,0.3,0,&offset); + } + else { + appendtone(&output,&outputlength,2000,rate,0,1,&offset); + appendtone(&output,&outputlength,6000,rate,0.1,0,&offset); + } + + for(i=0;i<numseg;i++) { + //appendtone(&output,&outputlength,6000,rate,1,0,&offset); + +//timing + if(i==0) { + if(!noformat) + j=28; + else + j=0; + } + else { + //j = 6 + ceil(inflate_times[i-1]); // 6 = write track time, may need to make it 7 + j = ceil(6.5 + inflate_times[i-1]); // 6 = write track time, may need to make it 7 + } + if(i==1) // seek time for track 0, just in case + j+=2; + +/* count down code + for(;j>=0;j--) { + checksum = 0xff; + WRITEBYTE(j/10 + 48 + 0x80); + checksum ^= (j/10 + 48 + 0x80); + WRITEBYTE(j%10 + 48 + 0x80); + checksum ^= (j%10 + 48 + 0x80); + WRITEBYTE(0x00); + checksum ^= 0x00; + WRITEBYTE(checksum); + appendtone(&output,&outputlength,2000,rate,0,1,&offset); + appendtone(&output,&outputlength,6000,rate,1,0,&offset); + } +*/ + + if(k8) + appendtone(&output,&outputlength,2000,rate,j,0,&offset); + else + appendtone(&output,&outputlength,6000,rate,j,0,&offset); + + checksum = 0xff; + for(j=0;j<segments[i].length;j++) { + WRITEBYTE(segments[i].data[j]); + checksum ^= segments[i].data[j]; + } + WRITEBYTE(checksum); + if(k8) + //appendtone(&output,&outputlength,770,rate,0,2,&offset); + appendtone(&output,&outputlength,770,rate,0,10,&offset); + else + //appendtone(&output,&outputlength,2000,rate,0,1,&offset); + appendtone(&output,&outputlength,2000,rate,0,10,&offset); + } + + fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\tLOAD\n\n",modeltypes[model]); + } + + // append zero to zero out last wave + appendtone(&output,&outputlength,0,rate,0,1,&offset); + + // 0.1 sec quiet to help some emulators + appendtone(&output,&outputlength,0,rate,0.1,0,&offset); + + // 0.4 sec quiet to help some IIs + // appendtone(&output,&outputlength,0,rate,0.4,0,&offset); + + // write it + if(outputtype == AIFF) + Write_AIFF(ofp,output,outputlength,rate,bits,amp); + else if(outputtype == WAVE) + Write_WAVE(ofp,output,outputlength,rate,bits,amp); + + fclose(ofp); + return 0; +} + +void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset) +{ + long i, n=time*rate; + static long grow = 0; + double *tmp = NULL; + + if(freq && cycles) + n=cycles*rate/freq; + + if(n == 0) + n=cycles; + +/* + if((tmp = (double *)realloc(*sound, (*length + n) * sizeof(double))) == NULL) + abort(); + *sound = tmp; +*/ + +// new code for speed up Windows realloc + if(*length + n > grow) { + grow = *length + n + 10000000; + if((tmp = (double *)realloc(*sound, (grow) * sizeof(double))) == NULL) + abort(); + *sound = tmp; + } + +//tmp -> (*sound) + if(square) { + int j; + + if(freq) + for (i = 0; i < n; i++) { + for(j = 0;j < rate / freq / 2;j++) + (*sound)[*length + i++] = 1; + for(j = 0;j < rate / freq / 2;j++) + (*sound)[*length + i++] = -1; + i--; + } + else + for (i = 0; i < n; i++) + (*sound)[*length + i] = 0; + } + else + for(i=0;i<n;i++) + (*sound)[*length+i] = sin(2*M_PI*i*freq/rate + *offset*M_PI); + + if(cycles - (int)cycles == 0.5) + *offset = (*offset == 0); + + *length += n; +} + +char *getext(char *filename) +{ + char stack[256], *rval; + int i, sp = 0; + + for(i=strlen(filename)-1;i>=0;i--) { + if(filename[i] == '.') + break; + stack[sp++] = filename[i]; + } + stack[sp] = '\0'; + + if(sp == strlen(filename) || sp == 0) + return(NULL); + + if((rval = (char *)malloc(sp * sizeof(char))) == NULL) + ; //do error code + + rval[sp] = '\0'; + for(i=0;i<sp+i;i++) + rval[i] = stack[--sp]; + + return(rval); +} + +void usage() +{ + fprintf(stderr,"%s",usagetext); +} + +// Code below from http://paulbourke.net/dataformats/audio/ +/* + Write an AIFF sound file + Only do one channel, only support 16 bit. + Supports sample frequencies of 11, 22, 44KHz (default). + Little/big endian independent! +*/ + +// egan: changed code to support any Hz and 8 bit. + +void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) +{ + unsigned short v; + int i; + unsigned long totalsize; + double themin, themax, scale, themid; + unsigned char bit80[10]; + + // Write the form chunk + fprintf(fptr, "FORM"); + totalsize = 4 + 8 + 18 + 8 + (bits / 8) * nsamples + 8; + fputc((totalsize & 0xff000000) >> 24, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x000000ff), fptr); + fprintf(fptr, "AIFF"); + + // Write the common chunk + fprintf(fptr, "COMM"); + fputc(0, fptr); // Size + fputc(0, fptr); + fputc(0, fptr); + fputc(18, fptr); + fputc(0, fptr); // Channels = 1 + fputc(1, fptr); + fputc((nsamples & 0xff000000) >> 24, fptr); // Samples + fputc((nsamples & 0x00ff0000) >> 16, fptr); + fputc((nsamples & 0x0000ff00) >> 8, fptr); + fputc((nsamples & 0x000000ff), fptr); + fputc(0, fptr); // Size = 16 + fputc(bits, fptr); + + ConvertToIeeeExtended(nfreq, bit80); + for (i = 0; i < 10; i++) + fputc(bit80[i], fptr); + + // Write the sound data chunk + fprintf(fptr, "SSND"); + fputc((((bits / 8) * nsamples + 8) & 0xff000000) >> 24, fptr); // Size + fputc((((bits / 8) * nsamples + 8) & 0x00ff0000) >> 16, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x0000ff00) >> 8, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x000000ff), fptr); + fputc(0, fptr); // Offset + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); // Block + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0xff00) >> 8, fptr); + fputc((v & 0x00ff), fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v, fptr); + } + } +} + +/* + Write an WAVE sound file + Only do one channel, only support 16 bit. + Supports any (reasonable) sample frequency + Little/big endian independent! +*/ + +// egan: changed code to support 8 bit. + +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) +{ + unsigned short v; + int i; + unsigned long totalsize, bytespersec; + double themin, themax, scale, themid; + + // Write the form chunk + fprintf(fptr, "RIFF"); + totalsize = (bits / 8) * nsamples + 36; + fputc((totalsize & 0x000000ff), fptr); // File size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + fprintf(fptr, "WAVE"); + fprintf(fptr, "fmt "); // fmt_ chunk + fputc(16, fptr); // Chunk size + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(1, fptr); // Format tag - uncompressed + fputc(0, fptr); + fputc(1, fptr); // Channels + fputc(0, fptr); + fputc((nfreq & 0x000000ff), fptr); // Sample frequency (Hz) + fputc((nfreq & 0x0000ff00) >> 8, fptr); + fputc((nfreq & 0x00ff0000) >> 16, fptr); + fputc((nfreq & 0xff000000) >> 24, fptr); + bytespersec = (bits / 8) * nfreq; + fputc((bytespersec & 0x000000ff), fptr); // Average bytes per second + fputc((bytespersec & 0x0000ff00) >> 8, fptr); + fputc((bytespersec & 0x00ff0000) >> 16, fptr); + fputc((bytespersec & 0xff000000) >> 24, fptr); + fputc((bits / 8), fptr); // Block alignment + fputc(0, fptr); + fputc(bits, fptr); // Bits per sample + fputc(0, fptr); + fprintf(fptr, "data"); + totalsize = (bits / 8) * nsamples; + fputc((totalsize & 0x000000ff), fptr); // Data size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0x00ff), fptr); + fputc((v & 0xff00) >> 8, fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v + 0x80, fptr); + } + } +} + + +/* + * C O N V E R T T O I E E E E X T E N D E D + */ + +/* Copyright (C) 1988-1991 Apple Computer, Inc. + * All rights reserved. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +#ifndef HUGE_VAL +#define HUGE_VAL HUGE +#endif /*HUGE_VAL */ + +#define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1) + +void ConvertToIeeeExtended(double num, unsigned char *bytes) +{ + int sign; + int expon; + double fMant, fsMant; + unsigned long hiMant, loMant; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else { + sign = 0; + } + + if (num == 0) { + expon = 0; + hiMant = 0; + loMant = 0; + } else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign | 0x7FFF; + hiMant = 0; + loMant = 0; /* infinity */ + } else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; +} + +uint8_t read6502(uint16_t address) +{ + return ram[address]; +} + +void write6502(uint16_t address, uint8_t value) +{ + ram[address] = value; +} diff --git a/c2t.h b/c2t.h new file mode 100644 index 0000000..b8a4a1b --- /dev/null +++ b/c2t.h @@ -0,0 +1,4352 @@ + +const char *usagetext="\n\ +usage: c2t [-vh?]\n\ + c2t [-elp] input[.mon],[addr] ... [output.mon]\n\ + c2t {-1} [-cepr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]]\n\ + c2t {-2} [-abcdef8pmqr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]]\n\ + c2t [-n8] input.dsk ... [output.[aif[f]|wav[e]]]\n\ +\n\ + -1 or -2 for Apple I or II tape format\n\ + -8 use 48k/8bit 8000 bps transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -f and -d.\n\ + -a assembly autoload and run (Apple II/II+/IIe 64K only)\n\ + -b basic autoload and run (Apple II+/IIe 64K only)\n\ + Implies -2a.\n\ + -c compress data\n\ + -d use fast 44.1k/16bit transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -f and -8. Use for burning CDs.\n\ + -e pad with $00 to end on page boundary\n\ + -f use faster 48k/8bit (9600 bps) transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -8 and -d. Unreliable on some systems.\n\ + -h|? this help\n\ + -l long monitor format (24 bytes/line)\n\ + -m jump to monitor after autoload\n\ + -n do not format disks\n\ + -p pipe to stdout\n\ + -q parameters and data only (for use with custom client)\n\ + -r #, where # overrides the sample rate (e.g. -r 48000)\n\ + Negates -a, -b, -8, -q, -f, and -d\n\ + -t 10 second preamble (default 4) for real tape use\n\ + -v print version number and exit\n\ +\n\ +input(s) without a .mon or .dsk extension is assumed to be a binary with a 4\n\ +byte header. If the header is missing then you must append ,load_address to\n\ +each binary input missing a header, e.g. filename,800. The load address\n\ +will be read as hex.\n\ +\n\ +input(s) with a .mon extension expected input format:\n\ +\n\ + 0280: A2 FF 9A 20 8C 02 20 4F\n\ + 0288: 03 4C 00 FF 20 9E 02 A9\n\ +\n\ +A single input with a .dsk extension expected to be a 140K disk image.\n\ +\n\ +output must have aiff, aif, wav, wave, or mon extention.\n\ +\n\ +Examples:\n\ +\n\ + c2t hello hello.mon\n\ + c2t -p hello.mon print.mon foo,800 | pbcopy\n\ + c2t -2f moon.patrol,801 moon.patrol.aif\n\ + c2t -2 hello,300 hello.aiff\n\ + c2t -1 hello.mon hello.wav\n\ + c2t -2 thief,801 thief.obj,3ffd thief.pic,2000 theif.aif\n\ + c2t foo.dsk foo.wav\n\ +\n\ +"; + +//51 00 D5, 3 byte header, LENGTH LSB/MSB, D5 for autorun +unsigned char header[] = {0x00,0x00,0xD5}; + + +/* +basic program "CALL 2060", 801.80B + +end of code[LSB,MSB] = 0x0B,0x08 +line #[LSB,MSG] = 0x01,0x00 +CALL[1] = 0x8C +vec[4] = 0x32,0x30,0x36,0x30 (2060 in ASCII) +end[2] = 0x00,0x00 +*/ +unsigned char basic[] = {0x0B,0x08,0x01,0x00,0x8C,0x32,0x30,0x36,0x30,0x00,0x00}; + +/* +;autoload.s + +org = $BF00 ; should be $BF00 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +readblk = $FEFD +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta load,x + inx + bne move1 ; move 256 bytes + jmp load +moved: + .org org +load: + lda #<loadm + ldy #>loadm + jsr print + + lda ld_beg + sta $3C ; starting tape address low + lda ld_beg+1 + sta $3D ; starting tape address high + lda ld_end + sta $3E ; ending tape address low + lda ld_end+1 + sta $3F ; ending tape address high + jsr readblk ; read block from tape + + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + +*/ +unsigned char autoloadcode[] = { + 0xA2,0x00,0xBD,0x1A, + 0x08,0x9D,0x00,0xBF,0xE8,0xD0,0xF7,0x4C, + 0x00,0xBF,0xA9,0xA9,0xA0,0xBF,0x20,0x71, + 0xBF,0xAD,0x9D,0xBF,0x85,0x3C,0xAD,0x9E, + 0xBF,0x85,0x3D,0xAD,0x9F,0xBF,0x85,0x3E, + 0xAD,0xA0,0xBF,0x85,0x3F,0x20,0xFD,0xFE, + 0xAD,0xA7,0xBF,0xF0,0x2F,0x20,0x8E,0xFD, + 0xA9,0x92,0xA0,0xBF,0x20,0x71,0xBF,0xAD, + 0xA1,0xBF,0x85,0x00,0xAD,0xA2,0xBF,0x85, + 0x01,0xAD,0xA3,0xBF,0x85,0x02,0xAD,0xA4, + 0xBF,0x85,0x03,0x20,0x00,0xBA,0xAD,0xA5, + 0xBF,0xC5,0x02,0xD0,0x1C,0xAD,0xA6,0xBF, + 0xC5,0x03,0xD0,0x15,0xAD,0xA8,0xBF,0xD0, + 0x03,0x6C,0xA3,0xBF,0x4C,0x69,0xFF,0x20, + 0x8E,0xFD,0xA9,0x84,0xA0,0xBF,0x20,0x71, + 0xBF,0xA9,0x8C,0xA0,0xBF,0x20,0x71,0xBF, + 0x4C,0x69,0xFF,0x85,0x06,0x84,0x07,0xA0, + 0x00,0xB1,0x06,0x09,0x80,0x20,0xED,0xFD, + 0xC8,0xB1,0x06,0xD0,0xF6,0x60,0x43,0x48, + 0x4B,0x53,0x55,0x4D,0x20,0x00,0x45,0x52, + 0x52,0x4F,0x52,0x00,0x49,0x4E,0x46,0x4C, + 0x41,0x54,0x49,0x4E,0x47,0x20,0x00 +}; +/* +; inflate - uncompress data stored in the DEFLATE format +; by Piotr Fusik <fox@scene.pl> +; Last modified: 2007-06-17 + +; Compile with xasm (http://xasm.atari.org/), for example: +; xasm inflate.asx /l /d:inflate=$b700 /d:inflate_data=$b900 /d:inflate_zp=$f0 +; inflate is 509 bytes of code and initialized data +; inflate_data is 764 bytes of uninitialized data +; inflate_zp is 10 bytes on page zero + +; hacked for apple ii and ca65 by Egan Ford <egan@sense.net> +; dates + +; compile nodes + +; changes + +;EFF +.define equ = + +inflate = $BA00 +inflate_zp = $0 +inflate_data = $BC00 + +; Pointer to compressed data +inputPointer equ inflate_zp ; 2 bytes + +; Pointer to uncompressed data +outputPointer equ inflate_zp+2 ; 2 bytes + +; Local variables + +getBit_buffer equ inflate_zp+4 ; 1 byte + +getBits_base equ inflate_zp+5 ; 1 byte +inflateStoredBlock_pageCounter equ inflate_zp+5 ; 1 byte + +inflateCodes_sourcePointer equ inflate_zp+6 ; 2 bytes +inflateDynamicBlock_lengthIndex equ inflate_zp+6 ; 1 byte +inflateDynamicBlock_lastLength equ inflate_zp+7 ; 1 byte +inflateDynamicBlock_tempCodes equ inflate_zp+7 ; 1 byte + +inflateCodes_lengthMinus2 equ inflate_zp+8 ; 1 byte +inflateDynamicBlock_allCodes equ inflate_zp+8 ; 1 byte + +inflateCodes_primaryCodes equ inflate_zp+9 ; 1 byte + + +; Argument values for getBits +GET_1_BIT equ $81 +GET_2_BITS equ $82 +GET_3_BITS equ $84 +GET_4_BITS equ $88 +GET_5_BITS equ $90 +GET_6_BITS equ $a0 +GET_7_BITS equ $c0 + +; Maximum length of a Huffman code +MAX_CODE_LENGTH equ 15 + +; Huffman trees +TREE_SIZE equ MAX_CODE_LENGTH+1 +PRIMARY_TREE equ 0 +DISTANCE_TREE equ TREE_SIZE + +; Alphabet +LENGTH_SYMBOLS equ 1+29+2 +DISTANCE_SYMBOLS equ 30 +CONTROL_SYMBOLS equ LENGTH_SYMBOLS+DISTANCE_SYMBOLS +TOTAL_SYMBOLS equ 256+CONTROL_SYMBOLS + +; Optional (recommend for c2t or DOS) +; DOS header location and size LSB/MSB +; .byte <START,>START,<(END-START),>(END-START) + +; Uncompress DEFLATE stream starting from the address stored in inputPointer +; to the memory starting from the address stored in outputPointer +;; org inflate + .org inflate +START: +;; mvy #0 getBit_buffer + LDY #0 + STY getBit_buffer + + +inflate_blockLoop: +; Get a bit of EOF and two bits of block type +; ldy #0 + sty getBits_base + lda #GET_3_BITS + jsr getBits +;; lsr @ + lsr A + php + tax + bne inflateCompressedBlock + +; Copy uncompressed block +; ldy #0 + sty getBit_buffer + jsr getWord + jsr getWord + sta inflateStoredBlock_pageCounter +; jmp inflateStoredBlock_firstByte + bcs inflateStoredBlock_firstByte +inflateStoredBlock_copyByte: + jsr getByte +inflateStoreByte: + jsr storeByte + bcc inflateCodes_loop +inflateStoredBlock_firstByte: + inx + bne inflateStoredBlock_copyByte + inc inflateStoredBlock_pageCounter + bne inflateStoredBlock_copyByte + +inflate_nextBlock: + plp + bcc inflate_blockLoop + rts + +inflateCompressedBlock: + +; Decompress a block with fixed Huffman trees +; :144 dta 8 +; :112 dta 9 +; :24 dta 7 +; :6 dta 8 +; :2 dta 8 ; codes with no meaning +; :30 dta 5+DISTANCE_TREE +; ldy #0 +inflateFixedBlock_setCodeLengths: + lda #4 + cpy #144 +;; rol @ + rol A + sta literalSymbolCodeLength,y + cpy #CONTROL_SYMBOLS + bcs inflateFixedBlock_noControlSymbol + lda #5+DISTANCE_TREE + cpy #LENGTH_SYMBOLS + bcs inflateFixedBlock_setControlCodeLength + cpy #24 + adc #2-DISTANCE_TREE +inflateFixedBlock_setControlCodeLength: + sta controlSymbolCodeLength,y +inflateFixedBlock_noControlSymbol: + iny + bne inflateFixedBlock_setCodeLengths +; mva #LENGTH_SYMBOLS inflateCodes_primaryCodes + LDA #LENGTH_SYMBOLS + STA inflateCodes_primaryCodes + + dex + beq inflateCodes + +; Decompress a block reading Huffman trees first + +; Build the tree for temporary codes + jsr buildTempHuffmanTree + +; Use temporary codes to get lengths of literal/length and distance codes + ldx #0 +; sec +inflateDynamicBlock_decodeLength: + php + stx inflateDynamicBlock_lengthIndex +; Fetch a temporary code + jsr fetchPrimaryCode +; Temporary code 0..15: put this length + tax + bpl inflateDynamicBlock_verbatimLength +; Temporary code 16: repeat last length 3 + getBits(2) times +; Temporary code 17: put zero length 3 + getBits(3) times +; Temporary code 18: put zero length 11 + getBits(7) times + jsr getBits +; sec + adc #1 + cpx #GET_7_BITS +;; scc:adc #7 + BCC S1 + adc #7 +S1: + tay + lda #0 + cpx #GET_3_BITS +;; scs:lda inflateDynamicBlock_lastLength + BCS S2 + lda inflateDynamicBlock_lastLength +S2: +inflateDynamicBlock_verbatimLength: + iny + ldx inflateDynamicBlock_lengthIndex + plp +inflateDynamicBlock_storeLength: + bcc inflateDynamicBlock_controlSymbolCodeLength +;; sta literalSymbolCodeLength,x+ + sta literalSymbolCodeLength,x + INX + cpx #1 +inflateDynamicBlock_storeNext: + dey + bne inflateDynamicBlock_storeLength + sta inflateDynamicBlock_lastLength +; jmp inflateDynamicBlock_decodeLength + beq inflateDynamicBlock_decodeLength +inflateDynamicBlock_controlSymbolCodeLength: + cpx inflateCodes_primaryCodes +;; scc:ora #DISTANCE_TREE + BCC S3 + ora #DISTANCE_TREE +S3: +;; sta controlSymbolCodeLength,x+ + sta controlSymbolCodeLength,x + INX + + cpx inflateDynamicBlock_allCodes + bcc inflateDynamicBlock_storeNext + dey +; ldy #0 +; jmp inflateCodes + +; Decompress a block +inflateCodes: + jsr buildHuffmanTree +inflateCodes_loop: + jsr fetchPrimaryCode + bcc inflateStoreByte + tax + beq inflate_nextBlock +; Copy sequence from look-behind buffer +; ldy #0 + sty getBits_base + cmp #9 + bcc inflateCodes_setSequenceLength + tya +; lda #0 + cpx #1+28 + bcs inflateCodes_setSequenceLength + dex + txa +;; lsr @ + lsr A + ror getBits_base + inc getBits_base +;; lsr @ + lsr A + rol getBits_base + jsr getAMinus1BitsMax8 +; sec + adc #0 +inflateCodes_setSequenceLength: + sta inflateCodes_lengthMinus2 + ldx #DISTANCE_TREE + jsr fetchCode +; sec + sbc inflateCodes_primaryCodes + tax + cmp #4 + bcc inflateCodes_setOffsetLowByte + inc getBits_base +;; lsr @ + lsr A + jsr getAMinus1BitsMax8 +inflateCodes_setOffsetLowByte: + eor #$ff + sta inflateCodes_sourcePointer + lda getBits_base + cpx #10 + bcc inflateCodes_setOffsetHighByte + lda getNPlus1Bits_mask-10,x + jsr getBits + clc +inflateCodes_setOffsetHighByte: + eor #$ff +; clc + adc outputPointer+1 + sta inflateCodes_sourcePointer+1 + jsr copyByte + jsr copyByte +inflateCodes_copyByte: + jsr copyByte + dec inflateCodes_lengthMinus2 + bne inflateCodes_copyByte +; jmp inflateCodes_loop + beq inflateCodes_loop + +buildTempHuffmanTree: +; ldy #0 + tya +inflateDynamicBlock_clearCodeLengths: + sta literalSymbolCodeLength,y + sta literalSymbolCodeLength+TOTAL_SYMBOLS-256,y + iny + bne inflateDynamicBlock_clearCodeLengths +; numberOfPrimaryCodes = 257 + getBits(5) +; numberOfDistanceCodes = 1 + getBits(5) +; numberOfTemporaryCodes = 4 + getBits(4) + ldx #3 +inflateDynamicBlock_getHeader: + lda inflateDynamicBlock_headerBits-1,x + jsr getBits +; sec + adc inflateDynamicBlock_headerBase-1,x + sta inflateDynamicBlock_tempCodes-1,x + sta inflateDynamicBlock_headerBase+1 + dex + bne inflateDynamicBlock_getHeader + +; Get lengths of temporary codes in the order stored in tempCodeLengthOrder +; ldx #0 +inflateDynamicBlock_getTempCodeLengths: + lda #GET_3_BITS + jsr getBits + ldy tempCodeLengthOrder,x + sta literalSymbolCodeLength,y + ldy #0 + inx + cpx inflateDynamicBlock_tempCodes + bcc inflateDynamicBlock_getTempCodeLengths + +; Build Huffman trees basing on code lengths (in bits) +; stored in the *SymbolCodeLength arrays +buildHuffmanTree: +; Clear nBitCode_totalCount, nBitCode_literalCount, nBitCode_controlCount + tya +; lda #0 +;; sta:rne nBitCode_clearFrom,y+ +R1: + sta nBitCode_clearFrom,y + INY + BNE R1 +; Count number of codes of each length +; ldy #0 +buildHuffmanTree_countCodeLengths: + ldx literalSymbolCodeLength,y + inc nBitCode_literalCount,x + inc nBitCode_totalCount,x + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol + ldx controlSymbolCodeLength,y + inc nBitCode_controlCount,x + inc nBitCode_totalCount,x +buildHuffmanTree_noControlSymbol: + iny + bne buildHuffmanTree_countCodeLengths +; Calculate offsets of symbols sorted by code length +; lda #0 + ldx #-3*TREE_SIZE +buildHuffmanTree_calculateOffsets: + sta nBitCode_literalOffset+3*TREE_SIZE-$100,x +;; add nBitCode_literalCount+3*TREE_SIZE-$100,x + CLC + ADC nBitCode_literalCount+3*TREE_SIZE-$100,x + inx + bne buildHuffmanTree_calculateOffsets +; Put symbols in their place in the sorted array +; ldy #0 +buildHuffmanTree_assignCode: + tya + ldx literalSymbolCodeLength,y +;; ldy:inc nBitCode_literalOffset,x + ldy nBitCode_literalOffset,x + inc nBitCode_literalOffset,x + sta codeToLiteralSymbol,y + tay + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol2 + ldx controlSymbolCodeLength,y +;; ldy:inc nBitCode_controlOffset,x + ldy nBitCode_controlOffset,x + inc nBitCode_controlOffset,x + sta codeToControlSymbol,y + tay +buildHuffmanTree_noControlSymbol2: + iny + bne buildHuffmanTree_assignCode + rts + +; Read Huffman code using the primary tree +fetchPrimaryCode: + ldx #PRIMARY_TREE +; Read a code from input basing on the tree specified in X, +; return low byte of this code in A, +; return C flag reset for literal code, set for length code +fetchCode: +; ldy #0 + tya +fetchCode_nextBit: + jsr getBit +;; rol @ + rol A + inx +;; sub nBitCode_totalCount,x + SEC + SBC nBitCode_totalCount,x + bcs fetchCode_nextBit +; clc + adc nBitCode_controlCount,x + bcs fetchCode_control +; clc + adc nBitCode_literalOffset,x + tax + lda codeToLiteralSymbol,x + clc + rts +fetchCode_control: +;; add nBitCode_controlOffset-1,x + CLC + ADC nBitCode_controlOffset-1,x + tax + lda codeToControlSymbol,x + sec + rts + +; Read A minus 1 bits, but no more than 8 +getAMinus1BitsMax8: + rol getBits_base + tax + cmp #9 + bcs getByte + lda getNPlus1Bits_mask-2,x +getBits: + jsr getBits_loop +getBits_normalizeLoop: + lsr getBits_base +;; ror @ + ror A + bcc getBits_normalizeLoop + rts + +; Read 16 bits +getWord: + jsr getByte + tax +; Read 8 bits +getByte: + lda #$80 +getBits_loop: + jsr getBit +;; ror @ + ror A + bcc getBits_loop + rts + +; Read one bit, return in the C flag +getBit: + lsr getBit_buffer + bne getBit_return + pha +; ldy #0 + lda (inputPointer),y +;; inw inputPointer + INC inputPointer + BNE S4 + INC inputPointer+1 +S4: + sec +;; ror @ + ror A + sta getBit_buffer + pla +getBit_return: + rts + +; Copy a previously written byte +copyByte: + ldy outputPointer + lda (inflateCodes_sourcePointer),y + ldy #0 +; Write a byte +storeByte: + sta (outputPointer),y + inc outputPointer + bne storeByte_return + inc outputPointer+1 + inc inflateCodes_sourcePointer+1 +storeByte_return: + rts + +getNPlus1Bits_mask: + .byte GET_1_BIT,GET_2_BITS,GET_3_BITS,GET_4_BITS,GET_5_BITS,GET_6_BITS,GET_7_BITS + +tempCodeLengthOrder: + .byte GET_2_BITS,GET_3_BITS,GET_7_BITS,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 + +inflateDynamicBlock_headerBits: + .byte GET_4_BITS,GET_5_BITS,GET_5_BITS +inflateDynamicBlock_headerBase: + .byte 3,0,0 ; second byte is modified at runtime! + + .org inflate_data + +; Data for building trees + +literalSymbolCodeLength: + .org *+256 +controlSymbolCodeLength: + .org *+CONTROL_SYMBOLS + +; Huffman trees + +nBitCode_clearFrom: +nBitCode_totalCount: + .org *+2*TREE_SIZE +nBitCode_literalCount: + .org *+TREE_SIZE +nBitCode_controlCount: + .org *+2*TREE_SIZE +nBitCode_literalOffset: + .org *+TREE_SIZE +nBitCode_controlOffset: + .org *+2*TREE_SIZE + +codeToLiteralSymbol: + .org *+256 +codeToControlSymbol: + .org *+CONTROL_SYMBOLS + +END: +*/ +unsigned char inflatecode[] = { + 0xA0,0x00,0x84,0x04,0x84,0x05,0xA9,0x84, + 0x20,0xA3,0xBB,0x4A,0x08,0xAA,0xD0,0x1F, + 0x84,0x04,0x20,0xAC,0xBB,0x20,0xAC,0xBB, + 0x85,0x05,0xB0,0x08,0x20,0xB0,0xBB,0x20, + 0xD2,0xBB,0x90,0x75,0xE8,0xD0,0xF5,0xE6, + 0x05,0xD0,0xF1,0x28,0x90,0xD6,0x60,0xA9, + 0x04,0xC0,0x90,0x2A,0x99,0x00,0xBC,0xC0, + 0x3E,0xB0,0x0D,0xA9,0x15,0xC0,0x20,0xB0, + 0x04,0xC0,0x18,0x69,0xF2,0x99,0x00,0xBD, + 0xC8,0xD0,0xE4,0xA9,0x20,0x85,0x09,0xCA, + 0xF0,0x44,0x20,0xF5,0xBA,0xA2,0x00,0x08, + 0x86,0x06,0x20,0x73,0xBB,0xAA,0x10,0x14, + 0x20,0xA3,0xBB,0x69,0x01,0xE0,0xC0,0x90, + 0x02,0x69,0x07,0xA8,0xA9,0x00,0xE0,0x84, + 0xB0,0x02,0xA5,0x07,0xC8,0xA6,0x06,0x28, + 0x90,0x0D,0x9D,0x00,0xBC,0xE8,0xE0,0x01, + 0x88,0xD0,0xF5,0x85,0x07,0xF0,0xD0,0xE4, + 0x09,0x90,0x02,0x09,0x10,0x9D,0x00,0xBD, + 0xE8,0xE4,0x08,0x90,0xEB,0x88,0x20,0x24, + 0xBB,0x20,0x73,0xBB,0x90,0x81,0xAA,0xF0, + 0x8A,0x84,0x05,0xC9,0x09,0x90,0x14,0x98, + 0xE0,0x1D,0xB0,0x0F,0xCA,0x8A,0x4A,0x66, + 0x05,0xE6,0x05,0x4A,0x26,0x05,0x20,0x99, + 0xBB,0x69,0x00,0x85,0x08,0xA2,0x10,0x20, + 0x75,0xBB,0xE5,0x09,0xAA,0xC9,0x04,0x90, + 0x06,0xE6,0x05,0x4A,0x20,0x99,0xBB,0x49, + 0xFF,0x85,0x06,0xA5,0x05,0xE0,0x0A,0x90, + 0x07,0xBD,0xD3,0xBB,0x20,0xA3,0xBB,0x18, + 0x49,0xFF,0x65,0x03,0x85,0x07,0x20,0xCC, + 0xBB,0x20,0xCC,0xBB,0x20,0xCC,0xBB,0xC6, + 0x08,0xD0,0xF9,0xF0,0xA4,0x98,0x99,0x00, + 0xBC,0x99,0x3E,0xBC,0xC8,0xD0,0xF7,0xA2, + 0x03,0xBD,0xF6,0xBB,0x20,0xA3,0xBB,0x7D, + 0xF9,0xBB,0x95,0x06,0x8D,0xFB,0xBB,0xCA, + 0xD0,0xEF,0xA9,0x84,0x20,0xA3,0xBB,0xBC, + 0xE4,0xBB,0x99,0x00,0xBC,0xA0,0x00,0xE8, + 0xE4,0x07,0x90,0xEE,0x98,0x99,0x3E,0xBD, + 0xC8,0xD0,0xFA,0xBE,0x00,0xBC,0xFE,0x5E, + 0xBD,0xFE,0x3E,0xBD,0xC0,0x3E,0xB0,0x09, + 0xBE,0x00,0xBD,0xFE,0x6E,0xBD,0xFE,0x3E, + 0xBD,0xC8,0xD0,0xE7,0xA2,0xD0,0x9D,0xBE, + 0xBC,0x18,0x7D,0x8E,0xBC,0xE8,0xD0,0xF6, + 0x98,0xBE,0x00,0xBC,0xBC,0x8E,0xBD,0xFE, + 0x8E,0xBD,0x99,0xBE,0xBD,0xA8,0xC0,0x3E, + 0xB0,0x0D,0xBE,0x00,0xBD,0xBC,0x9E,0xBD, + 0xFE,0x9E,0xBD,0x99,0xBE,0xBE,0xA8,0xC8, + 0xD0,0xDE,0x60,0xA2,0x00,0x98,0x20,0xB9, + 0xBB,0x2A,0xE8,0x38,0xFD,0x3E,0xBD,0xB0, + 0xF5,0x7D,0x6E,0xBD,0xB0,0x09,0x7D,0x8E, + 0xBD,0xAA,0xBD,0xBE,0xBD,0x18,0x60,0x18, + 0x7D,0x9D,0xBD,0xAA,0xBD,0xBE,0xBE,0x38, + 0x60,0x26,0x05,0xAA,0xC9,0x09,0xB0,0x10, + 0xBD,0xDB,0xBB,0x20,0xB2,0xBB,0x46,0x05, + 0x6A,0x90,0xFB,0x60,0x20,0xB0,0xBB,0xAA, + 0xA9,0x80,0x20,0xB9,0xBB,0x6A,0x90,0xFA, + 0x60,0x46,0x04,0xD0,0x0E,0x48,0xB1,0x00, + 0xE6,0x00,0xD0,0x02,0xE6,0x01,0x38,0x6A, + 0x85,0x04,0x68,0x60,0xA4,0x02,0xB1,0x06, + 0xA0,0x00,0x91,0x02,0xE6,0x02,0xD0,0x04, + 0xE6,0x03,0xE6,0x07,0x60,0x81,0x82,0x84, + 0x88,0x90,0xA0,0xC0,0x82,0x84,0xC0,0x00, + 0x08,0x07,0x09,0x06,0x0A,0x05,0x0B,0x04, + 0x0C,0x03,0x0D,0x02,0x0E,0x01,0x0F,0x88, + 0x90,0x90,0x03,0x00,0x00 +}; +/* +;fastload9600.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda #$ff + sta chksum + + lda ld_beg ; setup point to target + sta store+1 + lda ld_beg+1 + sta store+2 + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta store,x ; Store data byte + + eor chksum + sta chksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; pre pulse (81-87 cycles) + bit tapein + bpl pre ; pre pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck + inc store+2 +endcheck: + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda chksum + bne sumerror + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + +*/ +unsigned char fastload9600[] = { + 0xA2,0x00,0xBD,0x23, + 0x08,0x9D,0x80,0xBE,0xE8,0xD0,0xF7,0xBD, + 0x23,0x09,0x9D,0x80,0xBF,0xE8,0x10,0xF7, + 0x4C,0x80,0xBE,0xA9,0xC6,0xA0,0xBF,0x20, + 0x8E,0xBF,0xA9,0xFF,0x85,0x00,0xAD,0xBA, + 0xBF,0x8D,0xA9,0xBE,0xAD,0xBB,0xBF,0x8D, + 0xAA,0xBE,0x2C,0x60,0xC0,0x10,0xFB,0x2C, + 0x60,0xC0,0x30,0xFB,0xA9,0x01,0xA2,0x00, + 0x18,0x90,0x09,0x9D,0xA8,0xBE,0x45,0x00, + 0x85,0x00,0xA9,0x01,0x2C,0x60,0xC0,0x10, + 0xFB,0x90,0x12,0xE8,0xD0,0x14,0xEE,0xAA, + 0xBE,0xD0,0x14,0x38,0x2A,0x4C,0xA6,0xBE, + 0x18,0x2A,0x4C,0xA6,0xBE,0x2C,0x60,0xC0, + 0x10,0xF6,0x2C,0x60,0xC0,0x10,0xF1,0x2C, + 0x60,0xC0,0x10,0xEC,0x2C,0x60,0xC0,0x10, + 0xE7,0x2C,0x60,0xC0,0x10,0xE2,0x2C,0x60, + 0xC0,0x10,0xDD,0x2C,0x60,0xC0,0x10,0xD8, + 0x2C,0x60,0xC0,0x10,0xD3,0x2C,0x60,0xC0, + 0x10,0xCE,0x2C,0x60,0xC0,0x10,0xC4,0x2C, + 0x60,0xC0,0x10,0xBF,0x2C,0x60,0xC0,0x10, + 0xBA,0x2C,0x60,0xC0,0x10,0xB5,0x2C,0x60, + 0xC0,0x10,0x91,0x2C,0x60,0xC0,0x10,0x8C, + 0x2C,0x60,0xC0,0x10,0x87,0x8A,0x18,0x6D, + 0xA9,0xBE,0x8D,0xA9,0xBE,0x90,0x03,0xEE, + 0xAA,0xBE,0xAD,0xBC,0xBF,0xCD,0xA9,0xBE, + 0xD0,0x55,0xAD,0xBD,0xBF,0xCD,0xAA,0xBE, + 0xD0,0x4D,0xA5,0x00,0xD0,0x3F,0xAD,0xC4, + 0xBF,0xF0,0x2F,0x20,0x8E,0xFD,0xA9,0xAF, + 0xA0,0xBF,0x20,0x8E,0xBF,0xAD,0xBE,0xBF, + 0x85,0x00,0xAD,0xBF,0xBF,0x85,0x01,0xAD, + 0xC0,0xBF,0x85,0x02,0xAD,0xC1,0xBF,0x85, + 0x03,0x20,0x00,0xBA,0xAD,0xC2,0xBF,0xC5, + 0x02,0xD0,0x1C,0xAD,0xC3,0xBF,0xC5,0x03, + 0xD0,0x15,0xAD,0xC5,0xBF,0xD0,0x03,0x6C, + 0xC0,0xBF,0x4C,0x69,0xFF,0x20,0x8E,0xFD, + 0xA9,0xA1,0xA0,0xBF,0x20,0x8E,0xBF,0xA9, + 0xA9,0xA0,0xBF,0x20,0x8E,0xBF,0x4C,0x69, + 0xFF,0x85,0x06,0x84,0x07,0xA0,0x00,0xB1, + 0x06,0x09,0x80,0x20,0xED,0xFD,0xC8,0xB1, + 0x06,0xD0,0xF6,0x60,0x43,0x48,0x4B,0x53, + 0x55,0x4D,0x20,0x00,0x45,0x52,0x52,0x4F, + 0x52,0x00,0x49,0x4E,0x46,0x4C,0x41,0x54, + 0x49,0x4E,0x47,0x20,0x00 +}; +/* +;fastload8000.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda ld_beg ; load begin LSB location + sta store+1 ; store it + lda ld_beg+1 ; load begin MSB location + sta store+2 ; store it + + ldx #0 ; set X to 0 + lda #1 ; set A to 0 + +nsync: bit tapein ; 4 cycles, sync bit ; first pulse + bpl nsync ; 2 + 1 cycles + +main: ldy #0 ; 2 set Y to 0 + +psync: bit tapein ; + bmi psync + +ploop: iny ; 2 cycles + bit tapein ; 4 cycles + bpl ploop ; 2 +1 if branch, +1 if in another page + ; total ~9 cycles + + cpy #$40 ; 2 cycles if Y - $40 > 0 endcode (770Hz) + bpl endcode ; 2(3) + + cpy #$15 ; 2 cycles if Y - $15 > 0 main (2000Hz) + bpl main ; 2(3) + + cpy #$07 ; 2, if Y<, then clear carry, if Y>= set carry +store: rol store+1,x ; 7, roll carry bit into store + ldy #0 ; 2 + asl ; 2 A*=2 + bne main ; 2(3) + lda #1 ; 2 + inx ; 2 cycles + bne main ; 2(3) + inc store+2 ; 6 cycles + jmp main ; 3 cycles + ; 34 subtotal max + ; 36 subtotal max +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda #0 + sta pointer + lda ld_beg+1 + sta pointer+1 + lda #$ff ; init checksum + ldy ld_beg +sumloop: + eor (pointer),y + + ;last page? + + ldx pointer+1 + cpx ld_end+1 + beq last + iny + bne sumloop + inc pointer+1 + bne sumloop +last: + iny + cpy ld_end + bcc sumloop + +; sty $01 + sta chksum + lda chksum + bne sumerror + + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + +*/ +unsigned char fastload8000[] = { + 0xA2,0x00,0xBD,0x23, + 0x08,0x9D,0x80,0xBE,0xE8,0xD0,0xF7,0xBD, + 0x23,0x09,0x9D,0x80,0xBF,0xE8,0x10,0xF7, + 0x4C,0x80,0xBE,0xA9,0x98,0xA0,0xBF,0x20, + 0x60,0xBF,0xAD,0x8C,0xBF,0x8D,0xB4,0xBE, + 0xAD,0x8D,0xBF,0x8D,0xB5,0xBE,0xA2,0x00, + 0xA9,0x01,0x2C,0x60,0xC0,0x10,0xFB,0xA0, + 0x00,0x2C,0x60,0xC0,0x30,0xFB,0xC8,0x2C, + 0x60,0xC0,0x10,0xFA,0xC0,0x40,0x10,0x19, + 0xC0,0x15,0x10,0xEB,0xC0,0x07,0x3E,0xB4, + 0xBE,0xA0,0x00,0x0A,0xD0,0xE1,0xA9,0x01, + 0xE8,0xD0,0xDC,0xEE,0xB5,0xBE,0x4C,0x9C, + 0xBE,0x8A,0x18,0x6D,0xB4,0xBE,0x8D,0xB4, + 0xBE,0x90,0x03,0xEE,0xB5,0xBE,0xAD,0x8E, + 0xBF,0xCD,0xB4,0xBE,0xD0,0x7B,0xAD,0x8F, + 0xBF,0xCD,0xB5,0xBE,0xD0,0x73,0xA9,0x00, + 0x85,0x06,0xAD,0x8D,0xBF,0x85,0x07,0xA9, + 0xFF,0xAC,0x8C,0xBF,0x51,0x06,0xA6,0x07, + 0xEC,0x8F,0xBF,0xF0,0x07,0xC8,0xD0,0xF4, + 0xE6,0x07,0xD0,0xF0,0xC8,0xCC,0x8E,0xBF, + 0x90,0xEA,0x85,0x00,0xA5,0x00,0xD0,0x3F, + 0xAD,0x96,0xBF,0xF0,0x2F,0x20,0x8E,0xFD, + 0xA9,0x81,0xA0,0xBF,0x20,0x60,0xBF,0xAD, + 0x90,0xBF,0x85,0x00,0xAD,0x91,0xBF,0x85, + 0x01,0xAD,0x92,0xBF,0x85,0x02,0xAD,0x93, + 0xBF,0x85,0x03,0x20,0x00,0xBA,0xAD,0x94, + 0xBF,0xC5,0x02,0xD0,0x1C,0xAD,0x95,0xBF, + 0xC5,0x03,0xD0,0x15,0xAD,0x97,0xBF,0xD0, + 0x03,0x6C,0x92,0xBF,0x4C,0x69,0xFF,0x20, + 0x8E,0xFD,0xA9,0x73,0xA0,0xBF,0x20,0x60, + 0xBF,0xA9,0x7B,0xA0,0xBF,0x20,0x60,0xBF, + 0x4C,0x69,0xFF,0x85,0x06,0x84,0x07,0xA0, + 0x00,0xB1,0x06,0x09,0x80,0x20,0xED,0xFD, + 0xC8,0xB1,0x06,0xD0,0xF6,0x60,0x43,0x48, + 0x4B,0x53,0x55,0x4D,0x20,0x00,0x45,0x52, + 0x52,0x4F,0x52,0x00,0x49,0x4E,0x46,0x4C, + 0x41,0x54,0x49,0x4E,0x47,0x20,0x00 +}; +/* +;fastloadcd.s + +org = $BE80 ; should be $BE80 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA +warm = $FF69 ; back to monitor +tapein = $C060 +pointer = $06 +endbas = $80C +;target = $1000 +target = $801 +chksum = $00 +inflate = $BA00 +inf_zp = $0 + +start: + .org endbas +move: + ldx #0 +move1: lda moved,x + sta fast,x + inx + bne move1 ; move 256 bytes +; ldx #0 +move2: lda moved+256,x + sta fast+256,x + inx + bpl move2 ; only 128 bytes to move + jmp fast +moved: + .org org +fast: + lda #<loadm + ldy #>loadm + jsr print + + lda #$ff + sta chksum + + lda ld_beg ; setup point to target + sta store+1 + lda ld_beg+1 + sta store+2 + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta store,x ; Store data byte + + eor chksum + sta chksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; one pulse (81-87 cycles) + bit tapein + bpl one ; one pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + bit tapein + bpl pre ; pre pulse (99-111 cycles) + +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck + inc store+2 +endcheck: + lda ld_end + cmp store+1 + bne error + lda ld_end+1 + cmp store+2 + bne error +sumcheck: + lda chksum + bne sumerror + lda inf_flag ; if inf_flag = 0 runit + beq runit +inf: + jsr crout + lda #<infm + ldy #>infm + jsr print + + lda inf_src ;src lsb + sta inf_zp+0 + lda inf_src+1 ;src msb + sta inf_zp+1 + lda inf_dst ;dst lsb + sta inf_zp+2 + lda inf_dst+1 ;dst msb + sta inf_zp+3 + + jsr inflate + + lda inf_end ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda inf_end+1 ;dst end +1 msb + cmp inf_zp+3 + bne error +runit: + lda warm_flag ; if warm_flag = 1 warm boot + bne warmit + jmp (runcode) +warmit: + jmp warm ; run it +sumerror: + jsr crout + lda #<chkm + ldy #>chkm + jsr print +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts +chkm: .asciiz "CHKSUM " +errm: .asciiz "ERROR" +infm: .asciiz "INFLATING " +ld_beg: + .org *+2 +ld_end: + .org *+2 +inf_src: + .org *+2 +runcode: +inf_dst: + .org *+2 +inf_end: + .org *+2 +inf_flag: + .org *+1 +warm_flag: + .org *+1 +loadm: + ;.asciiz "LOADING " + +end: + +*/ +unsigned char fastloadcd[] = { + 0xA2,0x00,0xBD,0x23, + 0x08,0x9D,0x80,0xBE,0xE8,0xD0,0xF7,0xBD, + 0x23,0x09,0x9D,0x80,0xBF,0xE8,0x10,0xF7, + 0x4C,0x80,0xBE,0xA9,0xCB,0xA0,0xBF,0x20, + 0x93,0xBF,0xA9,0xFF,0x85,0x00,0xAD,0xBF, + 0xBF,0x8D,0xA9,0xBE,0xAD,0xC0,0xBF,0x8D, + 0xAA,0xBE,0x2C,0x60,0xC0,0x10,0xFB,0x2C, + 0x60,0xC0,0x30,0xFB,0xA9,0x01,0xA2,0x00, + 0x18,0x90,0x09,0x9D,0xA8,0xBE,0x45,0x00, + 0x85,0x00,0xA9,0x01,0x2C,0x60,0xC0,0x10, + 0xFB,0x90,0x12,0xE8,0xD0,0x14,0xEE,0xAA, + 0xBE,0xD0,0x14,0x38,0x2A,0x4C,0xA6,0xBE, + 0x18,0x2A,0x4C,0xA6,0xBE,0x2C,0x60,0xC0, + 0x10,0xF6,0x2C,0x60,0xC0,0x10,0xF1,0x2C, + 0x60,0xC0,0x10,0xEC,0x2C,0x60,0xC0,0x10, + 0xE7,0x2C,0x60,0xC0,0x10,0xE2,0x2C,0x60, + 0xC0,0x10,0xDD,0x2C,0x60,0xC0,0x10,0xD8, + 0x2C,0x60,0xC0,0x10,0xD3,0x2C,0x60,0xC0, + 0x10,0xCE,0x2C,0x60,0xC0,0x10,0xC4,0x2C, + 0x60,0xC0,0x10,0xBF,0x2C,0x60,0xC0,0x10, + 0xBA,0x2C,0x60,0xC0,0x10,0xB5,0x2C,0x60, + 0xC0,0x10,0xB0,0x2C,0x60,0xC0,0x10,0x8C, + 0x2C,0x60,0xC0,0x10,0x87,0x2C,0x60,0xC0, + 0x10,0x82,0x8A,0x18,0x6D,0xA9,0xBE,0x8D, + 0xA9,0xBE,0x90,0x03,0xEE,0xAA,0xBE,0xAD, + 0xC1,0xBF,0xCD,0xA9,0xBE,0xD0,0x55,0xAD, + 0xC2,0xBF,0xCD,0xAA,0xBE,0xD0,0x4D,0xA5, + 0x00,0xD0,0x3F,0xAD,0xC9,0xBF,0xF0,0x2F, + 0x20,0x8E,0xFD,0xA9,0xB4,0xA0,0xBF,0x20, + 0x93,0xBF,0xAD,0xC3,0xBF,0x85,0x00,0xAD, + 0xC4,0xBF,0x85,0x01,0xAD,0xC5,0xBF,0x85, + 0x02,0xAD,0xC6,0xBF,0x85,0x03,0x20,0x00, + 0xBA,0xAD,0xC7,0xBF,0xC5,0x02,0xD0,0x1C, + 0xAD,0xC8,0xBF,0xC5,0x03,0xD0,0x15,0xAD, + 0xCA,0xBF,0xD0,0x03,0x6C,0xC5,0xBF,0x4C, + 0x69,0xFF,0x20,0x8E,0xFD,0xA9,0xA6,0xA0, + 0xBF,0x20,0x93,0xBF,0xA9,0xAE,0xA0,0xBF, + 0x20,0x93,0xBF,0x4C,0x69,0xFF,0x85,0x06, + 0x84,0x07,0xA0,0x00,0xB1,0x06,0x09,0x80, + 0x20,0xED,0xFD,0xC8,0xB1,0x06,0xD0,0xF6, + 0x60,0x43,0x48,0x4B,0x53,0x55,0x4D,0x20, + 0x00,0x45,0x52,0x52,0x4F,0x52,0x00,0x49, + 0x4E,0x46,0x4C,0x41,0x54,0x49,0x4E,0x47, + 0x20,0x00 +}; +/* +;diskload9600.s + +org = $9000 ; should be $9000 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +endbas = $80C +target = $1000 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +pointer = $0C ; LSB/MSB pointer + +start: + .org endbas +move: ; end of BASIC, move code to readtape addr + ldx #0 +move1: + lda moved,x + sta readtape,x +; lda moved+256,x +; sta readtape+256,x + inx + bne move1 +phase1: + jsr crout ; print LOADING... + lda #<loadm + ldy #>loadm + jsr print + ; diskload2 ORG + lda #$D0 ; store begin location LSB + sta begload + lda #$96 ; store begin location MSB + sta begload+1 + ; end of DOS + 1 for comparison + lda #$00 ; store end location LSB + sta endload + lda #$C0 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code + jmp $9700 ; run it +loadm: + .byte "LOADING INSTA-DISK, ETA " +loadsec: ; 10 bytes for "XX SEC. ",$00 + .byte 0,0,0,0,0,0,0,0,0,0 +moved: + .org org ; $9000 +readtape: + lda begload ; load begin LSB location + sta store+1 ; store it + lda begload+1 ; load begin MSB location + sta store+2 ; store it + + lda #$ff ; init checksum + sta chksum + +wait: bit tapein + bpl wait +waithi: bit tapein ; Wait for input to go high. + bmi waithi +pre: lda #1 ; Load sentinel bit + ldx #0 ; Clear data index + clc ; Clear carry (byte complete flag) +data: bcc waitlo ; Skip if byte not complete +store: sta target,x ; Store data byte + + eor chksum ; compute checksum + sta chksum ; store checksum + + lda #1 ; Re-load sentinel bit +waitlo: bit tapein ; Wait for input to go low + bpl waitlo + bcc poll9 ; Poll at +9 cycles if no store + inx ; Stored, so increment data index + bne poll13 ; Poll at +13 cycles if no carry + inc store+2 ; Increment data page + bne poll21 ; (always) poll at +21 cycles + +one: sec ; one bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +zero: clc ; zero bit detected + rol ; shift it into A + jmp data ; and go handle data (C = sentinel) + +poll9: bit tapein + bpl zero ; zero bit (short)(9-15 cycles) +poll13: bit tapein ; (2 cycles early if branched here) + bpl zero ; zero bit (15-21 cycles) +poll21: bit tapein + bpl zero ; zero bit (21-27 cycles) + bit tapein + bpl zero ; zero bit (27-33 cycles) + bit tapein + bpl zero ; zero bit (33-39 cycles) + bit tapein + bpl zero ; zero bit (39-45 cycles) + bit tapein + bpl zero ; zero bit (45-51 cycles) + bit tapein + bpl zero ; zero bit (51-57 cycles) + bit tapein + bpl zero ; one bit (57-63 cycles) + bit tapein + bpl one ; one bit (63-69 cycles) + bit tapein + bpl one ; one bit (69-75 cycles) + bit tapein + bpl one ; one bit (75-81 cycles) + bit tapein + bpl one ; one pulse (81-87 cycles) + bit tapein + bpl pre ; pre pulse (87-93 cycles) + bit tapein + bpl pre ; pre pulse (93-99 cycles) + bit tapein + bpl pre ; pre pulse (99-105 cycles) + + ; low freq signals end of data +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda endload + cmp store+1 + bne error + lda endload+1 + cmp store+2 + bne error + jsr ok +sumcheck: + jsr crout + lda #<chkm + ldy #>chkm + jsr print + lda chksum + bne error + jmp ok ; return to caller +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +ok: + lda #<okm + ldy #>okm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts + +chkm: .asciiz "CHKSUM " +okm: .asciiz "OK" +errm: .asciiz "ERROR" +end: +*/ +unsigned char diskload9600[] = { + 0xA2,0x00,0xBD,0x59, + 0x08,0x9D,0x00,0x90,0xE8,0xD0,0xF7,0x20, + 0x8E,0xFD,0xA9,0x37,0xA0,0x08,0x20,0xCE, + 0x90,0xA9,0xD0,0x85,0x00,0xA9,0x96,0x85, + 0x01,0xA9,0x00,0x85,0x02,0xA9,0xC0,0x85, + 0x03,0x20,0x00,0x90,0x4C,0x00,0x97,0x4C, + 0x4F,0x41,0x44,0x49,0x4E,0x47,0x20,0x49, + 0x4E,0x53,0x54,0x41,0x2D,0x44,0x49,0x53, + 0x4B,0x2C,0x20,0x45,0x54,0x41,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xA5,0x00,0x8D,0x20,0x90,0xA5,0x01, + 0x8D,0x21,0x90,0xA9,0xFF,0x85,0x04,0x2C, + 0x60,0xC0,0x10,0xFB,0x2C,0x60,0xC0,0x30, + 0xFB,0xA9,0x01,0xA2,0x00,0x18,0x90,0x09, + 0x9D,0x00,0x10,0x45,0x04,0x85,0x04,0xA9, + 0x01,0x2C,0x60,0xC0,0x10,0xFB,0x90,0x12, + 0xE8,0xD0,0x14,0xEE,0x21,0x90,0xD0,0x14, + 0x38,0x2A,0x4C,0x1D,0x90,0x18,0x2A,0x4C, + 0x1D,0x90,0x2C,0x60,0xC0,0x10,0xF6,0x2C, + 0x60,0xC0,0x10,0xF1,0x2C,0x60,0xC0,0x10, + 0xEC,0x2C,0x60,0xC0,0x10,0xE7,0x2C,0x60, + 0xC0,0x10,0xE2,0x2C,0x60,0xC0,0x10,0xDD, + 0x2C,0x60,0xC0,0x10,0xD8,0x2C,0x60,0xC0, + 0x10,0xD3,0x2C,0x60,0xC0,0x10,0xCE,0x2C, + 0x60,0xC0,0x10,0xC4,0x2C,0x60,0xC0,0x10, + 0xBF,0x2C,0x60,0xC0,0x10,0xBA,0x2C,0x60, + 0xC0,0x10,0xB5,0x2C,0x60,0xC0,0x10,0x91, + 0x2C,0x60,0xC0,0x10,0x8C,0x2C,0x60,0xC0, + 0x10,0x87,0x8A,0x18,0x6D,0x20,0x90,0x8D, + 0x20,0x90,0x90,0x03,0xEE,0x21,0x90,0xA5, + 0x02,0xCD,0x20,0x90,0xD0,0x1B,0xA5,0x03, + 0xCD,0x21,0x90,0xD0,0x14,0x20,0xCA,0x90, + 0x20,0x8E,0xFD,0xA9,0xE1,0xA0,0x90,0x20, + 0xCE,0x90,0xA5,0x04,0xD0,0x03,0x4C,0xCA, + 0x90,0xA9,0xEC,0xA0,0x90,0x20,0xCE,0x90, + 0x4C,0x69,0xFF,0xA9,0xE9,0xA0,0x90,0x85, + 0x0C,0x84,0x0D,0xA0,0x00,0xB1,0x0C,0x09, + 0x80,0x20,0xED,0xFD,0xC8,0xB1,0x0C,0xD0, + 0xF6,0x60,0x43,0x48,0x4B,0x53,0x55,0x4D, + 0x20,0x00,0x4F,0x4B,0x00,0x45,0x52,0x52, + 0x4F,0x52,0x00 +}; +/* +;diskload8000.s + +org = $9000 ; should be $9000 +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +endbas = $80C +target = $1000 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +pointer = $0C ; LSB/MSB pointer + +start: + .org endbas +move: ; end of BASIC, move code to readtape addr + ldx #0 +move1: + lda moved,x + sta readtape,x +; lda moved+256,x +; sta readtape+256,x + inx + bne move1 +phase1: + jsr crout ; print LOADING... + lda #<loadm + ldy #>loadm + jsr print + ; diskload2 ORG + lda #$D0 ; store begin location LSB + sta begload + lda #$96 ; store begin location MSB + sta begload+1 + ; end of DOS + 1 for comparison + lda #$00 ; store end location LSB + sta endload + lda #$C0 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code + jmp $9700 ; run it +loadm: + .byte "LOADING INSTA-DI8K, ETA " +loadsec: ; 10 bytes for "XX SEC. ",$00 + .byte 0,0,0,0,0,0,0,0,0,0 +moved: + .org org ; $9000 +readtape: + lda begload ; load begin LSB location + sta store+1 ; store it + lda begload+1 ; load begin MSB location + sta store+2 ; store it + + ldx #0 ; set X to 0 + lda #1 ; set A to 0 + +nsync: bit tapein ; 4 cycles, sync bit ; first pulse + bpl nsync ; 2 + 1 cycles + +main: ldy #0 ; 2 set Y to 0 + +psync: bit tapein ; + bmi psync + +ploop: iny ; 2 cycles + bit tapein ; 4 cycles + bpl ploop ; 2 +1 if branch, +1 if in another page + ; total ~9 cycles + + cpy #$40 ; 2 cycles if Y - $40 > 0 endcode (770Hz) + bpl endcode ; 2(3) + + cpy #$15 ; 2 cycles if Y - $15 > 0 main (2000Hz) + bpl main ; 2(3) + + cpy #$07 ; 2, if Y<, then clear carry, if Y>= set carry +store: rol store+1,x ; 7, roll carry bit into store + ldy #0 ; 2 + asl ; 2 A*=2 + bne main ; 2(3) + lda #1 ; 2 + inx ; 2 cycles + bne main ; 2(3) + inc store+2 ; 6 cycles + jmp main ; 3 cycles + ; 34 subtotal max + ; 36 subtotal max +endcode: + txa ; write end of file location + 1 + clc + adc store+1 + sta store+1 + bcc endcheck ; LSB didn't roll over to zero + inc store+2 ; did roll over to zero, inc MSB +endcheck: ; check for match of expected length + lda endload + cmp store+1 + bne error + lda endload+1 + cmp store+2 + bne error + jsr ok +sumcheck: + jsr crout + lda #<chkm + ldy #>chkm + jsr print + + lda #0 + sta pointer + lda begload+1 + sta pointer+1 + lda #$ff ; init checksum + ldy begload +sumloop: + eor (pointer),y + + ;last page? + + ldx pointer+1 + cpx endload+1 + beq last + iny + bne sumloop + inc pointer+1 + bne sumloop +last: + iny + cpy endload + bcc sumloop + + ldy #0 + eor (endload),y +; sta chksum +; lda chksum + bne error + jmp ok ; return to caller +error: + lda #<errm + ldy #>errm + jsr print + jmp warm +ok: + lda #<okm + ldy #>okm +print: + sta pointer + sty pointer+1 + ldy #0 + lda (pointer),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (pointer),y + bne print1 + rts + +chkm: .asciiz "CHKSUM " +okm: .asciiz "OK" +errm: .asciiz "ERROR" +end: +*/ +unsigned char diskload8000[] = { + 0xA2,0x00,0xBD,0x59, + 0x08,0x9D,0x00,0x90,0xE8,0xD0,0xF7,0x20, + 0x8E,0xFD,0xA9,0x37,0xA0,0x08,0x20,0x9C, + 0x90,0xA9,0xD0,0x85,0x00,0xA9,0x96,0x85, + 0x01,0xA9,0x00,0x85,0x02,0xA9,0xC0,0x85, + 0x03,0x20,0x00,0x90,0x4C,0x00,0x97,0x4C, + 0x4F,0x41,0x44,0x49,0x4E,0x47,0x20,0x49, + 0x4E,0x53,0x54,0x41,0x2D,0x44,0x49,0x38, + 0x4B,0x2C,0x20,0x45,0x54,0x41,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xA5,0x00,0x8D,0x2B,0x90,0xA5,0x01, + 0x8D,0x2C,0x90,0xA2,0x00,0xA9,0x01,0x2C, + 0x60,0xC0,0x10,0xFB,0xA0,0x00,0x2C,0x60, + 0xC0,0x30,0xFB,0xC8,0x2C,0x60,0xC0,0x10, + 0xFA,0xC0,0x40,0x10,0x19,0xC0,0x15,0x10, + 0xEB,0xC0,0x07,0x3E,0x2B,0x90,0xA0,0x00, + 0x0A,0xD0,0xE1,0xA9,0x01,0xE8,0xD0,0xDC, + 0xEE,0x2C,0x90,0x4C,0x13,0x90,0x8A,0x18, + 0x6D,0x2B,0x90,0x8D,0x2B,0x90,0x90,0x03, + 0xEE,0x2C,0x90,0xA5,0x02,0xCD,0x2B,0x90, + 0xD0,0x3D,0xA5,0x03,0xCD,0x2C,0x90,0xD0, + 0x36,0x20,0x98,0x90,0x20,0x8E,0xFD,0xA9, + 0xAF,0xA0,0x90,0x20,0x9C,0x90,0xA9,0x00, + 0x85,0x0C,0xA5,0x01,0x85,0x0D,0xA9,0xFF, + 0xA4,0x00,0x51,0x0C,0xA6,0x0D,0xE4,0x03, + 0xF0,0x07,0xC8,0xD0,0xF5,0xE6,0x0D,0xD0, + 0xF1,0xC8,0xC4,0x02,0x90,0xEC,0xA0,0x00, + 0x51,0x02,0xD0,0x03,0x4C,0x98,0x90,0xA9, + 0xBA,0xA0,0x90,0x20,0x9C,0x90,0x4C,0x69, + 0xFF,0xA9,0xB7,0xA0,0x90,0x85,0x0C,0x84, + 0x0D,0xA0,0x00,0xB1,0x0C,0x09,0x80,0x20, + 0xED,0xFD,0xC8,0xB1,0x0C,0xD0,0xF6,0x60, + 0x43,0x48,0x4B,0x53,0x55,0x4D,0x20,0x00, + 0x4F,0x4B,0x00,0x45,0x52,0x52,0x4F,0x52, + 0x00 +}; +/* +;diskload2.s + +; apple vectors + +cout = $FDED ; character out sub +crout = $FD8E ; CR out sub +prbyte = $FDDA ; print byte in hex +tapein = $C060 ; read tape interface +warm = $FF69 ; back to monitor +clear = $FC58 ; clear screen +movecur = $FB5B ; move cursor to ch,a +dos = $9D84 +asrom = $9D72 +locrpl = $3E3 ; locate RWTS paramlist jsr +rwts = $3D9 ; RWTS jsr +cleos = $FC42 ; clear to end of screen +init = $A54F +motoroff= $C088 ; Turn drive motor off +motoron = $C089 ; Turn drive motor on +reboot = $FAA6 ; reboot machine +bell = $FBDD ; ding +rdkey = $FD0C ; read key + +; my vectors + +;print = $90CE ; from diskload.s +readtape= $9000 +inflate = $9B00 + +; zero page parameters + +begload = $00 ; begin load location LSB/MSB +endload = $02 ; end load location LSB/MSB +chksum = $04 ; checksum location +secnum = $05 ; loop var +trknum = $06 ; loop var +segcnt = $07 ; loop var +buffer = $08 ; MSB of RWTS buffer +trkcnt = $09 ; track counter (0-6) +pointer = $0A ; pointer LSB/MSB +prtptr = $0C ; pointer LSB/MSB +fmptr = $0E ; file manager pointer +inf_zp = $10 ; inflate vars (10) +temp = $1E ; temp var +ch = $24 ; cursor horizontal +preg = $48 ; mon p reg + +; other vars + +org = $9700 ; should be $9700 +invsp = $60 ; inverse space for draw +data = $1000 ; 7 track dump from inflate +boot1o = $96D0 ; tape loaded boot 1 location +boot1 = $3D0 ; target boot 1 location +cmpbuf = $9200 ; buffer for sector check +count = $900 + + .org org + + ldx #0 ; move 9cd0 to 3d0 +move1: + lda boot1o,x + sta boot1,x + inx + cpx #$48 + bne move1 ; branch on positive (0-127) +patch: + lda #$B3 ; hack since chksum could not be written to C000 + sta $BFFF ; chksum was written do BFFF +start: + jsr clear ; clear screen + lda #<title ; print title + ldy #>title + jsr inv + ; TRACK + lda #19 ; col 20 + sta ch + lda #0 ; row 0 + jsr movecur + lda #<track ; print track + ldy #>track + jsr print + + lda #<header ; print header + ldy #>header + jsr print + ldx #35 ; length of line + jsr line + + lda #<left ; print left side of grid + ldy #>left + jsr print + +setupiob: + jsr locrpl ; locate rwts paramlist + sty pointer ; and save pointer + sta pointer+1 + + lda #1 ; table type + ldy #0 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #6 * 16 ; slot 6 + ldy #1 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #1 ; drive number + ldy #2 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #254 ; volume number + ldy #3 ; offset in RWTS + sta (pointer),y ; write it to RWTS + +format: ; format the diskette + lda infdata+20 ; check noformat flag + bne endformat ; if not 0 jump to endformat + + jsr status + lda #<formatm ; print formatting + ldy #>formatm + jsr print + +;;; RWTS format (issues) +; lda #4 ; format(4) command +; ldy #$0C ; offset in RWTS +; sta (pointer),y ; write it to RWTS + +; jsr locrpl ; locate rwts paramlist +; jsr rwts ; do it! +; bcs formaterror +; lda #0 +; sta preg ; fix p reg so mon is happy +; jmp endformat + +;;; file manager format (works!) + jsr $3DC ; load up Y and A + sty fmptr + sta fmptr+1 + + lda #$0B ; init command + ldy #0 + sta (fmptr),y + + lda #$9D ; DOS location + ldy #1 + sta (fmptr),y + + lda #254 ; volume number + ldy #4 + sta (fmptr),y + + lda #$01 ; drive number + ldy #5 + sta (fmptr),y + + lda #$06 ; slot number + ldy #6 + sta (fmptr),y + + lda #$00 ; scratch area LSB + ldy #$0C + sta (fmptr),y + + lda #$92 ; scratch area MSB + ldy #$0D + sta (fmptr),y + + jsr $3D6 ; doit! + + ldy #$0A ; return code + lda (fmptr),y + beq endformat +formaterror: + jmp diskerror +endformat: + +;;;begin segment loop (5) + lda #0 ; 256 bytes/sector + ldy #$0b ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #0 ; buffer LSB + ldy #8 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #0 + sta trknum ; start with track 0 + lda #5 + sta segcnt +segloop: + +;;; fancy status here +; jsr status +; lda #<waitm ; print waiting for data +; ldy #>waitm +; jsr print +;countdown: +; lda #<count ; store begin location LSB +; sta begload +; lda #>count ; store begin location MSB +; sta begload+1 +; lda #<count+4 ; store end location LSB +; sta endload +; lda #>count ; store end location MSB +; sta endload+1 +;;;; hack readtape, fix later, POC for now +; lda #$60 ; return without check +; sta $9091 +; jsr readtape ; get the code +; lda #$8A ; put TXA back +; sta $9091 +;;;; end hack +; lda #18 +; sta $24 ; horiz +; lda #22 ; vert +; jsr movecur ; move cursor to $24,a; 0 base +; jsr cleos +; lda #<count ; print count down +; ldy #>count +; jsr print +; lda count +; cmp #$B0 +; bne countdown +; lda count+1 +; cmp #$B0 +; bne countdown +;;; end fancy stuff + +;;; get 7 tracks from tape +load: + jsr status + lda #<loadm ; print loading data + ldy #>loadm + jsr flash + lda #<loadm2 ; print loading data + ldy #>loadm2 + jsr print + + sec + lda #5 + sbc segcnt + asl + asl + tax + stx temp + + lda infdata+2,x ; get sec + jsr cout + lda infdata+3,x ; get sec + beq second + jsr cout +second: + lda #<secm ; print sec + ldy #>secm + jsr print + + ldx temp + lda infdata+0,x ; store begin location LSB + sta begload + lda infdata+1,x ; store begin location MSB + sta begload+1 + + lda #$00 ; store end location LSB + sta endload + lda #$90 ; store end location MSB + sta endload+1 + + jsr readtape ; get the code +inf: + ; turn motor on to save 1-2 sec + ldx #$60 ; slot #6 + lda motoron,x ; turn it on + + jsr status + lda #<inflatem ; print inflating + ldy #>inflatem + jsr print + + ldx temp + lda infdata+0,x ;src lsb + sta inf_zp+0 + lda infdata+1,x ;src msb + sta inf_zp+1 + lda #<data ;dst lsb + sta inf_zp+2 + lda #>data ;dst msb + sta inf_zp+3 + + jsr inflate + + lda #$00 ;dst end +1 lsb + cmp inf_zp+2 + bne error + lda #$80 ;dst end +1 msb + cmp inf_zp+3 + bne error + +;;;begin track loop (7) + jsr status + lda #<writem ; print writing + ldy #>writem + jsr print + + lda #>data + sta buffer + lda #7 + sta trkcnt ; do 7 tracks/segment +trkloop: + lda trknum ; track number + ldy #4 ; offset in RWTS + sta (pointer),y ; write it to RWTS + +; lda #0 ; seek(0) command +; ldy #$0C ; offset in RWTS +; sta (pointer),y ; write it to RWTS + +; jsr locrpl ; locate rwts paramlist +; jsr rwts ; do it! +; bcs diskerror + +;;;begin sector loop (16), backwards is faster, much faster + lda #$F + sta secnum +secloop: + ;jsr draw_w ; write sector from buffer to disk + jsr draw_s ; write sector from buffer to disk + lda secnum ; sector number + ldy #5 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda buffer ; buffer MSB + clc + adc secnum + ldy #9 ; offset in RWTS + sta (pointer),y ; write it to RWTS + + lda #2 ; read(1)/write(2) command + ldy #$0C ; offset in RWTS + sta (pointer),y ; write it to RWTS + + jsr locrpl ; locate rwts paramlist + jsr rwts ; do it! + bcs diskerror + lda #0 + sta preg ; fix p reg so mon is happy + + ;jsr draw_r ; read sector from disk to compare addr + ;lda #>cmpbuf ; compare MSB + ;ldy #9 ; offset in RWTS + ;sta (pointer),y ; write it to RWTS + + ;lda #1 ; read(1)/write(2) command + ;ldy #$0C ; offset in RWTS + ;sta (pointer),y ; write it to RWTS + + ;jsr locrpl ; locate rwts paramlist + ;jsr rwts ; do it! + ;bcs diskerror + ;lda #0 + ;sta preg ; fix p reg so mon is happy + + ;;; compare code + + ;jsr draw_s ; draw a space in the grid if OK + + dec secnum + bpl secloop +;;;end sector loop + + lda buffer ; buffer += $10 + clc + adc #$10 + sta buffer + + inc trknum ; next track + dec trkcnt ; + bne trkloop ; 0, all done with 7 tracks +;;;end track loop + + dec segcnt ; + beq done ; 0, all done with 5 segments + jmp segloop +;;;end segment loop + +;;; prompt for data only load? +done: + jsr status + lda #<donem ; print done + ldy #>donem + jsr print + jsr bell + jsr rdkey + jmp reboot +error: + ; turn motor off, just in case left on + ldx #$60 ; slot #6 + lda motoroff,x ; turn it off + + lda #<errorm ; print error + ldy #>errorm + jsr print + jmp warm +diskerror: + lda #0 + sta preg ; fix p reg so mon is happy + jsr status + lda #<diskerrorm ; print error + ldy #>diskerrorm + jsr print + jmp warm +status: + lda #0 + sta $24 ; horiz + lda #22 ; vert + jsr movecur ; move cursor to $24,a; 0 base + jmp cleos +draw_w: ; print a 'W' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #'W' + jmp draw +draw_r: ; print a 'R' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #'R' + jmp draw +draw_s: ; print a ' ' in the grid + clc + lda #4 + adc secnum + tay + lda #4 + adc trknum + ldx #invsp +draw: ; a=horiz, y=vert, x=letter + sta $24 ; horiz + tya + jsr movecur + txa + eor #$40 + jsr cout + rts +line: + lda #'-' + ora #$80 +loop0: + jsr cout + dex + bne loop0 + jsr crout + rts +inv: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +inv1: and #$3F + jsr cout + iny + lda (prtptr),y + bne inv1 + rts +flash: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +flash1: ora #$40 + jsr cout + iny + lda (prtptr),y + bne flash1 + rts +print: + sta prtptr + sty prtptr+1 + ldy #0 + lda (prtptr),y ; load initial char +print1: ora #$80 + jsr cout + iny + lda (prtptr),y + bne print1 + rts +title: + .asciiz "INSTA-DISK" +errorm: + .asciiz "ERROR" +diskerrorm: + .asciiz "DISK ERROR" +donem: + .asciiz "DONE. PRESS [RETURN] TO REBOOT." +inflatem: + .asciiz "INFLATING DATA " +loadm: + .asciiz "LOADING DATA" +loadm2: + .asciiz ", ETA " +secm: + .asciiz " SEC. " +formatm: + .asciiz "FORMATTING DISK " +waitm: + .asciiz "WAITING FOR DATA: " +writem: + .asciiz "WRITING DATA " +track: + .byte "TRACK",$0D,0 +header: + .byte " 1111111111222222222233333",$0D + .byte " 01234567890123456789012345678901234",$0D + .byte " ",0 +left: + .byte " 0|",$0D + .byte " 1|",$0D + .byte " 2|",$0D + .byte " 3|",$0D + .byte " 4|",$0D + .byte "S 5|",$0D + .byte "E 6|",$0D + .byte "C 7|",$0D + .byte "T 8|",$0D + .byte "O 9|",$0D + .byte "R A|",$0D + .byte " B|",$0D + .byte " C|",$0D + .byte " D|",$0D + .byte " E|",$0D + .byte " F|",$0D,0 +infdata: + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0,0,0,0 ; LSB/MSB start, ETA in sec + ;.byte 0 ; format flag, 1 = no format +*/ +unsigned char diskloadcode2[] = { + 0xA2,0x00,0xBD,0xD0,0x96,0x9D,0xD0,0x03, + 0xE8,0xE0,0x48,0xD0,0xF5,0xA9,0xB3,0x8D, + 0xFF,0xBF,0x20,0x58,0xFC,0xA9,0x4C,0xA0, + 0x99,0x20,0x13,0x99,0xA9,0x13,0x85,0x24, + 0xA9,0x00,0x20,0x5B,0xFB,0xA9,0xE5,0xA0, + 0x99,0x20,0x39,0x99,0xA9,0xEC,0xA0,0x99, + 0x20,0x39,0x99,0xA2,0x23,0x20,0x05,0x99, + 0xA9,0x41,0xA0,0x9A,0x20,0x39,0x99,0x20, + 0xE3,0x03,0x84,0x0A,0x85,0x0B,0xA9,0x01, + 0xA0,0x00,0x91,0x0A,0xA9,0x60,0xA0,0x01, + 0x91,0x0A,0xA9,0x01,0xA0,0x02,0x91,0x0A, + 0xA9,0xFE,0xA0,0x03,0x91,0x0A,0xAD,0xA6, + 0x9A,0xD0,0x47,0x20,0xC2,0x98,0xA9,0xB3, + 0xA0,0x99,0x20,0x39,0x99,0x20,0xDC,0x03, + 0x84,0x0E,0x85,0x0F,0xA9,0x0B,0xA0,0x00, + 0x91,0x0E,0xA9,0x9D,0xA0,0x01,0x91,0x0E, + 0xA9,0xFE,0xA0,0x04,0x91,0x0E,0xA9,0x01, + 0xA0,0x05,0x91,0x0E,0xA9,0x06,0xA0,0x06, + 0x91,0x0E,0xA9,0x00,0xA0,0x0C,0x91,0x0E, + 0xA9,0x92,0xA0,0x0D,0x91,0x0E,0x20,0xD6, + 0x03,0xA0,0x0A,0xB1,0x0E,0xF0,0x03,0x4C, + 0xB1,0x98,0xA9,0x00,0xA0,0x0B,0x91,0x0A, + 0xA9,0x00,0xA0,0x08,0x91,0x0A,0xA9,0x00, + 0x85,0x06,0xA9,0x05,0x85,0x07,0x20,0xC2, + 0x98,0xA9,0x98,0xA0,0x99,0x20,0x26,0x99, + 0xA9,0xA5,0xA0,0x99,0x20,0x39,0x99,0x38, + 0xA9,0x05,0xE5,0x07,0x0A,0x0A,0xAA,0x86, + 0x1E,0xBD,0x94,0x9A,0x20,0xED,0xFD,0xBD, + 0x95,0x9A,0xF0,0x03,0x20,0xED,0xFD,0xA9, + 0xAC,0xA0,0x99,0x20,0x39,0x99,0xA6,0x1E, + 0xBD,0x92,0x9A,0x85,0x00,0xBD,0x93,0x9A, + 0x85,0x01,0xA9,0x00,0x85,0x02,0xA9,0x90, + 0x85,0x03,0x20,0x00,0x90,0xA2,0x60,0xBD, + 0x89,0xC0,0x20,0xC2,0x98,0xA9,0x88,0xA0, + 0x99,0x20,0x39,0x99,0xA6,0x1E,0xBD,0x92, + 0x9A,0x85,0x10,0xBD,0x93,0x9A,0x85,0x11, + 0xA9,0x00,0x85,0x12,0xA9,0x10,0x85,0x13, + 0x20,0x00,0x9B,0xA9,0x00,0xC5,0x12,0xD0, + 0x71,0xA9,0x80,0xC5,0x13,0xD0,0x6B,0x20, + 0xC2,0x98,0xA9,0xD7,0xA0,0x99,0x20,0x39, + 0x99,0xA9,0x10,0x85,0x08,0xA9,0x07,0x85, + 0x09,0xA5,0x06,0xA0,0x04,0x91,0x0A,0xA9, + 0x0F,0x85,0x05,0x20,0xEC,0x98,0xA5,0x05, + 0xA0,0x05,0x91,0x0A,0xA5,0x08,0x18,0x65, + 0x05,0xA0,0x09,0x91,0x0A,0xA9,0x02,0xA0, + 0x0C,0x91,0x0A,0x20,0xE3,0x03,0x20,0xD9, + 0x03,0xB0,0x3E,0xA9,0x00,0x85,0x48,0xC6, + 0x05,0x10,0xD8,0xA5,0x08,0x18,0x69,0x10, + 0x85,0x08,0xE6,0x06,0xC6,0x09,0xD0,0xC1, + 0xC6,0x07,0xF0,0x03,0x4C,0xBE,0x97,0x20, + 0xC2,0x98,0xA9,0x68,0xA0,0x99,0x20,0x39, + 0x99,0x20,0xDD,0xFB,0x20,0x0C,0xFD,0x4C, + 0xA6,0xFA,0xA2,0x60,0xBD,0x88,0xC0,0xA9, + 0x57,0xA0,0x99,0x20,0x39,0x99,0x4C,0x69, + 0xFF,0xA9,0x00,0x85,0x48,0x20,0xC2,0x98, + 0xA9,0x5D,0xA0,0x99,0x20,0x39,0x99,0x4C, + 0x69,0xFF,0xA9,0x00,0x85,0x24,0xA9,0x16, + 0x20,0x5B,0xFB,0x4C,0x42,0xFC,0x18,0xA9, + 0x04,0x65,0x05,0xA8,0xA9,0x04,0x65,0x06, + 0xA2,0x57,0x4C,0xF8,0x98,0x18,0xA9,0x04, + 0x65,0x05,0xA8,0xA9,0x04,0x65,0x06,0xA2, + 0x52,0x4C,0xF8,0x98,0x18,0xA9,0x04,0x65, + 0x05,0xA8,0xA9,0x04,0x65,0x06,0xA2,0x60, + 0x85,0x24,0x98,0x20,0x5B,0xFB,0x8A,0x49, + 0x40,0x20,0xED,0xFD,0x60,0xA9,0x2D,0x09, + 0x80,0x20,0xED,0xFD,0xCA,0xD0,0xFA,0x20, + 0x8E,0xFD,0x60,0x85,0x0C,0x84,0x0D,0xA0, + 0x00,0xB1,0x0C,0x29,0x3F,0x20,0xED,0xFD, + 0xC8,0xB1,0x0C,0xD0,0xF6,0x60,0x85,0x0C, + 0x84,0x0D,0xA0,0x00,0xB1,0x0C,0x09,0x40, + 0x20,0xED,0xFD,0xC8,0xB1,0x0C,0xD0,0xF6, + 0x60,0x85,0x0C,0x84,0x0D,0xA0,0x00,0xB1, + 0x0C,0x09,0x80,0x20,0xED,0xFD,0xC8,0xB1, + 0x0C,0xD0,0xF6,0x60,0x49,0x4E,0x53,0x54, + 0x41,0x2D,0x44,0x49,0x53,0x4B,0x00,0x45, + 0x52,0x52,0x4F,0x52,0x00,0x44,0x49,0x53, + 0x4B,0x20,0x45,0x52,0x52,0x4F,0x52,0x00, + 0x44,0x4F,0x4E,0x45,0x2E,0x20,0x50,0x52, + 0x45,0x53,0x53,0x20,0x5B,0x52,0x45,0x54, + 0x55,0x52,0x4E,0x5D,0x20,0x54,0x4F,0x20, + 0x52,0x45,0x42,0x4F,0x4F,0x54,0x2E,0x00, + 0x49,0x4E,0x46,0x4C,0x41,0x54,0x49,0x4E, + 0x47,0x20,0x44,0x41,0x54,0x41,0x20,0x00, + 0x4C,0x4F,0x41,0x44,0x49,0x4E,0x47,0x20, + 0x44,0x41,0x54,0x41,0x00,0x2C,0x20,0x45, + 0x54,0x41,0x20,0x00,0x20,0x53,0x45,0x43, + 0x2E,0x20,0x00,0x46,0x4F,0x52,0x4D,0x41, + 0x54,0x54,0x49,0x4E,0x47,0x20,0x44,0x49, + 0x53,0x4B,0x20,0x00,0x57,0x41,0x49,0x54, + 0x49,0x4E,0x47,0x20,0x46,0x4F,0x52,0x20, + 0x44,0x41,0x54,0x41,0x3A,0x20,0x00,0x57, + 0x52,0x49,0x54,0x49,0x4E,0x47,0x20,0x44, + 0x41,0x54,0x41,0x20,0x00,0x54,0x52,0x41, + 0x43,0x4B,0x0D,0x00,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x31,0x31,0x31,0x31,0x31,0x31, + 0x31,0x31,0x31,0x31,0x32,0x32,0x32,0x32, + 0x32,0x32,0x32,0x32,0x32,0x32,0x33,0x33, + 0x33,0x33,0x33,0x0D,0x20,0x20,0x20,0x20, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x30,0x31,0x32,0x33,0x34,0x35, + 0x36,0x37,0x38,0x39,0x30,0x31,0x32,0x33, + 0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x31, + 0x32,0x33,0x34,0x0D,0x20,0x20,0x20,0x20, + 0x00,0x20,0x20,0x30,0x7C,0x0D,0x20,0x20, + 0x31,0x7C,0x0D,0x20,0x20,0x32,0x7C,0x0D, + 0x20,0x20,0x33,0x7C,0x0D,0x20,0x20,0x34, + 0x7C,0x0D,0x53,0x20,0x35,0x7C,0x0D,0x45, + 0x20,0x36,0x7C,0x0D,0x43,0x20,0x37,0x7C, + 0x0D,0x54,0x20,0x38,0x7C,0x0D,0x4F,0x20, + 0x39,0x7C,0x0D,0x52,0x20,0x41,0x7C,0x0D, + 0x20,0x20,0x42,0x7C,0x0D,0x20,0x20,0x43, + 0x7C,0x0D,0x20,0x20,0x44,0x7C,0x0D,0x20, + 0x20,0x45,0x7C,0x0D,0x20,0x20,0x46,0x7C, + 0x0D,0x00 +}; +/* +;diskload3.s + +; inflate - uncompress data stored in the DEFLATE format +; by Piotr Fusik <fox@scene.pl> +; Last modified: 2007-06-17 + +; Compile with xasm (http://xasm.atari.org/), for example: +; xasm inflate.asx /l /d:inflate=$b700 /d:inflate_data=$b900 /d:inflate_zp=$f0 +; inflate is 509 bytes of code and initialized data +; inflate_data is 764 bytes of uninitialized data +; inflate_zp is 10 bytes on page zero + +; hacked for apple ii and ca65 by Egan Ford <egan@sense.net> +; dates + +; compile nodes + +; changes + +;EFF +.define equ = + +inflate = $9B00 +inflate_zp = $10 +inflate_data = $9200 + +; Pointer to compressed data +inputPointer equ inflate_zp ; 2 bytes + +; Pointer to uncompressed data +outputPointer equ inflate_zp+2 ; 2 bytes + +; Local variables + +getBit_buffer equ inflate_zp+4 ; 1 byte + +getBits_base equ inflate_zp+5 ; 1 byte +inflateStoredBlock_pageCounter equ inflate_zp+5 ; 1 byte + +inflateCodes_sourcePointer equ inflate_zp+6 ; 2 bytes +inflateDynamicBlock_lengthIndex equ inflate_zp+6 ; 1 byte +inflateDynamicBlock_lastLength equ inflate_zp+7 ; 1 byte +inflateDynamicBlock_tempCodes equ inflate_zp+7 ; 1 byte + +inflateCodes_lengthMinus2 equ inflate_zp+8 ; 1 byte +inflateDynamicBlock_allCodes equ inflate_zp+8 ; 1 byte + +inflateCodes_primaryCodes equ inflate_zp+9 ; 1 byte + + +; Argument values for getBits +GET_1_BIT equ $81 +GET_2_BITS equ $82 +GET_3_BITS equ $84 +GET_4_BITS equ $88 +GET_5_BITS equ $90 +GET_6_BITS equ $a0 +GET_7_BITS equ $c0 + +; Maximum length of a Huffman code +MAX_CODE_LENGTH equ 15 + +; Huffman trees +TREE_SIZE equ MAX_CODE_LENGTH+1 +PRIMARY_TREE equ 0 +DISTANCE_TREE equ TREE_SIZE + +; Alphabet +LENGTH_SYMBOLS equ 1+29+2 +DISTANCE_SYMBOLS equ 30 +CONTROL_SYMBOLS equ LENGTH_SYMBOLS+DISTANCE_SYMBOLS +TOTAL_SYMBOLS equ 256+CONTROL_SYMBOLS + + ; Optional (recommend for c2t or DOS) + ; DOS header location and size LSB/MSB +; .byte <START,>START,<(END-START),>(END-START) + +; Uncompress DEFLATE stream starting from the address stored in inputPointer +; to the memory starting from the address stored in outputPointer +;; org inflate + .org inflate +START: +;; mvy #0 getBit_buffer + LDY #0 + STY getBit_buffer + + +inflate_blockLoop: +; Get a bit of EOF and two bits of block type +; ldy #0 + sty getBits_base + lda #GET_3_BITS + jsr getBits +;; lsr @ + lsr A + php + tax + bne inflateCompressedBlock + +; Copy uncompressed block +; ldy #0 + sty getBit_buffer + jsr getWord + jsr getWord + sta inflateStoredBlock_pageCounter +; jmp inflateStoredBlock_firstByte + bcs inflateStoredBlock_firstByte +inflateStoredBlock_copyByte: + jsr getByte +inflateStoreByte: + jsr storeByte + bcc inflateCodes_loop +inflateStoredBlock_firstByte: + inx + bne inflateStoredBlock_copyByte + inc inflateStoredBlock_pageCounter + bne inflateStoredBlock_copyByte + +inflate_nextBlock: + plp + bcc inflate_blockLoop + rts + +inflateCompressedBlock: + +; Decompress a block with fixed Huffman trees +; :144 dta 8 +; :112 dta 9 +; :24 dta 7 +; :6 dta 8 +; :2 dta 8 ; codes with no meaning +; :30 dta 5+DISTANCE_TREE +; ldy #0 +inflateFixedBlock_setCodeLengths: + lda #4 + cpy #144 +;; rol @ + rol A + sta literalSymbolCodeLength,y + cpy #CONTROL_SYMBOLS + bcs inflateFixedBlock_noControlSymbol + lda #5+DISTANCE_TREE + cpy #LENGTH_SYMBOLS + bcs inflateFixedBlock_setControlCodeLength + cpy #24 + adc #2-DISTANCE_TREE +inflateFixedBlock_setControlCodeLength: + sta controlSymbolCodeLength,y +inflateFixedBlock_noControlSymbol: + iny + bne inflateFixedBlock_setCodeLengths +; mva #LENGTH_SYMBOLS inflateCodes_primaryCodes + LDA #LENGTH_SYMBOLS + STA inflateCodes_primaryCodes + + dex + beq inflateCodes + +; Decompress a block reading Huffman trees first + +; Build the tree for temporary codes + jsr buildTempHuffmanTree + +; Use temporary codes to get lengths of literal/length and distance codes + ldx #0 +; sec +inflateDynamicBlock_decodeLength: + php + stx inflateDynamicBlock_lengthIndex +; Fetch a temporary code + jsr fetchPrimaryCode +; Temporary code 0..15: put this length + tax + bpl inflateDynamicBlock_verbatimLength +; Temporary code 16: repeat last length 3 + getBits(2) times +; Temporary code 17: put zero length 3 + getBits(3) times +; Temporary code 18: put zero length 11 + getBits(7) times + jsr getBits +; sec + adc #1 + cpx #GET_7_BITS +;; scc:adc #7 + BCC S1 + adc #7 +S1: + tay + lda #0 + cpx #GET_3_BITS +;; scs:lda inflateDynamicBlock_lastLength + BCS S2 + lda inflateDynamicBlock_lastLength +S2: +inflateDynamicBlock_verbatimLength: + iny + ldx inflateDynamicBlock_lengthIndex + plp +inflateDynamicBlock_storeLength: + bcc inflateDynamicBlock_controlSymbolCodeLength +;; sta literalSymbolCodeLength,x+ + sta literalSymbolCodeLength,x + INX + cpx #1 +inflateDynamicBlock_storeNext: + dey + bne inflateDynamicBlock_storeLength + sta inflateDynamicBlock_lastLength +; jmp inflateDynamicBlock_decodeLength + beq inflateDynamicBlock_decodeLength +inflateDynamicBlock_controlSymbolCodeLength: + cpx inflateCodes_primaryCodes +;; scc:ora #DISTANCE_TREE + BCC S3 + ora #DISTANCE_TREE +S3: +;; sta controlSymbolCodeLength,x+ + sta controlSymbolCodeLength,x + INX + + cpx inflateDynamicBlock_allCodes + bcc inflateDynamicBlock_storeNext + dey +; ldy #0 +; jmp inflateCodes + +; Decompress a block +inflateCodes: + jsr buildHuffmanTree +inflateCodes_loop: + jsr fetchPrimaryCode + bcc inflateStoreByte + tax + beq inflate_nextBlock +; Copy sequence from look-behind buffer +; ldy #0 + sty getBits_base + cmp #9 + bcc inflateCodes_setSequenceLength + tya +; lda #0 + cpx #1+28 + bcs inflateCodes_setSequenceLength + dex + txa +;; lsr @ + lsr A + ror getBits_base + inc getBits_base +;; lsr @ + lsr A + rol getBits_base + jsr getAMinus1BitsMax8 +; sec + adc #0 +inflateCodes_setSequenceLength: + sta inflateCodes_lengthMinus2 + ldx #DISTANCE_TREE + jsr fetchCode +; sec + sbc inflateCodes_primaryCodes + tax + cmp #4 + bcc inflateCodes_setOffsetLowByte + inc getBits_base +;; lsr @ + lsr A + jsr getAMinus1BitsMax8 +inflateCodes_setOffsetLowByte: + eor #$ff + sta inflateCodes_sourcePointer + lda getBits_base + cpx #10 + bcc inflateCodes_setOffsetHighByte + lda getNPlus1Bits_mask-10,x + jsr getBits + clc +inflateCodes_setOffsetHighByte: + eor #$ff +; clc + adc outputPointer+1 + sta inflateCodes_sourcePointer+1 + jsr copyByte + jsr copyByte +inflateCodes_copyByte: + jsr copyByte + dec inflateCodes_lengthMinus2 + bne inflateCodes_copyByte +; jmp inflateCodes_loop + beq inflateCodes_loop + +buildTempHuffmanTree: +; ldy #0 + tya +inflateDynamicBlock_clearCodeLengths: + sta literalSymbolCodeLength,y + sta literalSymbolCodeLength+TOTAL_SYMBOLS-256,y + iny + bne inflateDynamicBlock_clearCodeLengths +; numberOfPrimaryCodes = 257 + getBits(5) +; numberOfDistanceCodes = 1 + getBits(5) +; numberOfTemporaryCodes = 4 + getBits(4) + ldx #3 +inflateDynamicBlock_getHeader: + lda inflateDynamicBlock_headerBits-1,x + jsr getBits +; sec + adc inflateDynamicBlock_headerBase-1,x + sta inflateDynamicBlock_tempCodes-1,x + sta inflateDynamicBlock_headerBase+1 + dex + bne inflateDynamicBlock_getHeader + +; Get lengths of temporary codes in the order stored in tempCodeLengthOrder +; ldx #0 +inflateDynamicBlock_getTempCodeLengths: + lda #GET_3_BITS + jsr getBits + ldy tempCodeLengthOrder,x + sta literalSymbolCodeLength,y + ldy #0 + inx + cpx inflateDynamicBlock_tempCodes + bcc inflateDynamicBlock_getTempCodeLengths + +; Build Huffman trees basing on code lengths (in bits) +; stored in the *SymbolCodeLength arrays +buildHuffmanTree: +; Clear nBitCode_totalCount, nBitCode_literalCount, nBitCode_controlCount + tya +; lda #0 +;; sta:rne nBitCode_clearFrom,y+ +R1: + sta nBitCode_clearFrom,y + INY + BNE R1 +; Count number of codes of each length +; ldy #0 +buildHuffmanTree_countCodeLengths: + ldx literalSymbolCodeLength,y + inc nBitCode_literalCount,x + inc nBitCode_totalCount,x + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol + ldx controlSymbolCodeLength,y + inc nBitCode_controlCount,x + inc nBitCode_totalCount,x +buildHuffmanTree_noControlSymbol: + iny + bne buildHuffmanTree_countCodeLengths +; Calculate offsets of symbols sorted by code length +; lda #0 + ldx #-3*TREE_SIZE +buildHuffmanTree_calculateOffsets: + sta nBitCode_literalOffset+3*TREE_SIZE-$100,x +;; add nBitCode_literalCount+3*TREE_SIZE-$100,x + CLC + ADC nBitCode_literalCount+3*TREE_SIZE-$100,x + inx + bne buildHuffmanTree_calculateOffsets +; Put symbols in their place in the sorted array +; ldy #0 +buildHuffmanTree_assignCode: + tya + ldx literalSymbolCodeLength,y +;; ldy:inc nBitCode_literalOffset,x + ldy nBitCode_literalOffset,x + inc nBitCode_literalOffset,x + sta codeToLiteralSymbol,y + tay + cpy #CONTROL_SYMBOLS + bcs buildHuffmanTree_noControlSymbol2 + ldx controlSymbolCodeLength,y +;; ldy:inc nBitCode_controlOffset,x + ldy nBitCode_controlOffset,x + inc nBitCode_controlOffset,x + sta codeToControlSymbol,y + tay +buildHuffmanTree_noControlSymbol2: + iny + bne buildHuffmanTree_assignCode + rts + +; Read Huffman code using the primary tree +fetchPrimaryCode: + ldx #PRIMARY_TREE +; Read a code from input basing on the tree specified in X, +; return low byte of this code in A, +; return C flag reset for literal code, set for length code +fetchCode: +; ldy #0 + tya +fetchCode_nextBit: + jsr getBit +;; rol @ + rol A + inx +;; sub nBitCode_totalCount,x + SEC + SBC nBitCode_totalCount,x + bcs fetchCode_nextBit +; clc + adc nBitCode_controlCount,x + bcs fetchCode_control +; clc + adc nBitCode_literalOffset,x + tax + lda codeToLiteralSymbol,x + clc + rts +fetchCode_control: +;; add nBitCode_controlOffset-1,x + CLC + ADC nBitCode_controlOffset-1,x + tax + lda codeToControlSymbol,x + sec + rts + +; Read A minus 1 bits, but no more than 8 +getAMinus1BitsMax8: + rol getBits_base + tax + cmp #9 + bcs getByte + lda getNPlus1Bits_mask-2,x +getBits: + jsr getBits_loop +getBits_normalizeLoop: + lsr getBits_base +;; ror @ + ror A + bcc getBits_normalizeLoop + rts + +; Read 16 bits +getWord: + jsr getByte + tax +; Read 8 bits +getByte: + lda #$80 +getBits_loop: + jsr getBit +;; ror @ + ror A + bcc getBits_loop + rts + +; Read one bit, return in the C flag +getBit: + lsr getBit_buffer + bne getBit_return + pha +; ldy #0 + lda (inputPointer),y +;; inw inputPointer + INC inputPointer + BNE S4 + INC inputPointer+1 +S4: + sec +;; ror @ + ror A + sta getBit_buffer + pla +getBit_return: + rts + +; Copy a previously written byte +copyByte: + ldy outputPointer + lda (inflateCodes_sourcePointer),y + ldy #0 +; Write a byte +storeByte: + sta (outputPointer),y + inc outputPointer + bne storeByte_return + inc outputPointer+1 + inc inflateCodes_sourcePointer+1 +storeByte_return: + rts + +getNPlus1Bits_mask: + .byte GET_1_BIT,GET_2_BITS,GET_3_BITS,GET_4_BITS,GET_5_BITS,GET_6_BITS,GET_7_BITS + +tempCodeLengthOrder: + .byte GET_2_BITS,GET_3_BITS,GET_7_BITS,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 + +inflateDynamicBlock_headerBits: + .byte GET_4_BITS,GET_5_BITS,GET_5_BITS +inflateDynamicBlock_headerBase: + .byte 3,0,0 ; second byte is modified at runtime! + + .org inflate_data + +; Data for building trees + +literalSymbolCodeLength: + .org *+256 +controlSymbolCodeLength: + .org *+CONTROL_SYMBOLS + +; Huffman trees + +nBitCode_clearFrom: +nBitCode_totalCount: + .org *+2*TREE_SIZE +nBitCode_literalCount: + .org *+TREE_SIZE +nBitCode_controlCount: + .org *+2*TREE_SIZE +nBitCode_literalOffset: + .org *+TREE_SIZE +nBitCode_controlOffset: + .org *+2*TREE_SIZE + +codeToLiteralSymbol: + .org *+256 +codeToControlSymbol: + .org *+CONTROL_SYMBOLS + + .byte 0,0,0 ; round out block + +END: +*/ +unsigned char diskloadcode3[] = { + 0xA0,0x00,0x84,0x14,0x84,0x15,0xA9,0x84, + 0x20,0xA3,0x9C,0x4A,0x08,0xAA,0xD0,0x1F, + 0x84,0x14,0x20,0xAC,0x9C,0x20,0xAC,0x9C, + 0x85,0x15,0xB0,0x08,0x20,0xB0,0x9C,0x20, + 0xD2,0x9C,0x90,0x75,0xE8,0xD0,0xF5,0xE6, + 0x15,0xD0,0xF1,0x28,0x90,0xD6,0x60,0xA9, + 0x04,0xC0,0x90,0x2A,0x99,0x00,0x92,0xC0, + 0x3E,0xB0,0x0D,0xA9,0x15,0xC0,0x20,0xB0, + 0x04,0xC0,0x18,0x69,0xF2,0x99,0x00,0x93, + 0xC8,0xD0,0xE4,0xA9,0x20,0x85,0x19,0xCA, + 0xF0,0x44,0x20,0xF5,0x9B,0xA2,0x00,0x08, + 0x86,0x16,0x20,0x73,0x9C,0xAA,0x10,0x14, + 0x20,0xA3,0x9C,0x69,0x01,0xE0,0xC0,0x90, + 0x02,0x69,0x07,0xA8,0xA9,0x00,0xE0,0x84, + 0xB0,0x02,0xA5,0x17,0xC8,0xA6,0x16,0x28, + 0x90,0x0D,0x9D,0x00,0x92,0xE8,0xE0,0x01, + 0x88,0xD0,0xF5,0x85,0x17,0xF0,0xD0,0xE4, + 0x19,0x90,0x02,0x09,0x10,0x9D,0x00,0x93, + 0xE8,0xE4,0x18,0x90,0xEB,0x88,0x20,0x24, + 0x9C,0x20,0x73,0x9C,0x90,0x81,0xAA,0xF0, + 0x8A,0x84,0x15,0xC9,0x09,0x90,0x14,0x98, + 0xE0,0x1D,0xB0,0x0F,0xCA,0x8A,0x4A,0x66, + 0x15,0xE6,0x15,0x4A,0x26,0x15,0x20,0x99, + 0x9C,0x69,0x00,0x85,0x18,0xA2,0x10,0x20, + 0x75,0x9C,0xE5,0x19,0xAA,0xC9,0x04,0x90, + 0x06,0xE6,0x15,0x4A,0x20,0x99,0x9C,0x49, + 0xFF,0x85,0x16,0xA5,0x15,0xE0,0x0A,0x90, + 0x07,0xBD,0xD3,0x9C,0x20,0xA3,0x9C,0x18, + 0x49,0xFF,0x65,0x13,0x85,0x17,0x20,0xCC, + 0x9C,0x20,0xCC,0x9C,0x20,0xCC,0x9C,0xC6, + 0x18,0xD0,0xF9,0xF0,0xA4,0x98,0x99,0x00, + 0x92,0x99,0x3E,0x92,0xC8,0xD0,0xF7,0xA2, + 0x03,0xBD,0xF6,0x9C,0x20,0xA3,0x9C,0x7D, + 0xF9,0x9C,0x95,0x16,0x8D,0xFB,0x9C,0xCA, + 0xD0,0xEF,0xA9,0x84,0x20,0xA3,0x9C,0xBC, + 0xE4,0x9C,0x99,0x00,0x92,0xA0,0x00,0xE8, + 0xE4,0x17,0x90,0xEE,0x98,0x99,0x3E,0x93, + 0xC8,0xD0,0xFA,0xBE,0x00,0x92,0xFE,0x5E, + 0x93,0xFE,0x3E,0x93,0xC0,0x3E,0xB0,0x09, + 0xBE,0x00,0x93,0xFE,0x6E,0x93,0xFE,0x3E, + 0x93,0xC8,0xD0,0xE7,0xA2,0xD0,0x9D,0xBE, + 0x92,0x18,0x7D,0x8E,0x92,0xE8,0xD0,0xF6, + 0x98,0xBE,0x00,0x92,0xBC,0x8E,0x93,0xFE, + 0x8E,0x93,0x99,0xBE,0x93,0xA8,0xC0,0x3E, + 0xB0,0x0D,0xBE,0x00,0x93,0xBC,0x9E,0x93, + 0xFE,0x9E,0x93,0x99,0xBE,0x94,0xA8,0xC8, + 0xD0,0xDE,0x60,0xA2,0x00,0x98,0x20,0xB9, + 0x9C,0x2A,0xE8,0x38,0xFD,0x3E,0x93,0xB0, + 0xF5,0x7D,0x6E,0x93,0xB0,0x09,0x7D,0x8E, + 0x93,0xAA,0xBD,0xBE,0x93,0x18,0x60,0x18, + 0x7D,0x9D,0x93,0xAA,0xBD,0xBE,0x94,0x38, + 0x60,0x26,0x15,0xAA,0xC9,0x09,0xB0,0x10, + 0xBD,0xDB,0x9C,0x20,0xB2,0x9C,0x46,0x15, + 0x6A,0x90,0xFB,0x60,0x20,0xB0,0x9C,0xAA, + 0xA9,0x80,0x20,0xB9,0x9C,0x6A,0x90,0xFA, + 0x60,0x46,0x14,0xD0,0x0E,0x48,0xB1,0x10, + 0xE6,0x10,0xD0,0x02,0xE6,0x11,0x38,0x6A, + 0x85,0x14,0x68,0x60,0xA4,0x12,0xB1,0x16, + 0xA0,0x00,0x91,0x12,0xE6,0x12,0xD0,0x04, + 0xE6,0x13,0xE6,0x17,0x60,0x81,0x82,0x84, + 0x88,0x90,0xA0,0xC0,0x82,0x84,0xC0,0x00, + 0x08,0x07,0x09,0x06,0x0A,0x05,0x0B,0x04, + 0x0C,0x03,0x0D,0x02,0x0E,0x01,0x0F,0x88, + 0x90,0x90,0x03,0x00,0x00,0x00,0x00,0x00 + +}; + +//DOS 3.3 +unsigned char dosboot1[] = { + 0x4C,0xBF,0x9D,0x4C,0x84,0x9D,0x4C,0xFD, + 0xAA,0x4C,0xB5,0xB7,0xAD,0x0F,0x9D,0xAC, + 0x0E,0x9D,0x60,0xAD,0xC2,0xAA,0xAC,0xC1, + 0xAA,0x60,0x4C,0x51,0xA8,0xEA,0xEA,0x4C, + 0x59,0xFA,0xBF,0x9D,0x38,0x4C,0x58,0xFF, + 0x4C,0x65,0xFF,0x4C,0x65,0xFF,0x65,0xFF, +}; + +//DOS 3.3 +unsigned char dosboot2[] = { + 0xD3,0x9C,0x81,0x9E,0xBD,0x9E,0x75,0xAA, + 0x93,0xAA,0x60,0xAA,0x00,0x9D,0xBB,0xB5, + 0xEA,0x9E,0x11,0x9F,0x22,0x9F,0x2E,0x9F, + 0x51,0x9F,0x60,0x9F,0x70,0x9F,0x4E,0xA5, + 0x12,0xA4,0x96,0xA3,0xD0,0xA4,0xEF,0xA4, + 0x62,0xA2,0x70,0xA2,0x74,0xA2,0xE9,0xA2, + 0x1A,0xA5,0xC5,0xA5,0x0F,0xA5,0xDC,0xA5, + 0xA2,0xA2,0x97,0xA2,0x80,0xA2,0x6D,0xA5, + 0x32,0xA2,0x3C,0xA2,0x28,0xA2,0x2D,0xA2, + 0x50,0xA2,0x79,0xA5,0x9D,0xA5,0x30,0xA3, + 0x5C,0xA3,0x8D,0xA3,0x7C,0xA2,0xFC,0xA4, + 0xFC,0xA4,0x65,0xD8,0x00,0xE0,0x3C,0xD4, + 0xF2,0xD4,0x36,0xE8,0xE5,0xA4,0xE3,0xE3, + 0x00,0xE0,0x03,0xE0,0xFC,0xA4,0xFC,0xA4, + 0x65,0xD8,0x00,0xE0,0x3C,0xD4,0xF2,0xD4, + 0x06,0xA5,0x06,0xA5,0x67,0x10,0x84,0x9D, + 0x3C,0x0C,0xF2,0x0C,0xAD,0xE9,0xB7,0x4A, + 0x4A,0x4A,0x4A,0x8D,0x6A,0xAA,0xAD,0xEA, + 0xB7,0x8D,0x68,0xAA,0xAD,0x00,0xE0,0x49, + 0x20,0xD0,0x11,0x8D,0xB6,0xAA,0xA2,0x0A, + 0xBD,0x61,0x9D,0x9D,0x55,0x9D,0xCA,0xD0, + 0xF7,0x4C,0xBC,0x9D,0xA9,0x40,0x8D,0xB6, + 0xAA,0xA2,0x0C,0xBD,0x6B,0x9D,0x9D,0x55, + 0x9D,0xCA,0xD0,0xF7,0x38,0xB0,0x12,0xAD, + 0xB6,0xAA,0xD0,0x04,0xA9,0x20,0xD0,0x05, + 0x0A,0x10,0x05,0xA9,0x4C,0x20,0xB2,0xA5, + 0x18,0x08,0x20,0x51,0xA8,0xA9,0x00,0x8D, + 0x5E,0xAA,0x8D,0x52,0xAA,0x28,0x6A,0x8D, + 0x51,0xAA,0x30,0x03,0x6C,0x5E,0x9D,0x6C, + 0x5C,0x9D,0x0A,0x10,0x19,0x8D,0xB6,0xAA, + 0xA2,0x0C,0xBD,0x77,0x9D,0x9D,0x55,0x9D, + 0xCA,0xD0,0xF7,0xA2,0x1D,0xBD,0x93,0xAA, + 0x9D,0x75,0xAA,0xCA,0x10,0xF7,0xAD,0xB1, + 0xAA,0x8D,0x57,0xAA,0x20,0xD4,0xA7,0xAD, + 0xB3,0xAA,0xF0,0x09,0x48,0x20,0x9D,0xA6, + 0x68,0xA0,0x00,0x91,0x40,0x20,0x5B,0xA7, + 0xAD,0x5F,0xAA,0xD0,0x20,0xA2,0x2F,0xBD, + 0x51,0x9E,0x9D,0xD0,0x03,0xCA,0x10,0xF7, + 0xAD,0x53,0x9E,0x8D,0xF3,0x03,0x49,0xA5, + 0x8D,0xF4,0x03,0xAD,0x52,0x9E,0x8D,0xF2, + 0x03,0xA9,0x06,0xD0,0x05,0xAD,0x62,0xAA, + 0xF0,0x06,0x8D,0x5F,0xAA,0x4C,0x80,0xA1, + 0x60,0x4C,0xBF,0x9D,0x4C,0x84,0x9D,0x4C, + 0xFD,0xAA,0x4C,0xB5,0xB7,0xAD,0x0F,0x9D, + 0xAC,0x0E,0x9D,0x60,0xAD,0xC2,0xAA,0xAC, + 0xC1,0xAA,0x60,0x4C,0x51,0xA8,0xEA,0xEA, + 0x4C,0x59,0xFA,0x4C,0x65,0xFF,0x4C,0x58, + 0xFF,0x4C,0x65,0xFF,0x4C,0x65,0xFF,0x65, + 0xFF,0x20,0xD1,0x9E,0xAD,0x51,0xAA,0xF0, + 0x15,0x48,0xAD,0x5C,0xAA,0x91,0x28,0x68, + 0x30,0x03,0x4C,0x26,0xA6,0x20,0xEA,0x9D, + 0xA4,0x24,0xA9,0x60,0x91,0x28,0xAD,0xB3, + 0xAA,0xF0,0x03,0x20,0x82,0xA6,0xA9,0x03, + 0x8D,0x52,0xAA,0x20,0xBA,0x9F,0x20,0xBA, + 0x9E,0x8D,0x5C,0xAA,0x8E,0x5A,0xAA,0x4C, + 0xB3,0x9F,0x6C,0x38,0x00,0x20,0xD1,0x9E, + 0xAD,0x52,0xAA,0x0A,0xAA,0xBD,0x11,0x9D, + 0x48,0xBD,0x10,0x9D,0x48,0xAD,0x5C,0xAA, + 0x60,0x8D,0x5C,0xAA,0x8E,0x5A,0xAA,0x8C, + 0x5B,0xAA,0xBA,0xE8,0xE8,0x8E,0x59,0xAA, + 0xA2,0x03,0xBD,0x53,0xAA,0x95,0x36,0xCA, + 0x10,0xF8,0x60,0xAE,0xB7,0xAA,0xF0,0x03, + 0x4C,0x78,0x9F,0xAE,0x51,0xAA,0xF0,0x08, + 0xC9,0xBF,0xF0,0x75,0xC5,0x33,0xF0,0x27, + 0xA2,0x02,0x8E,0x52,0xAA,0xCD,0xB2,0xAA, + 0xD0,0x19,0xCA,0x8E,0x52,0xAA,0xCA,0x8E, + 0x5D,0xAA,0xAE,0x5D,0xAA,0x9D,0x00,0x02, + 0xE8,0x8E,0x5D,0xAA,0xC9,0x8D,0xD0,0x75, + 0x4C,0xCD,0x9F,0xC9,0x8D,0xD0,0x7D,0xA2, + 0x00,0x8E,0x52,0xAA,0x4C,0xA4,0x9F,0xA2, + 0x00,0x8E,0x52,0xAA,0xC9,0x8D,0xF0,0x07, + 0xAD,0xB3,0xAA,0xF0,0x67,0xD0,0x5E,0x48, + 0x38,0xAD,0xB3,0xAA,0xD0,0x03,0x20,0x5E, + 0xA6,0x68,0x90,0xEC,0xAE,0x5A,0xAA,0x4C, + 0x15,0x9F,0xC9,0x8D,0xD0,0x05,0xA9,0x05, + 0x8D,0x52,0xAA,0x20,0x0E,0xA6,0x4C,0x99, + 0x9F,0xCD,0xB2,0xAA,0xF0,0x85,0xC9,0x8A, + 0xF0,0xF1,0xA2,0x04,0x8E,0x52,0xAA,0xD0, + 0xE1,0xA9,0x00,0x8D,0x52,0xAA,0xF0,0x25, + 0xA9,0x00,0x8D,0xB7,0xAA,0x20,0x51,0xA8, + 0x4C,0xDC,0xA4,0xAD,0x00,0x02,0xCD,0xB2, + 0xAA,0xF0,0x0A,0xA9,0x8D,0x8D,0x00,0x02, + 0xA2,0x00,0x8E,0x5A,0xAA,0xA9,0x40,0xD0, + 0x06,0xA9,0x10,0xD0,0x02,0xA9,0x20,0x2D, + 0x5E,0xAA,0xF0,0x0F,0x20,0xBA,0x9F,0x20, + 0xC5,0x9F,0x8D,0x5C,0xAA,0x8C,0x5B,0xAA, + 0x8E,0x5A,0xAA,0x20,0x51,0xA8,0xAE,0x59, + 0xAA,0x9A,0xAD,0x5C,0xAA,0xAC,0x5B,0xAA, + 0xAE,0x5A,0xAA,0x38,0x60,0x6C,0x36,0x00, + 0xA9,0x8D,0x4C,0xC5,0x9F,0xA0,0xFF,0x8C, + 0x5F,0xAA,0xC8,0x8C,0x62,0xAA,0xEE,0x5F, + 0xAA,0xA2,0x00,0x08,0xBD,0x00,0x02,0xCD, + 0xB2,0xAA,0xD0,0x01,0xE8,0x8E,0x5D,0xAA, + 0x20,0xA4,0xA1,0x29,0x7F,0x59,0x84,0xA8, + 0xC8,0x0A,0xF0,0x02,0x68,0x08,0x90,0xF0, + 0x28,0xF0,0x20,0xB9,0x84,0xA8,0xD0,0xD6, + 0xAD,0x00,0x02,0xCD,0xB2,0xAA,0xF0,0x03, + 0x4C,0xA4,0x9F,0xAD,0x01,0x02,0xC9,0x8D, + 0xD0,0x06,0x20,0x5B,0xA7,0x4C,0x95,0x9F, + 0x4C,0xC4,0xA6,0x0E,0x5F,0xAA,0xAC,0x5F, + 0xAA,0x20,0x5E,0xA6,0x90,0x0C,0xA9,0x02, + 0x39,0x09,0xA9,0xF0,0x05,0xA9,0x0F,0x4C, + 0xD2,0xA6,0xC0,0x06,0xD0,0x02,0x84,0x33, + 0xA9,0x20,0x39,0x09,0xA9,0xF0,0x61,0x20, + 0x95,0xA0,0x08,0x20,0xA4,0xA1,0xF0,0x1E, + 0x0A,0x90,0x05,0x30,0x03,0x4C,0x00,0xA0, + 0x6A,0x4C,0x59,0xA0,0x20,0x93,0xA1,0xF0, + 0x0D,0x99,0x75,0xAA,0xC8,0xC0,0x3C,0x90, + 0xF3,0x20,0x93,0xA1,0xD0,0xFB,0x28,0xD0, + 0x0F,0xAC,0x5F,0xAA,0xA9,0x10,0x39,0x09, + 0xA9,0xF0,0x0C,0xA0,0x1E,0x08,0xD0,0xCB, + 0xAD,0x93,0xAA,0xC9,0xA0,0xF0,0x13,0xAD, + 0x75,0xAA,0xC9,0xA0,0xD0,0x4B,0xAC,0x5F, + 0xAA,0xA9,0xC0,0x39,0x09,0xA9,0xF0,0x02, + 0x10,0x3F,0x4C,0x00,0xA0,0xA0,0x3C,0xA9, + 0xA0,0x99,0x74,0xAA,0x88,0xD0,0xFA,0x60, + 0x8D,0x75,0xAA,0xA9,0x0C,0x39,0x09,0xA9, + 0xF0,0x27,0x20,0xB9,0xA1,0xB0,0x1F,0xA8, + 0xD0,0x17,0xE0,0x11,0xB0,0x13,0xAC,0x5F, + 0xAA,0xA9,0x08,0x39,0x09,0xA9,0xF0,0x06, + 0xE0,0x08,0xB0,0xCE,0x90,0x0B,0x8A,0xD0, + 0x08,0xA9,0x02,0x4C,0xD2,0xA6,0x4C,0xC4, + 0xA6,0xA9,0x00,0x8D,0x65,0xAA,0x8D,0x74, + 0xAA,0x8D,0x66,0xAA,0x8D,0x6C,0xAA,0x8D, + 0x6D,0xAA,0x20,0xDC,0xBF,0xAD,0x5D,0xAA, + 0x20,0xA4,0xA1,0xD0,0x1F,0xC9,0x8D,0xD0, + 0xF7,0xAE,0x5F,0xAA,0xAD,0x65,0xAA,0x1D, + 0x0A,0xA9,0x5D,0x0A,0xA9,0xD0,0x93,0xAE, + 0x63,0xAA,0xF0,0x76,0x8D,0x63,0xAA,0x8E, + 0x5D,0xAA,0xD0,0xDC,0xA2,0x0A,0xDD,0x40, + 0xA9,0xF0,0x05,0xCA,0xD0,0xF8,0xF0,0xB6, + 0xBD,0x4A,0xA9,0x30,0x47,0x0D,0x65,0xAA, + 0x8D,0x65,0xAA,0xCA,0x8E,0x64,0xAA,0x20, + 0xB9,0xA1,0xB0,0xA2,0xAD,0x64,0xAA,0x0A, + 0x0A,0xA8,0xA5,0x45,0xD0,0x09,0xA5,0x44, + 0xD9,0x55,0xA9,0x90,0x8C,0xA5,0x45,0xD9, + 0x58,0xA9,0x90,0x0B,0xD0,0x83,0xA5,0x44, + 0xD9,0x57,0xA9,0x90,0x02,0xD0,0xF5,0xAD, + 0x63,0xAA,0xD0,0x94,0x98,0x4A,0xA8,0xA5, + 0x45,0x99,0x67,0xAA,0xA5,0x44,0x99,0x66, + 0xAA,0x4C,0xE8,0xA0,0x48,0xA9,0x80,0x0D, + 0x65,0xAA,0x8D,0x65,0xAA,0x68,0x29,0x7F, + 0x0D,0x74,0xAA,0x8D,0x74,0xAA,0xD0,0xE9, + 0xF0,0x9C,0x20,0x80,0xA1,0x4C,0x83,0x9F, + 0x20,0x5B,0xA7,0x20,0xAE,0xA1,0xAD,0x5F, + 0xAA,0xAA,0xBD,0x1F,0x9D,0x48,0xBD,0x1E, + 0x9D,0x48,0x60,0xAE,0x5D,0xAA,0xBD,0x00, + 0x02,0xC9,0x8D,0xF0,0x06,0xE8,0x8E,0x5D, + 0xAA,0xC9,0xAC,0x60,0x20,0x93,0xA1,0xF0, + 0xFA,0xC9,0xA0,0xF0,0xF7,0x60,0xA9,0x00, + 0xA0,0x16,0x99,0xBA,0xB5,0x88,0xD0,0xFA, + 0x60,0xA9,0x00,0x85,0x44,0x85,0x45,0x20, + 0xA4,0xA1,0x08,0xC9,0xA4,0xF0,0x3C,0x28, + 0x4C,0xCE,0xA1,0x20,0xA4,0xA1,0xD0,0x06, + 0xA6,0x44,0xA5,0x45,0x18,0x60,0x38,0xE9, + 0xB0,0x30,0x21,0xC9,0x0A,0xB0,0x1D,0x20, + 0xFE,0xA1,0x65,0x44,0xAA,0xA9,0x00,0x65, + 0x45,0xA8,0x20,0xFE,0xA1,0x20,0xFE,0xA1, + 0x8A,0x65,0x44,0x85,0x44,0x98,0x65,0x45, + 0x85,0x45,0x90,0xCF,0x38,0x60,0x06,0x44, + 0x26,0x45,0x60,0x28,0x20,0xA4,0xA1,0xF0, + 0xC5,0x38,0xE9,0xB0,0x30,0xEE,0xC9,0x0A, + 0x90,0x08,0xE9,0x07,0x30,0xE6,0xC9,0x10, + 0xB0,0xE2,0xA2,0x04,0x20,0xFE,0xA1,0xCA, + 0xD0,0xFA,0x05,0x44,0x85,0x44,0x4C,0x04, + 0xA2,0xA5,0x44,0x4C,0x95,0xFE,0xA5,0x44, + 0x4C,0x8B,0xFE,0xAD,0x5E,0xAA,0x0D,0x74, + 0xAA,0x8D,0x5E,0xAA,0x60,0x2C,0x74,0xAA, + 0x50,0x03,0x20,0xC8,0x9F,0xA9,0x70,0x4D, + 0x74,0xAA,0x2D,0x5E,0xAA,0x8D,0x5E,0xAA, + 0x60,0xA9,0x00,0x8D,0xB3,0xAA,0xA5,0x44, + 0x48,0x20,0x16,0xA3,0x68,0x8D,0x57,0xAA, + 0x4C,0xD4,0xA7,0xA9,0x05,0x20,0xAA,0xA2, + 0x20,0x64,0xA7,0xA0,0x00,0x98,0x91,0x40, + 0x60,0xA9,0x07,0xD0,0x02,0xA9,0x08,0x20, + 0xAA,0xA2,0x4C,0xEA,0xA2,0xA9,0x0C,0xD0, + 0xF6,0xAD,0x08,0x9D,0x8D,0xBD,0xB5,0xAD, + 0x09,0x9D,0x8D,0xBE,0xB5,0xA9,0x09,0x8D, + 0x63,0xAA,0x20,0xC8,0xA2,0x4C,0xEA,0xA2, + 0x20,0xA3,0xA2,0x20,0x8C,0xA6,0xD0,0xFB, + 0x4C,0x71,0xB6,0xA9,0x00,0x4C,0xD5,0xA3, + 0xA9,0x01,0x8D,0x63,0xAA,0xAD,0x6C,0xAA, + 0xD0,0x0A,0xAD,0x6D,0xAA,0xD0,0x05,0xA9, + 0x01,0x8D,0x6C,0xAA,0xAD,0x6C,0xAA,0x8D, + 0xBD,0xB5,0xAD,0x6D,0xAA,0x8D,0xBE,0xB5, + 0x20,0xEA,0xA2,0xA5,0x45,0xD0,0x03,0x4C, + 0xC8,0xA6,0x85,0x41,0xA5,0x44,0x85,0x40, + 0x20,0x43,0xA7,0x20,0x4E,0xA7,0x20,0x1A, + 0xA7,0xAD,0x63,0xAA,0x8D,0xBB,0xB5,0x4C, + 0xA8,0xA6,0xAD,0x75,0xAA,0xC9,0xA0,0xF0, + 0x25,0x20,0x64,0xA7,0xB0,0x3A,0x20,0xFC, + 0xA2,0x4C,0xEA,0xA2,0x20,0xAF,0xA7,0xD0, + 0x05,0xA9,0x00,0x8D,0xB3,0xAA,0xA0,0x00, + 0x98,0x91,0x40,0x20,0x4E,0xA7,0xA9,0x02, + 0x8D,0xBB,0xB5,0x4C,0xA8,0xA6,0x20,0x92, + 0xA7,0xD0,0x05,0x20,0x9A,0xA7,0xF0,0x10, + 0x20,0xAF,0xA7,0xF0,0xF6,0x20,0xAA,0xA7, + 0xF0,0xF1,0x20,0xFC,0xA2,0x4C,0x16,0xA3, + 0x60,0xA9,0x09,0x2D,0x65,0xAA,0xC9,0x09, + 0xF0,0x03,0x4C,0x00,0xA0,0xA9,0x04,0x20, + 0xD5,0xA3,0xAD,0x73,0xAA,0xAC,0x72,0xAA, + 0x20,0xE0,0xA3,0xAD,0x6D,0xAA,0xAC,0x6C, + 0xAA,0x20,0xE0,0xA3,0xAD,0x73,0xAA,0xAC, + 0x72,0xAA,0x4C,0xFF,0xA3,0x20,0xA8,0xA2, + 0xA9,0x7F,0x2D,0xC2,0xB5,0xC9,0x04,0xF0, + 0x03,0x4C,0xD0,0xA6,0xA9,0x04,0x20,0xD5, + 0xA3,0x20,0x7A,0xA4,0xAA,0xAD,0x65,0xAA, + 0x29,0x01,0xD0,0x06,0x8E,0x72,0xAA,0x8C, + 0x73,0xAA,0x20,0x7A,0xA4,0xAE,0x72,0xAA, + 0xAC,0x73,0xAA,0x4C,0x71,0xA4,0x20,0x5D, + 0xA3,0x20,0x51,0xA8,0x6C,0x72,0xAA,0xAD, + 0xB6,0xAA,0xF0,0x20,0xA5,0xD6,0x10,0x03, + 0x4C,0xCC,0xA6,0xA9,0x02,0x20,0xD5,0xA3, + 0x38,0xA5,0xAF,0xE5,0x67,0xA8,0xA5,0xB0, + 0xE5,0x68,0x20,0xE0,0xA3,0xA5,0x68,0xA4, + 0x67,0x4C,0xFF,0xA3,0xA9,0x01,0x20,0xD5, + 0xA3,0x38,0xA5,0x4C,0xE5,0xCA,0xA8,0xA5, + 0x4D,0xE5,0xCB,0x20,0xE0,0xA3,0xA5,0xCB, + 0xA4,0xCA,0x4C,0xFF,0xA3,0x8D,0xC2,0xB5, + 0x48,0x20,0xA8,0xA2,0x68,0x4C,0xC4,0xA7, + 0x8C,0xC1,0xB5,0x8C,0xC3,0xB5,0x8D,0xC2, + 0xB5,0xA9,0x04,0x8D,0xBB,0xB5,0xA9,0x01, + 0x8D,0xBC,0xB5,0x20,0xA8,0xA6,0xAD,0xC2, + 0xB5,0x8D,0xC3,0xB5,0x4C,0xA8,0xA6,0x8C, + 0xC3,0xB5,0x8D,0xC4,0xB5,0xA9,0x02,0x4C, + 0x86,0xB6,0x20,0xA8,0xA6,0x4C,0xEA,0xA2, + 0x4C,0xD0,0xA6,0x20,0x16,0xA3,0x20,0xA8, + 0xA2,0xA9,0x23,0x2D,0xC2,0xB5,0xF0,0xF0, + 0x8D,0xC2,0xB5,0xAD,0xB6,0xAA,0xF0,0x28, + 0xA9,0x02,0x20,0xB1,0xA4,0x20,0x7A,0xA4, + 0x18,0x65,0x67,0xAA,0x98,0x65,0x68,0xC5, + 0x74,0xB0,0x70,0x85,0xB0,0x85,0x6A,0x86, + 0xAF,0x86,0x69,0xA6,0x67,0xA4,0x68,0x20, + 0x71,0xA4,0x20,0x51,0xA8,0x6C,0x60,0x9D, + 0xA9,0x01,0x20,0xB1,0xA4,0x20,0x7A,0xA4, + 0x38,0xA5,0x4C,0xED,0x60,0xAA,0xAA,0xA5, + 0x4D,0xED,0x61,0xAA,0x90,0x45,0xA8,0xC4, + 0x4B,0x90,0x40,0xF0,0x3E,0x84,0xCB,0x86, + 0xCA,0x8E,0xC3,0xB5,0x8C,0xC4,0xB5,0x4C, + 0x0A,0xA4,0xAD,0x0A,0x9D,0x8D,0xC3,0xB5, + 0xAD,0x0B,0x9D,0x8D,0xC4,0xB5,0xA9,0x00, + 0x8D,0xC2,0xB5,0xA9,0x02,0x8D,0xC1,0xB5, + 0xA9,0x03,0x8D,0xBB,0xB5,0xA9,0x02,0x8D, + 0xBC,0xB5,0x20,0xA8,0xA6,0xAD,0x61,0xAA, + 0x8D,0xC2,0xB5,0xA8,0xAD,0x60,0xAA,0x8D, + 0xC1,0xB5,0x60,0x20,0xEA,0xA2,0x4C,0xCC, + 0xA6,0xCD,0xC2,0xB5,0xF0,0x1A,0xAE,0x5F, + 0xAA,0x8E,0x62,0xAA,0x4A,0xF0,0x03,0x4C, + 0x9E,0xA5,0xA2,0x1D,0xBD,0x75,0xAA,0x9D, + 0x93,0xAA,0xCA,0x10,0xF7,0x4C,0x7A,0xA5, + 0x60,0xAD,0xB6,0xAA,0xF0,0x03,0x8D,0xB7, + 0xAA,0x20,0x13,0xA4,0x20,0xC8,0x9F,0x20, + 0x51,0xA8,0x6C,0x58,0x9D,0xA5,0x4A,0x85, + 0xCC,0xA5,0x4B,0x85,0xCD,0x6C,0x56,0x9D, + 0x20,0x16,0xA4,0x20,0xC8,0x9F,0x20,0x51, + 0xA8,0x6C,0x56,0x9D,0x20,0x65,0xD6,0x85, + 0x33,0x85,0xD8,0x4C,0xD2,0xD7,0x20,0x65, + 0x0E,0x85,0x33,0x85,0xD8,0x4C,0xD4,0x0F, + 0x20,0x26,0xA5,0xA9,0x05,0x8D,0x52,0xAA, + 0x4C,0x83,0x9F,0x20,0x26,0xA5,0xA9,0x01, + 0x8D,0x51,0xAA,0x4C,0x83,0x9F,0x20,0x64, + 0xA7,0x90,0x06,0x20,0xA3,0xA2,0x4C,0x34, + 0xA5,0x20,0x4E,0xA7,0xAD,0x65,0xAA,0x29, + 0x06,0xF0,0x13,0xA2,0x03,0xBD,0x6E,0xAA, + 0x9D,0xBD,0xB5,0xCA,0x10,0xF7,0xA9,0x0A, + 0x8D,0xBB,0xB5,0x20,0xA8,0xA6,0x60,0xA9, + 0x40,0x2D,0x65,0xAA,0xF0,0x05,0xAD,0x66, + 0xAA,0xD0,0x05,0xA9,0xFE,0x8D,0x66,0xAA, + 0xAD,0x0D,0x9D,0x8D,0xBC,0xB5,0xA9,0x0B, + 0x20,0xAA,0xA2,0x4C,0x97,0xA3,0xA9,0x06, + 0x20,0xAA,0xA2,0xAD,0xBF,0xB5,0x8D,0x66, + 0xAA,0x60,0xA9,0x4C,0x20,0xB2,0xA5,0xF0, + 0x2E,0xA9,0x00,0x8D,0xB6,0xAA,0xA0,0x1E, + 0x20,0x97,0xA0,0xA2,0x09,0xBD,0xB7,0xAA, + 0x9D,0x74,0xAA,0xCA,0xD0,0xF7,0xA9,0xC0, + 0x8D,0x51,0xAA,0x4C,0xD1,0xA4,0xA9,0x20, + 0x20,0xB2,0xA5,0xF0,0x05,0xA9,0x01,0x4C, + 0xD2,0xA6,0xA9,0x00,0x8D,0xB7,0xAA,0x4C, + 0x84,0x9D,0xCD,0x00,0xE0,0xF0,0x0E,0x8D, + 0x80,0xC0,0xCD,0x00,0xE0,0xF0,0x06,0x8D, + 0x81,0xC0,0xCD,0x00,0xE0,0x60,0x20,0xA3, + 0xA2,0xAD,0x4F,0xAA,0x8D,0xB4,0xAA,0xAD, + 0x50,0xAA,0x8D,0xB5,0xAA,0xAD,0x75,0xAA, + 0x8D,0xB3,0xAA,0xD0,0x0E,0x20,0x64,0xA7, + 0x90,0x06,0x20,0xA3,0xA2,0x4C,0xEB,0xA5, + 0x20,0x4E,0xA7,0xAD,0x65,0xAA,0x29,0x04, + 0xF0,0x1B,0xAD,0x6E,0xAA,0xD0,0x08,0xAE, + 0x6F,0xAA,0xF0,0x11,0xCE,0x6F,0xAA,0xCE, + 0x6E,0xAA,0x20,0x8C,0xA6,0xF0,0x38,0xC9, + 0x8D,0xD0,0xF7,0xF0,0xE5,0x60,0x20,0x5E, + 0xA6,0xB0,0x66,0xAD,0x5C,0xAA,0x8D,0xC3, + 0xB5,0xA9,0x04,0x8D,0xBB,0xB5,0xA9,0x01, + 0x8D,0xBC,0xB5,0x4C,0xA8,0xA6,0x20,0x5E, + 0xA6,0xB0,0x4E,0xA9,0x06,0x8D,0x52,0xAA, + 0x20,0x8C,0xA6,0xD0,0x0F,0x20,0xFC,0xA2, + 0xA9,0x03,0xCD,0x52,0xAA,0xF0,0xCE,0xA9, + 0x05,0x4C,0xD2,0xA6,0xC9,0xE0,0x90,0x02, + 0x29,0x7F,0x8D,0x5C,0xAA,0xAE,0x5A,0xAA, + 0xF0,0x09,0xCA,0xBD,0x00,0x02,0x09,0x80, + 0x9D,0x00,0x02,0x4C,0xB3,0x9F,0x48,0xAD, + 0xB6,0xAA,0xF0,0x0E,0xA6,0x76,0xE8,0xF0, + 0x0D,0xA6,0x33,0xE0,0xDD,0xF0,0x07,0x68, + 0x18,0x60,0xA5,0xD9,0x30,0xF9,0x68,0x38, + 0x60,0x20,0xFC,0xA2,0x20,0x5B,0xA7,0x4C, + 0xB3,0x9F,0x20,0x9D,0xA6,0x20,0x4E,0xA7, + 0xA9,0x03,0xD0,0xA1,0xA9,0x03,0x8D,0xBB, + 0xB5,0xA9,0x01,0x8D,0xBC,0xB5,0x20,0xA8, + 0xA6,0xAD,0xC3,0xB5,0x60,0xAD,0xB5,0xAA, + 0x85,0x41,0xAD,0xB4,0xAA,0x85,0x40,0x60, + 0x20,0x06,0xAB,0x90,0x16,0xAD,0xC5,0xB5, + 0xC9,0x05,0xF0,0x03,0x4C,0x5E,0xB6,0x4C, + 0x92,0xB6,0xEA,0x20,0x69,0xBA,0xA2,0x00, + 0x8E,0xC3,0xB5,0x60,0xA9,0x0B,0xD0,0x0A, + 0xA9,0x0C,0xD0,0x06,0xA9,0x0E,0xD0,0x02, + 0xA9,0x0D,0x8D,0x5C,0xAA,0x20,0xE6,0xBF, + 0xAD,0xB6,0xAA,0xF0,0x04,0xA5,0xD8,0x30, + 0x0E,0xA2,0x00,0x20,0x02,0xA7,0xAE,0x5C, + 0xAA,0x20,0x02,0xA7,0x20,0xC8,0x9F,0x20, + 0x51,0xA8,0x20,0x5E,0xA6,0xAE,0x5C,0xAA, + 0xA9,0x03,0xB0,0x03,0x6C,0x5A,0x9D,0x6C, + 0x5E,0x9D,0xBD,0x3F,0xAA,0xAA,0x8E,0x63, + 0xAA,0xBD,0x71,0xA9,0x48,0x09,0x80,0x20, + 0xC5,0x9F,0xAE,0x63,0xAA,0xE8,0x68,0x10, + 0xED,0x60,0xAD,0x66,0xAA,0x8D,0xBF,0xB5, + 0xAD,0x68,0xAA,0x8D,0xC0,0xB5,0xAD,0x6A, + 0xAA,0x8D,0xC1,0xB5,0xAD,0x06,0x9D,0x8D, + 0xC3,0xB5,0xAD,0x07,0x9D,0x8D,0xC4,0xB5, + 0xA5,0x40,0x8D,0x4F,0xAA,0xA5,0x41,0x8D, + 0x50,0xAA,0x60,0xA0,0x1D,0xB9,0x75,0xAA, + 0x91,0x40,0x88,0x10,0xF8,0x60,0xA0,0x1E, + 0xB1,0x40,0x99,0xA9,0xB5,0xC8,0xC0,0x26, + 0xD0,0xF6,0x60,0xA0,0x00,0x8C,0x51,0xAA, + 0x8C,0x52,0xAA,0x60,0xA9,0x00,0x85,0x45, + 0x20,0x92,0xA7,0x4C,0x73,0xA7,0x20,0x9A, + 0xA7,0xF0,0x1D,0x20,0xAA,0xA7,0xD0,0x0A, + 0xA5,0x40,0x85,0x44,0xA5,0x41,0x85,0x45, + 0xD0,0xEC,0xA0,0x1D,0xB1,0x40,0xD9,0x75, + 0xAA,0xD0,0xE3,0x88,0x10,0xF6,0x18,0x60, + 0x38,0x60,0xAD,0x00,0x9D,0xAE,0x01,0x9D, + 0xD0,0x0A,0xA0,0x25,0xB1,0x40,0xF0,0x09, + 0xAA,0x88,0xB1,0x40,0x86,0x41,0x85,0x40, + 0x8A,0x60,0xA0,0x00,0xB1,0x40,0x60,0xAD, + 0xB3,0xAA,0xF0,0x0E,0xAD,0xB4,0xAA,0xC5, + 0x40,0xD0,0x08,0xAD,0xB5,0xAA,0xC5,0x41, + 0xF0,0x01,0xCA,0x60,0x4D,0xC2,0xB5,0xF0, + 0x0A,0x29,0x7F,0xF0,0x06,0x20,0xEA,0xA2, + 0x4C,0xD0,0xA6,0x60,0x38,0xAD,0x00,0x9D, + 0x85,0x40,0xAD,0x01,0x9D,0x85,0x41,0xAD, + 0x57,0xAA,0x8D,0x63,0xAA,0xA0,0x00,0x98, + 0x91,0x40,0xA0,0x1E,0x38,0xA5,0x40,0xE9, + 0x2D,0x91,0x40,0x48,0xA5,0x41,0xE9,0x00, + 0xC8,0x91,0x40,0xAA,0xCA,0x68,0x48,0xC8, + 0x91,0x40,0x8A,0xC8,0x91,0x40,0xAA,0xCA, + 0x68,0x48,0xC8,0x91,0x40,0xC8,0x8A,0x91, + 0x40,0xCE,0x63,0xAA,0xF0,0x17,0xAA,0x68, + 0x38,0xE9,0x26,0xC8,0x91,0x40,0x48,0x8A, + 0xE9,0x00,0xC8,0x91,0x40,0x85,0x41,0x68, + 0x85,0x40,0x4C,0xE5,0xA7,0x48,0xA9,0x00, + 0xC8,0x91,0x40,0xC8,0x91,0x40,0xAD,0xB6, + 0xAA,0xF0,0x0B,0x68,0x85,0x74,0x85,0x70, + 0x68,0x85,0x73,0x85,0x6F,0x60,0x68,0x85, + 0x4D,0x85,0xCB,0x68,0x85,0x4C,0x85,0xCA, + 0x60,0xA5,0x39,0xCD,0x03,0x9D,0xF0,0x12, + 0x8D,0x56,0xAA,0xA5,0x38,0x8D,0x55,0xAA, + 0xAD,0x02,0x9D,0x85,0x38,0xAD,0x03,0x9D, + 0x85,0x39,0xA5,0x37,0xCD,0x05,0x9D,0xF0, + 0x12,0x8D,0x54,0xAA,0xA5,0x36,0x8D,0x53, + 0xAA,0xAD,0x04,0x9D,0x85,0x36,0xAD,0x05, + 0x9D,0x85,0x37,0x60,0x49,0x4E,0x49,0xD4, + 0x4C,0x4F,0x41,0xC4,0x53,0x41,0x56,0xC5, + 0x52,0x55,0xCE,0x43,0x48,0x41,0x49,0xCE, + 0x44,0x45,0x4C,0x45,0x54,0xC5,0x4C,0x4F, + 0x43,0xCB,0x55,0x4E,0x4C,0x4F,0x43,0xCB, + 0x43,0x4C,0x4F,0x53,0xC5,0x52,0x45,0x41, + 0xC4,0x45,0x58,0x45,0xC3,0x57,0x52,0x49, + 0x54,0xC5,0x50,0x4F,0x53,0x49,0x54,0x49, + 0x4F,0xCE,0x4F,0x50,0x45,0xCE,0x41,0x50, + 0x50,0x45,0x4E,0xC4,0x52,0x45,0x4E,0x41, + 0x4D,0xC5,0x43,0x41,0x54,0x41,0x4C,0x4F, + 0xC7,0x4D,0x4F,0xCE,0x4E,0x4F,0x4D,0x4F, + 0xCE,0x50,0x52,0xA3,0x49,0x4E,0xA3,0x4D, + 0x41,0x58,0x46,0x49,0x4C,0x45,0xD3,0x46, + 0xD0,0x49,0x4E,0xD4,0x42,0x53,0x41,0x56, + 0xC5,0x42,0x4C,0x4F,0x41,0xC4,0x42,0x52, + 0x55,0xCE,0x56,0x45,0x52,0x49,0x46,0xD9, + 0x00,0x21,0x70,0xA0,0x70,0xA1,0x70,0xA0, + 0x70,0x20,0x70,0x20,0x70,0x20,0x70,0x20, + 0x70,0x60,0x00,0x22,0x06,0x20,0x74,0x22, + 0x06,0x22,0x04,0x23,0x78,0x22,0x70,0x30, + 0x70,0x40,0x70,0x40,0x80,0x40,0x80,0x08, + 0x00,0x08,0x00,0x04,0x00,0x40,0x70,0x40, + 0x00,0x21,0x79,0x20,0x71,0x20,0x71,0x20, + 0x70,0xD6,0xC4,0xD3,0xCC,0xD2,0xC2,0xC1, + 0xC3,0xC9,0xCF,0x40,0x20,0x10,0x08,0x04, + 0x02,0x01,0xC0,0xA0,0x90,0x00,0x00,0xFE, + 0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x07, + 0x00,0x01,0x00,0xFF,0x7F,0x00,0x00,0xFF, + 0x7F,0x00,0x00,0xFF,0x7F,0x00,0x00,0xFF, + 0xFF,0x0D,0x07,0x8D,0x4C,0x41,0x4E,0x47, + 0x55,0x41,0x47,0x45,0x20,0x4E,0x4F,0x54, + 0x20,0x41,0x56,0x41,0x49,0x4C,0x41,0x42, + 0x4C,0xC5,0x52,0x41,0x4E,0x47,0x45,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x57,0x52,0x49, + 0x54,0x45,0x20,0x50,0x52,0x4F,0x54,0x45, + 0x43,0x54,0x45,0xC4,0x45,0x4E,0x44,0x20, + 0x4F,0x46,0x20,0x44,0x41,0x54,0xC1,0x46, + 0x49,0x4C,0x45,0x20,0x4E,0x4F,0x54,0x20, + 0x46,0x4F,0x55,0x4E,0xC4,0x56,0x4F,0x4C, + 0x55,0x4D,0x45,0x20,0x4D,0x49,0x53,0x4D, + 0x41,0x54,0x43,0xC8,0x49,0x2F,0x4F,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x44,0x49,0x53, + 0x4B,0x20,0x46,0x55,0x4C,0xCC,0x46,0x49, + 0x4C,0x45,0x20,0x4C,0x4F,0x43,0x4B,0x45, + 0xC4,0x53,0x59,0x4E,0x54,0x41,0x58,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x4E,0x4F,0x20, + 0x42,0x55,0x46,0x46,0x45,0x52,0x53,0x20, + 0x41,0x56,0x41,0x49,0x4C,0x41,0x42,0x4C, + 0xC5,0x46,0x49,0x4C,0x45,0x20,0x54,0x59, + 0x50,0x45,0x20,0x4D,0x49,0x53,0x4D,0x41, + 0x54,0x43,0xC8,0x50,0x52,0x4F,0x47,0x52, + 0x41,0x4D,0x20,0x54,0x4F,0x4F,0x20,0x4C, + 0x41,0x52,0x47,0xC5,0x4E,0x4F,0x54,0x20, + 0x44,0x49,0x52,0x45,0x43,0x54,0x20,0x43, + 0x4F,0x4D,0x4D,0x41,0x4E,0xC4,0x8D,0x00, + 0x03,0x19,0x19,0x24,0x33,0x3E,0x4C,0x5B, + 0x64,0x6D,0x78,0x84,0x98,0xAA,0xBB,0x2D, + 0x98,0x00,0x02,0x02,0xC1,0x1B,0xFD,0x03, + 0x03,0xF0,0x58,0x00,0xA0,0x06,0x00,0x1B, + 0x44,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x10,0x00,0x00,0xCF,0xC1, + 0xC4,0xC5,0xD2,0xAE,0xCF,0xC2,0xCA,0xB0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0x03,0x84,0x00,0x00,0x00,0x40,0x00, + 0xC1,0xD0,0xD0,0xCC,0xC5,0xD3,0xCF,0xC6, + 0xD4,0xE8,0xB7,0xBB,0xB3,0xBB,0xB4,0x00, + 0xC0,0x7E,0xB3,0x21,0xAB,0x05,0xAC,0x57, + 0xAC,0x6F,0xAC,0x2A,0xAD,0x97,0xAD,0xEE, + 0xAC,0xF5,0xAC,0x39,0xAC,0x11,0xAD,0x8D, + 0xAE,0x17,0xAD,0x7E,0xB3,0x7E,0xB3,0x89, + 0xAC,0x95,0xAC,0x86,0xAC,0x92,0xAC,0x7E, + 0xB3,0x7E,0xB3,0xBD,0xAC,0xC9,0xAC,0xBA, + 0xAC,0xC6,0xAC,0x7E,0xB3,0xE0,0x00,0xF0, + 0x02,0xA2,0x02,0x8E,0x5F,0xAA,0xBA,0x8E, + 0x9B,0xB3,0x20,0x6A,0xAE,0xAD,0xBB,0xB5, + 0xC9,0x0D,0xB0,0x0B,0x0A,0xAA,0xBD,0xCA, + 0xAA,0x48,0xBD,0xC9,0xAA,0x48,0x60,0x4C, + 0x63,0xB3,0x20,0x28,0xAB,0x4C,0x7F,0xB3, + 0x20,0xDC,0xAB,0xA9,0x01,0x8D,0xE3,0xB5, + 0xAE,0xBE,0xB5,0xAD,0xBD,0xB5,0xD0,0x05, + 0xE0,0x00,0xD0,0x01,0xE8,0x8D,0xE8,0xB5, + 0x8E,0xE9,0xB5,0x20,0xC9,0xB1,0x90,0x5E, + 0x8E,0x9C,0xB3,0xAE,0x5F,0xAA,0xBD,0x09, + 0xA9,0xAE,0x9C,0xB3,0x4A,0xB0,0x0D,0xAD, + 0x51,0xAA,0xC9,0xC0,0xD0,0x03,0x4C,0x5F, + 0xB3,0x4C,0x73,0xB3,0xA9,0x00,0x9D,0xE8, + 0xB4,0xA9,0x01,0x9D,0xE7,0xB4,0x8E,0x9C, + 0xB3,0x20,0x44,0xB2,0xAE,0x9C,0xB3,0x9D, + 0xC7,0xB4,0x8D,0xD2,0xB5,0x8D,0xD4,0xB5, + 0xAD,0xF1,0xB5,0x9D,0xC6,0xB4,0x8D,0xD1, + 0xB5,0x8D,0xD3,0xB5,0xAD,0xC2,0xB5,0x9D, + 0xC8,0xB4,0x20,0x37,0xB0,0x20,0x0C,0xAF, + 0x20,0xD6,0xB7,0x20,0x3A,0xAF,0xAE,0x9C, + 0xB3,0xA9,0x06,0x8D,0xC5,0xB5,0xBD,0xC6, + 0xB4,0x8D,0xD1,0xB5,0xBD,0xC7,0xB4,0x8D, + 0xD2,0xB5,0xBD,0xC8,0xB4,0x8D,0xC2,0xB5, + 0x8D,0xF6,0xB5,0xBD,0xE7,0xB4,0x8D,0xEE, + 0xB5,0xBD,0xE8,0xB4,0x8D,0xEF,0xB5,0x8E, + 0xD9,0xB5,0xA9,0xFF,0x8D,0xE0,0xB5,0x8D, + 0xE1,0xB5,0xAD,0xE2,0xB3,0x8D,0xDA,0xB5, + 0x18,0x4C,0x5E,0xAF,0xA9,0x00,0xAA,0x9D, + 0xD1,0xB5,0xE8,0xE0,0x2D,0xD0,0xF8,0xAD, + 0xBF,0xB5,0x49,0xFF,0x8D,0xF9,0xB5,0xAD, + 0xC0,0xB5,0x8D,0xF8,0xB5,0xAD,0xC1,0xB5, + 0x0A,0x0A,0x0A,0x0A,0xAA,0x8E,0xF7,0xB5, + 0xA9,0x11,0x8D,0xFA,0xB5,0x60,0x20,0x1D, + 0xAF,0x20,0x34,0xAF,0x20,0xC3,0xB2,0xA9, + 0x02,0x2D,0xD5,0xB5,0xF0,0x21,0x20,0xF7, + 0xAF,0xA9,0x00,0x18,0x20,0x11,0xB0,0x38, + 0xCE,0xD8,0xB5,0xD0,0xF7,0xAE,0xD9,0xB5, + 0xAD,0xEE,0xB5,0x9D,0xE7,0xB4,0xAD,0xEF, + 0xB5,0x9D,0xE8,0xB4,0x20,0x37,0xB0,0x4C, + 0x7F,0xB3,0x20,0x28,0xAB,0xAD,0xF6,0xB5, + 0x30,0x2B,0xAD,0xBD,0xB5,0x85,0x42,0xAD, + 0xBE,0xB5,0x85,0x43,0xAE,0x9C,0xB3,0x20, + 0x1C,0xB2,0x20,0x37,0xB0,0x4C,0x7F,0xB3, + 0xAD,0xBC,0xB5,0xC9,0x05,0xB0,0x0B,0x0A, + 0xAA,0xBD,0xE6,0xAA,0x48,0xBD,0xE5,0xAA, + 0x48,0x60,0x4C,0x67,0xB3,0x4C,0x7B,0xB3, + 0xAD,0xF6,0xB5,0x30,0xF8,0xAD,0xBC,0xB5, + 0xC9,0x05,0xB0,0xEE,0x0A,0xAA,0xBD,0xF2, + 0xAA,0x48,0xBD,0xF1,0xAA,0x48,0x60,0x20, + 0x00,0xB3,0x20,0xA8,0xAC,0x8D,0xC3,0xB5, + 0x4C,0x7F,0xB3,0x20,0x00,0xB3,0x20,0xB5, + 0xB1,0x20,0xA8,0xAC,0x48,0x20,0xA2,0xB1, + 0xA0,0x00,0x68,0x91,0x42,0x4C,0x96,0xAC, + 0x20,0xB6,0xB0,0xB0,0x0B,0xB1,0x42,0x48, + 0x20,0x5B,0xB1,0x20,0x94,0xB1,0x68,0x60, + 0x4C,0x6F,0xB3,0x20,0x00,0xB3,0xAD,0xC3, + 0xB5,0x20,0xDA,0xAC,0x4C,0x7F,0xB3,0x20, + 0x00,0xB3,0x20,0xA2,0xB1,0xA0,0x00,0xB1, + 0x42,0x20,0xDA,0xAC,0x20,0xB5,0xB1,0x4C, + 0xCA,0xAC,0x48,0x20,0xB6,0xB0,0x68,0x91, + 0x42,0xA9,0x40,0x0D,0xD5,0xB5,0x8D,0xD5, + 0xB5,0x20,0x5B,0xB1,0x4C,0x94,0xB1,0xA9, + 0x80,0x8D,0x9E,0xB3,0xD0,0x05,0xA9,0x00, + 0x8D,0x9E,0xB3,0x20,0x28,0xAB,0xAE,0x9C, + 0xB3,0xBD,0xC8,0xB4,0x29,0x7F,0x0D,0x9E, + 0xB3,0x9D,0xC8,0xB4,0x20,0x37,0xB0,0x4C, + 0x7F,0xB3,0x20,0x00,0xB3,0x4C,0x7F,0xB3, + 0x20,0x28,0xAB,0x20,0xB6,0xB0,0xB0,0xEF, + 0xEE,0xE4,0xB5,0xD0,0xF6,0xEE,0xE5,0xB5, + 0x4C,0x1B,0xAD,0x20,0x28,0xAB,0xAE,0x9C, + 0xB3,0xBD,0xC8,0xB4,0x10,0x03,0x4C,0x7B, + 0xB3,0xAE,0x9C,0xB3,0xBD,0xC6,0xB4,0x8D, + 0xD1,0xB5,0x9D,0xE6,0xB4,0xA9,0xFF,0x9D, + 0xC6,0xB4,0xBC,0xC7,0xB4,0x8C,0xD2,0xB5, + 0x20,0x37,0xB0,0x18,0x20,0x5E,0xAF,0xB0, + 0x2A,0x20,0x0C,0xAF,0xA0,0x0C,0x8C,0x9C, + 0xB3,0xB1,0x42,0x30,0x0B,0xF0,0x09,0x48, + 0xC8,0xB1,0x42,0xA8,0x68,0x20,0x89,0xAD, + 0xAC,0x9C,0xB3,0xC8,0xC8,0xD0,0xE7,0xAD, + 0xD3,0xB5,0xAC,0xD4,0xB5,0x20,0x89,0xAD, + 0x38,0xB0,0xD1,0x20,0xFB,0xAF,0x4C,0x7F, + 0xB3,0x38,0x20,0xDD,0xB2,0xA9,0x00,0xA2, + 0x05,0x9D,0xF0,0xB5,0xCA,0x10,0xFA,0x60, + 0x20,0xDC,0xAB,0xA9,0xFF,0x8D,0xF9,0xB5, + 0x20,0xF7,0xAF,0xA9,0x16,0x8D,0x9D,0xB3, + 0x20,0x2F,0xAE,0x20,0x2F,0xAE,0xA2,0x0B, + 0xBD,0xAF,0xB3,0x20,0xED,0xFD,0xCA,0x10, + 0xF7,0x86,0x45,0xAD,0xF6,0xB7,0x85,0x44, + 0x20,0x42,0xAE,0x20,0x2F,0xAE,0x20,0x2F, + 0xAE,0x18,0x20,0x11,0xB0,0xB0,0x5D,0xA2, + 0x00,0x8E,0x9C,0xB3,0xBD,0xC6,0xB4,0xF0, + 0x53,0x30,0x4A,0xA0,0xA0,0xBD,0xC8,0xB4, + 0x10,0x02,0xA0,0xAA,0x98,0x20,0xED,0xFD, + 0xBD,0xC8,0xB4,0x29,0x7F,0xA0,0x07,0x0A, + 0x0A,0xB0,0x03,0x88,0xD0,0xFA,0xB9,0xA7, + 0xB3,0x20,0xED,0xFD,0xA9,0xA0,0x20,0xED, + 0xFD,0xBD,0xE7,0xB4,0x85,0x44,0xBD,0xE8, + 0xB4,0x85,0x45,0x20,0x42,0xAE,0xA9,0xA0, + 0x20,0xED,0xFD,0xE8,0xE8,0xE8,0xA0,0x1D, + 0xBD,0xC6,0xB4,0x20,0xED,0xFD,0xE8,0x88, + 0x10,0xF6,0x20,0x2F,0xAE,0x20,0x30,0xB2, + 0x90,0xA7,0xB0,0x9E,0x4C,0x7F,0xB3,0xA9, + 0x8D,0x20,0xED,0xFD,0xCE,0x9D,0xB3,0xD0, + 0x08,0x20,0x0C,0xFD,0xA9,0x15,0x8D,0x9D, + 0xB3,0x60,0xA0,0x02,0xA9,0x00,0x48,0xA5, + 0x44,0xD9,0xA4,0xB3,0x90,0x12,0xF9,0xA4, + 0xB3,0x85,0x44,0xA5,0x45,0xE9,0x00,0x85, + 0x45,0x68,0x69,0x00,0x48,0x4C,0x47,0xAE, + 0x68,0x09,0xB0,0x20,0xED,0xFD,0x88,0x10, + 0xDB,0x60,0x20,0x08,0xAF,0xA0,0x00,0x8C, + 0xC5,0xB5,0xB1,0x42,0x99,0xD1,0xB5,0xC8, + 0xC0,0x2D,0xD0,0xF6,0x18,0x60,0x20,0x08, + 0xAF,0xA0,0x00,0xB9,0xD1,0xB5,0x91,0x42, + 0xC8,0xC0,0x2D,0xD0,0xF6,0x60,0x20,0xDC, + 0xAB,0xA9,0x04,0x20,0x58,0xB0,0xAD,0xF9, + 0xB5,0x49,0xFF,0x8D,0xC1,0xB3,0xA9,0x11, + 0x8D,0xEB,0xB3,0xA9,0x01,0x8D,0xEC,0xB3, + 0xA2,0x38,0xA9,0x00,0x9D,0xBB,0xB3,0xE8, + 0xD0,0xFA,0xA2,0x0C,0xE0,0x8C,0xF0,0x14, + 0xA0,0x03,0xB9,0xA0,0xB3,0x9D,0xF3,0xB3, + 0xE8,0x88,0x10,0xF6,0xE0,0x44,0xD0,0xEC, + 0xA2,0x48,0xD0,0xE8,0x20,0xFB,0xAF,0xA2, + 0x00,0x8A,0x9D,0xBB,0xB4,0xE8,0xD0,0xFA, + 0x20,0x45,0xB0,0xA9,0x11,0xAC,0xF0,0xB3, + 0x88,0x88,0x8D,0xEC,0xB7,0x8D,0xBC,0xB4, + 0x8C,0xBD,0xB4,0xC8,0x8C,0xED,0xB7,0xA9, + 0x02,0x20,0x58,0xB0,0xAC,0xBD,0xB4,0x88, + 0x30,0x05,0xD0,0xEC,0x98,0xF0,0xE6,0x20, + 0xC2,0xB7,0x20,0x4A,0xB7,0x4C,0x7F,0xB3, + 0xA2,0x00,0xF0,0x06,0xA2,0x02,0xD0,0x02, + 0xA2,0x04,0xBD,0xC7,0xB5,0x85,0x42,0xBD, + 0xC8,0xB5,0x85,0x43,0x60,0x2C,0xD5,0xB5, + 0x70,0x01,0x60,0x20,0xE4,0xAF,0xA9,0x02, + 0x20,0x52,0xB0,0xA9,0xBF,0x2D,0xD5,0xB5, + 0x8D,0xD5,0xB5,0x60,0xAD,0xD5,0xB5,0x30, + 0x01,0x60,0x20,0x4B,0xAF,0xA9,0x02,0x20, + 0x52,0xB0,0xA9,0x7F,0x2D,0xD5,0xB5,0x8D, + 0xD5,0xB5,0x60,0xAD,0xC9,0xB5,0x8D,0xF0, + 0xB7,0xAD,0xCA,0xB5,0x8D,0xF1,0xB7,0xAE, + 0xD3,0xB5,0xAC,0xD4,0xB5,0x60,0x08,0x20, + 0x34,0xAF,0x20,0x4B,0xAF,0x20,0x0C,0xAF, + 0x28,0xB0,0x09,0xAE,0xD1,0xB5,0xAC,0xD2, + 0xB5,0x4C,0xB5,0xAF,0xA0,0x01,0xB1,0x42, + 0xF0,0x08,0xAA,0xC8,0xB1,0x42,0xA8,0x4C, + 0xB5,0xAF,0xAD,0xBB,0xB5,0xC9,0x04,0xF0, + 0x02,0x38,0x60,0x20,0x44,0xB2,0xA0,0x02, + 0x91,0x42,0x48,0x88,0xAD,0xF1,0xB5,0x91, + 0x42,0x48,0x20,0x3A,0xAF,0x20,0xD6,0xB7, + 0xA0,0x05,0xAD,0xDE,0xB5,0x91,0x42,0xC8, + 0xAD,0xDF,0xB5,0x91,0x42,0x68,0xAA,0x68, + 0xA8,0xA9,0x02,0xD0,0x02,0xA9,0x01,0x8E, + 0xD3,0xB5,0x8C,0xD4,0xB5,0x20,0x52,0xB0, + 0xA0,0x05,0xB1,0x42,0x8D,0xDC,0xB5,0x18, + 0x6D,0xDA,0xB5,0x8D,0xDE,0xB5,0xC8,0xB1, + 0x42,0x8D,0xDD,0xB5,0x6D,0xDB,0xB5,0x8D, + 0xDF,0xB5,0x18,0x60,0x20,0xE4,0xAF,0xA9, + 0x01,0x4C,0x52,0xB0,0xAC,0xCB,0xB5,0xAD, + 0xCC,0xB5,0x8C,0xF0,0xB7,0x8D,0xF1,0xB7, + 0xAE,0xD6,0xB5,0xAC,0xD7,0xB5,0x60,0xA9, + 0x01,0xD0,0x02,0xA9,0x02,0xAC,0xC3,0xAA, + 0x8C,0xF0,0xB7,0xAC,0xC4,0xAA,0x8C,0xF1, + 0xB7,0xAE,0xFA,0xB5,0xA0,0x00,0x4C,0x52, + 0xB0,0x08,0x20,0x45,0xB0,0x28,0xB0,0x08, + 0xAC,0xBD,0xB3,0xAE,0xBC,0xB3,0xD0,0x0A, + 0xAE,0xBC,0xB4,0xD0,0x02,0x38,0x60,0xAC, + 0xBD,0xB4,0x8E,0x97,0xB3,0x8C,0x98,0xB3, + 0xA9,0x01,0x20,0x52,0xB0,0x18,0x60,0x20, + 0x45,0xB0,0xAE,0x97,0xB3,0xAC,0x98,0xB3, + 0xA9,0x02,0x4C,0x52,0xB0,0xAD,0xC5,0xAA, + 0x8D,0xF0,0xB7,0xAD,0xC6,0xAA,0x8D,0xF1, + 0xB7,0x60,0x8E,0xEC,0xB7,0x8C,0xED,0xB7, + 0x8D,0xF4,0xB7,0xC9,0x02,0xD0,0x06,0x0D, + 0xD5,0xB5,0x8D,0xD5,0xB5,0xAD,0xF9,0xB5, + 0x49,0xFF,0x8D,0xEB,0xB7,0xAD,0xF7,0xB5, + 0x8D,0xE9,0xB7,0xAD,0xF8,0xB5,0x8D,0xEA, + 0xB7,0xAD,0xE2,0xB5,0x8D,0xF2,0xB7,0xAD, + 0xE3,0xB5,0x8D,0xF3,0xB7,0xA9,0x01,0x8D, + 0xE8,0xB7,0xAC,0xC1,0xAA,0xAD,0xC2,0xAA, + 0x20,0xB5,0xB7,0xAD,0xF6,0xB7,0x8D,0xBF, + 0xB5,0xA9,0xFF,0x8D,0xEB,0xB7,0xB0,0x01, + 0x60,0xAD,0xF5,0xB7,0xA0,0x07,0xC9,0x20, + 0xF0,0x08,0xA0,0x04,0xC9,0x10,0xF0,0x02, + 0xA0,0x08,0x98,0x4C,0x85,0xB3,0xAD,0xE4, + 0xB5,0xCD,0xE0,0xB5,0xD0,0x08,0xAD,0xE5, + 0xB5,0xCD,0xE1,0xB5,0xF0,0x66,0x20,0x1D, + 0xAF,0xAD,0xE5,0xB5,0xCD,0xDD,0xB5,0x90, + 0x1C,0xD0,0x08,0xAD,0xE4,0xB5,0xCD,0xDC, + 0xB5,0x90,0x12,0xAD,0xE5,0xB5,0xCD,0xDF, + 0xB5,0x90,0x10,0xD0,0x08,0xAD,0xE4,0xB5, + 0xCD,0xDE,0xB5,0x90,0x06,0x20,0x5E,0xAF, + 0x90,0xD7,0x60,0x38,0xAD,0xE4,0xB5,0xED, + 0xDC,0xB5,0x0A,0x69,0x0C,0xA8,0x20,0x0C, + 0xAF,0xB1,0x42,0xD0,0x0F,0xAD,0xBB,0xB5, + 0xC9,0x04,0xF0,0x02,0x38,0x60,0x20,0x34, + 0xB1,0x4C,0x20,0xB1,0x8D,0xD6,0xB5,0xC8, + 0xB1,0x42,0x8D,0xD7,0xB5,0x20,0xDC,0xAF, + 0xAD,0xE4,0xB5,0x8D,0xE0,0xB5,0xAD,0xE5, + 0xB5,0x8D,0xE1,0xB5,0x20,0x10,0xAF,0xAC, + 0xE6,0xB5,0x18,0x60,0x8C,0x9D,0xB3,0x20, + 0x44,0xB2,0xAC,0x9D,0xB3,0xC8,0x91,0x42, + 0x8D,0xD7,0xB5,0x88,0xAD,0xF1,0xB5,0x91, + 0x42,0x8D,0xD6,0xB5,0x20,0x10,0xAF,0x20, + 0xD6,0xB7,0xA9,0xC0,0x0D,0xD5,0xB5,0x8D, + 0xD5,0xB5,0x60,0xAE,0xEA,0xB5,0x8E,0xBD, + 0xB5,0xAE,0xEB,0xB5,0x8E,0xBE,0xB5,0xAE, + 0xEC,0xB5,0xAC,0xED,0xB5,0x8E,0xBF,0xB5, + 0x8C,0xC0,0xB5,0xE8,0xD0,0x01,0xC8,0xCC, + 0xE9,0xB5,0xD0,0x11,0xEC,0xE8,0xB5,0xD0, + 0x0C,0xA2,0x00,0xA0,0x00,0xEE,0xEA,0xB5, + 0xD0,0x03,0xEE,0xEB,0xB5,0x8E,0xEC,0xB5, + 0x8C,0xED,0xB5,0x60,0xEE,0xE6,0xB5,0xD0, + 0x08,0xEE,0xE4,0xB5,0xD0,0x03,0xEE,0xE5, + 0xB5,0x60,0xAC,0xC3,0xB5,0xAE,0xC4,0xB5, + 0x84,0x42,0x86,0x43,0xEE,0xC3,0xB5,0xD0, + 0x03,0xEE,0xC4,0xB5,0x60,0xAC,0xC1,0xB5, + 0xD0,0x08,0xAE,0xC2,0xB5,0xF0,0x07,0xCE, + 0xC2,0xB5,0xCE,0xC1,0xB5,0x60,0x4C,0x7F, + 0xB3,0x20,0xF7,0xAF,0xAD,0xC3,0xB5,0x85, + 0x42,0xAD,0xC4,0xB5,0x85,0x43,0xA9,0x01, + 0x8D,0x9D,0xB3,0xA9,0x00,0x8D,0xD8,0xB5, + 0x18,0xEE,0xD8,0xB5,0x20,0x11,0xB0,0xB0, + 0x51,0xA2,0x00,0x8E,0x9C,0xB3,0xBD,0xC6, + 0xB4,0xF0,0x1F,0x30,0x22,0xA0,0x00,0xE8, + 0xE8,0xE8,0xB1,0x42,0xDD,0xC6,0xB4,0xD0, + 0x0A,0xC8,0xC0,0x1E,0xD0,0xF3,0xAE,0x9C, + 0xB3,0x18,0x60,0x20,0x30,0xB2,0x90,0xDB, + 0xB0,0xCF,0xAC,0x9D,0xB3,0xD0,0xC1,0xAC, + 0x9D,0xB3,0xD0,0xEF,0xA0,0x00,0xE8,0xE8, + 0xE8,0xB1,0x42,0x9D,0xC6,0xB4,0xC8,0xC0, + 0x1E,0xD0,0xF5,0xAE,0x9C,0xB3,0x38,0x60, + 0x18,0xAD,0x9C,0xB3,0x69,0x23,0xAA,0xE0, + 0xF5,0x60,0xA9,0x00,0xAC,0x9D,0xB3,0xD0, + 0x97,0x4C,0x77,0xB3,0xAD,0xF1,0xB5,0xF0, + 0x21,0xCE,0xF0,0xB5,0x30,0x17,0x18,0xA2, + 0x04,0x3E,0xF1,0xB5,0xCA,0xD0,0xFA,0x90, + 0xF0,0xEE,0xEE,0xB5,0xD0,0x03,0xEE,0xEF, + 0xB5,0xAD,0xF0,0xB5,0x60,0xA9,0x00,0x8D, + 0xF1,0xB5,0xA9,0x00,0x8D,0x9E,0xB3,0x20, + 0xF7,0xAF,0x18,0xAD,0xEB,0xB3,0x6D,0xEC, + 0xB3,0xF0,0x09,0xCD,0xEF,0xB3,0x90,0x14, + 0xA9,0xFF,0xD0,0x0A,0xAD,0x9E,0xB3,0xD0, + 0x37,0xA9,0x01,0x8D,0x9E,0xB3,0x8D,0xEC, + 0xB3,0x18,0x69,0x11,0x8D,0xEB,0xB3,0x8D, + 0xF1,0xB5,0xA8,0x0A,0x0A,0xA8,0xA2,0x04, + 0x18,0xB9,0xF6,0xB3,0x9D,0xF1,0xB5,0xF0, + 0x06,0x38,0xA9,0x00,0x99,0xF6,0xB3,0x88, + 0xCA,0xD0,0xEE,0x90,0xBD,0x20,0xFB,0xAF, + 0xAD,0xF0,0xB3,0x8D,0xF0,0xB5,0xD0,0x89, + 0x4C,0x77,0xB3,0xAD,0xF1,0xB5,0xD0,0x01, + 0x60,0x48,0x20,0xF7,0xAF,0xAC,0xF0,0xB5, + 0x68,0x18,0x20,0xDD,0xB2,0xA9,0x00,0x8D, + 0xF1,0xB5,0x4C,0xFB,0xAF,0xA2,0xFC,0x7E, + 0xF6,0xB4,0xE8,0xD0,0xFA,0xC8,0xCC,0xF0, + 0xB3,0xD0,0xF2,0x0A,0x0A,0xA8,0xF0,0x0F, + 0xA2,0x04,0xBD,0xF1,0xB5,0x19,0xF6,0xB3, + 0x99,0xF6,0xB3,0x88,0xCA,0xD0,0xF3,0x60, + 0xAD,0xBD,0xB5,0x8D,0xE6,0xB5,0x8D,0xEA, + 0xB5,0xAD,0xBE,0xB5,0x8D,0xE4,0xB5,0x8D, + 0xEB,0xB5,0xA9,0x00,0x8D,0xE5,0xB5,0xA0, + 0x10,0xAA,0xAD,0xE6,0xB5,0x4A,0xB0,0x03, + 0x8A,0x90,0x0E,0x18,0xAD,0xE5,0xB5,0x6D, + 0xE8,0xB5,0x8D,0xE5,0xB5,0x8A,0x6D,0xE9, + 0xB5,0x6A,0x6E,0xE5,0xB5,0x6E,0xE4,0xB5, + 0x6E,0xE6,0xB5,0x88,0xD0,0xDB,0x18,0xAD, + 0xBF,0xB5,0x8D,0xEC,0xB5,0x6D,0xE6,0xB5, + 0x8D,0xE6,0xB5,0xAD,0xC0,0xB5,0x8D,0xED, + 0xB5,0x6D,0xE4,0xB5,0x8D,0xE4,0xB5,0x90, + 0x03,0xEE,0xE5,0xB5,0x60,0x00,0x00,0xA9, + 0x01,0xD0,0x22,0xA9,0x02,0xD0,0x1E,0xA9, + 0x03,0xD0,0x1A,0xA9,0x04,0xD0,0x16,0xA9, + 0x05,0xD0,0x12,0xA9,0x06,0xD0,0x0E,0x4C, + 0xED,0xBF,0xEA,0xA9,0x0A,0xD0,0x06,0xAD, + 0xC5,0xB5,0x18,0x90,0x01,0x38,0x08,0x8D, + 0xC5,0xB5,0xA9,0x00,0x85,0x48,0x20,0x7E, + 0xAE,0x28,0xAE,0x9B,0xB3,0x9A,0x60,0x11, + 0x0F,0x00,0x00,0xEC,0x46,0x01,0x00,0x00, + 0x00,0x00,0xFF,0xFF,0x01,0x0A,0x64,0xD4, + 0xC9,0xC1,0xC2,0xD3,0xD2,0xC1,0xC2,0xA0, + 0xC5,0xCD,0xD5,0xCC,0xCF,0xD6,0xA0,0xCB, + 0xD3,0xC9,0xC4,0x04,0x11,0x0F,0x03,0x00, + 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7A,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x13,0x01,0x00,0x00,0x23, + 0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07, + 0xFF,0x00,0x00,0x3F,0xFF,0x00,0x00,0x3F, + 0xFF,0x00,0x00,0x00,0x7F,0x00,0x00,0xFF, + 0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF, + 0xFF,0x00,0x00,0x0F,0xFF,0x00,0x00,0x01, + 0xFF,0x00,0x00,0x1F,0xFF,0x00,0x00,0x00, + 0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x03, + 0xFF,0x00,0x00,0x1F,0xFF,0x00,0x00,0x03, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x1F,0xFF,0x00,0x00,0x00, + 0x7F,0x00,0x00,0x00,0x7F,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x7F,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x03,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x0E,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x0F, + 0x82,0xC8,0xC5,0xCC,0xCC,0xCF,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0x03, + 0x00,0x14,0x0F,0x81,0xC1,0xD0,0xD0,0xCC, + 0xC5,0xD3,0xCF,0xC6,0xD4,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0x03,0x00,0x15,0x0F,0x84,0xCC, + 0xCF,0xC1,0xC4,0xC5,0xD2,0xAE,0xCF,0xC2, + 0xCA,0xB0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0x06,0x00,0x16, + 0x0F,0x84,0xC6,0xD0,0xC2,0xC1,0xD3,0xC9, + 0xC3,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0x2A,0x00,0x17,0x0F,0x84,0xC9,0xCE,0xD4, + 0xC2,0xC1,0xD3,0xC9,0xC3,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0x2A,0x00,0x18,0x0F,0x82, + 0xCD,0xC1,0xD3,0xD4,0xC5,0xD2,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0x03,0x00, + 0x19,0x0F,0x84,0xCD,0xC1,0xD3,0xD4,0xC5, + 0xD2,0xA0,0xC3,0xD2,0xC5,0xC1,0xD4,0xC5, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0x09,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x15,0x0F,0x15,0x0F,0x00,0x15,0x0A, + 0x01,0x46,0x7A,0x00,0x00,0x00,0x7A,0x00, + 0x04,0x00,0x00,0x01,0x04,0x00,0x48,0x00, + 0x01,0x00,0x48,0x04,0x00,0x00,0x06,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x60, + 0x01,0xFF,0x11,0x00,0x00,0x00,0xFD,0xFE, + 0x01,0xA5,0x27,0xC9,0x09,0xD0,0x18,0xA5, + 0x2B,0x4A,0x4A,0x4A,0x4A,0x09,0xC0,0x85, + 0x3F,0xA9,0x5C,0x85,0x3E,0x18,0xAD,0xFE, + 0x08,0x6D,0xFF,0x08,0x8D,0xFE,0x08,0xAE, + 0xFF,0x08,0x30,0x15,0xBD,0x4D,0x08,0x85, + 0x3D,0xCE,0xFF,0x08,0xAD,0xFE,0x08,0x85, + 0x27,0xCE,0xFE,0x08,0xA6,0x2B,0x6C,0x3E, + 0x00,0xEE,0xFE,0x08,0xEE,0xFE,0x08,0x20, + 0x89,0xFE,0x20,0x93,0xFE,0x20,0x2F,0xFB, + 0xA6,0x2B,0x6C,0xFD,0x08,0x00,0x0D,0x0B, + 0x09,0x07,0x05,0x03,0x01,0x0E,0x0C,0x0A, + 0x08,0x06,0x04,0x02,0x0F,0x00,0x20,0x64, + 0xA7,0xB0,0x08,0xA9,0x00,0xA8,0x8D,0x5D, + 0xB6,0x91,0x40,0xAD,0xC5,0xB5,0x4C,0xD2, + 0xA6,0xAD,0x5D,0xB6,0xF0,0x08,0xEE,0xBD, + 0xB5,0xD0,0x03,0xEE,0xBE,0xB5,0xA9,0x00, + 0x8D,0x5D,0xB6,0x4C,0x84,0xBA,0x8D,0xBC, + 0xB5,0x20,0xA8,0xA6,0x20,0xEA,0xA2,0x4C, + 0x7D,0xA2,0xA0,0x13,0xB1,0x42,0xD0,0x14, + 0xC8,0xC0,0x17,0xD0,0xF7,0xA0,0x19,0xB1, + 0x42,0x99,0xA4,0xB5,0xC8,0xC0,0x1D,0xD0, + 0xF6,0x4C,0xBB,0xA6,0xA2,0xFF,0x8E,0x5D, + 0xB6,0xD0,0xF6,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x09, + 0x8E,0xE9,0xB7,0x8E,0xF7,0xB7,0xA9,0x01, + 0x8D,0xF8,0xB7,0x8D,0xEA,0xB7,0xAD,0xE0, + 0xB7,0x8D,0xE1,0xB7,0xA9,0x02,0x8D,0xEC, + 0xB7,0xA9,0x04,0x8D,0xED,0xB7,0xAC,0xE7, + 0xB7,0x88,0x8C,0xF1,0xB7,0xA9,0x01,0x8D, + 0xF4,0xB7,0x8A,0x4A,0x4A,0x4A,0x4A,0xAA, + 0xA9,0x00,0x9D,0xF8,0x04,0x9D,0x78,0x04, + 0x20,0x93,0xB7,0xA2,0xFF,0x9A,0x8E,0xEB, + 0xB7,0x4C,0xC8,0xBF,0x20,0x89,0xFE,0x4C, + 0x84,0x9D,0xAD,0xE7,0xB7,0x38,0xED,0xF1, + 0xB7,0x8D,0xE1,0xB7,0xAD,0xE7,0xB7,0x8D, + 0xF1,0xB7,0xCE,0xF1,0xB7,0xA9,0x02,0x8D, + 0xEC,0xB7,0xA9,0x04,0x8D,0xED,0xB7,0xA9, + 0x02,0x8D,0xF4,0xB7,0x20,0x93,0xB7,0xAD, + 0xE7,0xB7,0x8D,0xFE,0xB6,0x18,0x69,0x09, + 0x8D,0xF1,0xB7,0xA9,0x0A,0x8D,0xE1,0xB7, + 0x38,0xE9,0x01,0x8D,0xFF,0xB6,0x8D,0xED, + 0xB7,0x20,0x93,0xB7,0x60,0x00,0x00,0x00, + 0x00,0x00,0x00,0xAD,0xE5,0xB7,0xAC,0xE4, + 0xB7,0x20,0xB5,0xB7,0xAC,0xED,0xB7,0x88, + 0x10,0x07,0xA0,0x0F,0xEA,0xEA,0xCE,0xEC, + 0xB7,0x8C,0xED,0xB7,0xCE,0xF1,0xB7,0xCE, + 0xE1,0xB7,0xD0,0xDF,0x60,0x08,0x78,0x20, + 0x00,0xBD,0xB0,0x03,0x28,0x18,0x60,0x28, + 0x38,0x60,0xAD,0xBC,0xB5,0x8D,0xF1,0xB7, + 0xA9,0x00,0x8D,0xF0,0xB7,0xAD,0xF9,0xB5, + 0x49,0xFF,0x8D,0xEB,0xB7,0x60,0xA9,0x00, + 0xA8,0x91,0x42,0xC8,0xD0,0xFB,0x60,0x00, + 0x1B,0x00,0x0A,0x1B,0xE8,0xB7,0x00,0xB6, + 0x01,0x60,0x01,0xFF,0x15,0x0A,0xFB,0xB7, + 0x00,0x96,0x00,0x01,0x01,0x00,0xFE,0x60, + 0x01,0x00,0x00,0x00,0x01,0xEF,0xD8,0x00, + 0xA2,0x00,0xA0,0x02,0x88,0xB1,0x3E,0x4A, + 0x3E,0x00,0xBC,0x4A,0x3E,0x00,0xBC,0x99, + 0x00,0xBB,0xE8,0xE0,0x56,0x90,0xED,0xA2, + 0x00,0x98,0xD0,0xE8,0xA2,0x55,0xBD,0x00, + 0xBC,0x29,0x3F,0x9D,0x00,0xBC,0xCA,0x10, + 0xF5,0x60,0x38,0x86,0x27,0x8E,0x78,0x06, + 0xBD,0x8D,0xC0,0xBD,0x8E,0xC0,0x30,0x7C, + 0xAD,0x00,0xBC,0x85,0x26,0xA9,0xFF,0x9D, + 0x8F,0xC0,0x1D,0x8C,0xC0,0x48,0x68,0xEA, + 0xA0,0x04,0x48,0x68,0x20,0xB9,0xB8,0x88, + 0xD0,0xF8,0xA9,0xD5,0x20,0xB8,0xB8,0xA9, + 0xAA,0x20,0xB8,0xB8,0xA9,0xAD,0x20,0xB8, + 0xB8,0x98,0xA0,0x56,0xD0,0x03,0xB9,0x00, + 0xBC,0x59,0xFF,0xBB,0xAA,0xBD,0x29,0xBA, + 0xA6,0x27,0x9D,0x8D,0xC0,0xBD,0x8C,0xC0, + 0x88,0xD0,0xEB,0xA5,0x26,0xEA,0x59,0x00, + 0xBB,0xAA,0xBD,0x29,0xBA,0xAE,0x78,0x06, + 0x9D,0x8D,0xC0,0xBD,0x8C,0xC0,0xB9,0x00, + 0xBB,0xC8,0xD0,0xEA,0xAA,0xBD,0x29,0xBA, + 0xA6,0x27,0x20,0xBB,0xB8,0xA9,0xDE,0x20, + 0xB8,0xB8,0xA9,0xAA,0x20,0xB8,0xB8,0xA9, + 0xEB,0x20,0xB8,0xB8,0xA9,0xFF,0x20,0xB8, + 0xB8,0xBD,0x8E,0xC0,0xBD,0x8C,0xC0,0x60, + 0x18,0x48,0x68,0x9D,0x8D,0xC0,0x1D,0x8C, + 0xC0,0x60,0xA0,0x00,0xA2,0x56,0xCA,0x30, + 0xFB,0xB9,0x00,0xBB,0x5E,0x00,0xBC,0x2A, + 0x5E,0x00,0xBC,0x2A,0x91,0x3E,0xC8,0xC4, + 0x26,0xD0,0xEB,0x60,0xA0,0x20,0x88,0xF0, + 0x61,0xBD,0x8C,0xC0,0x10,0xFB,0x49,0xD5, + 0xD0,0xF4,0xEA,0xBD,0x8C,0xC0,0x10,0xFB, + 0xC9,0xAA,0xD0,0xF2,0xA0,0x56,0xBD,0x8C, + 0xC0,0x10,0xFB,0xC9,0xAD,0xD0,0xE7,0xA9, + 0x00,0x88,0x84,0x26,0xBC,0x8C,0xC0,0x10, + 0xFB,0x59,0x00,0xBA,0xA4,0x26,0x99,0x00, + 0xBC,0xD0,0xEE,0x84,0x26,0xBC,0x8C,0xC0, + 0x10,0xFB,0x59,0x00,0xBA,0xA4,0x26,0x99, + 0x00,0xBB,0xC8,0xD0,0xEE,0xBC,0x8C,0xC0, + 0x10,0xFB,0xD9,0x00,0xBA,0xD0,0x13,0xBD, + 0x8C,0xC0,0x10,0xFB,0xC9,0xDE,0xD0,0x0A, + 0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA, + 0xF0,0x5C,0x38,0x60,0xA0,0xFC,0x84,0x26, + 0xC8,0xD0,0x04,0xE6,0x26,0xF0,0xF3,0xBD, + 0x8C,0xC0,0x10,0xFB,0xC9,0xD5,0xD0,0xF0, + 0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA, + 0xD0,0xF2,0xA0,0x03,0xBD,0x8C,0xC0,0x10, + 0xFB,0xC9,0x96,0xD0,0xE7,0xA9,0x00,0x85, + 0x27,0xBD,0x8C,0xC0,0x10,0xFB,0x2A,0x85, + 0x26,0xBD,0x8C,0xC0,0x10,0xFB,0x25,0x26, + 0x99,0x2C,0x00,0x45,0x27,0x88,0x10,0xE7, + 0xA8,0xD0,0xB7,0xBD,0x8C,0xC0,0x10,0xFB, + 0xC9,0xDE,0xD0,0xAE,0xEA,0xBD,0x8C,0xC0, + 0x10,0xFB,0xC9,0xAA,0xD0,0xA4,0x18,0x60, + 0x86,0x2B,0x85,0x2A,0xCD,0x78,0x04,0xF0, + 0x53,0xA9,0x00,0x85,0x26,0xAD,0x78,0x04, + 0x85,0x27,0x38,0xE5,0x2A,0xF0,0x33,0xB0, + 0x07,0x49,0xFF,0xEE,0x78,0x04,0x90,0x05, + 0x69,0xFE,0xCE,0x78,0x04,0xC5,0x26,0x90, + 0x02,0xA5,0x26,0xC9,0x0C,0xB0,0x01,0xA8, + 0x38,0x20,0xEE,0xB9,0xB9,0x11,0xBA,0x20, + 0x00,0xBA,0xA5,0x27,0x18,0x20,0xF1,0xB9, + 0xB9,0x1D,0xBA,0x20,0x00,0xBA,0xE6,0x26, + 0xD0,0xC3,0x20,0x00,0xBA,0x18,0xAD,0x78, + 0x04,0x29,0x03,0x2A,0x05,0x2B,0xAA,0xBD, + 0x80,0xC0,0xA6,0x2B,0x60,0x00,0x00,0x00, + 0xA2,0x11,0xCA,0xD0,0xFD,0xE6,0x46,0xD0, + 0x02,0xE6,0x47,0x38,0xE9,0x01,0xD0,0xF0, + 0x60,0x01,0x30,0x28,0x24,0x20,0x1E,0x1D, + 0x1C,0x1C,0x1C,0x1C,0x1C,0x70,0x2C,0x26, + 0x22,0x1F,0x1E,0x1D,0x1C,0x1C,0x1C,0x1C, + 0x1C,0x96,0x97,0x9A,0x9B,0x9D,0x9E,0x9F, + 0xA6,0xA7,0xAB,0xAC,0xAD,0xAE,0xAF,0xB2, + 0xB3,0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB, + 0xBC,0xBD,0xBE,0xBF,0xCB,0xCD,0xCE,0xCF, + 0xD3,0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDD, + 0xDE,0xDF,0xE5,0xE6,0xE7,0xE9,0xEA,0xEB, + 0xEC,0xED,0xEE,0xEF,0xF2,0xF3,0xF4,0xF5, + 0xF6,0xF7,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE, + 0xFF,0xAE,0x5F,0xAA,0xE0,0x1C,0xF0,0x05, + 0xA2,0x00,0x8E,0x5D,0xB6,0x60,0xA9,0xFF, + 0x8D,0xFB,0x04,0x8D,0x0C,0xC0,0x8D,0x0E, + 0xC0,0x4C,0x2F,0xFB,0xAD,0xBD,0xB5,0x8D, + 0xE6,0xB5,0x8D,0xEA,0xB5,0xBA,0x8E,0x9B, + 0xB3,0x4C,0x7F,0xB3,0x00,0x00,0x00,0x01, + 0x98,0x99,0x02,0x03,0x9C,0x04,0x05,0x06, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0x07,0x08, + 0xA8,0xA9,0xAA,0x09,0x0A,0x0B,0x0C,0x0D, + 0xB0,0xB1,0x0E,0x0F,0x10,0x11,0x12,0x13, + 0xB8,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0x1B,0xCC,0x1C,0x1D,0x1E, + 0xD0,0xD1,0xD2,0x1F,0xD4,0xD5,0x20,0x21, + 0xD8,0x22,0x23,0x24,0x25,0x26,0x27,0x28, + 0xE0,0xE1,0xE2,0xE3,0xE4,0x29,0x2A,0x2B, + 0xE8,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, + 0xF0,0xF1,0x33,0x34,0x35,0x36,0x37,0x38, + 0xF8,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x3A,0x20,0x12,0x34, + 0x05,0x29,0x0F,0x29,0x0F,0x34,0x00,0x22, + 0x32,0x22,0x06,0x39,0x0E,0x21,0x0F,0x04, + 0x00,0x32,0x26,0x39,0x0E,0x34,0x1A,0x29, + 0x0B,0x2E,0x0F,0x00,0x24,0x0E,0x22,0x04, + 0x3E,0x08,0x06,0x3F,0x08,0x06,0x3F,0x08, + 0x34,0x3E,0x08,0x14,0x3E,0x21,0x0E,0x21, + 0x0E,0x13,0x25,0x3D,0x08,0x2F,0x3F,0x29, + 0x0D,0x08,0x29,0x3F,0x21,0x0D,0x28,0x05, + 0x22,0x0C,0x12,0x36,0x33,0x3F,0x34,0x3E, + 0x30,0x05,0x34,0x3A,0x29,0x0C,0x28,0x00, + 0x31,0x0D,0x08,0x00,0x3F,0x13,0x25,0x3D, + 0x29,0x0F,0x08,0x23,0x3E,0x2A,0x2F,0x00, + 0x3E,0x31,0x10,0x34,0x04,0x2F,0x30,0x3E, + 0x31,0x10,0x34,0x03,0x29,0x11,0x29,0x0B, + 0x30,0x27,0x3C,0x22,0x31,0x0B,0x3C,0x27, + 0x31,0x0F,0x34,0x37,0x39,0x11,0x31,0x0D, + 0x3C,0x35,0x29,0x0D,0x26,0x2A,0x08,0x12, + 0x3E,0x2A,0x37,0x08,0x3B,0x3F,0x08,0x0E, + 0x3F,0x2A,0x28,0x21,0x0C,0x08,0x19,0x3F, + 0x08,0x31,0x3F,0x2B,0x00,0x00,0x32,0x28, + 0x3C,0x04,0x32,0x32,0x29,0x3C,0x24,0x22, + 0x08,0x29,0x3F,0x32,0x24,0x34,0x35,0x22, + 0x3C,0x34,0x08,0x1E,0x3F,0x2A,0x00,0x21, + 0x0F,0x08,0x0D,0x3D,0x02,0x3A,0x2F,0x32, + 0x30,0x24,0x30,0x02,0x02,0x28,0x01,0x02, + 0x09,0x10,0x09,0x10,0x32,0x04,0x3E,0x31, + 0x0F,0x3C,0x3D,0x04,0x39,0x28,0x01,0x08, + 0x0D,0x3D,0x21,0x0D,0x37,0x2D,0x3E,0x34, + 0x04,0x08,0x0D,0x3D,0x37,0x2E,0x3E,0x3C, + 0x03,0x2F,0x2E,0x3E,0x3C,0x01,0x32,0x29, + 0x3C,0x00,0x29,0x0D,0x06,0x22,0x09,0x11, + 0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0xBD, + 0x8D,0xC0,0xBD,0x8E,0xC0,0x30,0x5E,0xA9, + 0xFF,0x9D,0x8F,0xC0,0xDD,0x8C,0xC0,0x48, + 0x68,0x20,0xC3,0xBC,0x20,0xC3,0xBC,0x9D, + 0x8D,0xC0,0xDD,0x8C,0xC0,0xEA,0x88,0xD0, + 0xF0,0xA9,0xD5,0x20,0xD5,0xBC,0xA9,0xAA, + 0x20,0xD5,0xBC,0xA9,0x96,0x20,0xD5,0xBC, + 0xA5,0x41,0x20,0xC4,0xBC,0xA5,0x44,0x20, + 0xC4,0xBC,0xA5,0x3F,0x20,0xC4,0xBC,0xA5, + 0x41,0x45,0x44,0x45,0x3F,0x48,0x4A,0x05, + 0x3E,0x9D,0x8D,0xC0,0xBD,0x8C,0xC0,0x68, + 0x09,0xAA,0x20,0xD4,0xBC,0xA9,0xDE,0x20, + 0xD5,0xBC,0xA9,0xAA,0x20,0xD5,0xBC,0xA9, + 0xEB,0x20,0xD5,0xBC,0x18,0xBD,0x8E,0xC0, + 0xBD,0x8C,0xC0,0x60,0x48,0x4A,0x05,0x3E, + 0x9D,0x8D,0xC0,0xDD,0x8C,0xC0,0x68,0xEA, + 0xEA,0xEA,0x09,0xAA,0xEA,0xEA,0x48,0x68, + 0x9D,0x8D,0xC0,0xDD,0x8C,0xC0,0x60,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x84,0x48,0x85,0x49,0xA0,0x02,0x8C,0xF8, + 0x06,0xA0,0x04,0x8C,0xF8,0x04,0xA0,0x01, + 0xB1,0x48,0xAA,0xA0,0x0F,0xD1,0x48,0xF0, + 0x1B,0x8A,0x48,0xB1,0x48,0xAA,0x68,0x48, + 0x91,0x48,0xBD,0x8E,0xC0,0xA0,0x08,0xBD, + 0x8C,0xC0,0xDD,0x8C,0xC0,0xD0,0xF6,0x88, + 0xD0,0xF8,0x68,0xAA,0xBD,0x8E,0xC0,0xBD, + 0x8C,0xC0,0xA0,0x08,0xBD,0x8C,0xC0,0x48, + 0x68,0x48,0x68,0x8E,0xF8,0x05,0xDD,0x8C, + 0xC0,0xD0,0x03,0x88,0xD0,0xEE,0x08,0xBD, + 0x89,0xC0,0xA0,0x06,0xB1,0x48,0x99,0x36, + 0x00,0xC8,0xC0,0x0A,0xD0,0xF6,0xA0,0x03, + 0xB1,0x3C,0x85,0x47,0xA0,0x02,0xB1,0x48, + 0xA0,0x10,0xD1,0x48,0xF0,0x06,0x91,0x48, + 0x28,0xA0,0x00,0x08,0x6A,0x90,0x05,0xBD, + 0x8A,0xC0,0xB0,0x03,0xBD,0x8B,0xC0,0x66, + 0x35,0x28,0x08,0xD0,0x0B,0xA0,0x07,0x20, + 0x00,0xBA,0x88,0xD0,0xFA,0xAE,0xF8,0x05, + 0xA0,0x04,0xB1,0x48,0x20,0x5A,0xBE,0x28, + 0xD0,0x11,0xA4,0x47,0x10,0x0D,0xA0,0x12, + 0x88,0xD0,0xFD,0xE6,0x46,0xD0,0xF7,0xE6, + 0x47,0xD0,0xF3,0xA0,0x0C,0xB1,0x48,0xF0, + 0x5A,0xC9,0x04,0xF0,0x58,0x6A,0x08,0xB0, + 0x03,0x20,0x00,0xB8,0xA0,0x30,0x8C,0x78, + 0x05,0xAE,0xF8,0x05,0x20,0x44,0xB9,0x90, + 0x24,0xCE,0x78,0x05,0x10,0xF3,0xAD,0x78, + 0x04,0x48,0xA9,0x60,0x20,0x95,0xBE,0xCE, + 0xF8,0x06,0xF0,0x28,0xA9,0x04,0x8D,0xF8, + 0x04,0xA9,0x00,0x20,0x5A,0xBE,0x68,0x20, + 0x5A,0xBE,0x4C,0xBC,0xBD,0xA4,0x2E,0xCC, + 0x78,0x04,0xF0,0x1C,0xAD,0x78,0x04,0x48, + 0x98,0x20,0x95,0xBE,0x68,0xCE,0xF8,0x04, + 0xD0,0xE5,0xF0,0xCA,0x68,0xA9,0x40,0x28, + 0x4C,0x48,0xBE,0xF0,0x39,0x4C,0xAF,0xBE, + 0xA0,0x03,0xB1,0x48,0x48,0xA5,0x2F,0xA0, + 0x0E,0x91,0x48,0x68,0xF0,0x08,0xC5,0x2F, + 0xF0,0x04,0xA9,0x20,0xD0,0xE1,0xA0,0x05, + 0xB1,0x48,0xA8,0xB9,0xB8,0xBF,0xC5,0x2D, + 0xD0,0x97,0x28,0x90,0x1C,0x20,0xDC,0xB8, + 0x08,0xB0,0x8E,0x28,0xA2,0x00,0x86,0x26, + 0x20,0xC2,0xB8,0xAE,0xF8,0x05,0x18,0x24, + 0x38,0xA0,0x0D,0x91,0x48,0xBD,0x88,0xC0, + 0x60,0x20,0x2A,0xB8,0x90,0xF0,0xA9,0x10, + 0xB0,0xEE,0x48,0xA0,0x01,0xB1,0x3C,0x6A, + 0x68,0x90,0x08,0x0A,0x20,0x6B,0xBE,0x4E, + 0x78,0x04,0x60,0x85,0x2A,0x20,0x8E,0xBE, + 0xB9,0x78,0x04,0x24,0x35,0x30,0x03,0xB9, + 0xF8,0x04,0x8D,0x78,0x04,0xA5,0x2A,0x24, + 0x35,0x30,0x05,0x99,0xF8,0x04,0x10,0x03, + 0x99,0x78,0x04,0x4C,0xA0,0xB9,0x8A,0x4A, + 0x4A,0x4A,0x4A,0xA8,0x60,0x48,0xA0,0x02, + 0xB1,0x48,0x6A,0x66,0x35,0x20,0x8E,0xBE, + 0x68,0x0A,0x24,0x35,0x30,0x05,0x99,0xF8, + 0x04,0x10,0x03,0x99,0x78,0x04,0x60,0xA0, + 0x03,0xB1,0x48,0x85,0x41,0xA9,0xAA,0x85, + 0x3E,0xA0,0x56,0xA9,0x00,0x85,0x44,0x99, + 0xFF,0xBB,0x88,0xD0,0xFA,0x99,0x00,0xBB, + 0x88,0xD0,0xFA,0xA9,0x50,0x20,0x95,0xBE, + 0xA9,0x28,0x85,0x45,0xA5,0x44,0x20,0x5A, + 0xBE,0x20,0x0D,0xBF,0xA9,0x08,0xB0,0x24, + 0xA9,0x30,0x8D,0x78,0x05,0x38,0xCE,0x78, + 0x05,0xF0,0x19,0x20,0x44,0xB9,0xB0,0xF5, + 0xA5,0x2D,0xD0,0xF1,0x20,0xDC,0xB8,0xB0, + 0xEC,0xE6,0x44,0xA5,0x44,0xC9,0x23,0x90, + 0xD3,0x18,0x90,0x05,0xA0,0x0D,0x91,0x48, + 0x38,0xBD,0x88,0xC0,0x60,0xA9,0x00,0x85, + 0x3F,0xA0,0x80,0xD0,0x02,0xA4,0x45,0x20, + 0x56,0xBC,0xB0,0x6B,0x20,0x2A,0xB8,0xB0, + 0x66,0xE6,0x3F,0xA5,0x3F,0xC9,0x10,0x90, + 0xEC,0xA0,0x0F,0x84,0x3F,0xA9,0x30,0x8D, + 0x78,0x05,0x99,0xA8,0xBF,0x88,0x10,0xFA, + 0xA4,0x45,0x20,0x87,0xBF,0x20,0x87,0xBF, + 0x20,0x87,0xBF,0x48,0x68,0xEA,0x88,0xD0, + 0xF1,0x20,0x44,0xB9,0xB0,0x23,0xA5,0x2D, + 0xF0,0x15,0xA9,0x10,0xC5,0x45,0xA5,0x45, + 0xE9,0x01,0x85,0x45,0xC9,0x05,0xB0,0x11, + 0x38,0x60,0x20,0x44,0xB9,0xB0,0x05,0x20, + 0xDC,0xB8,0x90,0x1C,0xCE,0x78,0x05,0xD0, + 0xF1,0x20,0x44,0xB9,0xB0,0x0B,0xA5,0x2D, + 0xC9,0x0F,0xD0,0x05,0x20,0xDC,0xB8,0x90, + 0x8C,0xCE,0x78,0x05,0xD0,0xEB,0x38,0x60, + 0xA4,0x2D,0xB9,0xA8,0xBF,0x30,0xDD,0xA9, + 0xFF,0x99,0xA8,0xBF,0xC6,0x3F,0x10,0xCA, + 0xA5,0x44,0xD0,0x0A,0xA5,0x45,0xC9,0x10, + 0x90,0xE5,0xC6,0x45,0xC6,0x45,0x18,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0D,0x0B,0x09,0x07,0x05,0x03,0x01, + 0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x0F, + 0x20,0x93,0xFE,0xAD,0x81,0xC0,0xAD,0x81, + 0xC0,0xA9,0x00,0x8D,0x00,0xE0,0x20,0x76, + 0xBA,0x4C,0x44,0xB7,0x8D,0x63,0xAA,0x8D, + 0x70,0xAA,0x8D,0x71,0xAA,0x60,0x20,0x5B, + 0xA7,0x8C,0xB7,0xAA,0x60,0x20,0x7E,0xAE, + 0xAE,0x9B,0xB3,0x9A,0x20,0x16,0xA3,0xBA, + 0x8E,0x9B,0xB3,0xA9,0x09,0x4C,0x85 +}; diff --git a/c2t.h.0 b/c2t.h.0 new file mode 100644 index 0000000..ddba13d --- /dev/null +++ b/c2t.h.0 @@ -0,0 +1,72 @@ + +const char *usagetext="\n\ +usage: c2t [-vh?]\n\ + c2t [-elp] input[.mon],[addr] ... [output.mon]\n\ + c2t {-1} [-cepr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]]\n\ + c2t {-2} [-abcdef8pmqr] input[.mon],[addr] ... [output.[aif[f]|wav[e]]]\n\ + c2t [-n8] input.dsk ... [output.[aif[f]|wav[e]]]\n\ +\n\ + -1 or -2 for Apple I or II tape format\n\ + -8 use 48k/8bit 8000 bps transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -f and -d.\n\ + -a assembly autoload and run (Apple II/II+/IIe 64K only)\n\ + -b basic autoload and run (Apple II+/IIe 64K only)\n\ + Implies -2a.\n\ + -c compress data\n\ + -d use fast 44.1k/16bit transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -f and -8. Use for burning CDs.\n\ + -e pad with $00 to end on page boundary\n\ + -f use faster 48k/8bit (9600 bps) transfer (Apple II/II+/IIe 64K only)\n\ + Implies -2a. Negates -8 and -d. Unreliable on some systems.\n\ + -h|? this help\n\ + -l long monitor format (24 bytes/line)\n\ + -m jump to monitor after autoload\n\ + -n do not format disks\n\ + -p pipe to stdout\n\ + -q parameters and data only (for use with custom client)\n\ + -r #, where # overrides the sample rate (e.g. -r 48000)\n\ + Negates -a, -b, -8, -q, -f, and -d\n\ + -t 10 second preamble (default 4) for real tape use\n\ + -v print version number and exit\n\ +\n\ +input(s) without a .mon or .dsk extension is assumed to be a binary with a 4\n\ +byte header. If the header is missing then you must append ,load_address to\n\ +each binary input missing a header, e.g. filename,800. The load address\n\ +will be read as hex.\n\ +\n\ +input(s) with a .mon extension expected input format:\n\ +\n\ + 0280: A2 FF 9A 20 8C 02 20 4F\n\ + 0288: 03 4C 00 FF 20 9E 02 A9\n\ +\n\ +A single input with a .dsk extension expected to be a 140K disk image.\n\ +\n\ +output must have aiff, aif, wav, wave, or mon extention.\n\ +\n\ +Examples:\n\ +\n\ + c2t hello hello.mon\n\ + c2t -p hello.mon print.mon foo,800 | pbcopy\n\ + c2t -2f moon.patrol,801 moon.patrol.aif\n\ + c2t -2 hello,300 hello.aiff\n\ + c2t -1 hello.mon hello.wav\n\ + c2t -2 thief,801 thief.obj,3ffd thief.pic,2000 theif.aif\n\ + c2t foo.dsk foo.wav\n\ +\n\ +"; + +//51 00 D5, 3 byte header, LENGTH LSB/MSB, D5 for autorun +unsigned char header[] = {0x00,0x00,0xD5}; + + +/* +basic program "CALL 2060", 801.80B + +end of code[LSB,MSB] = 0x0B,0x08 +line #[LSB,MSG] = 0x01,0x00 +CALL[1] = 0x8C +vec[4] = 0x32,0x30,0x36,0x30 (2060 in ASCII) +end[2] = 0x00,0x00 +*/ +unsigned char basic[] = {0x0B,0x08,0x01,0x00,0x8C,0x32,0x30,0x36,0x30,0x00,0x00}; + diff --git a/c2t.h.2 b/c2t.h.2 new file mode 100644 index 0000000..d69d6d8 --- /dev/null +++ b/c2t.h.2 @@ -0,0 +1,1134 @@ + +//DOS 3.3 +unsigned char dosboot1[] = { + 0x4C,0xBF,0x9D,0x4C,0x84,0x9D,0x4C,0xFD, + 0xAA,0x4C,0xB5,0xB7,0xAD,0x0F,0x9D,0xAC, + 0x0E,0x9D,0x60,0xAD,0xC2,0xAA,0xAC,0xC1, + 0xAA,0x60,0x4C,0x51,0xA8,0xEA,0xEA,0x4C, + 0x59,0xFA,0xBF,0x9D,0x38,0x4C,0x58,0xFF, + 0x4C,0x65,0xFF,0x4C,0x65,0xFF,0x65,0xFF, +}; + +//DOS 3.3 +unsigned char dosboot2[] = { + 0xD3,0x9C,0x81,0x9E,0xBD,0x9E,0x75,0xAA, + 0x93,0xAA,0x60,0xAA,0x00,0x9D,0xBB,0xB5, + 0xEA,0x9E,0x11,0x9F,0x22,0x9F,0x2E,0x9F, + 0x51,0x9F,0x60,0x9F,0x70,0x9F,0x4E,0xA5, + 0x12,0xA4,0x96,0xA3,0xD0,0xA4,0xEF,0xA4, + 0x62,0xA2,0x70,0xA2,0x74,0xA2,0xE9,0xA2, + 0x1A,0xA5,0xC5,0xA5,0x0F,0xA5,0xDC,0xA5, + 0xA2,0xA2,0x97,0xA2,0x80,0xA2,0x6D,0xA5, + 0x32,0xA2,0x3C,0xA2,0x28,0xA2,0x2D,0xA2, + 0x50,0xA2,0x79,0xA5,0x9D,0xA5,0x30,0xA3, + 0x5C,0xA3,0x8D,0xA3,0x7C,0xA2,0xFC,0xA4, + 0xFC,0xA4,0x65,0xD8,0x00,0xE0,0x3C,0xD4, + 0xF2,0xD4,0x36,0xE8,0xE5,0xA4,0xE3,0xE3, + 0x00,0xE0,0x03,0xE0,0xFC,0xA4,0xFC,0xA4, + 0x65,0xD8,0x00,0xE0,0x3C,0xD4,0xF2,0xD4, + 0x06,0xA5,0x06,0xA5,0x67,0x10,0x84,0x9D, + 0x3C,0x0C,0xF2,0x0C,0xAD,0xE9,0xB7,0x4A, + 0x4A,0x4A,0x4A,0x8D,0x6A,0xAA,0xAD,0xEA, + 0xB7,0x8D,0x68,0xAA,0xAD,0x00,0xE0,0x49, + 0x20,0xD0,0x11,0x8D,0xB6,0xAA,0xA2,0x0A, + 0xBD,0x61,0x9D,0x9D,0x55,0x9D,0xCA,0xD0, + 0xF7,0x4C,0xBC,0x9D,0xA9,0x40,0x8D,0xB6, + 0xAA,0xA2,0x0C,0xBD,0x6B,0x9D,0x9D,0x55, + 0x9D,0xCA,0xD0,0xF7,0x38,0xB0,0x12,0xAD, + 0xB6,0xAA,0xD0,0x04,0xA9,0x20,0xD0,0x05, + 0x0A,0x10,0x05,0xA9,0x4C,0x20,0xB2,0xA5, + 0x18,0x08,0x20,0x51,0xA8,0xA9,0x00,0x8D, + 0x5E,0xAA,0x8D,0x52,0xAA,0x28,0x6A,0x8D, + 0x51,0xAA,0x30,0x03,0x6C,0x5E,0x9D,0x6C, + 0x5C,0x9D,0x0A,0x10,0x19,0x8D,0xB6,0xAA, + 0xA2,0x0C,0xBD,0x77,0x9D,0x9D,0x55,0x9D, + 0xCA,0xD0,0xF7,0xA2,0x1D,0xBD,0x93,0xAA, + 0x9D,0x75,0xAA,0xCA,0x10,0xF7,0xAD,0xB1, + 0xAA,0x8D,0x57,0xAA,0x20,0xD4,0xA7,0xAD, + 0xB3,0xAA,0xF0,0x09,0x48,0x20,0x9D,0xA6, + 0x68,0xA0,0x00,0x91,0x40,0x20,0x5B,0xA7, + 0xAD,0x5F,0xAA,0xD0,0x20,0xA2,0x2F,0xBD, + 0x51,0x9E,0x9D,0xD0,0x03,0xCA,0x10,0xF7, + 0xAD,0x53,0x9E,0x8D,0xF3,0x03,0x49,0xA5, + 0x8D,0xF4,0x03,0xAD,0x52,0x9E,0x8D,0xF2, + 0x03,0xA9,0x06,0xD0,0x05,0xAD,0x62,0xAA, + 0xF0,0x06,0x8D,0x5F,0xAA,0x4C,0x80,0xA1, + 0x60,0x4C,0xBF,0x9D,0x4C,0x84,0x9D,0x4C, + 0xFD,0xAA,0x4C,0xB5,0xB7,0xAD,0x0F,0x9D, + 0xAC,0x0E,0x9D,0x60,0xAD,0xC2,0xAA,0xAC, + 0xC1,0xAA,0x60,0x4C,0x51,0xA8,0xEA,0xEA, + 0x4C,0x59,0xFA,0x4C,0x65,0xFF,0x4C,0x58, + 0xFF,0x4C,0x65,0xFF,0x4C,0x65,0xFF,0x65, + 0xFF,0x20,0xD1,0x9E,0xAD,0x51,0xAA,0xF0, + 0x15,0x48,0xAD,0x5C,0xAA,0x91,0x28,0x68, + 0x30,0x03,0x4C,0x26,0xA6,0x20,0xEA,0x9D, + 0xA4,0x24,0xA9,0x60,0x91,0x28,0xAD,0xB3, + 0xAA,0xF0,0x03,0x20,0x82,0xA6,0xA9,0x03, + 0x8D,0x52,0xAA,0x20,0xBA,0x9F,0x20,0xBA, + 0x9E,0x8D,0x5C,0xAA,0x8E,0x5A,0xAA,0x4C, + 0xB3,0x9F,0x6C,0x38,0x00,0x20,0xD1,0x9E, + 0xAD,0x52,0xAA,0x0A,0xAA,0xBD,0x11,0x9D, + 0x48,0xBD,0x10,0x9D,0x48,0xAD,0x5C,0xAA, + 0x60,0x8D,0x5C,0xAA,0x8E,0x5A,0xAA,0x8C, + 0x5B,0xAA,0xBA,0xE8,0xE8,0x8E,0x59,0xAA, + 0xA2,0x03,0xBD,0x53,0xAA,0x95,0x36,0xCA, + 0x10,0xF8,0x60,0xAE,0xB7,0xAA,0xF0,0x03, + 0x4C,0x78,0x9F,0xAE,0x51,0xAA,0xF0,0x08, + 0xC9,0xBF,0xF0,0x75,0xC5,0x33,0xF0,0x27, + 0xA2,0x02,0x8E,0x52,0xAA,0xCD,0xB2,0xAA, + 0xD0,0x19,0xCA,0x8E,0x52,0xAA,0xCA,0x8E, + 0x5D,0xAA,0xAE,0x5D,0xAA,0x9D,0x00,0x02, + 0xE8,0x8E,0x5D,0xAA,0xC9,0x8D,0xD0,0x75, + 0x4C,0xCD,0x9F,0xC9,0x8D,0xD0,0x7D,0xA2, + 0x00,0x8E,0x52,0xAA,0x4C,0xA4,0x9F,0xA2, + 0x00,0x8E,0x52,0xAA,0xC9,0x8D,0xF0,0x07, + 0xAD,0xB3,0xAA,0xF0,0x67,0xD0,0x5E,0x48, + 0x38,0xAD,0xB3,0xAA,0xD0,0x03,0x20,0x5E, + 0xA6,0x68,0x90,0xEC,0xAE,0x5A,0xAA,0x4C, + 0x15,0x9F,0xC9,0x8D,0xD0,0x05,0xA9,0x05, + 0x8D,0x52,0xAA,0x20,0x0E,0xA6,0x4C,0x99, + 0x9F,0xCD,0xB2,0xAA,0xF0,0x85,0xC9,0x8A, + 0xF0,0xF1,0xA2,0x04,0x8E,0x52,0xAA,0xD0, + 0xE1,0xA9,0x00,0x8D,0x52,0xAA,0xF0,0x25, + 0xA9,0x00,0x8D,0xB7,0xAA,0x20,0x51,0xA8, + 0x4C,0xDC,0xA4,0xAD,0x00,0x02,0xCD,0xB2, + 0xAA,0xF0,0x0A,0xA9,0x8D,0x8D,0x00,0x02, + 0xA2,0x00,0x8E,0x5A,0xAA,0xA9,0x40,0xD0, + 0x06,0xA9,0x10,0xD0,0x02,0xA9,0x20,0x2D, + 0x5E,0xAA,0xF0,0x0F,0x20,0xBA,0x9F,0x20, + 0xC5,0x9F,0x8D,0x5C,0xAA,0x8C,0x5B,0xAA, + 0x8E,0x5A,0xAA,0x20,0x51,0xA8,0xAE,0x59, + 0xAA,0x9A,0xAD,0x5C,0xAA,0xAC,0x5B,0xAA, + 0xAE,0x5A,0xAA,0x38,0x60,0x6C,0x36,0x00, + 0xA9,0x8D,0x4C,0xC5,0x9F,0xA0,0xFF,0x8C, + 0x5F,0xAA,0xC8,0x8C,0x62,0xAA,0xEE,0x5F, + 0xAA,0xA2,0x00,0x08,0xBD,0x00,0x02,0xCD, + 0xB2,0xAA,0xD0,0x01,0xE8,0x8E,0x5D,0xAA, + 0x20,0xA4,0xA1,0x29,0x7F,0x59,0x84,0xA8, + 0xC8,0x0A,0xF0,0x02,0x68,0x08,0x90,0xF0, + 0x28,0xF0,0x20,0xB9,0x84,0xA8,0xD0,0xD6, + 0xAD,0x00,0x02,0xCD,0xB2,0xAA,0xF0,0x03, + 0x4C,0xA4,0x9F,0xAD,0x01,0x02,0xC9,0x8D, + 0xD0,0x06,0x20,0x5B,0xA7,0x4C,0x95,0x9F, + 0x4C,0xC4,0xA6,0x0E,0x5F,0xAA,0xAC,0x5F, + 0xAA,0x20,0x5E,0xA6,0x90,0x0C,0xA9,0x02, + 0x39,0x09,0xA9,0xF0,0x05,0xA9,0x0F,0x4C, + 0xD2,0xA6,0xC0,0x06,0xD0,0x02,0x84,0x33, + 0xA9,0x20,0x39,0x09,0xA9,0xF0,0x61,0x20, + 0x95,0xA0,0x08,0x20,0xA4,0xA1,0xF0,0x1E, + 0x0A,0x90,0x05,0x30,0x03,0x4C,0x00,0xA0, + 0x6A,0x4C,0x59,0xA0,0x20,0x93,0xA1,0xF0, + 0x0D,0x99,0x75,0xAA,0xC8,0xC0,0x3C,0x90, + 0xF3,0x20,0x93,0xA1,0xD0,0xFB,0x28,0xD0, + 0x0F,0xAC,0x5F,0xAA,0xA9,0x10,0x39,0x09, + 0xA9,0xF0,0x0C,0xA0,0x1E,0x08,0xD0,0xCB, + 0xAD,0x93,0xAA,0xC9,0xA0,0xF0,0x13,0xAD, + 0x75,0xAA,0xC9,0xA0,0xD0,0x4B,0xAC,0x5F, + 0xAA,0xA9,0xC0,0x39,0x09,0xA9,0xF0,0x02, + 0x10,0x3F,0x4C,0x00,0xA0,0xA0,0x3C,0xA9, + 0xA0,0x99,0x74,0xAA,0x88,0xD0,0xFA,0x60, + 0x8D,0x75,0xAA,0xA9,0x0C,0x39,0x09,0xA9, + 0xF0,0x27,0x20,0xB9,0xA1,0xB0,0x1F,0xA8, + 0xD0,0x17,0xE0,0x11,0xB0,0x13,0xAC,0x5F, + 0xAA,0xA9,0x08,0x39,0x09,0xA9,0xF0,0x06, + 0xE0,0x08,0xB0,0xCE,0x90,0x0B,0x8A,0xD0, + 0x08,0xA9,0x02,0x4C,0xD2,0xA6,0x4C,0xC4, + 0xA6,0xA9,0x00,0x8D,0x65,0xAA,0x8D,0x74, + 0xAA,0x8D,0x66,0xAA,0x8D,0x6C,0xAA,0x8D, + 0x6D,0xAA,0x20,0xDC,0xBF,0xAD,0x5D,0xAA, + 0x20,0xA4,0xA1,0xD0,0x1F,0xC9,0x8D,0xD0, + 0xF7,0xAE,0x5F,0xAA,0xAD,0x65,0xAA,0x1D, + 0x0A,0xA9,0x5D,0x0A,0xA9,0xD0,0x93,0xAE, + 0x63,0xAA,0xF0,0x76,0x8D,0x63,0xAA,0x8E, + 0x5D,0xAA,0xD0,0xDC,0xA2,0x0A,0xDD,0x40, + 0xA9,0xF0,0x05,0xCA,0xD0,0xF8,0xF0,0xB6, + 0xBD,0x4A,0xA9,0x30,0x47,0x0D,0x65,0xAA, + 0x8D,0x65,0xAA,0xCA,0x8E,0x64,0xAA,0x20, + 0xB9,0xA1,0xB0,0xA2,0xAD,0x64,0xAA,0x0A, + 0x0A,0xA8,0xA5,0x45,0xD0,0x09,0xA5,0x44, + 0xD9,0x55,0xA9,0x90,0x8C,0xA5,0x45,0xD9, + 0x58,0xA9,0x90,0x0B,0xD0,0x83,0xA5,0x44, + 0xD9,0x57,0xA9,0x90,0x02,0xD0,0xF5,0xAD, + 0x63,0xAA,0xD0,0x94,0x98,0x4A,0xA8,0xA5, + 0x45,0x99,0x67,0xAA,0xA5,0x44,0x99,0x66, + 0xAA,0x4C,0xE8,0xA0,0x48,0xA9,0x80,0x0D, + 0x65,0xAA,0x8D,0x65,0xAA,0x68,0x29,0x7F, + 0x0D,0x74,0xAA,0x8D,0x74,0xAA,0xD0,0xE9, + 0xF0,0x9C,0x20,0x80,0xA1,0x4C,0x83,0x9F, + 0x20,0x5B,0xA7,0x20,0xAE,0xA1,0xAD,0x5F, + 0xAA,0xAA,0xBD,0x1F,0x9D,0x48,0xBD,0x1E, + 0x9D,0x48,0x60,0xAE,0x5D,0xAA,0xBD,0x00, + 0x02,0xC9,0x8D,0xF0,0x06,0xE8,0x8E,0x5D, + 0xAA,0xC9,0xAC,0x60,0x20,0x93,0xA1,0xF0, + 0xFA,0xC9,0xA0,0xF0,0xF7,0x60,0xA9,0x00, + 0xA0,0x16,0x99,0xBA,0xB5,0x88,0xD0,0xFA, + 0x60,0xA9,0x00,0x85,0x44,0x85,0x45,0x20, + 0xA4,0xA1,0x08,0xC9,0xA4,0xF0,0x3C,0x28, + 0x4C,0xCE,0xA1,0x20,0xA4,0xA1,0xD0,0x06, + 0xA6,0x44,0xA5,0x45,0x18,0x60,0x38,0xE9, + 0xB0,0x30,0x21,0xC9,0x0A,0xB0,0x1D,0x20, + 0xFE,0xA1,0x65,0x44,0xAA,0xA9,0x00,0x65, + 0x45,0xA8,0x20,0xFE,0xA1,0x20,0xFE,0xA1, + 0x8A,0x65,0x44,0x85,0x44,0x98,0x65,0x45, + 0x85,0x45,0x90,0xCF,0x38,0x60,0x06,0x44, + 0x26,0x45,0x60,0x28,0x20,0xA4,0xA1,0xF0, + 0xC5,0x38,0xE9,0xB0,0x30,0xEE,0xC9,0x0A, + 0x90,0x08,0xE9,0x07,0x30,0xE6,0xC9,0x10, + 0xB0,0xE2,0xA2,0x04,0x20,0xFE,0xA1,0xCA, + 0xD0,0xFA,0x05,0x44,0x85,0x44,0x4C,0x04, + 0xA2,0xA5,0x44,0x4C,0x95,0xFE,0xA5,0x44, + 0x4C,0x8B,0xFE,0xAD,0x5E,0xAA,0x0D,0x74, + 0xAA,0x8D,0x5E,0xAA,0x60,0x2C,0x74,0xAA, + 0x50,0x03,0x20,0xC8,0x9F,0xA9,0x70,0x4D, + 0x74,0xAA,0x2D,0x5E,0xAA,0x8D,0x5E,0xAA, + 0x60,0xA9,0x00,0x8D,0xB3,0xAA,0xA5,0x44, + 0x48,0x20,0x16,0xA3,0x68,0x8D,0x57,0xAA, + 0x4C,0xD4,0xA7,0xA9,0x05,0x20,0xAA,0xA2, + 0x20,0x64,0xA7,0xA0,0x00,0x98,0x91,0x40, + 0x60,0xA9,0x07,0xD0,0x02,0xA9,0x08,0x20, + 0xAA,0xA2,0x4C,0xEA,0xA2,0xA9,0x0C,0xD0, + 0xF6,0xAD,0x08,0x9D,0x8D,0xBD,0xB5,0xAD, + 0x09,0x9D,0x8D,0xBE,0xB5,0xA9,0x09,0x8D, + 0x63,0xAA,0x20,0xC8,0xA2,0x4C,0xEA,0xA2, + 0x20,0xA3,0xA2,0x20,0x8C,0xA6,0xD0,0xFB, + 0x4C,0x71,0xB6,0xA9,0x00,0x4C,0xD5,0xA3, + 0xA9,0x01,0x8D,0x63,0xAA,0xAD,0x6C,0xAA, + 0xD0,0x0A,0xAD,0x6D,0xAA,0xD0,0x05,0xA9, + 0x01,0x8D,0x6C,0xAA,0xAD,0x6C,0xAA,0x8D, + 0xBD,0xB5,0xAD,0x6D,0xAA,0x8D,0xBE,0xB5, + 0x20,0xEA,0xA2,0xA5,0x45,0xD0,0x03,0x4C, + 0xC8,0xA6,0x85,0x41,0xA5,0x44,0x85,0x40, + 0x20,0x43,0xA7,0x20,0x4E,0xA7,0x20,0x1A, + 0xA7,0xAD,0x63,0xAA,0x8D,0xBB,0xB5,0x4C, + 0xA8,0xA6,0xAD,0x75,0xAA,0xC9,0xA0,0xF0, + 0x25,0x20,0x64,0xA7,0xB0,0x3A,0x20,0xFC, + 0xA2,0x4C,0xEA,0xA2,0x20,0xAF,0xA7,0xD0, + 0x05,0xA9,0x00,0x8D,0xB3,0xAA,0xA0,0x00, + 0x98,0x91,0x40,0x20,0x4E,0xA7,0xA9,0x02, + 0x8D,0xBB,0xB5,0x4C,0xA8,0xA6,0x20,0x92, + 0xA7,0xD0,0x05,0x20,0x9A,0xA7,0xF0,0x10, + 0x20,0xAF,0xA7,0xF0,0xF6,0x20,0xAA,0xA7, + 0xF0,0xF1,0x20,0xFC,0xA2,0x4C,0x16,0xA3, + 0x60,0xA9,0x09,0x2D,0x65,0xAA,0xC9,0x09, + 0xF0,0x03,0x4C,0x00,0xA0,0xA9,0x04,0x20, + 0xD5,0xA3,0xAD,0x73,0xAA,0xAC,0x72,0xAA, + 0x20,0xE0,0xA3,0xAD,0x6D,0xAA,0xAC,0x6C, + 0xAA,0x20,0xE0,0xA3,0xAD,0x73,0xAA,0xAC, + 0x72,0xAA,0x4C,0xFF,0xA3,0x20,0xA8,0xA2, + 0xA9,0x7F,0x2D,0xC2,0xB5,0xC9,0x04,0xF0, + 0x03,0x4C,0xD0,0xA6,0xA9,0x04,0x20,0xD5, + 0xA3,0x20,0x7A,0xA4,0xAA,0xAD,0x65,0xAA, + 0x29,0x01,0xD0,0x06,0x8E,0x72,0xAA,0x8C, + 0x73,0xAA,0x20,0x7A,0xA4,0xAE,0x72,0xAA, + 0xAC,0x73,0xAA,0x4C,0x71,0xA4,0x20,0x5D, + 0xA3,0x20,0x51,0xA8,0x6C,0x72,0xAA,0xAD, + 0xB6,0xAA,0xF0,0x20,0xA5,0xD6,0x10,0x03, + 0x4C,0xCC,0xA6,0xA9,0x02,0x20,0xD5,0xA3, + 0x38,0xA5,0xAF,0xE5,0x67,0xA8,0xA5,0xB0, + 0xE5,0x68,0x20,0xE0,0xA3,0xA5,0x68,0xA4, + 0x67,0x4C,0xFF,0xA3,0xA9,0x01,0x20,0xD5, + 0xA3,0x38,0xA5,0x4C,0xE5,0xCA,0xA8,0xA5, + 0x4D,0xE5,0xCB,0x20,0xE0,0xA3,0xA5,0xCB, + 0xA4,0xCA,0x4C,0xFF,0xA3,0x8D,0xC2,0xB5, + 0x48,0x20,0xA8,0xA2,0x68,0x4C,0xC4,0xA7, + 0x8C,0xC1,0xB5,0x8C,0xC3,0xB5,0x8D,0xC2, + 0xB5,0xA9,0x04,0x8D,0xBB,0xB5,0xA9,0x01, + 0x8D,0xBC,0xB5,0x20,0xA8,0xA6,0xAD,0xC2, + 0xB5,0x8D,0xC3,0xB5,0x4C,0xA8,0xA6,0x8C, + 0xC3,0xB5,0x8D,0xC4,0xB5,0xA9,0x02,0x4C, + 0x86,0xB6,0x20,0xA8,0xA6,0x4C,0xEA,0xA2, + 0x4C,0xD0,0xA6,0x20,0x16,0xA3,0x20,0xA8, + 0xA2,0xA9,0x23,0x2D,0xC2,0xB5,0xF0,0xF0, + 0x8D,0xC2,0xB5,0xAD,0xB6,0xAA,0xF0,0x28, + 0xA9,0x02,0x20,0xB1,0xA4,0x20,0x7A,0xA4, + 0x18,0x65,0x67,0xAA,0x98,0x65,0x68,0xC5, + 0x74,0xB0,0x70,0x85,0xB0,0x85,0x6A,0x86, + 0xAF,0x86,0x69,0xA6,0x67,0xA4,0x68,0x20, + 0x71,0xA4,0x20,0x51,0xA8,0x6C,0x60,0x9D, + 0xA9,0x01,0x20,0xB1,0xA4,0x20,0x7A,0xA4, + 0x38,0xA5,0x4C,0xED,0x60,0xAA,0xAA,0xA5, + 0x4D,0xED,0x61,0xAA,0x90,0x45,0xA8,0xC4, + 0x4B,0x90,0x40,0xF0,0x3E,0x84,0xCB,0x86, + 0xCA,0x8E,0xC3,0xB5,0x8C,0xC4,0xB5,0x4C, + 0x0A,0xA4,0xAD,0x0A,0x9D,0x8D,0xC3,0xB5, + 0xAD,0x0B,0x9D,0x8D,0xC4,0xB5,0xA9,0x00, + 0x8D,0xC2,0xB5,0xA9,0x02,0x8D,0xC1,0xB5, + 0xA9,0x03,0x8D,0xBB,0xB5,0xA9,0x02,0x8D, + 0xBC,0xB5,0x20,0xA8,0xA6,0xAD,0x61,0xAA, + 0x8D,0xC2,0xB5,0xA8,0xAD,0x60,0xAA,0x8D, + 0xC1,0xB5,0x60,0x20,0xEA,0xA2,0x4C,0xCC, + 0xA6,0xCD,0xC2,0xB5,0xF0,0x1A,0xAE,0x5F, + 0xAA,0x8E,0x62,0xAA,0x4A,0xF0,0x03,0x4C, + 0x9E,0xA5,0xA2,0x1D,0xBD,0x75,0xAA,0x9D, + 0x93,0xAA,0xCA,0x10,0xF7,0x4C,0x7A,0xA5, + 0x60,0xAD,0xB6,0xAA,0xF0,0x03,0x8D,0xB7, + 0xAA,0x20,0x13,0xA4,0x20,0xC8,0x9F,0x20, + 0x51,0xA8,0x6C,0x58,0x9D,0xA5,0x4A,0x85, + 0xCC,0xA5,0x4B,0x85,0xCD,0x6C,0x56,0x9D, + 0x20,0x16,0xA4,0x20,0xC8,0x9F,0x20,0x51, + 0xA8,0x6C,0x56,0x9D,0x20,0x65,0xD6,0x85, + 0x33,0x85,0xD8,0x4C,0xD2,0xD7,0x20,0x65, + 0x0E,0x85,0x33,0x85,0xD8,0x4C,0xD4,0x0F, + 0x20,0x26,0xA5,0xA9,0x05,0x8D,0x52,0xAA, + 0x4C,0x83,0x9F,0x20,0x26,0xA5,0xA9,0x01, + 0x8D,0x51,0xAA,0x4C,0x83,0x9F,0x20,0x64, + 0xA7,0x90,0x06,0x20,0xA3,0xA2,0x4C,0x34, + 0xA5,0x20,0x4E,0xA7,0xAD,0x65,0xAA,0x29, + 0x06,0xF0,0x13,0xA2,0x03,0xBD,0x6E,0xAA, + 0x9D,0xBD,0xB5,0xCA,0x10,0xF7,0xA9,0x0A, + 0x8D,0xBB,0xB5,0x20,0xA8,0xA6,0x60,0xA9, + 0x40,0x2D,0x65,0xAA,0xF0,0x05,0xAD,0x66, + 0xAA,0xD0,0x05,0xA9,0xFE,0x8D,0x66,0xAA, + 0xAD,0x0D,0x9D,0x8D,0xBC,0xB5,0xA9,0x0B, + 0x20,0xAA,0xA2,0x4C,0x97,0xA3,0xA9,0x06, + 0x20,0xAA,0xA2,0xAD,0xBF,0xB5,0x8D,0x66, + 0xAA,0x60,0xA9,0x4C,0x20,0xB2,0xA5,0xF0, + 0x2E,0xA9,0x00,0x8D,0xB6,0xAA,0xA0,0x1E, + 0x20,0x97,0xA0,0xA2,0x09,0xBD,0xB7,0xAA, + 0x9D,0x74,0xAA,0xCA,0xD0,0xF7,0xA9,0xC0, + 0x8D,0x51,0xAA,0x4C,0xD1,0xA4,0xA9,0x20, + 0x20,0xB2,0xA5,0xF0,0x05,0xA9,0x01,0x4C, + 0xD2,0xA6,0xA9,0x00,0x8D,0xB7,0xAA,0x4C, + 0x84,0x9D,0xCD,0x00,0xE0,0xF0,0x0E,0x8D, + 0x80,0xC0,0xCD,0x00,0xE0,0xF0,0x06,0x8D, + 0x81,0xC0,0xCD,0x00,0xE0,0x60,0x20,0xA3, + 0xA2,0xAD,0x4F,0xAA,0x8D,0xB4,0xAA,0xAD, + 0x50,0xAA,0x8D,0xB5,0xAA,0xAD,0x75,0xAA, + 0x8D,0xB3,0xAA,0xD0,0x0E,0x20,0x64,0xA7, + 0x90,0x06,0x20,0xA3,0xA2,0x4C,0xEB,0xA5, + 0x20,0x4E,0xA7,0xAD,0x65,0xAA,0x29,0x04, + 0xF0,0x1B,0xAD,0x6E,0xAA,0xD0,0x08,0xAE, + 0x6F,0xAA,0xF0,0x11,0xCE,0x6F,0xAA,0xCE, + 0x6E,0xAA,0x20,0x8C,0xA6,0xF0,0x38,0xC9, + 0x8D,0xD0,0xF7,0xF0,0xE5,0x60,0x20,0x5E, + 0xA6,0xB0,0x66,0xAD,0x5C,0xAA,0x8D,0xC3, + 0xB5,0xA9,0x04,0x8D,0xBB,0xB5,0xA9,0x01, + 0x8D,0xBC,0xB5,0x4C,0xA8,0xA6,0x20,0x5E, + 0xA6,0xB0,0x4E,0xA9,0x06,0x8D,0x52,0xAA, + 0x20,0x8C,0xA6,0xD0,0x0F,0x20,0xFC,0xA2, + 0xA9,0x03,0xCD,0x52,0xAA,0xF0,0xCE,0xA9, + 0x05,0x4C,0xD2,0xA6,0xC9,0xE0,0x90,0x02, + 0x29,0x7F,0x8D,0x5C,0xAA,0xAE,0x5A,0xAA, + 0xF0,0x09,0xCA,0xBD,0x00,0x02,0x09,0x80, + 0x9D,0x00,0x02,0x4C,0xB3,0x9F,0x48,0xAD, + 0xB6,0xAA,0xF0,0x0E,0xA6,0x76,0xE8,0xF0, + 0x0D,0xA6,0x33,0xE0,0xDD,0xF0,0x07,0x68, + 0x18,0x60,0xA5,0xD9,0x30,0xF9,0x68,0x38, + 0x60,0x20,0xFC,0xA2,0x20,0x5B,0xA7,0x4C, + 0xB3,0x9F,0x20,0x9D,0xA6,0x20,0x4E,0xA7, + 0xA9,0x03,0xD0,0xA1,0xA9,0x03,0x8D,0xBB, + 0xB5,0xA9,0x01,0x8D,0xBC,0xB5,0x20,0xA8, + 0xA6,0xAD,0xC3,0xB5,0x60,0xAD,0xB5,0xAA, + 0x85,0x41,0xAD,0xB4,0xAA,0x85,0x40,0x60, + 0x20,0x06,0xAB,0x90,0x16,0xAD,0xC5,0xB5, + 0xC9,0x05,0xF0,0x03,0x4C,0x5E,0xB6,0x4C, + 0x92,0xB6,0xEA,0x20,0x69,0xBA,0xA2,0x00, + 0x8E,0xC3,0xB5,0x60,0xA9,0x0B,0xD0,0x0A, + 0xA9,0x0C,0xD0,0x06,0xA9,0x0E,0xD0,0x02, + 0xA9,0x0D,0x8D,0x5C,0xAA,0x20,0xE6,0xBF, + 0xAD,0xB6,0xAA,0xF0,0x04,0xA5,0xD8,0x30, + 0x0E,0xA2,0x00,0x20,0x02,0xA7,0xAE,0x5C, + 0xAA,0x20,0x02,0xA7,0x20,0xC8,0x9F,0x20, + 0x51,0xA8,0x20,0x5E,0xA6,0xAE,0x5C,0xAA, + 0xA9,0x03,0xB0,0x03,0x6C,0x5A,0x9D,0x6C, + 0x5E,0x9D,0xBD,0x3F,0xAA,0xAA,0x8E,0x63, + 0xAA,0xBD,0x71,0xA9,0x48,0x09,0x80,0x20, + 0xC5,0x9F,0xAE,0x63,0xAA,0xE8,0x68,0x10, + 0xED,0x60,0xAD,0x66,0xAA,0x8D,0xBF,0xB5, + 0xAD,0x68,0xAA,0x8D,0xC0,0xB5,0xAD,0x6A, + 0xAA,0x8D,0xC1,0xB5,0xAD,0x06,0x9D,0x8D, + 0xC3,0xB5,0xAD,0x07,0x9D,0x8D,0xC4,0xB5, + 0xA5,0x40,0x8D,0x4F,0xAA,0xA5,0x41,0x8D, + 0x50,0xAA,0x60,0xA0,0x1D,0xB9,0x75,0xAA, + 0x91,0x40,0x88,0x10,0xF8,0x60,0xA0,0x1E, + 0xB1,0x40,0x99,0xA9,0xB5,0xC8,0xC0,0x26, + 0xD0,0xF6,0x60,0xA0,0x00,0x8C,0x51,0xAA, + 0x8C,0x52,0xAA,0x60,0xA9,0x00,0x85,0x45, + 0x20,0x92,0xA7,0x4C,0x73,0xA7,0x20,0x9A, + 0xA7,0xF0,0x1D,0x20,0xAA,0xA7,0xD0,0x0A, + 0xA5,0x40,0x85,0x44,0xA5,0x41,0x85,0x45, + 0xD0,0xEC,0xA0,0x1D,0xB1,0x40,0xD9,0x75, + 0xAA,0xD0,0xE3,0x88,0x10,0xF6,0x18,0x60, + 0x38,0x60,0xAD,0x00,0x9D,0xAE,0x01,0x9D, + 0xD0,0x0A,0xA0,0x25,0xB1,0x40,0xF0,0x09, + 0xAA,0x88,0xB1,0x40,0x86,0x41,0x85,0x40, + 0x8A,0x60,0xA0,0x00,0xB1,0x40,0x60,0xAD, + 0xB3,0xAA,0xF0,0x0E,0xAD,0xB4,0xAA,0xC5, + 0x40,0xD0,0x08,0xAD,0xB5,0xAA,0xC5,0x41, + 0xF0,0x01,0xCA,0x60,0x4D,0xC2,0xB5,0xF0, + 0x0A,0x29,0x7F,0xF0,0x06,0x20,0xEA,0xA2, + 0x4C,0xD0,0xA6,0x60,0x38,0xAD,0x00,0x9D, + 0x85,0x40,0xAD,0x01,0x9D,0x85,0x41,0xAD, + 0x57,0xAA,0x8D,0x63,0xAA,0xA0,0x00,0x98, + 0x91,0x40,0xA0,0x1E,0x38,0xA5,0x40,0xE9, + 0x2D,0x91,0x40,0x48,0xA5,0x41,0xE9,0x00, + 0xC8,0x91,0x40,0xAA,0xCA,0x68,0x48,0xC8, + 0x91,0x40,0x8A,0xC8,0x91,0x40,0xAA,0xCA, + 0x68,0x48,0xC8,0x91,0x40,0xC8,0x8A,0x91, + 0x40,0xCE,0x63,0xAA,0xF0,0x17,0xAA,0x68, + 0x38,0xE9,0x26,0xC8,0x91,0x40,0x48,0x8A, + 0xE9,0x00,0xC8,0x91,0x40,0x85,0x41,0x68, + 0x85,0x40,0x4C,0xE5,0xA7,0x48,0xA9,0x00, + 0xC8,0x91,0x40,0xC8,0x91,0x40,0xAD,0xB6, + 0xAA,0xF0,0x0B,0x68,0x85,0x74,0x85,0x70, + 0x68,0x85,0x73,0x85,0x6F,0x60,0x68,0x85, + 0x4D,0x85,0xCB,0x68,0x85,0x4C,0x85,0xCA, + 0x60,0xA5,0x39,0xCD,0x03,0x9D,0xF0,0x12, + 0x8D,0x56,0xAA,0xA5,0x38,0x8D,0x55,0xAA, + 0xAD,0x02,0x9D,0x85,0x38,0xAD,0x03,0x9D, + 0x85,0x39,0xA5,0x37,0xCD,0x05,0x9D,0xF0, + 0x12,0x8D,0x54,0xAA,0xA5,0x36,0x8D,0x53, + 0xAA,0xAD,0x04,0x9D,0x85,0x36,0xAD,0x05, + 0x9D,0x85,0x37,0x60,0x49,0x4E,0x49,0xD4, + 0x4C,0x4F,0x41,0xC4,0x53,0x41,0x56,0xC5, + 0x52,0x55,0xCE,0x43,0x48,0x41,0x49,0xCE, + 0x44,0x45,0x4C,0x45,0x54,0xC5,0x4C,0x4F, + 0x43,0xCB,0x55,0x4E,0x4C,0x4F,0x43,0xCB, + 0x43,0x4C,0x4F,0x53,0xC5,0x52,0x45,0x41, + 0xC4,0x45,0x58,0x45,0xC3,0x57,0x52,0x49, + 0x54,0xC5,0x50,0x4F,0x53,0x49,0x54,0x49, + 0x4F,0xCE,0x4F,0x50,0x45,0xCE,0x41,0x50, + 0x50,0x45,0x4E,0xC4,0x52,0x45,0x4E,0x41, + 0x4D,0xC5,0x43,0x41,0x54,0x41,0x4C,0x4F, + 0xC7,0x4D,0x4F,0xCE,0x4E,0x4F,0x4D,0x4F, + 0xCE,0x50,0x52,0xA3,0x49,0x4E,0xA3,0x4D, + 0x41,0x58,0x46,0x49,0x4C,0x45,0xD3,0x46, + 0xD0,0x49,0x4E,0xD4,0x42,0x53,0x41,0x56, + 0xC5,0x42,0x4C,0x4F,0x41,0xC4,0x42,0x52, + 0x55,0xCE,0x56,0x45,0x52,0x49,0x46,0xD9, + 0x00,0x21,0x70,0xA0,0x70,0xA1,0x70,0xA0, + 0x70,0x20,0x70,0x20,0x70,0x20,0x70,0x20, + 0x70,0x60,0x00,0x22,0x06,0x20,0x74,0x22, + 0x06,0x22,0x04,0x23,0x78,0x22,0x70,0x30, + 0x70,0x40,0x70,0x40,0x80,0x40,0x80,0x08, + 0x00,0x08,0x00,0x04,0x00,0x40,0x70,0x40, + 0x00,0x21,0x79,0x20,0x71,0x20,0x71,0x20, + 0x70,0xD6,0xC4,0xD3,0xCC,0xD2,0xC2,0xC1, + 0xC3,0xC9,0xCF,0x40,0x20,0x10,0x08,0x04, + 0x02,0x01,0xC0,0xA0,0x90,0x00,0x00,0xFE, + 0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x07, + 0x00,0x01,0x00,0xFF,0x7F,0x00,0x00,0xFF, + 0x7F,0x00,0x00,0xFF,0x7F,0x00,0x00,0xFF, + 0xFF,0x0D,0x07,0x8D,0x4C,0x41,0x4E,0x47, + 0x55,0x41,0x47,0x45,0x20,0x4E,0x4F,0x54, + 0x20,0x41,0x56,0x41,0x49,0x4C,0x41,0x42, + 0x4C,0xC5,0x52,0x41,0x4E,0x47,0x45,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x57,0x52,0x49, + 0x54,0x45,0x20,0x50,0x52,0x4F,0x54,0x45, + 0x43,0x54,0x45,0xC4,0x45,0x4E,0x44,0x20, + 0x4F,0x46,0x20,0x44,0x41,0x54,0xC1,0x46, + 0x49,0x4C,0x45,0x20,0x4E,0x4F,0x54,0x20, + 0x46,0x4F,0x55,0x4E,0xC4,0x56,0x4F,0x4C, + 0x55,0x4D,0x45,0x20,0x4D,0x49,0x53,0x4D, + 0x41,0x54,0x43,0xC8,0x49,0x2F,0x4F,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x44,0x49,0x53, + 0x4B,0x20,0x46,0x55,0x4C,0xCC,0x46,0x49, + 0x4C,0x45,0x20,0x4C,0x4F,0x43,0x4B,0x45, + 0xC4,0x53,0x59,0x4E,0x54,0x41,0x58,0x20, + 0x45,0x52,0x52,0x4F,0xD2,0x4E,0x4F,0x20, + 0x42,0x55,0x46,0x46,0x45,0x52,0x53,0x20, + 0x41,0x56,0x41,0x49,0x4C,0x41,0x42,0x4C, + 0xC5,0x46,0x49,0x4C,0x45,0x20,0x54,0x59, + 0x50,0x45,0x20,0x4D,0x49,0x53,0x4D,0x41, + 0x54,0x43,0xC8,0x50,0x52,0x4F,0x47,0x52, + 0x41,0x4D,0x20,0x54,0x4F,0x4F,0x20,0x4C, + 0x41,0x52,0x47,0xC5,0x4E,0x4F,0x54,0x20, + 0x44,0x49,0x52,0x45,0x43,0x54,0x20,0x43, + 0x4F,0x4D,0x4D,0x41,0x4E,0xC4,0x8D,0x00, + 0x03,0x19,0x19,0x24,0x33,0x3E,0x4C,0x5B, + 0x64,0x6D,0x78,0x84,0x98,0xAA,0xBB,0x2D, + 0x98,0x00,0x02,0x02,0xC1,0x1B,0xFD,0x03, + 0x03,0xF0,0x58,0x00,0xA0,0x06,0x00,0x1B, + 0x44,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x10,0x00,0x00,0xCF,0xC1, + 0xC4,0xC5,0xD2,0xAE,0xCF,0xC2,0xCA,0xB0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0x03,0x84,0x00,0x00,0x00,0x40,0x00, + 0xC1,0xD0,0xD0,0xCC,0xC5,0xD3,0xCF,0xC6, + 0xD4,0xE8,0xB7,0xBB,0xB3,0xBB,0xB4,0x00, + 0xC0,0x7E,0xB3,0x21,0xAB,0x05,0xAC,0x57, + 0xAC,0x6F,0xAC,0x2A,0xAD,0x97,0xAD,0xEE, + 0xAC,0xF5,0xAC,0x39,0xAC,0x11,0xAD,0x8D, + 0xAE,0x17,0xAD,0x7E,0xB3,0x7E,0xB3,0x89, + 0xAC,0x95,0xAC,0x86,0xAC,0x92,0xAC,0x7E, + 0xB3,0x7E,0xB3,0xBD,0xAC,0xC9,0xAC,0xBA, + 0xAC,0xC6,0xAC,0x7E,0xB3,0xE0,0x00,0xF0, + 0x02,0xA2,0x02,0x8E,0x5F,0xAA,0xBA,0x8E, + 0x9B,0xB3,0x20,0x6A,0xAE,0xAD,0xBB,0xB5, + 0xC9,0x0D,0xB0,0x0B,0x0A,0xAA,0xBD,0xCA, + 0xAA,0x48,0xBD,0xC9,0xAA,0x48,0x60,0x4C, + 0x63,0xB3,0x20,0x28,0xAB,0x4C,0x7F,0xB3, + 0x20,0xDC,0xAB,0xA9,0x01,0x8D,0xE3,0xB5, + 0xAE,0xBE,0xB5,0xAD,0xBD,0xB5,0xD0,0x05, + 0xE0,0x00,0xD0,0x01,0xE8,0x8D,0xE8,0xB5, + 0x8E,0xE9,0xB5,0x20,0xC9,0xB1,0x90,0x5E, + 0x8E,0x9C,0xB3,0xAE,0x5F,0xAA,0xBD,0x09, + 0xA9,0xAE,0x9C,0xB3,0x4A,0xB0,0x0D,0xAD, + 0x51,0xAA,0xC9,0xC0,0xD0,0x03,0x4C,0x5F, + 0xB3,0x4C,0x73,0xB3,0xA9,0x00,0x9D,0xE8, + 0xB4,0xA9,0x01,0x9D,0xE7,0xB4,0x8E,0x9C, + 0xB3,0x20,0x44,0xB2,0xAE,0x9C,0xB3,0x9D, + 0xC7,0xB4,0x8D,0xD2,0xB5,0x8D,0xD4,0xB5, + 0xAD,0xF1,0xB5,0x9D,0xC6,0xB4,0x8D,0xD1, + 0xB5,0x8D,0xD3,0xB5,0xAD,0xC2,0xB5,0x9D, + 0xC8,0xB4,0x20,0x37,0xB0,0x20,0x0C,0xAF, + 0x20,0xD6,0xB7,0x20,0x3A,0xAF,0xAE,0x9C, + 0xB3,0xA9,0x06,0x8D,0xC5,0xB5,0xBD,0xC6, + 0xB4,0x8D,0xD1,0xB5,0xBD,0xC7,0xB4,0x8D, + 0xD2,0xB5,0xBD,0xC8,0xB4,0x8D,0xC2,0xB5, + 0x8D,0xF6,0xB5,0xBD,0xE7,0xB4,0x8D,0xEE, + 0xB5,0xBD,0xE8,0xB4,0x8D,0xEF,0xB5,0x8E, + 0xD9,0xB5,0xA9,0xFF,0x8D,0xE0,0xB5,0x8D, + 0xE1,0xB5,0xAD,0xE2,0xB3,0x8D,0xDA,0xB5, + 0x18,0x4C,0x5E,0xAF,0xA9,0x00,0xAA,0x9D, + 0xD1,0xB5,0xE8,0xE0,0x2D,0xD0,0xF8,0xAD, + 0xBF,0xB5,0x49,0xFF,0x8D,0xF9,0xB5,0xAD, + 0xC0,0xB5,0x8D,0xF8,0xB5,0xAD,0xC1,0xB5, + 0x0A,0x0A,0x0A,0x0A,0xAA,0x8E,0xF7,0xB5, + 0xA9,0x11,0x8D,0xFA,0xB5,0x60,0x20,0x1D, + 0xAF,0x20,0x34,0xAF,0x20,0xC3,0xB2,0xA9, + 0x02,0x2D,0xD5,0xB5,0xF0,0x21,0x20,0xF7, + 0xAF,0xA9,0x00,0x18,0x20,0x11,0xB0,0x38, + 0xCE,0xD8,0xB5,0xD0,0xF7,0xAE,0xD9,0xB5, + 0xAD,0xEE,0xB5,0x9D,0xE7,0xB4,0xAD,0xEF, + 0xB5,0x9D,0xE8,0xB4,0x20,0x37,0xB0,0x4C, + 0x7F,0xB3,0x20,0x28,0xAB,0xAD,0xF6,0xB5, + 0x30,0x2B,0xAD,0xBD,0xB5,0x85,0x42,0xAD, + 0xBE,0xB5,0x85,0x43,0xAE,0x9C,0xB3,0x20, + 0x1C,0xB2,0x20,0x37,0xB0,0x4C,0x7F,0xB3, + 0xAD,0xBC,0xB5,0xC9,0x05,0xB0,0x0B,0x0A, + 0xAA,0xBD,0xE6,0xAA,0x48,0xBD,0xE5,0xAA, + 0x48,0x60,0x4C,0x67,0xB3,0x4C,0x7B,0xB3, + 0xAD,0xF6,0xB5,0x30,0xF8,0xAD,0xBC,0xB5, + 0xC9,0x05,0xB0,0xEE,0x0A,0xAA,0xBD,0xF2, + 0xAA,0x48,0xBD,0xF1,0xAA,0x48,0x60,0x20, + 0x00,0xB3,0x20,0xA8,0xAC,0x8D,0xC3,0xB5, + 0x4C,0x7F,0xB3,0x20,0x00,0xB3,0x20,0xB5, + 0xB1,0x20,0xA8,0xAC,0x48,0x20,0xA2,0xB1, + 0xA0,0x00,0x68,0x91,0x42,0x4C,0x96,0xAC, + 0x20,0xB6,0xB0,0xB0,0x0B,0xB1,0x42,0x48, + 0x20,0x5B,0xB1,0x20,0x94,0xB1,0x68,0x60, + 0x4C,0x6F,0xB3,0x20,0x00,0xB3,0xAD,0xC3, + 0xB5,0x20,0xDA,0xAC,0x4C,0x7F,0xB3,0x20, + 0x00,0xB3,0x20,0xA2,0xB1,0xA0,0x00,0xB1, + 0x42,0x20,0xDA,0xAC,0x20,0xB5,0xB1,0x4C, + 0xCA,0xAC,0x48,0x20,0xB6,0xB0,0x68,0x91, + 0x42,0xA9,0x40,0x0D,0xD5,0xB5,0x8D,0xD5, + 0xB5,0x20,0x5B,0xB1,0x4C,0x94,0xB1,0xA9, + 0x80,0x8D,0x9E,0xB3,0xD0,0x05,0xA9,0x00, + 0x8D,0x9E,0xB3,0x20,0x28,0xAB,0xAE,0x9C, + 0xB3,0xBD,0xC8,0xB4,0x29,0x7F,0x0D,0x9E, + 0xB3,0x9D,0xC8,0xB4,0x20,0x37,0xB0,0x4C, + 0x7F,0xB3,0x20,0x00,0xB3,0x4C,0x7F,0xB3, + 0x20,0x28,0xAB,0x20,0xB6,0xB0,0xB0,0xEF, + 0xEE,0xE4,0xB5,0xD0,0xF6,0xEE,0xE5,0xB5, + 0x4C,0x1B,0xAD,0x20,0x28,0xAB,0xAE,0x9C, + 0xB3,0xBD,0xC8,0xB4,0x10,0x03,0x4C,0x7B, + 0xB3,0xAE,0x9C,0xB3,0xBD,0xC6,0xB4,0x8D, + 0xD1,0xB5,0x9D,0xE6,0xB4,0xA9,0xFF,0x9D, + 0xC6,0xB4,0xBC,0xC7,0xB4,0x8C,0xD2,0xB5, + 0x20,0x37,0xB0,0x18,0x20,0x5E,0xAF,0xB0, + 0x2A,0x20,0x0C,0xAF,0xA0,0x0C,0x8C,0x9C, + 0xB3,0xB1,0x42,0x30,0x0B,0xF0,0x09,0x48, + 0xC8,0xB1,0x42,0xA8,0x68,0x20,0x89,0xAD, + 0xAC,0x9C,0xB3,0xC8,0xC8,0xD0,0xE7,0xAD, + 0xD3,0xB5,0xAC,0xD4,0xB5,0x20,0x89,0xAD, + 0x38,0xB0,0xD1,0x20,0xFB,0xAF,0x4C,0x7F, + 0xB3,0x38,0x20,0xDD,0xB2,0xA9,0x00,0xA2, + 0x05,0x9D,0xF0,0xB5,0xCA,0x10,0xFA,0x60, + 0x20,0xDC,0xAB,0xA9,0xFF,0x8D,0xF9,0xB5, + 0x20,0xF7,0xAF,0xA9,0x16,0x8D,0x9D,0xB3, + 0x20,0x2F,0xAE,0x20,0x2F,0xAE,0xA2,0x0B, + 0xBD,0xAF,0xB3,0x20,0xED,0xFD,0xCA,0x10, + 0xF7,0x86,0x45,0xAD,0xF6,0xB7,0x85,0x44, + 0x20,0x42,0xAE,0x20,0x2F,0xAE,0x20,0x2F, + 0xAE,0x18,0x20,0x11,0xB0,0xB0,0x5D,0xA2, + 0x00,0x8E,0x9C,0xB3,0xBD,0xC6,0xB4,0xF0, + 0x53,0x30,0x4A,0xA0,0xA0,0xBD,0xC8,0xB4, + 0x10,0x02,0xA0,0xAA,0x98,0x20,0xED,0xFD, + 0xBD,0xC8,0xB4,0x29,0x7F,0xA0,0x07,0x0A, + 0x0A,0xB0,0x03,0x88,0xD0,0xFA,0xB9,0xA7, + 0xB3,0x20,0xED,0xFD,0xA9,0xA0,0x20,0xED, + 0xFD,0xBD,0xE7,0xB4,0x85,0x44,0xBD,0xE8, + 0xB4,0x85,0x45,0x20,0x42,0xAE,0xA9,0xA0, + 0x20,0xED,0xFD,0xE8,0xE8,0xE8,0xA0,0x1D, + 0xBD,0xC6,0xB4,0x20,0xED,0xFD,0xE8,0x88, + 0x10,0xF6,0x20,0x2F,0xAE,0x20,0x30,0xB2, + 0x90,0xA7,0xB0,0x9E,0x4C,0x7F,0xB3,0xA9, + 0x8D,0x20,0xED,0xFD,0xCE,0x9D,0xB3,0xD0, + 0x08,0x20,0x0C,0xFD,0xA9,0x15,0x8D,0x9D, + 0xB3,0x60,0xA0,0x02,0xA9,0x00,0x48,0xA5, + 0x44,0xD9,0xA4,0xB3,0x90,0x12,0xF9,0xA4, + 0xB3,0x85,0x44,0xA5,0x45,0xE9,0x00,0x85, + 0x45,0x68,0x69,0x00,0x48,0x4C,0x47,0xAE, + 0x68,0x09,0xB0,0x20,0xED,0xFD,0x88,0x10, + 0xDB,0x60,0x20,0x08,0xAF,0xA0,0x00,0x8C, + 0xC5,0xB5,0xB1,0x42,0x99,0xD1,0xB5,0xC8, + 0xC0,0x2D,0xD0,0xF6,0x18,0x60,0x20,0x08, + 0xAF,0xA0,0x00,0xB9,0xD1,0xB5,0x91,0x42, + 0xC8,0xC0,0x2D,0xD0,0xF6,0x60,0x20,0xDC, + 0xAB,0xA9,0x04,0x20,0x58,0xB0,0xAD,0xF9, + 0xB5,0x49,0xFF,0x8D,0xC1,0xB3,0xA9,0x11, + 0x8D,0xEB,0xB3,0xA9,0x01,0x8D,0xEC,0xB3, + 0xA2,0x38,0xA9,0x00,0x9D,0xBB,0xB3,0xE8, + 0xD0,0xFA,0xA2,0x0C,0xE0,0x8C,0xF0,0x14, + 0xA0,0x03,0xB9,0xA0,0xB3,0x9D,0xF3,0xB3, + 0xE8,0x88,0x10,0xF6,0xE0,0x44,0xD0,0xEC, + 0xA2,0x48,0xD0,0xE8,0x20,0xFB,0xAF,0xA2, + 0x00,0x8A,0x9D,0xBB,0xB4,0xE8,0xD0,0xFA, + 0x20,0x45,0xB0,0xA9,0x11,0xAC,0xF0,0xB3, + 0x88,0x88,0x8D,0xEC,0xB7,0x8D,0xBC,0xB4, + 0x8C,0xBD,0xB4,0xC8,0x8C,0xED,0xB7,0xA9, + 0x02,0x20,0x58,0xB0,0xAC,0xBD,0xB4,0x88, + 0x30,0x05,0xD0,0xEC,0x98,0xF0,0xE6,0x20, + 0xC2,0xB7,0x20,0x4A,0xB7,0x4C,0x7F,0xB3, + 0xA2,0x00,0xF0,0x06,0xA2,0x02,0xD0,0x02, + 0xA2,0x04,0xBD,0xC7,0xB5,0x85,0x42,0xBD, + 0xC8,0xB5,0x85,0x43,0x60,0x2C,0xD5,0xB5, + 0x70,0x01,0x60,0x20,0xE4,0xAF,0xA9,0x02, + 0x20,0x52,0xB0,0xA9,0xBF,0x2D,0xD5,0xB5, + 0x8D,0xD5,0xB5,0x60,0xAD,0xD5,0xB5,0x30, + 0x01,0x60,0x20,0x4B,0xAF,0xA9,0x02,0x20, + 0x52,0xB0,0xA9,0x7F,0x2D,0xD5,0xB5,0x8D, + 0xD5,0xB5,0x60,0xAD,0xC9,0xB5,0x8D,0xF0, + 0xB7,0xAD,0xCA,0xB5,0x8D,0xF1,0xB7,0xAE, + 0xD3,0xB5,0xAC,0xD4,0xB5,0x60,0x08,0x20, + 0x34,0xAF,0x20,0x4B,0xAF,0x20,0x0C,0xAF, + 0x28,0xB0,0x09,0xAE,0xD1,0xB5,0xAC,0xD2, + 0xB5,0x4C,0xB5,0xAF,0xA0,0x01,0xB1,0x42, + 0xF0,0x08,0xAA,0xC8,0xB1,0x42,0xA8,0x4C, + 0xB5,0xAF,0xAD,0xBB,0xB5,0xC9,0x04,0xF0, + 0x02,0x38,0x60,0x20,0x44,0xB2,0xA0,0x02, + 0x91,0x42,0x48,0x88,0xAD,0xF1,0xB5,0x91, + 0x42,0x48,0x20,0x3A,0xAF,0x20,0xD6,0xB7, + 0xA0,0x05,0xAD,0xDE,0xB5,0x91,0x42,0xC8, + 0xAD,0xDF,0xB5,0x91,0x42,0x68,0xAA,0x68, + 0xA8,0xA9,0x02,0xD0,0x02,0xA9,0x01,0x8E, + 0xD3,0xB5,0x8C,0xD4,0xB5,0x20,0x52,0xB0, + 0xA0,0x05,0xB1,0x42,0x8D,0xDC,0xB5,0x18, + 0x6D,0xDA,0xB5,0x8D,0xDE,0xB5,0xC8,0xB1, + 0x42,0x8D,0xDD,0xB5,0x6D,0xDB,0xB5,0x8D, + 0xDF,0xB5,0x18,0x60,0x20,0xE4,0xAF,0xA9, + 0x01,0x4C,0x52,0xB0,0xAC,0xCB,0xB5,0xAD, + 0xCC,0xB5,0x8C,0xF0,0xB7,0x8D,0xF1,0xB7, + 0xAE,0xD6,0xB5,0xAC,0xD7,0xB5,0x60,0xA9, + 0x01,0xD0,0x02,0xA9,0x02,0xAC,0xC3,0xAA, + 0x8C,0xF0,0xB7,0xAC,0xC4,0xAA,0x8C,0xF1, + 0xB7,0xAE,0xFA,0xB5,0xA0,0x00,0x4C,0x52, + 0xB0,0x08,0x20,0x45,0xB0,0x28,0xB0,0x08, + 0xAC,0xBD,0xB3,0xAE,0xBC,0xB3,0xD0,0x0A, + 0xAE,0xBC,0xB4,0xD0,0x02,0x38,0x60,0xAC, + 0xBD,0xB4,0x8E,0x97,0xB3,0x8C,0x98,0xB3, + 0xA9,0x01,0x20,0x52,0xB0,0x18,0x60,0x20, + 0x45,0xB0,0xAE,0x97,0xB3,0xAC,0x98,0xB3, + 0xA9,0x02,0x4C,0x52,0xB0,0xAD,0xC5,0xAA, + 0x8D,0xF0,0xB7,0xAD,0xC6,0xAA,0x8D,0xF1, + 0xB7,0x60,0x8E,0xEC,0xB7,0x8C,0xED,0xB7, + 0x8D,0xF4,0xB7,0xC9,0x02,0xD0,0x06,0x0D, + 0xD5,0xB5,0x8D,0xD5,0xB5,0xAD,0xF9,0xB5, + 0x49,0xFF,0x8D,0xEB,0xB7,0xAD,0xF7,0xB5, + 0x8D,0xE9,0xB7,0xAD,0xF8,0xB5,0x8D,0xEA, + 0xB7,0xAD,0xE2,0xB5,0x8D,0xF2,0xB7,0xAD, + 0xE3,0xB5,0x8D,0xF3,0xB7,0xA9,0x01,0x8D, + 0xE8,0xB7,0xAC,0xC1,0xAA,0xAD,0xC2,0xAA, + 0x20,0xB5,0xB7,0xAD,0xF6,0xB7,0x8D,0xBF, + 0xB5,0xA9,0xFF,0x8D,0xEB,0xB7,0xB0,0x01, + 0x60,0xAD,0xF5,0xB7,0xA0,0x07,0xC9,0x20, + 0xF0,0x08,0xA0,0x04,0xC9,0x10,0xF0,0x02, + 0xA0,0x08,0x98,0x4C,0x85,0xB3,0xAD,0xE4, + 0xB5,0xCD,0xE0,0xB5,0xD0,0x08,0xAD,0xE5, + 0xB5,0xCD,0xE1,0xB5,0xF0,0x66,0x20,0x1D, + 0xAF,0xAD,0xE5,0xB5,0xCD,0xDD,0xB5,0x90, + 0x1C,0xD0,0x08,0xAD,0xE4,0xB5,0xCD,0xDC, + 0xB5,0x90,0x12,0xAD,0xE5,0xB5,0xCD,0xDF, + 0xB5,0x90,0x10,0xD0,0x08,0xAD,0xE4,0xB5, + 0xCD,0xDE,0xB5,0x90,0x06,0x20,0x5E,0xAF, + 0x90,0xD7,0x60,0x38,0xAD,0xE4,0xB5,0xED, + 0xDC,0xB5,0x0A,0x69,0x0C,0xA8,0x20,0x0C, + 0xAF,0xB1,0x42,0xD0,0x0F,0xAD,0xBB,0xB5, + 0xC9,0x04,0xF0,0x02,0x38,0x60,0x20,0x34, + 0xB1,0x4C,0x20,0xB1,0x8D,0xD6,0xB5,0xC8, + 0xB1,0x42,0x8D,0xD7,0xB5,0x20,0xDC,0xAF, + 0xAD,0xE4,0xB5,0x8D,0xE0,0xB5,0xAD,0xE5, + 0xB5,0x8D,0xE1,0xB5,0x20,0x10,0xAF,0xAC, + 0xE6,0xB5,0x18,0x60,0x8C,0x9D,0xB3,0x20, + 0x44,0xB2,0xAC,0x9D,0xB3,0xC8,0x91,0x42, + 0x8D,0xD7,0xB5,0x88,0xAD,0xF1,0xB5,0x91, + 0x42,0x8D,0xD6,0xB5,0x20,0x10,0xAF,0x20, + 0xD6,0xB7,0xA9,0xC0,0x0D,0xD5,0xB5,0x8D, + 0xD5,0xB5,0x60,0xAE,0xEA,0xB5,0x8E,0xBD, + 0xB5,0xAE,0xEB,0xB5,0x8E,0xBE,0xB5,0xAE, + 0xEC,0xB5,0xAC,0xED,0xB5,0x8E,0xBF,0xB5, + 0x8C,0xC0,0xB5,0xE8,0xD0,0x01,0xC8,0xCC, + 0xE9,0xB5,0xD0,0x11,0xEC,0xE8,0xB5,0xD0, + 0x0C,0xA2,0x00,0xA0,0x00,0xEE,0xEA,0xB5, + 0xD0,0x03,0xEE,0xEB,0xB5,0x8E,0xEC,0xB5, + 0x8C,0xED,0xB5,0x60,0xEE,0xE6,0xB5,0xD0, + 0x08,0xEE,0xE4,0xB5,0xD0,0x03,0xEE,0xE5, + 0xB5,0x60,0xAC,0xC3,0xB5,0xAE,0xC4,0xB5, + 0x84,0x42,0x86,0x43,0xEE,0xC3,0xB5,0xD0, + 0x03,0xEE,0xC4,0xB5,0x60,0xAC,0xC1,0xB5, + 0xD0,0x08,0xAE,0xC2,0xB5,0xF0,0x07,0xCE, + 0xC2,0xB5,0xCE,0xC1,0xB5,0x60,0x4C,0x7F, + 0xB3,0x20,0xF7,0xAF,0xAD,0xC3,0xB5,0x85, + 0x42,0xAD,0xC4,0xB5,0x85,0x43,0xA9,0x01, + 0x8D,0x9D,0xB3,0xA9,0x00,0x8D,0xD8,0xB5, + 0x18,0xEE,0xD8,0xB5,0x20,0x11,0xB0,0xB0, + 0x51,0xA2,0x00,0x8E,0x9C,0xB3,0xBD,0xC6, + 0xB4,0xF0,0x1F,0x30,0x22,0xA0,0x00,0xE8, + 0xE8,0xE8,0xB1,0x42,0xDD,0xC6,0xB4,0xD0, + 0x0A,0xC8,0xC0,0x1E,0xD0,0xF3,0xAE,0x9C, + 0xB3,0x18,0x60,0x20,0x30,0xB2,0x90,0xDB, + 0xB0,0xCF,0xAC,0x9D,0xB3,0xD0,0xC1,0xAC, + 0x9D,0xB3,0xD0,0xEF,0xA0,0x00,0xE8,0xE8, + 0xE8,0xB1,0x42,0x9D,0xC6,0xB4,0xC8,0xC0, + 0x1E,0xD0,0xF5,0xAE,0x9C,0xB3,0x38,0x60, + 0x18,0xAD,0x9C,0xB3,0x69,0x23,0xAA,0xE0, + 0xF5,0x60,0xA9,0x00,0xAC,0x9D,0xB3,0xD0, + 0x97,0x4C,0x77,0xB3,0xAD,0xF1,0xB5,0xF0, + 0x21,0xCE,0xF0,0xB5,0x30,0x17,0x18,0xA2, + 0x04,0x3E,0xF1,0xB5,0xCA,0xD0,0xFA,0x90, + 0xF0,0xEE,0xEE,0xB5,0xD0,0x03,0xEE,0xEF, + 0xB5,0xAD,0xF0,0xB5,0x60,0xA9,0x00,0x8D, + 0xF1,0xB5,0xA9,0x00,0x8D,0x9E,0xB3,0x20, + 0xF7,0xAF,0x18,0xAD,0xEB,0xB3,0x6D,0xEC, + 0xB3,0xF0,0x09,0xCD,0xEF,0xB3,0x90,0x14, + 0xA9,0xFF,0xD0,0x0A,0xAD,0x9E,0xB3,0xD0, + 0x37,0xA9,0x01,0x8D,0x9E,0xB3,0x8D,0xEC, + 0xB3,0x18,0x69,0x11,0x8D,0xEB,0xB3,0x8D, + 0xF1,0xB5,0xA8,0x0A,0x0A,0xA8,0xA2,0x04, + 0x18,0xB9,0xF6,0xB3,0x9D,0xF1,0xB5,0xF0, + 0x06,0x38,0xA9,0x00,0x99,0xF6,0xB3,0x88, + 0xCA,0xD0,0xEE,0x90,0xBD,0x20,0xFB,0xAF, + 0xAD,0xF0,0xB3,0x8D,0xF0,0xB5,0xD0,0x89, + 0x4C,0x77,0xB3,0xAD,0xF1,0xB5,0xD0,0x01, + 0x60,0x48,0x20,0xF7,0xAF,0xAC,0xF0,0xB5, + 0x68,0x18,0x20,0xDD,0xB2,0xA9,0x00,0x8D, + 0xF1,0xB5,0x4C,0xFB,0xAF,0xA2,0xFC,0x7E, + 0xF6,0xB4,0xE8,0xD0,0xFA,0xC8,0xCC,0xF0, + 0xB3,0xD0,0xF2,0x0A,0x0A,0xA8,0xF0,0x0F, + 0xA2,0x04,0xBD,0xF1,0xB5,0x19,0xF6,0xB3, + 0x99,0xF6,0xB3,0x88,0xCA,0xD0,0xF3,0x60, + 0xAD,0xBD,0xB5,0x8D,0xE6,0xB5,0x8D,0xEA, + 0xB5,0xAD,0xBE,0xB5,0x8D,0xE4,0xB5,0x8D, + 0xEB,0xB5,0xA9,0x00,0x8D,0xE5,0xB5,0xA0, + 0x10,0xAA,0xAD,0xE6,0xB5,0x4A,0xB0,0x03, + 0x8A,0x90,0x0E,0x18,0xAD,0xE5,0xB5,0x6D, + 0xE8,0xB5,0x8D,0xE5,0xB5,0x8A,0x6D,0xE9, + 0xB5,0x6A,0x6E,0xE5,0xB5,0x6E,0xE4,0xB5, + 0x6E,0xE6,0xB5,0x88,0xD0,0xDB,0x18,0xAD, + 0xBF,0xB5,0x8D,0xEC,0xB5,0x6D,0xE6,0xB5, + 0x8D,0xE6,0xB5,0xAD,0xC0,0xB5,0x8D,0xED, + 0xB5,0x6D,0xE4,0xB5,0x8D,0xE4,0xB5,0x90, + 0x03,0xEE,0xE5,0xB5,0x60,0x00,0x00,0xA9, + 0x01,0xD0,0x22,0xA9,0x02,0xD0,0x1E,0xA9, + 0x03,0xD0,0x1A,0xA9,0x04,0xD0,0x16,0xA9, + 0x05,0xD0,0x12,0xA9,0x06,0xD0,0x0E,0x4C, + 0xED,0xBF,0xEA,0xA9,0x0A,0xD0,0x06,0xAD, + 0xC5,0xB5,0x18,0x90,0x01,0x38,0x08,0x8D, + 0xC5,0xB5,0xA9,0x00,0x85,0x48,0x20,0x7E, + 0xAE,0x28,0xAE,0x9B,0xB3,0x9A,0x60,0x11, + 0x0F,0x00,0x00,0xEC,0x46,0x01,0x00,0x00, + 0x00,0x00,0xFF,0xFF,0x01,0x0A,0x64,0xD4, + 0xC9,0xC1,0xC2,0xD3,0xD2,0xC1,0xC2,0xA0, + 0xC5,0xCD,0xD5,0xCC,0xCF,0xD6,0xA0,0xCB, + 0xD3,0xC9,0xC4,0x04,0x11,0x0F,0x03,0x00, + 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7A,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x13,0x01,0x00,0x00,0x23, + 0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07, + 0xFF,0x00,0x00,0x3F,0xFF,0x00,0x00,0x3F, + 0xFF,0x00,0x00,0x00,0x7F,0x00,0x00,0xFF, + 0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF, + 0xFF,0x00,0x00,0x0F,0xFF,0x00,0x00,0x01, + 0xFF,0x00,0x00,0x1F,0xFF,0x00,0x00,0x00, + 0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x03, + 0xFF,0x00,0x00,0x1F,0xFF,0x00,0x00,0x03, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x1F,0xFF,0x00,0x00,0x00, + 0x7F,0x00,0x00,0x00,0x7F,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x7F,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x03,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x1F, + 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x0E,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x0F, + 0x82,0xC8,0xC5,0xCC,0xCC,0xCF,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0x03, + 0x00,0x14,0x0F,0x81,0xC1,0xD0,0xD0,0xCC, + 0xC5,0xD3,0xCF,0xC6,0xD4,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0x03,0x00,0x15,0x0F,0x84,0xCC, + 0xCF,0xC1,0xC4,0xC5,0xD2,0xAE,0xCF,0xC2, + 0xCA,0xB0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0x06,0x00,0x16, + 0x0F,0x84,0xC6,0xD0,0xC2,0xC1,0xD3,0xC9, + 0xC3,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0x2A,0x00,0x17,0x0F,0x84,0xC9,0xCE,0xD4, + 0xC2,0xC1,0xD3,0xC9,0xC3,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0x2A,0x00,0x18,0x0F,0x82, + 0xCD,0xC1,0xD3,0xD4,0xC5,0xD2,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0x03,0x00, + 0x19,0x0F,0x84,0xCD,0xC1,0xD3,0xD4,0xC5, + 0xD2,0xA0,0xC3,0xD2,0xC5,0xC1,0xD4,0xC5, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, + 0xA0,0x09,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x15,0x0F,0x15,0x0F,0x00,0x15,0x0A, + 0x01,0x46,0x7A,0x00,0x00,0x00,0x7A,0x00, + 0x04,0x00,0x00,0x01,0x04,0x00,0x48,0x00, + 0x01,0x00,0x48,0x04,0x00,0x00,0x06,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x60, + 0x01,0xFF,0x11,0x00,0x00,0x00,0xFD,0xFE, + 0x01,0xA5,0x27,0xC9,0x09,0xD0,0x18,0xA5, + 0x2B,0x4A,0x4A,0x4A,0x4A,0x09,0xC0,0x85, + 0x3F,0xA9,0x5C,0x85,0x3E,0x18,0xAD,0xFE, + 0x08,0x6D,0xFF,0x08,0x8D,0xFE,0x08,0xAE, + 0xFF,0x08,0x30,0x15,0xBD,0x4D,0x08,0x85, + 0x3D,0xCE,0xFF,0x08,0xAD,0xFE,0x08,0x85, + 0x27,0xCE,0xFE,0x08,0xA6,0x2B,0x6C,0x3E, + 0x00,0xEE,0xFE,0x08,0xEE,0xFE,0x08,0x20, + 0x89,0xFE,0x20,0x93,0xFE,0x20,0x2F,0xFB, + 0xA6,0x2B,0x6C,0xFD,0x08,0x00,0x0D,0x0B, + 0x09,0x07,0x05,0x03,0x01,0x0E,0x0C,0x0A, + 0x08,0x06,0x04,0x02,0x0F,0x00,0x20,0x64, + 0xA7,0xB0,0x08,0xA9,0x00,0xA8,0x8D,0x5D, + 0xB6,0x91,0x40,0xAD,0xC5,0xB5,0x4C,0xD2, + 0xA6,0xAD,0x5D,0xB6,0xF0,0x08,0xEE,0xBD, + 0xB5,0xD0,0x03,0xEE,0xBE,0xB5,0xA9,0x00, + 0x8D,0x5D,0xB6,0x4C,0x84,0xBA,0x8D,0xBC, + 0xB5,0x20,0xA8,0xA6,0x20,0xEA,0xA2,0x4C, + 0x7D,0xA2,0xA0,0x13,0xB1,0x42,0xD0,0x14, + 0xC8,0xC0,0x17,0xD0,0xF7,0xA0,0x19,0xB1, + 0x42,0x99,0xA4,0xB5,0xC8,0xC0,0x1D,0xD0, + 0xF6,0x4C,0xBB,0xA6,0xA2,0xFF,0x8E,0x5D, + 0xB6,0xD0,0xF6,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x09, + 0x8E,0xE9,0xB7,0x8E,0xF7,0xB7,0xA9,0x01, + 0x8D,0xF8,0xB7,0x8D,0xEA,0xB7,0xAD,0xE0, + 0xB7,0x8D,0xE1,0xB7,0xA9,0x02,0x8D,0xEC, + 0xB7,0xA9,0x04,0x8D,0xED,0xB7,0xAC,0xE7, + 0xB7,0x88,0x8C,0xF1,0xB7,0xA9,0x01,0x8D, + 0xF4,0xB7,0x8A,0x4A,0x4A,0x4A,0x4A,0xAA, + 0xA9,0x00,0x9D,0xF8,0x04,0x9D,0x78,0x04, + 0x20,0x93,0xB7,0xA2,0xFF,0x9A,0x8E,0xEB, + 0xB7,0x4C,0xC8,0xBF,0x20,0x89,0xFE,0x4C, + 0x84,0x9D,0xAD,0xE7,0xB7,0x38,0xED,0xF1, + 0xB7,0x8D,0xE1,0xB7,0xAD,0xE7,0xB7,0x8D, + 0xF1,0xB7,0xCE,0xF1,0xB7,0xA9,0x02,0x8D, + 0xEC,0xB7,0xA9,0x04,0x8D,0xED,0xB7,0xA9, + 0x02,0x8D,0xF4,0xB7,0x20,0x93,0xB7,0xAD, + 0xE7,0xB7,0x8D,0xFE,0xB6,0x18,0x69,0x09, + 0x8D,0xF1,0xB7,0xA9,0x0A,0x8D,0xE1,0xB7, + 0x38,0xE9,0x01,0x8D,0xFF,0xB6,0x8D,0xED, + 0xB7,0x20,0x93,0xB7,0x60,0x00,0x00,0x00, + 0x00,0x00,0x00,0xAD,0xE5,0xB7,0xAC,0xE4, + 0xB7,0x20,0xB5,0xB7,0xAC,0xED,0xB7,0x88, + 0x10,0x07,0xA0,0x0F,0xEA,0xEA,0xCE,0xEC, + 0xB7,0x8C,0xED,0xB7,0xCE,0xF1,0xB7,0xCE, + 0xE1,0xB7,0xD0,0xDF,0x60,0x08,0x78,0x20, + 0x00,0xBD,0xB0,0x03,0x28,0x18,0x60,0x28, + 0x38,0x60,0xAD,0xBC,0xB5,0x8D,0xF1,0xB7, + 0xA9,0x00,0x8D,0xF0,0xB7,0xAD,0xF9,0xB5, + 0x49,0xFF,0x8D,0xEB,0xB7,0x60,0xA9,0x00, + 0xA8,0x91,0x42,0xC8,0xD0,0xFB,0x60,0x00, + 0x1B,0x00,0x0A,0x1B,0xE8,0xB7,0x00,0xB6, + 0x01,0x60,0x01,0xFF,0x15,0x0A,0xFB,0xB7, + 0x00,0x96,0x00,0x01,0x01,0x00,0xFE,0x60, + 0x01,0x00,0x00,0x00,0x01,0xEF,0xD8,0x00, + 0xA2,0x00,0xA0,0x02,0x88,0xB1,0x3E,0x4A, + 0x3E,0x00,0xBC,0x4A,0x3E,0x00,0xBC,0x99, + 0x00,0xBB,0xE8,0xE0,0x56,0x90,0xED,0xA2, + 0x00,0x98,0xD0,0xE8,0xA2,0x55,0xBD,0x00, + 0xBC,0x29,0x3F,0x9D,0x00,0xBC,0xCA,0x10, + 0xF5,0x60,0x38,0x86,0x27,0x8E,0x78,0x06, + 0xBD,0x8D,0xC0,0xBD,0x8E,0xC0,0x30,0x7C, + 0xAD,0x00,0xBC,0x85,0x26,0xA9,0xFF,0x9D, + 0x8F,0xC0,0x1D,0x8C,0xC0,0x48,0x68,0xEA, + 0xA0,0x04,0x48,0x68,0x20,0xB9,0xB8,0x88, + 0xD0,0xF8,0xA9,0xD5,0x20,0xB8,0xB8,0xA9, + 0xAA,0x20,0xB8,0xB8,0xA9,0xAD,0x20,0xB8, + 0xB8,0x98,0xA0,0x56,0xD0,0x03,0xB9,0x00, + 0xBC,0x59,0xFF,0xBB,0xAA,0xBD,0x29,0xBA, + 0xA6,0x27,0x9D,0x8D,0xC0,0xBD,0x8C,0xC0, + 0x88,0xD0,0xEB,0xA5,0x26,0xEA,0x59,0x00, + 0xBB,0xAA,0xBD,0x29,0xBA,0xAE,0x78,0x06, + 0x9D,0x8D,0xC0,0xBD,0x8C,0xC0,0xB9,0x00, + 0xBB,0xC8,0xD0,0xEA,0xAA,0xBD,0x29,0xBA, + 0xA6,0x27,0x20,0xBB,0xB8,0xA9,0xDE,0x20, + 0xB8,0xB8,0xA9,0xAA,0x20,0xB8,0xB8,0xA9, + 0xEB,0x20,0xB8,0xB8,0xA9,0xFF,0x20,0xB8, + 0xB8,0xBD,0x8E,0xC0,0xBD,0x8C,0xC0,0x60, + 0x18,0x48,0x68,0x9D,0x8D,0xC0,0x1D,0x8C, + 0xC0,0x60,0xA0,0x00,0xA2,0x56,0xCA,0x30, + 0xFB,0xB9,0x00,0xBB,0x5E,0x00,0xBC,0x2A, + 0x5E,0x00,0xBC,0x2A,0x91,0x3E,0xC8,0xC4, + 0x26,0xD0,0xEB,0x60,0xA0,0x20,0x88,0xF0, + 0x61,0xBD,0x8C,0xC0,0x10,0xFB,0x49,0xD5, + 0xD0,0xF4,0xEA,0xBD,0x8C,0xC0,0x10,0xFB, + 0xC9,0xAA,0xD0,0xF2,0xA0,0x56,0xBD,0x8C, + 0xC0,0x10,0xFB,0xC9,0xAD,0xD0,0xE7,0xA9, + 0x00,0x88,0x84,0x26,0xBC,0x8C,0xC0,0x10, + 0xFB,0x59,0x00,0xBA,0xA4,0x26,0x99,0x00, + 0xBC,0xD0,0xEE,0x84,0x26,0xBC,0x8C,0xC0, + 0x10,0xFB,0x59,0x00,0xBA,0xA4,0x26,0x99, + 0x00,0xBB,0xC8,0xD0,0xEE,0xBC,0x8C,0xC0, + 0x10,0xFB,0xD9,0x00,0xBA,0xD0,0x13,0xBD, + 0x8C,0xC0,0x10,0xFB,0xC9,0xDE,0xD0,0x0A, + 0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA, + 0xF0,0x5C,0x38,0x60,0xA0,0xFC,0x84,0x26, + 0xC8,0xD0,0x04,0xE6,0x26,0xF0,0xF3,0xBD, + 0x8C,0xC0,0x10,0xFB,0xC9,0xD5,0xD0,0xF0, + 0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA, + 0xD0,0xF2,0xA0,0x03,0xBD,0x8C,0xC0,0x10, + 0xFB,0xC9,0x96,0xD0,0xE7,0xA9,0x00,0x85, + 0x27,0xBD,0x8C,0xC0,0x10,0xFB,0x2A,0x85, + 0x26,0xBD,0x8C,0xC0,0x10,0xFB,0x25,0x26, + 0x99,0x2C,0x00,0x45,0x27,0x88,0x10,0xE7, + 0xA8,0xD0,0xB7,0xBD,0x8C,0xC0,0x10,0xFB, + 0xC9,0xDE,0xD0,0xAE,0xEA,0xBD,0x8C,0xC0, + 0x10,0xFB,0xC9,0xAA,0xD0,0xA4,0x18,0x60, + 0x86,0x2B,0x85,0x2A,0xCD,0x78,0x04,0xF0, + 0x53,0xA9,0x00,0x85,0x26,0xAD,0x78,0x04, + 0x85,0x27,0x38,0xE5,0x2A,0xF0,0x33,0xB0, + 0x07,0x49,0xFF,0xEE,0x78,0x04,0x90,0x05, + 0x69,0xFE,0xCE,0x78,0x04,0xC5,0x26,0x90, + 0x02,0xA5,0x26,0xC9,0x0C,0xB0,0x01,0xA8, + 0x38,0x20,0xEE,0xB9,0xB9,0x11,0xBA,0x20, + 0x00,0xBA,0xA5,0x27,0x18,0x20,0xF1,0xB9, + 0xB9,0x1D,0xBA,0x20,0x00,0xBA,0xE6,0x26, + 0xD0,0xC3,0x20,0x00,0xBA,0x18,0xAD,0x78, + 0x04,0x29,0x03,0x2A,0x05,0x2B,0xAA,0xBD, + 0x80,0xC0,0xA6,0x2B,0x60,0x00,0x00,0x00, + 0xA2,0x11,0xCA,0xD0,0xFD,0xE6,0x46,0xD0, + 0x02,0xE6,0x47,0x38,0xE9,0x01,0xD0,0xF0, + 0x60,0x01,0x30,0x28,0x24,0x20,0x1E,0x1D, + 0x1C,0x1C,0x1C,0x1C,0x1C,0x70,0x2C,0x26, + 0x22,0x1F,0x1E,0x1D,0x1C,0x1C,0x1C,0x1C, + 0x1C,0x96,0x97,0x9A,0x9B,0x9D,0x9E,0x9F, + 0xA6,0xA7,0xAB,0xAC,0xAD,0xAE,0xAF,0xB2, + 0xB3,0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB, + 0xBC,0xBD,0xBE,0xBF,0xCB,0xCD,0xCE,0xCF, + 0xD3,0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDD, + 0xDE,0xDF,0xE5,0xE6,0xE7,0xE9,0xEA,0xEB, + 0xEC,0xED,0xEE,0xEF,0xF2,0xF3,0xF4,0xF5, + 0xF6,0xF7,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE, + 0xFF,0xAE,0x5F,0xAA,0xE0,0x1C,0xF0,0x05, + 0xA2,0x00,0x8E,0x5D,0xB6,0x60,0xA9,0xFF, + 0x8D,0xFB,0x04,0x8D,0x0C,0xC0,0x8D,0x0E, + 0xC0,0x4C,0x2F,0xFB,0xAD,0xBD,0xB5,0x8D, + 0xE6,0xB5,0x8D,0xEA,0xB5,0xBA,0x8E,0x9B, + 0xB3,0x4C,0x7F,0xB3,0x00,0x00,0x00,0x01, + 0x98,0x99,0x02,0x03,0x9C,0x04,0x05,0x06, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0x07,0x08, + 0xA8,0xA9,0xAA,0x09,0x0A,0x0B,0x0C,0x0D, + 0xB0,0xB1,0x0E,0x0F,0x10,0x11,0x12,0x13, + 0xB8,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0x1B,0xCC,0x1C,0x1D,0x1E, + 0xD0,0xD1,0xD2,0x1F,0xD4,0xD5,0x20,0x21, + 0xD8,0x22,0x23,0x24,0x25,0x26,0x27,0x28, + 0xE0,0xE1,0xE2,0xE3,0xE4,0x29,0x2A,0x2B, + 0xE8,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, + 0xF0,0xF1,0x33,0x34,0x35,0x36,0x37,0x38, + 0xF8,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x3A,0x20,0x12,0x34, + 0x05,0x29,0x0F,0x29,0x0F,0x34,0x00,0x22, + 0x32,0x22,0x06,0x39,0x0E,0x21,0x0F,0x04, + 0x00,0x32,0x26,0x39,0x0E,0x34,0x1A,0x29, + 0x0B,0x2E,0x0F,0x00,0x24,0x0E,0x22,0x04, + 0x3E,0x08,0x06,0x3F,0x08,0x06,0x3F,0x08, + 0x34,0x3E,0x08,0x14,0x3E,0x21,0x0E,0x21, + 0x0E,0x13,0x25,0x3D,0x08,0x2F,0x3F,0x29, + 0x0D,0x08,0x29,0x3F,0x21,0x0D,0x28,0x05, + 0x22,0x0C,0x12,0x36,0x33,0x3F,0x34,0x3E, + 0x30,0x05,0x34,0x3A,0x29,0x0C,0x28,0x00, + 0x31,0x0D,0x08,0x00,0x3F,0x13,0x25,0x3D, + 0x29,0x0F,0x08,0x23,0x3E,0x2A,0x2F,0x00, + 0x3E,0x31,0x10,0x34,0x04,0x2F,0x30,0x3E, + 0x31,0x10,0x34,0x03,0x29,0x11,0x29,0x0B, + 0x30,0x27,0x3C,0x22,0x31,0x0B,0x3C,0x27, + 0x31,0x0F,0x34,0x37,0x39,0x11,0x31,0x0D, + 0x3C,0x35,0x29,0x0D,0x26,0x2A,0x08,0x12, + 0x3E,0x2A,0x37,0x08,0x3B,0x3F,0x08,0x0E, + 0x3F,0x2A,0x28,0x21,0x0C,0x08,0x19,0x3F, + 0x08,0x31,0x3F,0x2B,0x00,0x00,0x32,0x28, + 0x3C,0x04,0x32,0x32,0x29,0x3C,0x24,0x22, + 0x08,0x29,0x3F,0x32,0x24,0x34,0x35,0x22, + 0x3C,0x34,0x08,0x1E,0x3F,0x2A,0x00,0x21, + 0x0F,0x08,0x0D,0x3D,0x02,0x3A,0x2F,0x32, + 0x30,0x24,0x30,0x02,0x02,0x28,0x01,0x02, + 0x09,0x10,0x09,0x10,0x32,0x04,0x3E,0x31, + 0x0F,0x3C,0x3D,0x04,0x39,0x28,0x01,0x08, + 0x0D,0x3D,0x21,0x0D,0x37,0x2D,0x3E,0x34, + 0x04,0x08,0x0D,0x3D,0x37,0x2E,0x3E,0x3C, + 0x03,0x2F,0x2E,0x3E,0x3C,0x01,0x32,0x29, + 0x3C,0x00,0x29,0x0D,0x06,0x22,0x09,0x11, + 0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0xBD, + 0x8D,0xC0,0xBD,0x8E,0xC0,0x30,0x5E,0xA9, + 0xFF,0x9D,0x8F,0xC0,0xDD,0x8C,0xC0,0x48, + 0x68,0x20,0xC3,0xBC,0x20,0xC3,0xBC,0x9D, + 0x8D,0xC0,0xDD,0x8C,0xC0,0xEA,0x88,0xD0, + 0xF0,0xA9,0xD5,0x20,0xD5,0xBC,0xA9,0xAA, + 0x20,0xD5,0xBC,0xA9,0x96,0x20,0xD5,0xBC, + 0xA5,0x41,0x20,0xC4,0xBC,0xA5,0x44,0x20, + 0xC4,0xBC,0xA5,0x3F,0x20,0xC4,0xBC,0xA5, + 0x41,0x45,0x44,0x45,0x3F,0x48,0x4A,0x05, + 0x3E,0x9D,0x8D,0xC0,0xBD,0x8C,0xC0,0x68, + 0x09,0xAA,0x20,0xD4,0xBC,0xA9,0xDE,0x20, + 0xD5,0xBC,0xA9,0xAA,0x20,0xD5,0xBC,0xA9, + 0xEB,0x20,0xD5,0xBC,0x18,0xBD,0x8E,0xC0, + 0xBD,0x8C,0xC0,0x60,0x48,0x4A,0x05,0x3E, + 0x9D,0x8D,0xC0,0xDD,0x8C,0xC0,0x68,0xEA, + 0xEA,0xEA,0x09,0xAA,0xEA,0xEA,0x48,0x68, + 0x9D,0x8D,0xC0,0xDD,0x8C,0xC0,0x60,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x84,0x48,0x85,0x49,0xA0,0x02,0x8C,0xF8, + 0x06,0xA0,0x04,0x8C,0xF8,0x04,0xA0,0x01, + 0xB1,0x48,0xAA,0xA0,0x0F,0xD1,0x48,0xF0, + 0x1B,0x8A,0x48,0xB1,0x48,0xAA,0x68,0x48, + 0x91,0x48,0xBD,0x8E,0xC0,0xA0,0x08,0xBD, + 0x8C,0xC0,0xDD,0x8C,0xC0,0xD0,0xF6,0x88, + 0xD0,0xF8,0x68,0xAA,0xBD,0x8E,0xC0,0xBD, + 0x8C,0xC0,0xA0,0x08,0xBD,0x8C,0xC0,0x48, + 0x68,0x48,0x68,0x8E,0xF8,0x05,0xDD,0x8C, + 0xC0,0xD0,0x03,0x88,0xD0,0xEE,0x08,0xBD, + 0x89,0xC0,0xA0,0x06,0xB1,0x48,0x99,0x36, + 0x00,0xC8,0xC0,0x0A,0xD0,0xF6,0xA0,0x03, + 0xB1,0x3C,0x85,0x47,0xA0,0x02,0xB1,0x48, + 0xA0,0x10,0xD1,0x48,0xF0,0x06,0x91,0x48, + 0x28,0xA0,0x00,0x08,0x6A,0x90,0x05,0xBD, + 0x8A,0xC0,0xB0,0x03,0xBD,0x8B,0xC0,0x66, + 0x35,0x28,0x08,0xD0,0x0B,0xA0,0x07,0x20, + 0x00,0xBA,0x88,0xD0,0xFA,0xAE,0xF8,0x05, + 0xA0,0x04,0xB1,0x48,0x20,0x5A,0xBE,0x28, + 0xD0,0x11,0xA4,0x47,0x10,0x0D,0xA0,0x12, + 0x88,0xD0,0xFD,0xE6,0x46,0xD0,0xF7,0xE6, + 0x47,0xD0,0xF3,0xA0,0x0C,0xB1,0x48,0xF0, + 0x5A,0xC9,0x04,0xF0,0x58,0x6A,0x08,0xB0, + 0x03,0x20,0x00,0xB8,0xA0,0x30,0x8C,0x78, + 0x05,0xAE,0xF8,0x05,0x20,0x44,0xB9,0x90, + 0x24,0xCE,0x78,0x05,0x10,0xF3,0xAD,0x78, + 0x04,0x48,0xA9,0x60,0x20,0x95,0xBE,0xCE, + 0xF8,0x06,0xF0,0x28,0xA9,0x04,0x8D,0xF8, + 0x04,0xA9,0x00,0x20,0x5A,0xBE,0x68,0x20, + 0x5A,0xBE,0x4C,0xBC,0xBD,0xA4,0x2E,0xCC, + 0x78,0x04,0xF0,0x1C,0xAD,0x78,0x04,0x48, + 0x98,0x20,0x95,0xBE,0x68,0xCE,0xF8,0x04, + 0xD0,0xE5,0xF0,0xCA,0x68,0xA9,0x40,0x28, + 0x4C,0x48,0xBE,0xF0,0x39,0x4C,0xAF,0xBE, + 0xA0,0x03,0xB1,0x48,0x48,0xA5,0x2F,0xA0, + 0x0E,0x91,0x48,0x68,0xF0,0x08,0xC5,0x2F, + 0xF0,0x04,0xA9,0x20,0xD0,0xE1,0xA0,0x05, + 0xB1,0x48,0xA8,0xB9,0xB8,0xBF,0xC5,0x2D, + 0xD0,0x97,0x28,0x90,0x1C,0x20,0xDC,0xB8, + 0x08,0xB0,0x8E,0x28,0xA2,0x00,0x86,0x26, + 0x20,0xC2,0xB8,0xAE,0xF8,0x05,0x18,0x24, + 0x38,0xA0,0x0D,0x91,0x48,0xBD,0x88,0xC0, + 0x60,0x20,0x2A,0xB8,0x90,0xF0,0xA9,0x10, + 0xB0,0xEE,0x48,0xA0,0x01,0xB1,0x3C,0x6A, + 0x68,0x90,0x08,0x0A,0x20,0x6B,0xBE,0x4E, + 0x78,0x04,0x60,0x85,0x2A,0x20,0x8E,0xBE, + 0xB9,0x78,0x04,0x24,0x35,0x30,0x03,0xB9, + 0xF8,0x04,0x8D,0x78,0x04,0xA5,0x2A,0x24, + 0x35,0x30,0x05,0x99,0xF8,0x04,0x10,0x03, + 0x99,0x78,0x04,0x4C,0xA0,0xB9,0x8A,0x4A, + 0x4A,0x4A,0x4A,0xA8,0x60,0x48,0xA0,0x02, + 0xB1,0x48,0x6A,0x66,0x35,0x20,0x8E,0xBE, + 0x68,0x0A,0x24,0x35,0x30,0x05,0x99,0xF8, + 0x04,0x10,0x03,0x99,0x78,0x04,0x60,0xA0, + 0x03,0xB1,0x48,0x85,0x41,0xA9,0xAA,0x85, + 0x3E,0xA0,0x56,0xA9,0x00,0x85,0x44,0x99, + 0xFF,0xBB,0x88,0xD0,0xFA,0x99,0x00,0xBB, + 0x88,0xD0,0xFA,0xA9,0x50,0x20,0x95,0xBE, + 0xA9,0x28,0x85,0x45,0xA5,0x44,0x20,0x5A, + 0xBE,0x20,0x0D,0xBF,0xA9,0x08,0xB0,0x24, + 0xA9,0x30,0x8D,0x78,0x05,0x38,0xCE,0x78, + 0x05,0xF0,0x19,0x20,0x44,0xB9,0xB0,0xF5, + 0xA5,0x2D,0xD0,0xF1,0x20,0xDC,0xB8,0xB0, + 0xEC,0xE6,0x44,0xA5,0x44,0xC9,0x23,0x90, + 0xD3,0x18,0x90,0x05,0xA0,0x0D,0x91,0x48, + 0x38,0xBD,0x88,0xC0,0x60,0xA9,0x00,0x85, + 0x3F,0xA0,0x80,0xD0,0x02,0xA4,0x45,0x20, + 0x56,0xBC,0xB0,0x6B,0x20,0x2A,0xB8,0xB0, + 0x66,0xE6,0x3F,0xA5,0x3F,0xC9,0x10,0x90, + 0xEC,0xA0,0x0F,0x84,0x3F,0xA9,0x30,0x8D, + 0x78,0x05,0x99,0xA8,0xBF,0x88,0x10,0xFA, + 0xA4,0x45,0x20,0x87,0xBF,0x20,0x87,0xBF, + 0x20,0x87,0xBF,0x48,0x68,0xEA,0x88,0xD0, + 0xF1,0x20,0x44,0xB9,0xB0,0x23,0xA5,0x2D, + 0xF0,0x15,0xA9,0x10,0xC5,0x45,0xA5,0x45, + 0xE9,0x01,0x85,0x45,0xC9,0x05,0xB0,0x11, + 0x38,0x60,0x20,0x44,0xB9,0xB0,0x05,0x20, + 0xDC,0xB8,0x90,0x1C,0xCE,0x78,0x05,0xD0, + 0xF1,0x20,0x44,0xB9,0xB0,0x0B,0xA5,0x2D, + 0xC9,0x0F,0xD0,0x05,0x20,0xDC,0xB8,0x90, + 0x8C,0xCE,0x78,0x05,0xD0,0xEB,0x38,0x60, + 0xA4,0x2D,0xB9,0xA8,0xBF,0x30,0xDD,0xA9, + 0xFF,0x99,0xA8,0xBF,0xC6,0x3F,0x10,0xCA, + 0xA5,0x44,0xD0,0x0A,0xA5,0x45,0xC9,0x10, + 0x90,0xE5,0xC6,0x45,0xC6,0x45,0x18,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0D,0x0B,0x09,0x07,0x05,0x03,0x01, + 0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x0F, + 0x20,0x93,0xFE,0xAD,0x81,0xC0,0xAD,0x81, + 0xC0,0xA9,0x00,0x8D,0x00,0xE0,0x20,0x76, + 0xBA,0x4C,0x44,0xB7,0x8D,0x63,0xAA,0x8D, + 0x70,0xAA,0x8D,0x71,0xAA,0x60,0x20,0x5B, + 0xA7,0x8C,0xB7,0xAA,0x60,0x20,0x7E,0xAE, + 0xAE,0x9B,0xB3,0x9A,0x20,0x16,0xA3,0xBA, + 0x8E,0x9B,0xB3,0xA9,0x09,0x4C,0x85 +}; diff --git a/fake6502.h b/fake6502.h new file mode 100644 index 0000000..91ac5cd --- /dev/null +++ b/fake6502.h @@ -0,0 +1,998 @@ +/* Fake6502 CPU emulator core v1.1 ******************* + * (c)2011 Mike Chambers (miker00lz@gmail.com) * + ***************************************************** + * v1.1 - Small bugfix in BIT opcode, but it was the * + * difference between a few games in my NES * + * emulator working and being broken! * + * I went through the rest carefully again * + * after fixing it just to make sure I didn't * + * have any other typos! (Dec. 17, 2011) * + * * + * v1.0 - First release (Nov. 24, 2011) * + ***************************************************** + * LICENSE: This source code is released into the * + * public domain, but if you use it please do give * + * credit. I put a lot of effort into writing this! * + * * + ***************************************************** + * Fake6502 is a MOS Technology 6502 CPU emulation * + * engine in C. It was written as part of a Nintendo * + * Entertainment System emulator I've been writing. * + * * + * It has been pretty well-tested in the NES emu, * + * and the clock-cycle timing in particular has been * + * VERY thoroughly checked out. It matches with the * + * real 6502 processor 100%. * + * * + * A couple important things to know about are two * + * defines in the code. One is "UNDOCUMENTED" which, * + * when defined, allows Fake6502 to compile with * + * full support for the more predictable * + * undocumented instructions of the 6502. If it is * + * undefined, undocumented opcodes just act as NOPs. * + * * + * The other define is "NES_CPU", which causes the * + * code to compile without support for binary-coded * + * decimal (BCD) support for the ADC and SBC * + * opcodes. The Ricoh 2A03 CPU in the NES does not * + * support BCD, but is otherwise identical to the * + * standard MOS 6502. (Note that this define is * + * enabled in this file if you haven't changed it * + * yourself. If you're not emulating a NES, you * + * should comment it out.) * + * * + * If you do discover an error in timing accuracy, * + * or operation in general please e-mail me at the * + * address above so that I can fix it. Thank you! * + * * + ***************************************************** + * Usage: * + * * + * Fake6502 requires you to provide two external * + * functions: * + * * + * uint8_t read6502(uint16_t address) * + * void write6502(uint16_t address, uint8_t value) * + * * + * You may optionally pass Fake6502 the pointer to a * + * function which you want to be called after every * + * emulated instruction. This function should be a * + * void with no parameters expected to be passed to * + * it. * + * * + * This can be very useful. For example, in a NES * + * emulator, you check the number of clock ticks * + * that have passed so you can know when to handle * + * APU events. * + * * + * To pass Fake6502 this pointer, use the * + * hookexternal(void *funcptr) function provided. * + * * + * To disable the hook later, pass NULL to it. * + ***************************************************** + * Useful functions in this emulator: * + * * + * void reset6502() * + * - Call this once before you begin execution. * + * * + * void exec6502(uint32_t tickcount) * + * - Execute 6502 code up to the next specified * + * count of clock ticks. * + * * + * void step6502() * + * - Execute a single instrution. * + * * + * void irq6502() * + * - Trigger a hardware IRQ in the 6502 core. * + * * + * void nmi6502() * + * - Trigger an NMI in the 6502 core. * + * * + * void hookexternal(void *funcptr) * + * - Pass a pointer to a void function taking no * + * parameters. This will cause Fake6502 to call * + * that function once after each emulated * + * instruction. * + * * + ***************************************************** + * Useful variables in this emulator: * + * * + * uint32_t clockticks6502 * + * - A running total of the emulated cycle count. * + * * + * uint32_t instructions * + * - A running total of the total emulated * + * instruction count. This is not related to * + * clock cycle timing. * + * * + *****************************************************/ + +#include <stdio.h> +#include <stdint.h> + +//6502 defines +//#define UNDOCUMENTED //when this is defined, undocumented opcodes are handled. + //otherwise, they're simply treated as NOPs. + +//#define NES_CPU //when this is defined, the binary-coded decimal (BCD) + //status flag is not honored by ADC and SBC. the 2A03 + //CPU in the Nintendo Entertainment System does not + //support BCD operation. + +#define FLAG_CARRY 0x01 +#define FLAG_ZERO 0x02 +#define FLAG_INTERRUPT 0x04 +#define FLAG_DECIMAL 0x08 +#define FLAG_BREAK 0x10 +#define FLAG_CONSTANT 0x20 +#define FLAG_OVERFLOW 0x40 +#define FLAG_SIGN 0x80 + +#define BASE_STACK 0x100 + +#define saveaccum(n) a = (uint8_t)((n) & 0x00FF) + + +//flag modifier macros +#define setcarry() status |= FLAG_CARRY +#define clearcarry() status &= (~FLAG_CARRY) +#define setzero() status |= FLAG_ZERO +#define clearzero() status &= (~FLAG_ZERO) +#define setinterrupt() status |= FLAG_INTERRUPT +#define clearinterrupt() status &= (~FLAG_INTERRUPT) +#define setdecimal() status |= FLAG_DECIMAL +#define cleardecimal() status &= (~FLAG_DECIMAL) +#define setoverflow() status |= FLAG_OVERFLOW +#define clearoverflow() status &= (~FLAG_OVERFLOW) +#define setsign() status |= FLAG_SIGN +#define clearsign() status &= (~FLAG_SIGN) + + +//flag calculation macros +#define zerocalc(n) {\ + if ((n) & 0x00FF) clearzero();\ + else setzero();\ +} + +#define signcalc(n) {\ + if ((n) & 0x0080) setsign();\ + else clearsign();\ +} + +#define carrycalc(n) {\ + if ((n) & 0xFF00) setcarry();\ + else clearcarry();\ +} + +#define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \ + if (((n) ^ (uint16_t)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\ + else clearoverflow();\ +} + + +//6502 CPU registers +uint16_t pc; +uint8_t sp, a, x, y, status; + + +//helper variables +uint32_t instructions = 0; //keep track of total instructions executed +uint32_t clockticks6502 = 0, clockgoal6502 = 0; +uint16_t oldpc, ea, reladdr, value, result; +uint8_t opcode, oldstatus; + +//externally supplied functions +extern uint8_t read6502(uint16_t address); +extern void write6502(uint16_t address, uint8_t value); + +//a few general functions used by various other functions +void push16(uint16_t pushval) { + write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF); + write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF); + sp -= 2; +} + +void push8(uint8_t pushval) { + write6502(BASE_STACK + sp--, pushval); +} + +uint16_t pull16() { + uint16_t temp16; + temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8); + sp += 2; + return(temp16); +} + +uint8_t pull8() { + return (read6502(BASE_STACK + ++sp)); +} + +void reset6502() { + pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8); + a = 0; + x = 0; + y = 0; + sp = 0xFD; + status |= FLAG_CONSTANT; +} + + +static void (*addrtable[256])(); +static void (*optable[256])(); +uint8_t penaltyop, penaltyaddr; + +//addressing mode functions, calculates effective addresses +static void imp() { //implied +} + +static void acc() { //accumulator +} + +static void imm() { //immediate + ea = pc++; +} + +static void zp() { //zero-page + ea = (uint16_t)read6502((uint16_t)pc++); +} + +static void zpx() { //zero-page,X + ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)x) & 0xFF; //zero-page wraparound +} + +static void zpy() { //zero-page,Y + ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)y) & 0xFF; //zero-page wraparound +} + +static void rel() { //relative for branch ops (8-bit immediate value, sign-extended) + reladdr = (uint16_t)read6502(pc++); + if (reladdr & 0x80) reladdr |= 0xFF00; +} + +static void abso() { //absolute + ea = (uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8); + pc += 2; +} + +static void absx() { //absolute,X + uint16_t startpage; + ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8)); + startpage = ea & 0xFF00; + ea += (uint16_t)x; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } + + pc += 2; +} + +static void absy() { //absolute,Y + uint16_t startpage; + ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8)); + startpage = ea & 0xFF00; + ea += (uint16_t)y; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } + + pc += 2; +} + +static void ind() { //indirect + uint16_t eahelp, eahelp2; + eahelp = (uint16_t)read6502(pc) | (uint16_t)((uint16_t)read6502(pc+1) << 8); + eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //replicate 6502 page-boundary wraparound bug + ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8); + pc += 2; +} + +static void indx() { // (indirect,X) + uint16_t eahelp; + eahelp = (uint16_t)(((uint16_t)read6502(pc++) + (uint16_t)x) & 0xFF); //zero-page wraparound for table pointer + ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8); +} + +static void indy() { // (indirect),Y + uint16_t eahelp, eahelp2, startpage; + eahelp = (uint16_t)read6502(pc++); + eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //zero-page wraparound + ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8); + startpage = ea & 0xFF00; + ea += (uint16_t)y; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } +} + +static uint16_t getvalue() { + if (addrtable[opcode] == acc) return((uint16_t)a); + else return((uint16_t)read6502(ea)); +} + +static uint16_t getvalue16() { + return((uint16_t)read6502(ea) | ((uint16_t)read6502(ea+1) << 8)); +} + +static void putvalue(uint16_t saveval) { + if (addrtable[opcode] == acc) a = (uint8_t)(saveval & 0x00FF); + else write6502(ea, (saveval & 0x00FF)); +} + + +//instruction handler functions +static void adc() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + overflowcalc(result, a, value); + signcalc(result); + + #ifndef NES_CPU + if (status & FLAG_DECIMAL) { + clearcarry(); + + if ((a & 0x0F) > 0x09) { + a += 0x06; + } + if ((a & 0xF0) > 0x90) { + a += 0x60; + setcarry(); + } + + clockticks6502++; + } + #endif + + saveaccum(result); +} + +static void and() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a & value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void asl() { + value = getvalue(); + result = value << 1; + + carrycalc(result); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void bcc() { + if ((status & FLAG_CARRY) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bcs() { + if ((status & FLAG_CARRY) == FLAG_CARRY) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void beq() { + if ((status & FLAG_ZERO) == FLAG_ZERO) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bit() { + value = getvalue(); + result = (uint16_t)a & value; + + zerocalc(result); + status = (status & 0x3F) | (uint8_t)(value & 0xC0); +} + +static void bmi() { + if ((status & FLAG_SIGN) == FLAG_SIGN) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bne() { + if ((status & FLAG_ZERO) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bpl() { + if ((status & FLAG_SIGN) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void brk6502() { + pc++; + push16(pc); //push next instruction address onto stack + push8(status | FLAG_BREAK); //push CPU status to stack + setinterrupt(); //set interrupt flag + pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8); +} + +static void bvc() { + if ((status & FLAG_OVERFLOW) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bvs() { + if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void clc() { + clearcarry(); +} + +static void cld() { + cleardecimal(); +} + +static void cli() { + clearinterrupt(); +} + +static void clv() { + clearoverflow(); +} + +static void cmp() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a - value; + + if (a >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (a == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void cpx() { + value = getvalue(); + result = (uint16_t)x - value; + + if (x >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (x == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void cpy() { + value = getvalue(); + result = (uint16_t)y - value; + + if (y >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (y == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void dec() { + value = getvalue(); + result = value - 1; + + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void dex() { + x--; + + zerocalc(x); + signcalc(x); +} + +static void dey() { + y--; + + zerocalc(y); + signcalc(y); +} + +static void eor() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a ^ value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void inc() { + value = getvalue(); + result = value + 1; + + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void inx() { + x++; + + zerocalc(x); + signcalc(x); +} + +static void iny() { + y++; + + zerocalc(y); + signcalc(y); +} + +static void jmp() { + pc = ea; +} + +static void jsr() { + push16(pc - 1); + pc = ea; +} + +static void lda() { + penaltyop = 1; + value = getvalue(); + a = (uint8_t)(value & 0x00FF); + + zerocalc(a); + signcalc(a); +} + +static void ldx() { + penaltyop = 1; + value = getvalue(); + x = (uint8_t)(value & 0x00FF); + + zerocalc(x); + signcalc(x); +} + +static void ldy() { + penaltyop = 1; + value = getvalue(); + y = (uint8_t)(value & 0x00FF); + + zerocalc(y); + signcalc(y); +} + +static void lsr() { + value = getvalue(); + result = value >> 1; + + if (value & 1) setcarry(); + else clearcarry(); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void nop() { + switch (opcode) { + case 0x1C: + case 0x3C: + case 0x5C: + case 0x7C: + case 0xDC: + case 0xFC: + penaltyop = 1; + break; + } +} + +static void ora() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a | value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void pha() { + push8(a); +} + +static void php() { + push8(status | FLAG_BREAK); +} + +static void pla() { + a = pull8(); + + zerocalc(a); + signcalc(a); +} + +static void plp() { + status = pull8() | FLAG_CONSTANT; +} + +static void rol() { + value = getvalue(); + result = (value << 1) | (status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void ror() { + value = getvalue(); + result = (value >> 1) | ((status & FLAG_CARRY) << 7); + + if (value & 1) setcarry(); + else clearcarry(); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void rti() { + status = pull8(); + value = pull16(); + pc = value; +} + +static void rts() { + value = pull16(); + pc = value + 1; +} + +static void sbc() { + penaltyop = 1; + value = getvalue() ^ 0x00FF; + result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + overflowcalc(result, a, value); + signcalc(result); + + #ifndef NES_CPU + if (status & FLAG_DECIMAL) { + clearcarry(); + + a -= 0x66; + if ((a & 0x0F) > 0x09) { + a += 0x06; + } + if ((a & 0xF0) > 0x90) { + a += 0x60; + setcarry(); + } + + clockticks6502++; + } + #endif + + saveaccum(result); +} + +static void sec() { + setcarry(); +} + +static void sed() { + setdecimal(); +} + +static void sei() { + setinterrupt(); +} + +static void sta() { + putvalue(a); +} + +static void stx() { + putvalue(x); +} + +static void sty() { + putvalue(y); +} + +static void tax() { + x = a; + + zerocalc(x); + signcalc(x); +} + +static void tay() { + y = a; + + zerocalc(y); + signcalc(y); +} + +static void tsx() { + x = sp; + + zerocalc(x); + signcalc(x); +} + +static void txa() { + a = x; + + zerocalc(a); + signcalc(a); +} + +static void txs() { + sp = x; +} + +static void tya() { + a = y; + + zerocalc(a); + signcalc(a); +} + +//undocumented instructions +#ifdef UNDOCUMENTED + static void lax() { + lda(); + ldx(); + } + + static void sax() { + sta(); + stx(); + putvalue(a & x); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void dcp() { + dec(); + cmp(); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void isb() { + inc(); + sbc(); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void slo() { + asl(); + ora(); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void rla() { + rol(); + and(); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void sre() { + lsr(); + eor(); + if (penaltyop && penaltyaddr) clockticks6502--; + } + + static void rra() { + ror(); + adc(); + if (penaltyop && penaltyaddr) clockticks6502--; + } +#else + #define lax nop + #define sax nop + #define dcp nop + #define isb nop + #define slo nop + #define rla nop + #define sre nop + #define rra nop +#endif + + +static void (*addrtable[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 0 */ +/* 1 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 1 */ +/* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 2 */ +/* 3 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 3 */ +/* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */ +/* 5 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 5 */ +/* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */ +/* 7 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 7 */ +/* 8 */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */ +/* 9 */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* 9 */ +/* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */ +/* B */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */ +/* C */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */ +/* D */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* D */ +/* E */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */ +/* F */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx /* F */ +}; + +static void (*optable[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ brk6502, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, /* 0 */ +/* 1 */ bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, /* 1 */ +/* 2 */ jsr, and, nop, rla, bit, and, rol, rla, plp, and, rol, nop, bit, and, rol, rla, /* 2 */ +/* 3 */ bmi, and, nop, rla, nop, and, rol, rla, sec, and, nop, rla, nop, and, rol, rla, /* 3 */ +/* 4 */ rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, /* 4 */ +/* 5 */ bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, /* 5 */ +/* 6 */ rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, /* 6 */ +/* 7 */ bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, /* 7 */ +/* 8 */ nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, /* 8 */ +/* 9 */ bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, /* 9 */ +/* A */ ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, /* A */ +/* B */ bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, /* B */ +/* C */ cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, /* C */ +/* D */ bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, /* D */ +/* E */ cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, /* E */ +/* F */ beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb /* F */ +}; + +static const uint32_t ticktable[256] = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */ +/* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */ +/* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */ +/* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */ +/* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */ +/* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */ +/* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */ +/* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */ +/* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */ +/* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */ +/* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */ +/* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */ +/* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */ +/* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */ +/* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */ +/* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */ +}; + + +void nmi6502() { + push16(pc); + push8(status); + status |= FLAG_INTERRUPT; + pc = (uint16_t)read6502(0xFFFA) | ((uint16_t)read6502(0xFFFB) << 8); +} + +void irq6502() { + push16(pc); + push8(status); + status |= FLAG_INTERRUPT; + pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8); +} + +uint8_t callexternal = 0; +void (*loopexternal)(); + +/* +void exec6502(uint32_t tickcount) { + clockgoal6502 += tickcount; + + while (clockticks6502 < clockgoal6502) { + opcode = read6502(pc++); + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + + instructions++; + + if (callexternal) (*loopexternal)(); + } +} +*/ + +void exec6502(unsigned int org) { + clockticks6502 = 0; + pc = org; + + while ((opcode = read6502(pc++)) != 0x00) { // end on BRK + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + + instructions++; + + if (callexternal) (*loopexternal)(); + } +} + +void step6502() { + opcode = read6502(pc++); + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + clockgoal6502 = clockticks6502; + + instructions++; + + if (callexternal) (*loopexternal)(); +} + +void hookexternal(void *funcptr) { + if (funcptr != (void *)NULL) { + loopexternal = funcptr; + callexternal = 1; + } else callexternal = 0; +} diff --git a/makeheader b/makeheader new file mode 100755 index 0000000..70e24fa --- /dev/null +++ b/makeheader @@ -0,0 +1,53 @@ +#!/bin/bash + +header() +{ + FILE=$1 + VAR=$2 + EOL=$3 + BYTES=$(hexdump -v $FILE | sed 's/^.......//' | wc -w | awk '{print $1}'); + + echo "/*" + expand ${FILE}.s + echo "*/" + + printf "unsigned char $VAR[] = {\n\t" + + for i in $(hexdump -v $FILE | sed 's/^.......//'); + do + printf "0x%02X" 0x$i + BYTES=$((BYTES - 1)) + if ((BYTES != 0)) + then + printf "," + fi + EOL=$((EOL - 1)) + if ((EOL == 0)) + then + EOL=8 + printf "\n\t" + fi + done + + printf "\n};\n" +} + +cd asm +make clean +make + +( +header autoload autoloadcode 4 +header inflate inflatecode 8 +header fastload9600 fastload9600 4 +header fastload8000 fastload8000 4 +header fastloadcd fastloadcd 4 +header diskload9600 diskload9600 4 +header diskload8000 diskload8000 4 +header diskload2 diskloadcode2 8 +header diskload3 diskloadcode3 8 +) > ../c2t.h.1 + +cd .. + +cat c2t.h.[012] > c2t.h diff --git a/miniz.h b/miniz.h new file mode 100644 index 0000000..35c6ade --- /dev/null +++ b/miniz.h @@ -0,0 +1,4679 @@ +/* miniz.c v1.11 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich <richgel99@gmail.com>, last updated May 27, 2011 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + May 15, v1.09 - Initial stable release. + May 27, v1.10 - Substantial compressor optimizations: + Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + Refactored the compression code for better readability and maintainability. + Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + May 28, v1.11 - Added statement from unlicense.org + + * Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB wrapping buffer or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_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); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include <stdlib.h> + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times. +#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// 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). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. +typedef unsigned long mz_ulong; + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +#define MZ_VERSION "9.1.11" +#define MZ_VERNUM 0x91B0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 11 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other stuff is for advanced use. +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { 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 }; + +// Compression levels. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// 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) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// 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. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// 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). +// 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. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// 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.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// 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. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// 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: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// 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. +// Parameters: +// 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. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// 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). +// 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. +// Return values: +// 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. +// 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. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// 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 +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// 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. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// Works around MSVC's spammy "warning C4127: conditional expression is constant" message. +#define MZ_MACRO_END while (0, 0) + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// 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. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_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); +mz_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); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_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); +mz_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); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// 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. +// 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). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +mz_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); +mz_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); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +mz_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); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +mz_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); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_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); + +// 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. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int 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); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// 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. +// 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. +tinfl_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); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + 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]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// Compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// 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). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; +// 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. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// 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). +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_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); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_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); + +enum { 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 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { 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 }; +#else +enum { 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 }; +#endif + +// 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. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + 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; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the input as possible, and writing as much compressed data as possible. +tdefl_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); +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a valid tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include <string.h> +#include <assert.h> + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else + #define MZ_MALLOC(x) malloc(x) + #define MZ_FREE(x) free(x) + #define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #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)) +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__forceinline) + #ifdef __cplusplus + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque; return MZ_REALLOC(address, items * size); } + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// 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/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + if (!ptr) return MZ_CRC32_INIT; + crc = ~crc; while (buf_len--) { mz_uint8 b = *ptr++; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)]; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)]; } return ~crc; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + 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; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + pStream; + // 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.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + 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); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // 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. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { 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" } + }; + 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; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// 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 +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#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)) +#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 +#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 + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// 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 +// 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 +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// 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 +// 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 +// 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. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + 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; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_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) +{ + 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 }; + 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 }; + 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}; + 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}; + 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 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + 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; + + // 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). + 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; } + + 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; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + 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))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + 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]); } + 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); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + 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]; } + 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; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + 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); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + 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; } + 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; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + 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]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + 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]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + 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; } +#else + 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; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + 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; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + 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; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + 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; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + 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; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + 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, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_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) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + 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); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int 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) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 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, + 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, + 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 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + 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); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + 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; + 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); + } + 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; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + 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); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + 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; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + 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); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + 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; \ + } else if (rle_z_count <= 10) { \ + 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); \ + } else { \ + 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); \ +} rle_z_count = 0; } } + +static 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 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + 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; + 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; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + 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; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + 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; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + 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); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + 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; + 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; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + 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; } } + } + else + { + 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); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + 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++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static __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) + +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + 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; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static __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) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + 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; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + 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)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + 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; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + 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; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static __forceinline void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *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++; } + d->m_huff_count[0][lit]++; +} + +static __forceinline void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *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++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + 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; + 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]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + 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; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + 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); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + 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; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + 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++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + 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))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + 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; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[cur_pos]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + 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; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (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))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_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) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + 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; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + 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; + 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; + 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_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_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) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_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) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// 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). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : 6] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + 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; + if (!pComp) return NULL; + 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; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, TDEFL_DEFAULT_MAX_PROBES | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + y * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,"\0\0\04\02\06"[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + 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); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + 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; } + 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); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifndef MINIZ_NO_TIME +#include <time.h> +#endif + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include <stdio.h> + #include <sys/stat.h> + #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) + #include <sys/utime.h> + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 _ftelli64 + #define MZ_FSEEK64 _fseeki64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #else + #include <utime.h> + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// 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. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + 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, + 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, + 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, + 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, + // Local directory header offsets + 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, + 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, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + 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, + 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, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static __forceinline void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + 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; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static __forceinline mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static __forceinline mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static __forceinline mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static __forceinline mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ + struct tm *tm = localtime(&time); + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ +#ifndef MINIZ_NO_TIME + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +#else + pFilename, access_time, modified_time; + return MZ_TRUE; +#endif // #ifndef MINIZ_NO_TIME +} +#endif + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static __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) +{ + 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; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + 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); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// 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.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint i, n, cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + // 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. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + 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) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((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))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + // 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. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // 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). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + 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); + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + 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) + return MZ_FALSE; + 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) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pMem = (void *)pMem; + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + 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)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return MZ_FALSE; + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static __forceinline const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + 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)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, internal_attr, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((!internal_attr) && ((external_attr & 0x10) != 0)) + return MZ_TRUE; + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + 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)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + 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'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static __forceinline mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static __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) +{ + 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; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + 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); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_p)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + 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)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + 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); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_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) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + 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; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + 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); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + 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); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_uncomp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + 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)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + 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)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_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) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_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) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + 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; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + 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); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + 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); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_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) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static 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); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + 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)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_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) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static 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) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static 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) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static 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) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + 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)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // 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. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_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) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level = level_and_flags & 0xF, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + mz_zip_internal_state *pState; + + 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 > 10)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // 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.) + 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))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + 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) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_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) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level = level_and_flags & 0xF, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > 10)) + return MZ_FALSE; + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + return MZ_FALSE; + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + 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)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + 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) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + 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; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + 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) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + 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))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + 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); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)pState->m_central_dir.m_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + 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)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + 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) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + 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))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_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) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > 9)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // 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.) + status = status && mz_zip_writer_finalize_archive(&zip_archive); + status = status && mz_zip_writer_end(&zip_archive); + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + MZ_DELETE_FILE(pZip_filename); + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <http://unlicense.org/> +*/ diff --git a/windows/c2t.exe b/windows/c2t.exe new file mode 100755 index 0000000000000000000000000000000000000000..4f5a8528f488f9e5a5a32c6d2e19672904044232 GIT binary patch literal 167756 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 aX>HaVl4xMXV-k(MayY>CoepJr)cHT8^Os=& literal 0 HcmV?d00001 diff --git a/windows/miniz.h b/windows/miniz.h new file mode 100644 index 0000000..4225e61 --- /dev/null +++ b/windows/miniz.h @@ -0,0 +1,4679 @@ +/* miniz.c v1.11 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich <richgel99@gmail.com>, last updated May 27, 2011 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + May 15, v1.09 - Initial stable release. + May 27, v1.10 - Substantial compressor optimizations: + Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + Refactored the compression code for better readability and maintainability. + Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + May 28, v1.11 - Added statement from unlicense.org + + * Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB wrapping buffer or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_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); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include <stdlib.h> + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times. +#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// 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). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. +typedef unsigned long mz_ulong; + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +#define MZ_VERSION "9.1.11" +#define MZ_VERNUM 0x91B0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 11 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other stuff is for advanced use. +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { 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 }; + +// Compression levels. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// 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) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// 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. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// 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). +// 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. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// 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.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// 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. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// 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: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// 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. +// Parameters: +// 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. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// 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). +// 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. +// Return values: +// 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. +// 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. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// 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 +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// 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. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// Works around MSVC's spammy "warning C4127: conditional expression is constant" message. +#define MZ_MACRO_END while (0, 0) + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// 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. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_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); +mz_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); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_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); +mz_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); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// 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. +// 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). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +mz_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); +mz_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); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +mz_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); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +mz_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); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_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); + +// 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. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int 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); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// 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. +// 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. +tinfl_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); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + 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]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// Compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// 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). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; +// 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. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// 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). +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_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); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_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); + +enum { 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 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { 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 }; +#else +enum { 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 }; +#endif + +// 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. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + 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; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the input as possible, and writing as much compressed data as possible. +tdefl_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); +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a valid tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include <string.h> +#include <assert.h> + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else + #define MZ_MALLOC(x) malloc(x) + #define MZ_FREE(x) free(x) + #define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #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)) +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__forceinline) + #ifdef __cplusplus + #define __forceinline inline + #else + #define __forceinline inline + #endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque; return MZ_REALLOC(address, items * size); } + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// 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/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + if (!ptr) return MZ_CRC32_INIT; + crc = ~crc; while (buf_len--) { mz_uint8 b = *ptr++; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)]; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)]; } return ~crc; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + 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; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + pStream; + // 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.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + 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); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // 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. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { 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" } + }; + 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; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// 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 +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#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)) +#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 +#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 + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// 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 +// 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 +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// 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 +// 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 +// 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. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + 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; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_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) +{ + 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 }; + 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 }; + 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}; + 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}; + 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 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + 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; + + // 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). + 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; } + + 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; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + 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))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + 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]); } + 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); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + 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]; } + 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; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + 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); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + 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; } + 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; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + 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]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + 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]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + 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; } +#else + 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; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + 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; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + 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; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + 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; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + 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; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + 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, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_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) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + 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); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int 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) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 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, + 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, + 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 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 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, + 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, + 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 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + 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); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + 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; + 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); + } + 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; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + 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); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + 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; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + 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); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + 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; \ + } else if (rle_z_count <= 10) { \ + 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); \ + } else { \ + 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); \ +} rle_z_count = 0; } } + +static 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 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + 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; + 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; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + 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; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + 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; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + 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); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + 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]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + 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; + 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; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + 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; } } + } + else + { + 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); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + 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++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static inline 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) + +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + 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; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static inline 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) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + 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; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + 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)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + 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; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + 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; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + 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; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static inline void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *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++; } + d->m_huff_count[0][lit]++; +} + +static inline void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *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++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + 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; + 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]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + 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; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + 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); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + 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; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + 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++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + 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))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + 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; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[cur_pos]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + 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; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (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))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_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) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + 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; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + 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; + 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; + 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_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_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) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_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) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// 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). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : 6] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + 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; + if (!pComp) return NULL; + 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; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, TDEFL_DEFAULT_MAX_PROBES | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + y * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,"\0\0\04\02\06"[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + 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); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + 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; } + 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); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifndef MINIZ_NO_TIME +#include <time.h> +#endif + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include <stdio.h> + #include <sys/stat.h> + #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) + #include <sys/utime.h> + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftell + #define MZ_FSEEK64 fseek + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #else + #include <utime.h> + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// 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. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + 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, + 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, + 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, + 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, + // Local directory header offsets + 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, + 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, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + 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, + 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, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static inline void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + 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; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static inline mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static inline mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static inline mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static inline mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ + struct tm *tm = localtime(&time); + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ +#ifndef MINIZ_NO_TIME + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +#else + pFilename, access_time, modified_time; + return MZ_TRUE; +#endif // #ifndef MINIZ_NO_TIME +} +#endif + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static inline 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) +{ + 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; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + 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); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// 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.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint i, n, cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + // 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. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + 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) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((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))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + // 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. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // 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). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + 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); + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + 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) + return MZ_FALSE; + 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) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pMem = (void *)pMem; + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + 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)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return MZ_FALSE; + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static inline const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + 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)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, internal_attr, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((!internal_attr) && ((external_attr & 0x10) != 0)) + return MZ_TRUE; + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + 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)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + 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'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static inline mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static inline 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) +{ + 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; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + 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); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_p)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + 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)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + 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); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_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) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + 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; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + 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); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + 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); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_uncomp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + 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)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + 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)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_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) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_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) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + 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; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + 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); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + 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); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_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) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static 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); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + 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)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_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) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static 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) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static 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) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static 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) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + 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)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // 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. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_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) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level = level_and_flags & 0xF, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + mz_zip_internal_state *pState; + + 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 > 10)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // 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.) + 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))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + 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) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_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) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level = level_and_flags & 0xF, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > 10)) + return MZ_FALSE; + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + return MZ_FALSE; + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + 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)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + 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) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + 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)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + 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; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + 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)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + 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) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + 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) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + 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))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + 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); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)pState->m_central_dir.m_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + 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)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + 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) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + 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))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_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) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > 9)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // 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.) + status = status && mz_zip_writer_finalize_archive(&zip_archive); + status = status && mz_zip_writer_end(&zip_archive); + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + MZ_DELETE_FILE(pZip_filename); + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <http://unlicense.org/> +*/