# HG changeset patch # User Laurens Holst # Date 1483482626 -3600 # Tue Jan 03 23:30:26 2017 +0100 # Node ID 6fee29082926b0df45d0a27cb3bd8082de5b2be0 # Parent 7fa1924b4016deb96882610bd03bb44f4083cc95 ZlibArchive: Introduce decompressor for zlib format streams. diff --git a/src/Adler32Checker.asm b/src/Adler32Checker.asm new file mode 100644 --- /dev/null +++ b/src/Adler32Checker.asm @@ -0,0 +1,143 @@ +; +; Adler32 summer +; +Adler32Checker: MACRO + this: + Process: + push ix + ld ix,this + Process_this: equ $ - 2 + call Adler32Checker_UpdateAdler32 + pop ix + jp 0 + Process_oldHook: equ $ - 2 + adler32: + dd 1 + _size: + ENDM + +Adler32Checker_class: Class Adler32Checker, Adler32Checker_template, Heap_main +Adler32Checker_template: Adler32Checker + +; de = writer +; ix = this +; ix <- this +; de <- this +Adler32Checker_Construct: + ld iyl,e + ld iyh,d + ld e,(iy + Writer.flusher) + ld d,(iy + Writer.flusher + 1) + ld (ix + Adler32Checker.Process_oldHook),e + ld (ix + Adler32Checker.Process_oldHook + 1),d + ld e,ixl + ld d,ixh + ld (iy + Writer.flusher),e + ld (iy + Writer.flusher + 1),d + ld (ix + Adler32Checker.Process_this),e + ld (ix + Adler32Checker.Process_this + 1),d + ret + +; ix = this +; ix <- this +Adler32Checker_Destruct: + ret + +; bc = byte count +; de = buffer start +; ix = this +; Modifies: none +Adler32Checker_UpdateAdler32: + push af + push bc + push de + push hl + ex de,hl + exx + push bc + push de + push hl + ld e,(ix + Adler32Checker.adler32) + ld d,(ix + Adler32Checker.adler32 + 1) + ld c,(ix + Adler32Checker.adler32 + 2) + ld b,(ix + Adler32Checker.adler32 + 3) + call Adler32Checker_CalculateAdler32 + ld (ix + Adler32Checker.adler32),e + ld (ix + Adler32Checker.adler32 + 1),d + ld (ix + Adler32Checker.adler32 + 2),c + ld (ix + Adler32Checker.adler32 + 3),b + pop hl + pop de + pop bc + exx + pop hl + pop de + pop bc + pop af + ret + +; bcde = expected adler32 +; ix = this +; f <- nz: mismatch +; Modifies: af, bc, de, hl +Adler32Checker_VerifyAdler32: + ld l,(ix + Adler32Checker.adler32) + ld h,(ix + Adler32Checker.adler32 + 1) + and a + sbc hl,de + ret nz + ld l,(ix + Adler32Checker.adler32 + 2) + ld h,(ix + Adler32Checker.adler32 + 3) + and a + sbc hl,bc + ret + +; bc' = byte count +; hl' = read address +; bcde = current adler +; ix = this +; bcde <- updated adler +; Modifies: af, bc, de, hl, bc', hl' +Adler32Checker_CalculateAdler32: PROC + exx + ld a,c ; convert 16-bit counter bc to two 8-bit counters in b and c + dec bc + inc b + ld c,b + ld b,a +Loop: + ld a,(hl) + inc hl + exx + ld l,a + ld h,0 + AddModulo hl, de, 65521 + ld e,l + ld d,h + AddModulo hl, bc, 65521 + ld c,l + ld b,h + exx + djnz Loop + dec c + jp nz,Loop + exx + ret + ENDP + +; ?hl = addend +; ?de = addend +; ?modulo = modulo value +; Modifies: ?de +AddModulo: MACRO ?hl, ?de, ?modulo + add ?hl,?de + jr nc,Check + ld ?de,10000H - ?modulo + add ?hl,?de +Check: + ld ?de,10000H - ?modulo + add ?hl,?de + jr c,Done + sbc ?hl,?de +Done: + ENDM diff --git a/src/ZlibArchive.asm b/src/ZlibArchive.asm new file mode 100644 --- /dev/null +++ b/src/ZlibArchive.asm @@ -0,0 +1,246 @@ +; +; Zlib archive +; +ZlibArchive_DEFLATE_ID: equ 8 +ZlibArchive_MAX_WINDOW: equ 7 + +ZlibArchive: MACRO + reader: + dw 0 + inflate: + dw 0 + adler32CheckEnabled: + db 0 + adler32Checker: + dw 0 + cm: + db 0 + cinfo: + dd 0 + flevel: + dd 0 + adler32: + dd 0 + _size: + ENDM + +ZlibArchive_class: Class ZlibArchive, ZlibArchive_template, Heap_main +ZlibArchive_template: ZlibArchive + +; a = -1: Adler32 check enabled, 0: disabled +; de = reader +; ix = this +; ix <- this +; de <- this +ZlibArchive_Construct: + ld (ix + ZlibArchive.reader),e + ld (ix + ZlibArchive.reader + 1),d + ld (ix + ZlibArchive.adler32CheckEnabled),a + call ZlibArchive_ReadHeader + ld e,ixl + ld d,ixh + ret + +; ix = this +; ix <- this +ZlibArchive_Destruct: + ret + +; ix = this +; de <- file reader +; iy <- file reader +ZlibArchive_GetReaderIY: + ld e,(ix + ZlibArchive.reader) + ld d,(ix + ZlibArchive.reader + 1) + ld iyl,e + ld iyh,d + ret + +; ix = this +; de <- Inflate implementation +; ix <- Inflate implementation +ZlibArchive_GetInflate: + ld e,(ix + ZlibArchive.inflate) + ld d,(ix + ZlibArchive.inflate + 1) + ld ixl,e + ld ixh,d + ret + +; ix = this +ZlibArchive_ReadHeader: + call ZlibArchive_GetReaderIY + call Reader_ReadWordBE_IY + call ZlibArchive_CheckFCHECK + ld hl,ZlibArchive_invalidFCHECKError + jp nz,System_TerminateWithError + ld a,d + and 00001111B + cp ZlibArchive_DEFLATE_ID + ld hl,ZlibArchive_notDeflateError + jp nz,System_TerminateWithError + ld (ix + ZlibArchive.cm),a + ld a,d + and 11110000B + rrca + rrca + rrca + rrca + cp ZlibArchive_MAX_WINDOW + 1 + ld hl,ZlibArchive_invalidWindowError + jp nc,System_TerminateWithError + ld (ix + ZlibArchive.cinfo),a + ld a,e + and 00100000B + ld hl,ZlibArchive_unsupportedPresetDictionaryError + jp nz,System_TerminateWithError + ld a,e + and 11000000B + rlca + rlca + ld (ix + ZlibArchive.flevel),a + ret + +; d = CMF +; e = FLG +; f <- nz: invalid +; Modifies: af, bc, hl +ZlibArchive_CheckFCHECK: PROC + ld l,e + ld h,d + ld c,31 + xor a + ld b,16 +Loop: + add hl,hl + rla + cp c + jr c,NoAdd + sub c + inc l +NoAdd: + djnz Loop + and a + ret + ENDP + +; ix = this +ZlibArchive_ReadFooter: + call ZlibArchive_GetReaderIY + call Reader_ReadDoubleWordBE_IY + ld (ix + ZlibArchive.adler32),l + ld (ix + ZlibArchive.adler32 + 1),h + ld (ix + ZlibArchive.adler32 + 2),e + ld (ix + ZlibArchive.adler32 + 3),d + ret + +; de = writer (min 32K) +; ix = this +ZlibArchive_Extract: + bit 0,(ix + ZlibArchive.adler32CheckEnabled) + call nz,ZlibArchive_CreateAdler32Checker + call ZlibArchive_CreateInflate + call ZlibArchive_Inflate + call ZlibArchive_ReadFooter + call ZlibArchive_Verify + call ZlibArchive_DestroyInflate + bit 0,(ix + ZlibArchive.adler32CheckEnabled) + call nz,ZlibArchive_DestroyAdler32Checker + ret + +; de = writer (min 32K) +; ix = this +ZlibArchive_CreateInflate: + ld l,(ix + ZlibArchive.reader) + ld h,(ix + ZlibArchive.reader + 1) + push ix + call Inflate_class.New + call Inflate_Construct + pop ix + ld (ix + ZlibArchive.inflate),e + ld (ix + ZlibArchive.inflate + 1),d + ret + +; ix = this +ZlibArchive_DestroyInflate: + push ix + call ZlibArchive_GetInflate + call Inflate_Destruct + call Inflate_class.Delete + pop ix + ld (ix + ZlibArchive.inflate),0 + ld (ix + ZlibArchive.inflate + 1),0 + ret + +; de = writer (min 32K) +; ix = this +ZlibArchive_CreateAdler32Checker: + push de + push ix + call Adler32Checker_class.New + call Adler32Checker_Construct + pop ix + ld (ix + ZlibArchive.adler32Checker),e + ld (ix + ZlibArchive.adler32Checker + 1),d + pop de + ret + +; de = writer (min 32K) +; ix = this +ZlibArchive_DestroyAdler32Checker: + ld e,(ix + ZlibArchive.adler32Checker) + ld d,(ix + ZlibArchive.adler32Checker + 1) + push ix + ld ixl,e + ld ixh,d + call Adler32Checker_Destruct + call Adler32Checker_class.Delete + pop ix + ret + +; ix = this +ZlibArchive_Inflate: + push ix + call ZlibArchive_GetInflate + call Inflate_Inflate + pop ix + ret + +; ix = this +ZlibArchive_Verify: + call ZlibArchive_VerifyAdler32 + ld hl,ZlibArchive_adler32MismatchError + jp nz,System_TerminateWithError + ret + +; ix = this +; f <- nz: mismatch +ZlibArchive_VerifyAdler32: + bit 0,(ix + ZlibArchive.adler32CheckEnabled) + ret z + ld e,(ix + ZlibArchive.adler32Checker) + ld d,(ix + ZlibArchive.adler32Checker + 1) + push de + ld e,(ix + ZlibArchive.adler32) + ld d,(ix + ZlibArchive.adler32 + 1) + ld c,(ix + ZlibArchive.adler32 + 2) + ld b,(ix + ZlibArchive.adler32 + 3) + ex (sp),ix + call Adler32Checker_VerifyAdler32 + pop ix + ret + +; +ZlibArchive_invalidFCHECKError: + db "Invalid FCHECK.",13,10,0 + +ZlibArchive_notDeflateError: + db "Not compressed with DEFLATE.",13,10,0 + +ZlibArchive_invalidWindowError: + db "Invalid window size.",13,10,0 + +ZlibArchive_unsupportedPresetDictionaryError: + db "Unsupported preset dictionary.",13,10,0 + +ZlibArchive_adler32MismatchError: + db "Inflated Adler32 checksum mismatch.",13,10,0