From 49c39e0e4d157c1eb75898cc26084d24e180a789 Mon Sep 17 00:00:00 2001 From: byuu Date: Sat, 3 Dec 2005 21:05:52 +0000 Subject: [PATCH] Update to bsnes v015 release. - Added GZ / ZIP / JMA archive support [Nach, NSRT team] - Fixed bug in APU ADDW/SUBW opcode flags, thanks to DMV27, anomymous for info - Mosaic support is now (mostly) hardware accurate, thanks to TRAC for info - Fixed a bug in SC tilemap clipping, fixes Seiken Densetsu 3 - Emulated pseudo-hires mode, uses a fairly poor color filter to simulate TV effect, the same one that SNES9x and Super Sleuth use - Rewrote the ROM loading code to be more port-friendly, and improved header detection - Added C4 emulation -- mostly correct. Only minor bugs remain, possibly not C4 related [Nach, byuu], also uses code from zsKnight, Overload, and anomie - Fixed noise channel generation for DSP, fixes Dual Orb 2 opening. Thanks to DMV27 for info - Fixed bug with DSP VxSRCN registers, fixes horrible sound corruption in Mortal Kombat 2/3 - Modified DSP KON register reading to act according to anomie's research, while still allowing Der Langrisser, etc. to play sounds correctly - Fixed a bug in CPU BCD math, fixes numbers in SimEarth, thanks to DMV27 for info - Rewrote the windows port from scratch - -- Added triple buffering support (buggy) - -- Added DirectInput (joypad) support, allows both keyboard and joypad to be mapped to the same SNES controller button. Only one controller supported for this release, will be improved shortly - -- Added pause key (mapped to Pause/Break) - -- bsnes no longer consumes CPU time when paused or when no ROM is loaded - -- Updated DirectDraw to 7, and added video mode configuration options to configuration file - -- Video modes can specify screen width+height, refresh rate, and render width+height - -- Added CTRL+[1-0] hotkeys for swapping video modes - -- Added +/- hotkeys for adjusting frameskipping rate - -- Added adjustable speed regulation. There are five modes, all can be adjusted inside the configuration file. CTRL+[+/-] will adjust the speed mode. - -- Added PPU options to toggle any BG / OAM layers with any priority, HDMA effects, and offset per tile effects - -- Added option to accept invalid button combinations (up+down, left+right) to joypad config menu - -- bsnes now properly clears the main window when unloading games - [code] Made destructors for base classes virtual, so the correct destructors will be called now --- bsnes.cfg | 181 +- bsnes.exe | Bin 843776 -> 724992 bytes src/apu/apu.h | 3 + src/apu/bapu/bapu_op_fn.cpp | 18 +- src/base.h | 10 + src/cart/cart.cpp | 243 +++ src/cart/cart.h | 59 + src/chip/c4/c4.cpp | 197 +++ src/chip/c4/c4.h | 94 + src/chip/c4/c4data.cpp | 183 ++ src/chip/c4/c4fn.cpp | 242 +++ src/chip/c4/c4oam.cpp | 224 +++ src/chip/c4/c4ops.cpp | 222 +++ src/chip/sdd1/sdd1.cpp | 3 +- src/chip/srtc/srtc.cpp | 3 +- src/config/config.cpp | 22 +- src/config/config.h | 15 + src/cpu/bcpu/bcpu_dma.cpp | 11 +- src/cpu/bcpu/bcpu_opfn.cpp | 132 +- src/cpu/cpu.h | 1 + src/dsp/bdsp/bdsp.cpp | 87 +- src/dsp/bdsp/bdsp.h | 10 +- src/dsp/dsp.h | 3 + src/interface.h | 7 +- src/lib/libbase.h | 25 +- src/lib/libconfig.cpp | 87 +- src/lib/libconfig.h | 24 +- src/lib/libstring.cpp | 118 +- src/lib/libstring.h | 62 +- src/memory/bmemory/bcart_lorom.cpp | 13 + src/memory/bmemory/bmemory.cpp | 141 +- src/memory/bmemory/bmemory.h | 8 +- src/memory/memory.cpp | 2 - src/memory/memory.h | 7 +- src/ppu/bppu/Copy of bppu_render_addsub.cpp | 64 - src/ppu/bppu/Copy of bppu_render_line.cpp | 82 - src/ppu/bppu/bppu.cpp | 54 +- src/ppu/bppu/bppu.h | 9 +- src/ppu/bppu/bppu_mmio.cpp | 12 +- src/ppu/bppu/bppu_render.cpp | 34 + src/ppu/bppu/bppu_render.h | 18 +- src/ppu/bppu/bppu_render_addsub.cpp | 12 +- src/ppu/bppu/bppu_render_bg.cpp | 83 +- src/ppu/bppu/bppu_render_cache.cpp | 14 +- src/ppu/bppu/bppu_render_line.cpp | 106 +- src/ppu/bppu/bppu_render_mode7.cpp | 13 +- src/ppu/bppu/bppu_render_oam.cpp | 105 +- src/ppu/bppu/bppu_render_windows.cpp | 27 +- src/ppu/ppu.h | 2 +- src/reader/filereader.cpp | 4 +- src/reader/filereader.h | 2 +- src/reader/gzreader.cpp | 76 + src/reader/gzreader.h | 16 + src/reader/jma/7z.h | 29 + src/reader/jma/7zlzma.cpp | 51 + src/reader/jma/aribitcd.h | 73 + src/reader/jma/ariconst.h | 30 + src/reader/jma/ariprice.h | 12 + src/reader/jma/btreecd.h | 127 ++ src/reader/jma/crc32.h | 29 + src/reader/jma/iiostrm.cpp | 133 ++ src/reader/jma/iiostrm.h | 211 +++ src/reader/jma/inbyte.cpp | 61 + src/reader/jma/inbyte.h | 77 + src/reader/jma/jcrc32.cpp | 82 + src/reader/jma/jma.cpp | 552 ++++++ src/reader/jma/jma.h | 90 + src/reader/jma/lencoder.h | 94 + src/reader/jma/litcoder.h | 123 ++ src/reader/jma/lzma.cpp | 42 + src/reader/jma/lzma.h | 125 ++ src/reader/jma/lzmadec.cpp | 299 ++++ src/reader/jma/lzmadec.h | 83 + src/reader/jma/portable.h | 85 + src/reader/jma/rcdefs.h | 61 + src/reader/jma/rngcoder.h | 144 ++ src/reader/jma/winout.cpp | 90 + src/reader/jma/winout.h | 90 + src/reader/jmareader.cpp | 43 + src/reader/jmareader.h | 17 + src/reader/reader.cpp | 20 + src/reader/reader.h | 19 +- src/reader/zipreader.cpp | 54 + src/reader/zipreader.h | 27 + src/reader/zlib/adler32.c | 149 ++ src/reader/zlib/compress.c | 79 + src/reader/zlib/crc32.c | 423 +++++ src/reader/zlib/crc32.h | 441 +++++ src/reader/zlib/crypt.h | 132 ++ src/reader/zlib/deflate.c | 1736 +++++++++++++++++++ src/reader/zlib/deflate.h | 331 ++++ src/reader/zlib/gzio.c | 1026 +++++++++++ src/reader/zlib/inffast.c | 724 ++++++++ src/reader/zlib/inffast.h | 11 + src/reader/zlib/inffixed.h | 94 + src/reader/zlib/inflate.c | 1368 +++++++++++++++ src/reader/zlib/inflate.h | 115 ++ src/reader/zlib/inftrees.c | 329 ++++ src/reader/zlib/inftrees.h | 55 + src/reader/zlib/ioapi.c | 177 ++ src/reader/zlib/ioapi.h | 75 + src/reader/zlib/trees.c | 1219 +++++++++++++ src/reader/zlib/trees.h | 128 ++ src/reader/zlib/unzip.c | 1605 +++++++++++++++++ src/reader/zlib/unzip.h | 354 ++++ src/reader/zlib/zconf.h | 332 ++++ src/reader/zlib/zip.c | 1220 +++++++++++++ src/reader/zlib/zip.h | 235 +++ src/reader/zlib/zlib.h | 1357 +++++++++++++++ src/reader/zlib/zutil.c | 318 ++++ src/reader/zlib/zutil.h | 269 +++ src/sdl/Makefile | 76 +- src/sdl/Makefile.win32 | 42 +- src/sdl/bsnes.cpp | 2 - src/sdl/bsnes_sdl.cfg | 2 +- src/sdl/rom.cpp | 99 -- src/sdl/sdlmain.cpp | 20 +- src/sdl/sdlrun.bat | 2 +- src/snes/snes.cpp | 17 +- src/snes/snes.h | 1 + src/win/Makefile | 54 +- src/win/audio/audio.h | 13 + src/win/audio/dsound.cpp | 108 ++ src/win/audio/dsound.h | 32 + src/win/bsnes.cpp | 562 +----- src/win/bsnes.h | 81 +- src/win/bsnes.lnk | Bin 501 -> 431 bytes src/win/bsnes.rc | 6 +- src/win/bsnes.res | Bin 244452 -> 0 bytes src/win/config.cpp | 88 +- src/win/input/dinput.cpp | 222 +++ src/win/input/dinput.h | 34 + src/win/input/input.h | 14 + src/win/main.cpp | 104 ++ src/win/main.h | 8 + src/win/rom.cpp | 101 -- src/win/snes_controller.bmp | Bin 240054 -> 0 bytes src/win/ui.cpp | 88 +- src/win/ui.h | 441 ++--- src/win/ui_inputcfg.cpp | 219 +++ src/win/ui_main.cpp | 570 +++--- src/win/ui_ppucfg.cpp | 72 + src/win/ui_window.cpp | 319 ++-- src/win/video/ddraw.cpp | 232 +++ src/win/video/ddraw.h | 43 + src/win/video/video.h | 24 + src/win_legacy/Makefile | 123 ++ src/win_legacy/bsnes.cpp | 570 ++++++ src/win_legacy/bsnes.h | 83 + src/win_legacy/bsnes.ico | Bin 0 -> 4286 bytes src/win_legacy/bsnes.lnk | Bin 0 -> 501 bytes src/win_legacy/bsnes.rc | 3 + src/win_legacy/cc.bat | 3 + src/win_legacy/clean.bat | 1 + src/win_legacy/config.cpp | 60 + src/{win => win_legacy}/dd_renderer.cpp | 0 src/{win => win_legacy}/dd_renderer.h | 0 src/{win => win_legacy}/ds_sound.cpp | 0 src/{win => win_legacy}/ds_sound.h | 11 + src/{win => win_legacy}/lib.cpp | 0 src/win_legacy/ui.cpp | 58 + src/win_legacy/ui.h | 281 +++ src/{win => win_legacy}/ui_about.cpp | 0 src/{win => win_legacy}/ui_bp.cpp | 2 +- src/{win => win_legacy}/ui_console.cpp | 2 +- src/{win => win_legacy}/ui_inputconfig.cpp | 4 +- src/win_legacy/ui_main.cpp | 408 +++++ src/{win => win_legacy}/ui_memory.cpp | 6 +- src/win_legacy/ui_window.cpp | 163 ++ src/{win => win_legacy}/uictl_editex.cpp | 7 +- src/{win => win_legacy}/winmain.cpp | 8 +- src/{win => win_legacy}/winmain.h | 0 172 files changed, 23583 insertions(+), 2371 deletions(-) create mode 100644 src/cart/cart.cpp create mode 100644 src/cart/cart.h create mode 100644 src/chip/c4/c4.cpp create mode 100644 src/chip/c4/c4.h create mode 100644 src/chip/c4/c4data.cpp create mode 100644 src/chip/c4/c4fn.cpp create mode 100644 src/chip/c4/c4oam.cpp create mode 100644 src/chip/c4/c4ops.cpp delete mode 100644 src/ppu/bppu/Copy of bppu_render_addsub.cpp delete mode 100644 src/ppu/bppu/Copy of bppu_render_line.cpp create mode 100644 src/reader/gzreader.cpp create mode 100644 src/reader/gzreader.h create mode 100644 src/reader/jma/7z.h create mode 100644 src/reader/jma/7zlzma.cpp create mode 100644 src/reader/jma/aribitcd.h create mode 100644 src/reader/jma/ariconst.h create mode 100644 src/reader/jma/ariprice.h create mode 100644 src/reader/jma/btreecd.h create mode 100644 src/reader/jma/crc32.h create mode 100644 src/reader/jma/iiostrm.cpp create mode 100644 src/reader/jma/iiostrm.h create mode 100644 src/reader/jma/inbyte.cpp create mode 100644 src/reader/jma/inbyte.h create mode 100644 src/reader/jma/jcrc32.cpp create mode 100644 src/reader/jma/jma.cpp create mode 100644 src/reader/jma/jma.h create mode 100644 src/reader/jma/lencoder.h create mode 100644 src/reader/jma/litcoder.h create mode 100644 src/reader/jma/lzma.cpp create mode 100644 src/reader/jma/lzma.h create mode 100644 src/reader/jma/lzmadec.cpp create mode 100644 src/reader/jma/lzmadec.h create mode 100644 src/reader/jma/portable.h create mode 100644 src/reader/jma/rcdefs.h create mode 100644 src/reader/jma/rngcoder.h create mode 100644 src/reader/jma/winout.cpp create mode 100644 src/reader/jma/winout.h create mode 100644 src/reader/jmareader.cpp create mode 100644 src/reader/jmareader.h create mode 100644 src/reader/zipreader.cpp create mode 100644 src/reader/zipreader.h create mode 100644 src/reader/zlib/adler32.c create mode 100644 src/reader/zlib/compress.c create mode 100644 src/reader/zlib/crc32.c create mode 100644 src/reader/zlib/crc32.h create mode 100644 src/reader/zlib/crypt.h create mode 100644 src/reader/zlib/deflate.c create mode 100644 src/reader/zlib/deflate.h create mode 100644 src/reader/zlib/gzio.c create mode 100644 src/reader/zlib/inffast.c create mode 100644 src/reader/zlib/inffast.h create mode 100644 src/reader/zlib/inffixed.h create mode 100644 src/reader/zlib/inflate.c create mode 100644 src/reader/zlib/inflate.h create mode 100644 src/reader/zlib/inftrees.c create mode 100644 src/reader/zlib/inftrees.h create mode 100644 src/reader/zlib/ioapi.c create mode 100644 src/reader/zlib/ioapi.h create mode 100644 src/reader/zlib/trees.c create mode 100644 src/reader/zlib/trees.h create mode 100644 src/reader/zlib/unzip.c create mode 100644 src/reader/zlib/unzip.h create mode 100644 src/reader/zlib/zconf.h create mode 100644 src/reader/zlib/zip.c create mode 100644 src/reader/zlib/zip.h create mode 100644 src/reader/zlib/zlib.h create mode 100644 src/reader/zlib/zutil.c create mode 100644 src/reader/zlib/zutil.h delete mode 100644 src/sdl/rom.cpp create mode 100644 src/win/audio/audio.h create mode 100644 src/win/audio/dsound.cpp create mode 100644 src/win/audio/dsound.h delete mode 100644 src/win/bsnes.res create mode 100644 src/win/input/dinput.cpp create mode 100644 src/win/input/dinput.h create mode 100644 src/win/input/input.h create mode 100644 src/win/main.cpp create mode 100644 src/win/main.h delete mode 100644 src/win/rom.cpp delete mode 100644 src/win/snes_controller.bmp create mode 100644 src/win/ui_inputcfg.cpp create mode 100644 src/win/ui_ppucfg.cpp create mode 100644 src/win/video/ddraw.cpp create mode 100644 src/win/video/ddraw.h create mode 100644 src/win/video/video.h create mode 100644 src/win_legacy/Makefile create mode 100644 src/win_legacy/bsnes.cpp create mode 100644 src/win_legacy/bsnes.h create mode 100644 src/win_legacy/bsnes.ico create mode 100644 src/win_legacy/bsnes.lnk create mode 100644 src/win_legacy/bsnes.rc create mode 100644 src/win_legacy/cc.bat create mode 100644 src/win_legacy/clean.bat create mode 100644 src/win_legacy/config.cpp rename src/{win => win_legacy}/dd_renderer.cpp (100%) rename src/{win => win_legacy}/dd_renderer.h (100%) rename src/{win => win_legacy}/ds_sound.cpp (100%) rename src/{win => win_legacy}/ds_sound.h (70%) rename src/{win => win_legacy}/lib.cpp (100%) create mode 100644 src/win_legacy/ui.cpp create mode 100644 src/win_legacy/ui.h rename src/{win => win_legacy}/ui_about.cpp (100%) rename src/{win => win_legacy}/ui_bp.cpp (97%) rename src/{win => win_legacy}/ui_console.cpp (97%) rename src/{win => win_legacy}/ui_inputconfig.cpp (95%) create mode 100644 src/win_legacy/ui_main.cpp rename src/{win => win_legacy}/ui_memory.cpp (96%) create mode 100644 src/win_legacy/ui_window.cpp rename src/{win => win_legacy}/uictl_editex.cpp (91%) rename src/{win => win_legacy}/winmain.cpp (89%) rename src/{win => win_legacy}/winmain.h (100%) diff --git a/bsnes.cfg b/bsnes.cfg index 78af5efa..23359e55 100644 --- a/bsnes.cfg +++ b/bsnes.cfg @@ -13,127 +13,190 @@ snes.video_color_curve = true snes.video_color_adjust_mode = 0 # Mutes SNES audio output when enabled -# (default = true) +# (default = false) snes.mute = false # Regulate speed to 60hz (NTSC) / 50hz (PAL) # (default = true) system.regulate_speed = true -# Video mode -# 0 = 256x224w -# 1 = 512x448w -# 2 = 960x720w -# 3 = 640x480f -# 4 = 1024x768f -# (default = 1) -video.mode = 1 +# Slowest speed setting (in hz) +# (default = 16000) +system.speed_slowest = 16000 + +# Slow speed setting +# (default = 24000) +system.speed_slow = 24000 + +# Normal speed setting +# (default = 32000) +system.speed_normal = 32000 + +# Fast speed setting +# (default = 48000) +system.speed_fast = 48000 + +# Fastest speed setting +# (default = 64000) +system.speed_fastest = 64000 + +# Video mode at startup +# (default = 2) +video.mode = 2 + +# Video mode 0 (windowed) +# (default = "256x223") +video.mode_0 = "256x223" + +# Video mode 1 (windowed) +# (default = "512x446") +video.mode_1 = "512x446" + +# Video mode 2 (windowed) +# (default = "640x480") +video.mode_2 = "640x480" + +# Video mode 3 (windowed) +# (default = "960x720") +video.mode_3 = "960x720" + +# Video mode 4 (windowed) +# (default = "1152x864") +video.mode_4 = "1152x864" + +# Video mode 5 (fullscreen) +# (default = "640x480@60:640x480") +video.mode_5 = "640x480@60:640x480" + +# Video mode 6 (fullscreen) +# (default = "800x600@60:800x600") +video.mode_6 = "800x600@60:800x600" + +# Video mode 7 (fullscreen) +# (default = "1024x768@60:1024x768") +video.mode_7 = "1024x768@60:1024x768" + +# Video mode 8 (fullscreen) +# (default = "1280x960@60:1280x960") +video.mode_8 = "1280x960@60:1280x960" + +# Video mode 9 (fullscreen) +# (default = "1600x1200@60:1600x1200") +video.mode_9 = "1600x1200@60:1600x1200" # Use Video RAM instead of System RAM # (default = true) video.use_vram = true -# Wait for vertical retrace when updating screen +# Use triple buffering # (default = false) -video.vblank = false +video.triple_buffering = false # Show framerate in window title # (default = true) gui.show_fps = true +# Allow "impossible" key combinations for joypad 1 (not recommended) +# (default = false) +input.joypad1.allow_invalid_input = false + # Joypad1 up -# (default = 0x26) -input.joypad1.up = 0x26 +# (default = 0x80c8) +input.joypad1.up = 0x80c8 # Joypad1 down -# (default = 0x28) -input.joypad1.down = 0x28 +# (default = 0x81d0) +input.joypad1.down = 0x81d0 # Joypad1 left -# (default = 0x25) -input.joypad1.left = 0x25 +# (default = 0x82cb) +input.joypad1.left = 0x82cb # Joypad1 right -# (default = 0x27) -input.joypad1.right = 0x27 +# (default = 0x83cd) +input.joypad1.right = 0x83cd # Joypad1 A -# (default = 0x58) -input.joypad1.a = 0x58 +# (default = 0x42d) +input.joypad1.a = 0x42d # Joypad1 B -# (default = 0x5a) -input.joypad1.b = 0x5a +# (default = 0x32c) +input.joypad1.b = 0x32c # Joypad1 X -# (default = 0x53) -input.joypad1.x = 0x53 +# (default = 0x11f) +input.joypad1.x = 0x11f # Joypad1 Y -# (default = 0x41) -input.joypad1.y = 0x41 +# (default = 0x1e) +input.joypad1.y = 0x1e # Joypad1 L -# (default = 0x44) -input.joypad1.l = 0x44 +# (default = 0x620) +input.joypad1.l = 0x620 # Joypad1 R -# (default = 0x43) -input.joypad1.r = 0x43 +# (default = 0x72e) +input.joypad1.r = 0x72e # Joypad1 select -# (default = 0x10) -input.joypad1.select = 0x10 +# (default = 0x836) +input.joypad1.select = 0x836 # Joypad1 start -# (default = 0xd) -input.joypad1.start = 0xd +# (default = 0x91c) +input.joypad1.start = 0x91c + +# Allow "impossible" key combinations for joypad 2 (not recommended) +# (default = false) +input.joypad2.allow_invalid_input = false # Joypad2 up -# (default = 0x54) -input.joypad2.up = 0x54 +# (default = 0xff14) +input.joypad2.up = 0xff14 # Joypad2 down -# (default = 0x47) -input.joypad2.down = 0x47 +# (default = 0xff22) +input.joypad2.down = 0xff22 # Joypad2 left -# (default = 0x46) -input.joypad2.left = 0x46 +# (default = 0xff21) +input.joypad2.left = 0xff21 # Joypad2 right -# (default = 0x48) -input.joypad2.right = 0x48 +# (default = 0xff23) +input.joypad2.right = 0xff23 # Joypad2 A -# (default = 0x4b) -input.joypad2.a = 0x4b +# (default = 0xff25) +input.joypad2.a = 0xff25 # Joypad2 B -# (default = 0x4a) -input.joypad2.b = 0x4a +# (default = 0xff24) +input.joypad2.b = 0xff24 # Joypad2 X -# (default = 0x49) -input.joypad2.x = 0x49 +# (default = 0xff17) +input.joypad2.x = 0xff17 # Joypad2 Y -# (default = 0x55) -input.joypad2.y = 0x55 +# (default = 0xff16) +input.joypad2.y = 0xff16 # Joypad2 L -# (default = 0x4f) -input.joypad2.l = 0x4f +# (default = 0xff18) +input.joypad2.l = 0xff18 # Joypad2 R -# (default = 0x4c) -input.joypad2.r = 0x4c +# (default = 0xff26) +input.joypad2.r = 0xff26 # Joypad2 select -# (default = 0x5b) -input.joypad2.select = 0x5b +# (default = 0xff1a) +input.joypad2.select = 0xff1a # Joypad2 start -# (default = 0x5d) -input.joypad2.start = 0x5d +# (default = 0xff1b) +input.joypad2.start = 0xff1b diff --git a/bsnes.exe b/bsnes.exe index 8514825d05380a4be37292cb85db80df60fb6851..dff32fcc5a6b63d67a7bd1bd15d1bb9adfdeed09 100644 GIT binary patch literal 724992 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

Z;S&8sei2P2+VDkH;+@Q@y!gA;i|KSXl@ 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 ^)oIaN@Kvk}C#pChx=Ab9sSup$8T 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 literal 843776 zcmeEveOy%4_V*cPfDzF%8Y&SfiD;~Qd`4WMADXk`;|t?R9sv>WBYnSo)B zV^pTqEnl*_y=QNrWjKKHVy38Bmfb?@)|s%fEYwQp`L2D=nHONFp5O2JJpW`J_S$=| zz4zLCuf6u#Yro93H&k(2j^lLr{C3= z@ojgdEWY#ax88Z&m`t*r2Dp^0@ z@JhzFPk9%p-_C`vEU1Hj=fZav{KDLq7u*7O)XVD@9A)m43)d2!;}+mM7Q=!ktVW9%04_!t zwFbfXsGaDa>T(NsISye_ECxt~OMjRfa9He1U+cLQ1g-DQ&3=bk{{QZu;h;Xek3i>G znSgFH9A&*_sTDC7<)c1C7u8jvPfd`5&{20TywQ3i#~raFlGM#mLgR8X1k8t zqeVjsd_NlUm~X#fQcAgp3qwwG{uQWS#h+tVbshT}30)h@2Q zhVx8AbPC5wb&D8*87-cBHI*^;28~$v*a^Ql&|atEUu`HmMJs7b<>af+lzwGz)Rmpt zgv8?KrS+iPz)5x`wSVIx3O`m^YBaRRc>YC=c4)HEka6hKG2Fo%>hJAf6N76ZhAh zimv-5&fQ=fA{-tf92sKw^Som<67bdDk4uexE0K<`nQZJ!8T8@eIgUaOw(!+0A1^{w z^}a)M$w6S{bfW=beO(GRk`p2l2NEVAJ^=|5+|!K-l{1axHz4PP9&r^sH}bWhKug_` zXt*tw-h1JAnYVYVrE(zJ%ib7O_A4iV{1pSy`}QWye-7IH25Jd@#mO^%|NJ7xDs$}H z;~UcVrMI}I8~e6DvuhVxP{&CV{zhDhp4zzRcJy<)>_tB>Cht(@MHg(ek~e{Q_3$nw zZ#475UbC^3yd5Rfm5Cb#c)*w$-i^9zui-2k_3UpP`y0>x_GNz!`1@3U`g0cjNuWOi z@TV#Iwl%N>A>4G zfXZkFWN4zW96-l*vsk2E95oqXyHJKHhstdQR8_-L06OMkc*xKE20^=!wrw#%@kG_f zgcpxoyisAIKY||l@f+)C*%FqMimatgl-1Z*J1ufiGLb?2g6jj?O z_BX`-Z7$WQl+vbAY$;-oEu|E{fdWGx%Flq`dPG@@!1av4%cIVYfQSp@{W1ibZ=i29chD;yx`IY3KhQS3;h^)92d&jI2Rsk9N4 z-vNR~Qn@@qFM$?s1>tQ1bZk6J0cb&QhKKx2KM_?gA#K$zf{r5Xzh+VyM-f}~G2uyo zw&hdUBH#%v6t)O>U*s`7@Bo>AW3fnKzMr6@ND<5Mjv+QRmr}GLZK|0;z)QW3 zpasB7oxvcAy_UryMeCE4%L2li&cX=q8iJkx9)C515PLj_pk%~;G?hV!eJ7j6BA0bj zSQzl^lUW$>ZkZHVTCCKxezvf2thf3 zE*MOZ0i$u#e<+tKziuHF8Eop$N3K)d6 z+)Rp1LyGlwhKI1Ku~fHB2unFlVZ{hrpFq-f1VOVY>=<&HdN!4@7|>aX3_|SZ0~v(0 zFAgB+7{Zn$FbH9j&LU_!s50C_ylV#3+Mn{v0kr8DQRMdj7G@wmK2ucQ2W1!f%fHv|3Spl^UqH>LZ)_+ZS3jzJRFU8IV zRJDU>IS9z~BDL;pw3;cNg#kMDGo_u4wlcR->_I@S&oMkub&U1GK|ob!DCb1vBD7Q5 z6hPUmj2y&nT0^leKnvy))C}l_K16{Yc$-dAelC>OT0_vID8qCXmAjrGk@C}n!^~cS z5~;Kr%H>f&-U|tDJ0Rg@iuEFwO|2B0g0NCn#&(3IoTk`fgsmS;&K{}*0 zzeSJ(Ih#Lc5XwzsqfrOErf;bpI->7Wisg`E8VeH;+j^8@GZEXil41qKdSe-cuwyq9 zG#UAs_A>}6j_qU+V$&K4%0#UB1%fss%#cM8N4?cbHR6z>Z68s;9e9i1r`YWXTfd5> zKv?Taij`0<$J)La(DYu^3+s{JW_=E~Syd@F(3;HYl^0L6VzZuQmZr>S>LUgqBI#dn z#kGN!O@(F|pDni17wY7tQHCFDuB;|2Z zu6~T;9W(EU=J*<6IJ}l|@zH!mF#| z=JGXt1|uNPoXgt2(P2!+LXnf^fKtAum9L3TX3?JMMq}~lM{}^KeC~Q3=b3CAY2ThA zwtD3DaO>?}jibeO!n4&Fj|@`l?M;67bsDUI5ZWO&*ZHEvACEsHHg7Op1C8Ub_W09$ zb=3(J#aFvJ;N-#kGxG{R!N~>P*HOtJd$Bu4U+Pt0WwLW z?b}Cyq=iQAkJAHEJPt;)`S{af?~+?`{R2kanZwO5nj@}YZJM!5y0uWpm2IasS6D7r z`X^R%(p!l-EUHWV(r*9?q}3q2K*9=*yFSR8tiQ{SQmX{}N)MmtFdEj9%lUo%rK`rQjWH42QTLCF6B6vW7HFH#HP0eQ0UeN3$Zt9%9;ceRCeZj{4L!rlo|(V zc*hxdz*tnttOnlsE8Ipo6K&mBu^6@~o>il$tx{Vz+txh{T7KSw&f+q>$Dg%EpX0S3 z^pjohGX#k{ROTUpm|E}2(QLleVzHd?)%_AJep52D?%N^gh-B?Iu-XwPN_T8x9OC>| zym%9;*)-XhI1?GcO@=cW?nqRIyMI&JAvXl_)efTr zAhnre=ik7clExMf5f@uL0M!Ksy2y%VN2}-M5!H(tFi4k_`;E@3VVK@Q2xsCr-<=?s zJXOfdcZ=eIu;YBQ;i>xEXyTRw--fRk-)23ergV2|=zOL_&<2t>POtTHVKa#f&N_+& z4k9444RFjf3+VHt-ZL@V6E1iIt%|DZCFxhcAJ~))sC_z@c!Y`}(L75$YX9=2{pjdm zFQ*AxM!@u@fhAVFPtQBW1oA| zf8e@ZpV8l{Pw`i~v+NX|TH{yM33VyL<^#((E}eHIAu(V5vFF}4yFYpf?>D~d9l^T5 z-~}>xd1+938BrBSf1JbY=Q*#O^YzB(rX*2v7p$;K;?Yfhs5 zaIF25mUlL9KGy&tqzFiOb!)y48i$T-oD#V$K zpTJ*fC}Q)AHxZZ!P+UnF)%oIbi<>D-M`5cOk&}%hwDo9(;xTA~#pB&Mquu7Kaw%X~ z9)(=s&M~^p!&%5BEF{&PGsQ(rLuz z%50se5lzY9zJxq~t9!DMcX%7i83S2$V>PyE>0asbvuR5K17<9g39#UK*I}mn?>tOf zo4x2kvSj;iKm(wd^guE3P25xhc8>eu`;nJ4@_(_Cc zhVN;>FYl1Q9e`#2#te8d4T^$7&s#?^dbStLE`LsF`6qZKKT&3?i-BWl6dLtxw2@4Y<(P& zO`LH91f)p^0^z{=4MsnP89WuziDvLpAQf!M$({y&`wbdeMhw!ATa=9jgY_Iw1?^x+ z7#u3jPjN82^T2U-Sfi!k1JM{2j27tzz?(Gli)5NN&!vPP4iA-2GZ9o!STY^sfn-kO zWaC0k+V}$6s*e$kF7`vMNf04^b}nRL{B)EH?lJ&QTo3jH1B?JXQU}`MnmB`=+??c9 zIh=pMXv{QVyQ}0Tl9F`T8(>V`${JKh*-9TIfc{r$5cScqGR27of2%l#R4A$NXN0dX z66y0vKin{#L?C$^ioZlNnx(Q?)Vg}77yN9^D2bwNqysmj1~YWI(nmJ|D9}Opb9t57 zdTA>n?Aw#=_v<0lo>>@;=4nXx${kuD{@^YzMz8SI%{q;Xfnps^9+%}8O0Ue(U=u3_ z0$Tz65D=l&$K*d%_hs zGNhM|v&9Q3XZUK)-bOm9pw|T8U3@jmKcBBIN}}S2X)ueK?O$lbrn)a;rFY>|P+Fz9 z1{Xv2O0iTN^iJ3d6e<=tR4V=nc{+cNT1(b`SVO4oN1)t(3GVC|akq31a&cwHG?r5g z*Qs@O?aHSN3gYaIaqec}h{o%SwO%On)kJyY>fHzV>IOzjqcjYKIu7xx&<>5#NwiOq zyJ1d)F#JsW0l6L2zxnQiLIdhg36VAOr4(Nwh2xNwr*>?VXr02+Z_<$WdDTmK$nDuj zNLki_K8s_zx}X=@e_-nVu3a6hg*4QZwlTs5XIfvO26D4d3}e?D3)FUc9Zjn?AJN?; znNZOD27%YKx2WXqa%Zz{F>2(D1?KtCVf0hoG`N7*3ty;f6ZO3xRF$FKq@6dUU#p7El1%HD)NYLX^` zc8jxvcht^Pn~Ij{g54uT4z2|y#3Z1~$^ zNv+H0YYujxLmu%Kh(9W<@(vm`z-cStu{aO$Pogt0RmNNO;LV~rlWg{7+#**ek7Zg#G?6h=_b@edbV>~zGfvcaW3n|Vo-pbv30(>uTpPDpCwwu z$uwpI0}7yI>9tbE0D3{JMa%GMP~}NOg9u?q-q(tgg}x&>uVXe+K#=Uj@Er@HOF6`yWG2C&q||TyeL5 zAI7Loqt47S#_!bkm|Wj#l5?TbJFLD-FJ_I{LwyT?@jLa+07|gF$V;y6(Xrv&ehUb? z8Q&6okKn7pr`DC+I7n}Ycj-ClEwE9XE!-5h3?gn2^9_LSG(_`jn)WY*tK)?B{{s3N zP!s~yJlti(-x$15_82JE6U+4$ZjAf9kp-xhQaJ@{zi@er@kIekW?_68E;Df**-0tZ zBq$PT*TT^Fp{sl>Vx{AQv=Fc3Sz+Zj!YOzyMp_vc#Yrt48kR;DzD(rCJgikkt}I)h z0S=S6E|wM|Y2&XN8dJxy8LUU48{t;$&*r+JPn{2DgT8~I^i@p{p)Ws3Urly@EtBg7 zuSH4iy+Gpdt|XpS6POib5~G472Iqute-6+sPYE=A91I5DfQBe)fSqtP2zwEq7vCg& zkH*V$!ufLlp2vE0h}^%h9ynU8u}V({d-*@Cmp@m#mbnr8cUoLZ#Qv380{TCb2dey> zg!W6xG^s+!>@M#Q>LH=Y5JVy;T#iztJk5_793r5)4p3#F$wHE^!Ge6C*xwu&zmdn` zu?qv^kP2h%Sa-RJYs2TqXUE6afUJ(x-6!fe0UDfbO!^^cGtRP^(Hu3V#o1yT{)WbJ zXb$W|6o%2^Y$+Y|8g@H)$L(^6rr`~!sOg8=52m-Ye;wAmyP%~X*4Tz4uQRO@2I2+O zQM~h)d^vxpz|>h7PZ*00$X(NZkjiMlQ2dB;>Ome|K=aiV)ZR?`E0asEJiB}VgJNKA zirA*=4A8kvBhOk-jOa8$xM#!&hXn^C!IsX}Y)n1fh&3s+V8?3eH`}?ibZC<9Kp^@n zJI2zBi8ClWP`r1gd)p6##|NM?qw(eA`v<<`V5Z#PX@E^vc5MJQlkGnz*Z|j)rqxO8 zhq4XDhv+Mtp{*mDZbbYc$W5o?xqUdE`8INIJ$3N}pS zcKP$8ZTav(tI4CS>Jkt)1K;iVj-zf=FLyJRtTqxU0RAT%}j?7^ggcc1VK|cZTcF zFAS60Q&Ig$YwtW>%0(77-4mw|xG%Ooa%}1^ZHWf++ zEe7=iv`}_c$TMQa0(f!@UyGLR)xsiu3`v$hi8+wv#1+kN5U0kAC#1FC({wU5UQ^^^ zDX@B8mdIH@_kFrF23fK&%H%T2Zbo0`v}1#q7Vrc#z*ZF`BD2AxOkl|AiLur56%Yz|@r^_?p|Ig5KgN_;n$E9sDsN z{up~5wg>h0?=-fWvN|pX8NO~rarUMtd%MQtSj=HjX!&d+KZ3@EW*8EFoR-l9$DzAr92C1g0C#6dMPIx>JInS_MsCYdoO_gH>3|GWE>yQX?3`ZD1TE;GRu|ZmQ8Cigd^o9)%;z+Y@3%SZA2me0W!<-0K z+CX<4%*CT&rG^PRzIZZ#TrOd2(3H+z!xvq!lN4A_mpL$=I5f>Fu z{t)n-N`2@0ht8igby{yA0!J&_icH`O(9Y2`%`mr+1te&OD?1*(@a|gmuz9ho3Kphi zDkMf&k6hrM5HnKaZuNc>YfHx3lUA`w>H8e}Y>Dulc1?(JJxHsP24NZ&vPWLmw0{XG z3gMj=F%kQZcMuEx**>(0&BJI5*iz@~0|fz_G!UyHy?gt)P|W0D2!^*Ye^5LrSNo6P zu$QwHwp7@yQ_;a^{$K~Ab2Wj^-9-m#=&%{KtXm7}zA?Zq`_(f0nel~u%}Ktz8MB~! zt}(%0UuFNL&yr*@H^sTnI)o*#*ZW~&Sj-Kr7jqL`$p+yb2ruVXNP$gE9Iuc&B)q+_ zPb^bOy6eK7^P{4jZa9^QVT<81+rx3Ofjor!Uh4QnS`W8`CbfGy?GSRQoRM3?~c{+v)t zSJwd-Hln&lhmFT>`c)bP`5gITr7s!9ZZ7)EG4g?RYidcK|_$qv_;(H%o20rW$ALDCw z@->aAb#xe)C-c)y!mV-3#O93M89xsF;Y1@Rklb830o#U$ipaF(tON|4ZRtC`K3&IN zq=8N9JcOLE;AFyu%(!>~yr-cyK1s&K-okZpng;yQxBmy>X9yJ9pMcfs0H7$@c13-_ z*1zHHuXRzzUDd|4rsw@twxkwSJ-V6ZR7|*4d;G1vX{bP<y7;*qAew1!l^w4ce)_JIkR?GBsppa&!N# z^*M!lB!alh@ihQe>tjEfl0S>D_7#XfryshlBMS8&tHB8=USqr5^X9<5P-BEs(bkz` zHP$p*-PkbZEkN8wqE1~9%suHC#K=F zYk@Q>3~?;<)x(H)FW(6?sL=z(ytKR$O@R~>j;FQO0@v|GMl?sHac^Li?tTer_?kLE zF9U+T7LzCWUXgu?vhP*+pi7?usq!&TAg1t+dC1D`xM&q(QEo5PXgxsdja1A`#8FjG z!bKG*0)&18AJCqKk9WKbkF(`&tQHp;Q!>0+J4(m#HG8t^mR;t4&lR7BBiLCJ)9#eW zkU}luUj81E`I4aL^VR!v^9rP44%T7;gz3h!+8eu$=dk_SD==QR=bBJyd1|xQYiK~{ zNzG8#i5nDN!X`YiF(w~!EI2Xtf1&t=aThodhd4B>Sjvkjy7HKuj|5#?qws#xsz!w|j-Yi{erFe?crZBp3e z^=l1F9`n1l7Bb5O*aLQ!$B?Da{x6kR$&E-q?{J|uTM?1wRv#$PLW zaoxz-VyVnYu`b>MGd@?DGor!n?jZ&S3-B<{1Y0O z&Cb%NiQ#H@I6sBVceasT2U$mUoIv?0nb~3kEJ4fkQ{7r*LdGJ2k;@CvpU=)(kugi9 z-;IN@GWoaT=zh?og@dtpPl@zNFYXZ*6!OkCbPs7?(~T)OfZZ0OEjtOj5uVvetou6p zT|HW~J&q#jZe3H9n49d5ztTPTN{wgqmC!j1xVO?DZa@3CSkU5LUIGHn;$4rRD=?xD z@h&f*j>*RU0`J738v@S)n2u6uS5nhaXM%x|VIwL5o;6AWfV-3g051(DNa3B60}0@{ zUr7M)4J83UI+CrFVBnoefduf(QxX8YQ%L|2`>tvNy?om*eIJnoPlA#F;B86*fOiKI zU^jCwT1rj;k5)+lFilASa7-{k6z_Z}kN}=~c&L>Co&X4o@>v}X@DSZfEi|_Ia{e0I zGD3yMyOmPmY&k2>`K=4J$fX@PDA1xyhlK&QdJ-FTaj-X&ni~%_=XH(G0u(qN*{PA8 z!^~-4hltxS>%hU|-sD_FlTm$?FB1sl(gi3`T$@NcW)>8k-fIk;prZXjt$=inyF3&cF>ua7807%qX1khQ>DwC~(eY zPWyuZ=TdJpMEO$X7>nY)Rrba!-tme%SPs5J_O@40%3L|@E(K){+;9OGS-#B=lh5uM z`ha+&8CMZ-0P8HN_1Nkm-J;&H&w3GQ1Eey*V;dS!8K9Bc&LM5VHA62;?IKbhmbHJt zG=@X?t3gyiK&wpj7 zNDbM35YQ*V;v>&TWXg)N<)TBFE=;P8TzN0={0~Y9sXVJY?a9V2&1Xx8Tp~!gKbEtl zZ_;4tkVbtH=!%H-lGP_1Zrg?QVGD&98SO8F?jG!;=R^N9-)Mq;l$kQgOe&oV({zY& z^v~Hw+c4;4-i%}$tp!H7ZS>dz7z{wf#rXCEF5qjdMi}q+gequyZ4WZiukg2~P-Yte(@J3Wpr!7dxh zqR9+>26F?mFh9<$%WdfNMnL-ZWAa5EHi)m}oTLkyuj9q!laAj5RI zjFnVQ`6I^=I}h1VI|`)_G1K*futImL+8YiNhMmQ{hOzE6V%^|5%w)#DNyNW25@IYT zS}o@>RSe0&sYi%YyT)K``vzm#x7OYS`!T-H@SVnI#TPRCc3DIhoh7sM%E5DvIR<$H z!a7jMNfIpfr3TL0k98_9EJ(t6*9A$-+9h=1w}@xYo$Tt=-XRLCH5Ywq9Z92jvO7Cg zZf96i6!G=Pn!9szc5ZH^J(&%dJ?#7IgjgIdz>O)$?csDFLAXaTEU>fjr{%-) zbQ~c{J%PZECL9cHEWmUL{RjVm?R3zE-6n-{Wypek5^p**!1~0L1MFMl7>o{&C(gE$W=IQJiQ7FdVCr54k~{Axb`j z7*2wc54k~{Xa#2g!#S$tLv9eKJzs90fedG>Qg?ELIPXA-CwdYX&R9A%1)9kX;=HWn zlgMypDmdf@aUNE1&IXQfKMqwmM1%;n)HfXOTn2K5T54c;?q{B4*>fNB43<5m@X$dF z*m*Hg*Whtir7&_kcmE8W8YI#wBXPi&3MRQhvPLU7=Q5nFN;8og#7RB# zRKYoq;Ve?Fve&sr~ZSF@eF1b z^7$S?#&k=U^NnLg5bFVlI2jHxJscVzaA+vOp-~5i1}GdF%WxQ1HkOlwfR8b(T10xF zh^w(?4@j}d@=>pz*E&Z&uO(}|3G#6*eduwmFXW?IdR0&cG6xWOKHwd1p~5scE(y_M zv9FDbw4If?@M)6LoRlYRcbyUAa|JTO^rmU`!Fc1!Kn65-Vs#9N=1**R+t_S+7fp{L zr6FIKo7nVEQ$FvERSVD472-`C9FT%CUC6Y6`Jrk+&+`sSIpg~~rgzM{l{8nki)x#6 zbDmr$pC{)lUO+YIO!?wPCU&eFheozd#AFOPlZbsRZ1$)p%*&5NsYlCe3UNrPVm8pU z^@TVjbT)|bR9CE<2+SV=LRN?MIfm6Con zswI`lCH>;k;DMrK<=FcJR1=(EUlPB8)#1asZ+f}oqfmzjSRGzkM|EKJv45k>lg0*f z-Vn(70y*dPl=D7|K56(ciuF=$B~?#9V1^L8Og?|vVP!ch+?VeT1kgYu!zR+30*ldHFlrc zJ(20fb}{M>QDI#Qm6Lapj3C80a5-ck7WHPl47A(dig4AY4HXJIgZfrl%(QX~yjbH0pML1NfDct)u^8w{is04p_J2`Zv_Fa620N>^ z6B_<%gu%!VEG5ts3<&SPu>A%<032hp-;-yp5>T15#qTLC3C?BB+k(0bLyC`B` z%FASn$dXy`1ihTF4tpBiBvP6{-I`z zM<83jq7G2SazoWEf5%-~d?LOr_`bszVSc+5=aJ>p$d_Y6qpM8pXmVQURM%IY>OxK? zhn`2qoTtGN2Rg32JERN3+dpLaW6KO{b=KnYx$#k~hHMbfnQ^wei5E`7D;%sQ>fOq? zfeiw;ISE#{0&8hT2X;~n*csbQ>$lWBsk=Ms_49dWj0tP#I@x$-Gt-KVuU?FY_^Svl z+7DpO`Wzrr`wJm`M_}as`?K~q+y%NJ;+Ub688Dgs&*fiF`qd65 z0Hb(Er7Q_~=OzI1M)gS8!Fbn~@KXcJ=WwVC5bAh25K0;eLc{i}X>_9larsZNkuuGx zrk-~?fz33x9+fAg4G3XbQh05%dsg^*mXz1Sn&_JW=A*7<7GY-9^pR0mwr_3(!XOzz zV5e|`ogTTrCv+XJRBffw>f~ijMybpmOnf(h8Y^`2LW{yC1G++|uwdz(`7P{xK1wU- z1?e1%+f622ZrM&UaN+i;zg5WY72N#|&8^4$kV@mu?nO3Ys))F|4`qluOfZxNglJ@b zvR+MRZM3j6A@vMBOgPG+0O~~imIn+u5cx+66`t>r2O>6Y&~T$vD{}vg>6%Gk`WQj1 zOw-5j2~+Ak^=T1F3}CE68h0L+4LSAQqzK z(P)sexeehsX<$TrR`W96QdT=0V4jY*KZY1gQ}LP(t-+n0tQCC*fZ^fJF^^L4xjZJp86|rd{0a zD#xRHob&_q=Ri*4?#6PelSaDXdM!s%i?)J%e`}7xWjgkbrO;dO(Mv;t=KC4NDNW2f z-lPq@+QrC$Hof4%EqrQNvD}Kk8T(HBAl9YUTdy#ql@DSq)L72?##ahdTotD_vSgLx zmLA@Q{1_QGmQ#akq))el3flb4MZ$F<2`3>89LAnw5-SPWJ=3VSw`pCg^gp2manOYr zFe1NO7m_Y(BJ=5N48XvB6EQ@Og}{)X2?`$TEj<~!LElOBV@k-M(Mfxv?Bo2By7KpSqRt}4T0C}x<7;(VZlS%kn-kP(s3-`>l-M|Nm=c|(|hW>d-8=ru!x2|zSm4o@!d}x17X5+$|b~4U9=r#yFxY> zjaSS5o!Krzy_MMx<(y%s@rd`^1!DR33WZ_qM-9ULV=xn>PDM4n;*61a=`nv6^!(Z^ z$iuqBaoXm(uMOJTpYUu1+XA~HJIyolYMcO}1p6u%8RO}=4LxV+S$P6U=%jSJ-t)8( zt;tonC-`Y97==lqae_Zu%r;s?&Qdwy*TxAjo{*C0>_u^!Xh^+JkQ{NRh1y+!I9uON z!CTW4FF`o2>}JF8b`LBy&VFppn&20~kr+lO`yZL~O+k%iee^e;;gnI3Qk+6ARep@>)dQR7>R)Ja8R_L)G@WI6HoR zOv9Rv5S$-DcF(6XE`|o9`@TX8IpaQP6r9_@7gA>?=$vxdlr$7!LK zTBjU;fQHLXOk#vsuVCx4%V~(PBa=i4X}@P&1u=njIC-?ri?hC#6 zQQ46;Y|!=ogawx!7Rd9l_z|8qFFQgWaZlM{;wbyV9_syxIAD{!K_eco`wkX@ z@3)Y@>?<0uWna)=y1Z{4Tz5vxS66SxxyriJfL>)c!bB@|1czqz$&$++Xx&RZK4SLtOZ|Pc^+POu%E(^{EosfI6+L^S21;` zqk8{b_f>S=uiDU;MyavHRG*7&# z?y%0|xZ*vK3_kK&IAq0V48o(Ud?r~%il3Q=7)Cc2T>*(-SsGoH3pdYps*E%}) zr%-5bqqC}0*s0pe2c8YXb*)?3$^Sqn|Ee&*C&5&AMCW?q$`8VW8Og3EG7oeUtZi~V zdI;gsltwzfADU30&E)(%x+`6eejGq)?FJ?Di7O8R0hlmjpc~z;|H9kx@hsP)6b^~7 zh90ee+kQ@qrS3?KMSH-a-Df`>g})Zou#s>1CCt7eqrRJ zQT`Q{m1`X>sDe%4-Y*>Ck#D_g-Q$#1U0ZKUWyMQjIIeYMwhpFot@Cv9Q{{v4qJNDY zcSrDjvfRY|iZ9M#;=YFe0k}Kh?%Xzo`xIYa@N)(HEeIbB_*TH@;9G((72g|3yA$6N z_(mZ<3t^uC_dBHh2JZ6+54}tB|Kn~64d5QhWunOKbBo$Obf4fI4F`2%pL<5EMN>r2Q+x46Jao(6S790( z9l9Muq^L@#b)dc|&NV|dV3NU!+G;;=&ddph%ln9Uhdi7=qmET#7Y7Kx6LqS~%_zI^Fm^34C^Z4fswe zZ~`i!_DAIL66yTKjd&NWGgl~-Mp#dKkHlg_hIfkSQ)+j-tY6P|HmrA3XJEB(ALILb zspEQHP=BH})Bkiqy{z4ZvNgWNXh@8pGGf+tJKis3#~X2(B&Ywq)0wYZcqBJG;Lt+W>ii7|4sHWtl|=h>I&5tOtb|Nl^hjcpxu= zCfk2cB9pCE@HL`3ShGHB5m(C=)z!?RYJV8vmMhkhQJQz0k1q6;A;iU6*0t7xU>JEf zKpljWhiEE*-7n}ozC+(T>cO5CT%C53525KX2$(%B=FiyESbp8{OCh`aD&N!)8+|v@rf?M(R%d z5xN7=2MO`SE3=;ktoB=;BUbIS-{D1IV827p$|J@`@2!*3?>|?u?eoqz5dxt)P$O@D z;719H28s>J38F?9+uxA3-%ICw*btzzN@$Th+@{Kx_d05@afQ=L*y|XFKxMB3FZ|xj zsF1fhMuxN#izfl_7< z_Z@~|nOyvk39_azCf_2iquC-)!kaIlHy-Bv?v;m*sdS}fgDxoB=|4DUGY*Ku#tp6S@=%G@;GaRPn{{~j(*?@k!)9zeg@iH9TC(@%K1 zCR`>?7zv9>I{m2PAmyEGe`DEE0vgM{ zW&T=W%asJ!S~I-v4HE#m%S`yIg?d^x5kSCVABi8LY}1sPm!57y_czInq+S?eW^;KX z<`QOBWJa>U7OJ%mO`rjdgeLNU2LgN#&GXDi7G6HnuR`DRTD%HJ8`+s3!S>JYcB2$c zL*8=fdF9eT6;jOcIO~Z=7$T@Gs)AL^4tiQNGlGxnRDJibiR%NRM&kPdaBsLb;8Xb_ zqWs1|a33nf6_4<(=P)7&^`R z$eq%nrZh)t)-J+dsK2wsUwOyJIOyv!$FNt>zY$kYMijrTC2J?YVi}T%t8)-QJ~#7C zSA6#(mAKlh_^yBt`oTy_TjND;S$pU4o^`Btmm&~CoXk5yZJO-i5o6J?ZDMts8M60! zpjiZWm&cO|5|GGX5re9{ z^p`K+$_Z~E`bl}Q)4sH$1vQ`uYaMAOA<0*!vwE|(D|cnDr-ImZZ}w4CFzKOF?(hlU%(lVYQ# ziO~Zi+lx3 zBmdxBcKQl=nwx`A-^W*zw&q)_l0ua7+uqOG%RjIcr^|&}H&qy-qPCI%D3I(hJ7f9A z@<|ltdl*6dw*O@9UzVic^A56;s%h980pjPrdBC%`_{DbLa7rQbzOv(fSKnaODp?Wo z-ur@QK;%*cc(Rk^@-TIhvWWK*isfn7g|qLdf*lSu$kFOK^+Jz*IHc$eaD}^ek`QW$22$p|st~n|CA1o8c%sC~q!hK2_ed6aNL| z%^UD_l{fgM5YVp38?uxJjcQfh7x)V}unha=Mop*Zd)wL2tMEPR>E!(4y!erIiDc<19pXmK^kxxIxMB z0}Vg<;UqkIfQ$l?gSIJU$?@z3kQ{W!9crIJ6OfU(-w>!4u4b$9k?Olujc^!1L70pL zpZ|ocl#xD>ikC+^?>Lw0p^o+ui1GDCNayjLM@1=Ejw$e9IDLy@8Wc0D$H-!4A!RWJ zS#%X`cRiwrwokqa6>T(AbP{b>;LJsEB!`H$CsC#>QzimZs>Our36$GaXb1~sq0t}< zjaoR71<_(;sVeFptw%NHvw%(l;~L6?#Gm*%|G@k3G?rgQ0>)RTMl4GaYF!A@*aiu; z;}`(r7G;B1gUL^=w}GWpMHBf@oAIbbC!w}7zq3%IH`GHVR=B&;!bXCE~ z!Y5l%4WJz$9ky4l)WaY;jvYJBqIC$y-!OKpekiG$P;g2wV#n%7z>8z`s)etJ4#s~> z3TXQwoHoXE4$YIUzJ7o(9E=a*v1;~Y|Js1Yq_u7Md6LUUb*)866<{c4>tXf1Csd-8 z*^&M!SHilK`R*nPd$x5oT&xuIKeBbfNVS}`fdf!EHC?+aq=O^&FMj16=JK<;eIur; z-i?|FA&p=?6wv-d$|6v`KYX5ie^~xm9$N})lfh(ZW4DOAGqrujOl@tQ@{8Pm*15EQ za(URHRK@Cwr_QnZ$2D&GROXeU<8hC?uDmYedHo!+Wq<`V_fL^=PKq? z^^slB9fJCZ8@{gk$Vyf_MIVthRAv3s7cpJ+kryKBBlA#p5BdmuV2GX~FQs`JHrNDq z&_{l8MOnclpvgMWaHNsgU`7vH0Clg8T>FQ#kqby0nS<6<)?Nv`>u;!@toamE6EKYk z<+C^UoMDOqn>ve-LuVz1Ud`#O#E>=}RARQm7f@o(L-_$E<|)L;O3dNGm=_<07js)Q zO6jT-%=aidLE~qkI>C#eyOT~Z?7Ij$!P&?^!>gPn&Me;m_T-&!WfAYuuh+n{jd?zn zJx$C*zh8rxJR?*u%kToqu}mQ-0q z&H5X(hpr2%Sxl#3V@O?4kt)+!;i8*w0fmbWc0gr_2cE3_93WLAY(d4_pMy0hxTqQp zT;C%I>Z%hhnj`B(<7J)WN~VZ(QgS-5wv;U$8r!?Dc|LljoTfLBeP6-s7+h7|ji63C z%O_WN)>+cfEb6onaXzzMs?J*U_KmW?NSlj}qPUQmYtEm1BrUwWu={lXfO!Au-zHLO zR?=g6aOHc0VnDy)*=ZHhad0z&Iz>Ks=A@Me={mgQQwo*!9exF!Xd#_PF}OhQ!DT$k zIxyRIwdpE#lWkmT6Uc&HeXcEVgjtyjXlCG@<-;QCIr7tgVKc`~k+Dg!D{8=FUIQ^m zNfq&(4Iq7=FPZ#MgM35C@AVCp{bxE?Y6u$fDtz1KoF00IM3=_Aa;!~jl3GxXoNP@>)^d6}iW2|Wf5SPxJ5 zeUk5H2kw*Pe~ll%VtXtfBTzB);X26)mV>;<5`LW|erJ#KTq0_VB8NKAzG9uA{sMLkNxF3lZvjjq;{lCC7Q> zeG6W`)@#vb$Ir*0oQ-b4d>CN)9!>BU z>zEjfYLFioz=alU>c*iP9P^WAdH$Ycn-q9oV8vc)-^_Jnq?3Qe4!e#B6SgVVB!PJp zmV26E%fD@hoBAP-00`TdMH^R`T@0|1VBs1sS` z=JC!`#BK5m1~|RhfEIClgrL+0+p$1nnD=yT3>U%zjWL;dl*TA#*heGQq9^e?3D~~J?`1rdX8ew7Pi0wT^m8k{$s5WeBl|6*7K>yp2DG@(83EL)o5>pdh#Q^gf7n z8Hs@KwfT&}5$7y63n^=JOWCvJUFK(EotucplF&MRx*VjG0A7TN^)B7QrZ{khfH&QUldFO{{2fk)We{BtQxja&Fq#Fld>11=* zqaC?(!=DE^47;83Xh-&8b#U%NU|?|mdtQvny|`K!IymbQ6VADGwNT|+dpVYE?!shH z=vqSq6Z=QcVI<&%k*$*7U!)FDdIls!xP-oC^7Nc=Vn+vweXLC-c1S3(&(Qj_TS?L| zw?|@KSlNMC_CSei4f{oB_CQGl%Hldv_IPff8U3HDlsOR?Xht%scTZW%oE|CLj}BKU zD{`%&9`1><#n=-Id;cX!*)0Le+RY)9T^*6K`8`wi_i)OtXOsm5EIo#c-H2^$OGuq= z{)-#~5_TQ)C=zx%Jb3(uJq@G$plZ$3{vNAd!`_Hd*tzpdG0U%~+6G^W>5qyj?7ZYN zwI5Dj7gAe0E3~`0W-VfRU}suyp&wjp=$JYqj1t zghu5kQTI|RZ&<2}l_*kSeEY&u^;e=6(kd+;QP3?4fd-nQf+MoALSF1uSU@15T!un$ z2t$_p56E)gKRYHw?w>~#9;Kj8XnK$qoE}yY+Rt6lmiMC?oM^0|xke-1TM4w$v}`;f z`S-y>Fqn2S{m*SOaT8xNaed+DylUb;1xy0E2m95X;6mC6>U5vGUS`hl``pXU1#_yZ z<|O7(n6n6;bUfWQ9@L2g=*=Q_pZk6MZff8@cfY`W?gExnQ3$5N6Mn|&j%)Gw(vD#q z=jFz6oC(Zw3OE40G>s2NJ0P+Rzc-3FjxS$E!zVZ# zm4e*V9eQZ)XNcRBQ)J<%qdrAG3VW*;!9z|*UB?vl5$;ew!0{Um&T4>RvB##Fnx z#?+|9qZ8*a_E!PDOe+8zk~PCwx=u^R?ETaQd!n3dN?m5ufhM! z^8fY$5mhO9`Huzt92)*GPWXxdV65LH`@2{raGq)~8e$y2x%l3w4zAyO(pO0FCVeG- zE7tudq)YdZsuH57*c+orwqsn8s?v37@$9RllPiPiF?d47{m`oe;@$%1l6DpfUp@&eVsHR}Yo3LfnPu5b{hN)(t)O!Q-K1dpeQ5E+;^? zEOkJK~Sw2TGW(0WKV(4oRG#?kah^z)%aKhcTkZ>9!_zV1V{tq&kD zKy+3J(Idmu-V=k4dz9zf#%HT|T>2hS+jGV(!c>59L7@U_sCVgN}SP&iJ z3FhM~1A@>spXL3jE`f==p3n?T3Wu)}UnMF7tHh1e3Qtf4@h^`x++4F~LS z#4??QmR$uO-I~QVq5Gil1zo~BM=7G=$>8Ic<_FvBTOoVVPBOhP!EQ|7D!o+P!Pr`$i%Gc+j)|Se* zi{Qvj0GjUZ?Qxh6##6-TGO>&r2BD@nOEek=cNQ&`%Wool7UMSca(G?KEeOD`vhBbx z?&Xzekrm2TD|#2-ufzP^w$K$Y0Nc>4~Gy{kJ;T-xF(rz&naA0A5+ew)JXjmKPXJMMX9 z4#M9_LAZymb8>Dx!RY6m7f|wVHTHIm?OV@UN{in{Po(604MzMa?65c+;IlWxqs4!gru{c!ZplOh= zp7aZ9%-8(wq`fWHlh8~o;7MpD+|7t*KN*igx%L>j{c!gd*_WNBLhd_Tz)4izNjsaK zoHMwZ+Yd)0+3%r%$GXF$4H8cd-s+JtqmfBx%tSfQ8eodeD&h(FIwLBDKhCnmgWYnL z5@9Z)>*HP>XhI_UDLubp3K-dU2}Bb%&KReTO93II2$7Gkp!1KoO5QgL6VYBg4dF=7 z0#EoJM=(T+!h|vl;&kTaShD!uB_TT9XuyMsKKfY_g4uwP1O2}7N}#>&jBg%bcmA2E zy{=WGei{u9x#l;F#{^gWCBD6&Dnb-Z0uiFViyU>Ds#)D)Kc%tF;j8BwjY6rhuf{fB zD+yai5m#}xOZe)ztoQC4;Yw@m98F=B z<(I*6teqE9GDUQhQ;1qJ;h9p^bb4@COuIDdn@(;vPa_m0d2Ofn1gm& z!kHyI&zKHz?uOT*Y3LF6angHaf4NT{dMub2k(1t};5!iU8_R7gwyMA2f0ojd2{RoNip? zS}v#WB7$;w z=j9;HJ!3Skqv8i(dFMoU`I;5ki%>p2Ycz%Vw`N=6ANu~#Um#RxPz{kdQ;yBpCqA+I zidh1ccd`@7nCjlkiPFWZxprN*0gw3NaGoq2I>-c$9s?%!n=Ja z40lrDu|wD3%W0#`;cfdzgZ2#X)fdC0ND8VQQC_xcWsTvH?L}>zI=pYKHxFVvW zCW4BRu)85-6Tk*ms$zW#Dg-Q$f&ug?0qi^h`(8jnk*bpa_netKx9o1>d%r)Q&ysuQ z&OOsln=@ysvuZV9ImeSXn?B3$-4XSq`#JGZjt5`qKDraDXn?1FG=Qq7Nqsmo?OYBTAX8*3rwe@MujL% zNVPLd6Kl{5BaBx-gcnc1%rtY4oW=-KD1&8PGV}QZhBN)Nvs9bmazQ^-n{BFc^1p8&Id0#_Y z%p$U1=59ly`qM_j1PTmm-r4XOOvz!pTqL}yU9MpaWT7{}wDxj#xxck`&P3|NvgxT} zmofHGod&Hke5)*>uK=(c56|qW3|bFYfw}WF4{=CT2Cb{mkjPfJpMa|lw?u>+J`bE_tNR`R_PXEDL)E>q(yaRcB&xcvLlw-rJB@J{wG6Af z%^26#X7r@K#^&3sR1CvF&{HEBpISDx<1xH{@rm&hYG);X%gMU`Pb>vvGrn-z(d_OL zOK7nxOW3+Q@j(faoFC}E+koG)gq=q<8AU}N3#Fi$*t(^PLQ7vAAKHYy+@d)*0LG^I zoF=iFwMCb|F$>9{a)d5eRXvBcS>g2Jr9YupaWsnzAdi507&9nG?T(MLv79w5 z)MP>CZipL#>)jm5@7J()Jc6@CSpKlfWdPB2_;tr`41N>wqkeGi|JeLH{_A+LdcaGD zA6I-Sao$dQdW$|+jy*MsKJK`4g7%X3?K<)m@;mt(x>{)tUH{y}^l2SHzqN$c)3^j0}EC`r(rexDF+@}|2#kTIrA z4zP9Rf)*wukI0pD%Mh_R!=C8wQ714eDKW+yP!7mPt?S|jY_?c^} zE`G9G@{`mrn4cifxPzbEH<^7Pon8~Kko0ak>D`7LV)cWN&ExEXpRgZTpa?1OVrhW` zfzw-9*Xt;f9Aopm_hO0# z*5J!v(j?r{1?JU;2v}TGb9)+oeg#0J$aow+G|9Nrm8cEg)WL!VgNIUp^8T%g`Zv$* z#3QH&ej<*BnB=qQ}Mw-yJGm8 zp~V?=wR9LaJ{t#^h#L=wRE`!5!JjY>o6Awj1$#fu((b}9VP}@s?=y>@3>EUBBZ$t( z(Lpne&O;ED5GtBOO33&T8>WKpfza#zUobgJo8w-ZEqa+fcm&k=M7`vDT;5|H=%&lk zg9``aQwk?Bm!TX9!Hoa(R|JHy0XDb9r`Zs0`+_54XpV zKS(P~A-GCo-$4%;DBD}m53bE)D@p%%A93oOp0RJ{EA<6v$SX3&0FnE265K?>PA9SJ ztqRUC3OUs2q~Sopla_)r^u`-;I_WCh;_c@sulo&$QG=Cpa-dio7mS>|!;Vw$_RAVuH!OU7rma0-2Gx%}w>V0}+C zu0eL3|Iy118qF9zeblzzv&Qsk9P+yk>GV9qeMqM*y26x~BAs_Y1SCuo< z@s)L6@CaCdZMw^NDSNne^1R?0p8Q?V02yj5oTO#<_KRQi# zF-Et(<%US{$YpFx#@apBFImgi14~um&&g_ew|lW?E@QSJ2$jlEB5Q|uEpU?L>iKU3 zKF5he_fmcVGt+(0@DZA=YAO2wsiph?s-~9mZFnP=@(ef`8P@>>1>T822}8~@D3$`i zSMUcl@EHHXL-+y2-6P~$@4M2z*55%}Xu;UxtvtUb%33D}jIaZUKk77@-{<)gILN?P z0yxG1Y>}8*fm#MIT?lB1#^rC22M~1zt`=|3U;tc>vb6y~tZzMWweta>l@#5DTRC2| zda8i)jl1w54h`0TGf9%9;mh!&iTj9k6=qSz%%h24@Hfs_FBC!BBw>C(02$37{G?$_ zJHZ;`fdzHc2$|#5qCE`En_Rl^5FI-KEu)wS<{88lID^<+DC>bTn6|L@5ih})0Pgo$ z=Mh`rGZ(2Fc$%Bkor7L6<@xCPvF_i1=7pbOsQe{d3zP~?GI|e3%jg?WuGso3c3X4WEHMW67` zk3g`txe&K&jpvn$c$>6j0h8I9lu+X~JeYk&e{J#ceJ62ECYcIzJyrVJY>E>x6xY|h zQ3(&27F5w}y3D2kz94v%QEBB>-3Y52XvbtpibU9`h^|IypBL|6!YeM0!3b zLrx*aUysFWrTxze{r+@qEjl!S&?NHQ#K_=X@}j1&Q%?WEM@nzl20&k1CWJ2bb@Kfh z=P6Ti7fh6pyRg>*x$EoTqCRLK2knBVQLB2(@< z6OdjuzcYQ0z+KI|klb}1p!|)J5A7$Sx>53>*U>t!r;IXKKE~x>P#f_AKE_#i%%{Q~ zKE^F5#pPoxJdFFpfX}Ua7Z^3q64}1sVckz-R+sF&4X6tUy zOC|xdH7_TQn)eG5Rr7}1oA&|$**v9+EGH%#@ythW)T0hnBnQRtfe+Yq^GUa4FHiUf zppb~CsKly03RB&b=V7qemA-^T6{S}uJ%U)TOGu;E10Fm>%bSf&h;%+crTaUfp6Pss z3+HI;O64ucTSqlFp*lrN`0T*=adcv7em5#Y3=2my>q61YPJm+j=Fs?1G_ynbvqUtr zIW$Y)!@`582X+!i_!b35LqnXkel)C8EqWBQ=2&R_3M{CNM4Q!-JE|oY7+alLj(MY( z^aolFN>)9GW~wLjMT)-hBg~ii{~D)GkHgA|#Q45BLMCUk?!IUR3|H>qPb?#kGy&pQjf4u7g-<8GkP zH}22)OrPJ!{`CX)uRGG$laXSnk1rk&>La-a8Sgn>mB?4;q4~z!c-5%_o=Cw%!Oh;y z+-)%7PKM1RSRLFJ+V&ht9p;(5*Qr{VJn3|NP(*pV=wEKBgS%KW)}!VSXB=ponP_J? z(0+nJa-sbKfk>T-i$v=KJ5_x+eZH*^|4r?=1t}0GjD5()y2x(h?~`hny%~IL&~7)+ zLNzTlIv>YTj0=(E)VA+wW}Xe1KpEbq484}5(L(UQ2*7-sO>Y+xMs!kdtCMbP>}K#u zU|KxZP>?nj(VWaXBY$HiKQt8jxu<##!D1eaqeh5%a1r+af1?M^VIbmnMm*lRDzdkc zyCN5&*X-OA_s6OGfaJ-d6t>&??w3L{F5xk+1HvUiGBJ0*x)ca|k8!07D8?7uP4)Xu z*UM_)8H$z^4^B%cN{qoZbffgLL!}Ej0^-Rg)0@?%J+U;DrNO%`v8do23D45RV6*nIcrhv`cx7U34Sj}P$VA+DF}xzw zu=}o~m2Ha(+5$^%V$s<{C}8T@*W&-9mC2Mam=mtS|LITlL?%7(^A~Y=3E5=_9=eyO zKXoyZiwfvvUi{I@n7n>Ky*=dH>q&p=0)9mq@|xj(Tp)bvd`fz%1O7)gWExjgK)JYD z+_v@-DthWd&Uz94-~Sp_x#*Ej^4)^nKWAxA;rAkbTU@_0%lm5d%R_fPf?r2G4-IZF zp6%Wb<%H>tXz#Gx$Rd$12!DpUZmXai(kxD9Ydzq@+;dyGN!o*1Jdx^3A0Q$EWwu?( zoHy3}js5lo{)Vm=EXHL}u|imc=MHQ(aUyg=S@}?oEMW3HI9g`ICR2lQLhJWoS=09WeF;cT z4mJ&5J=nJ?k+2WT8C8qG1=LFVH{Q1?0c|z=ivIczBtn4w4PibDZ&I zPwA4Sthmg*=vzIopOU^r620FC0EJ}t$UMX^PZ69#Wz6mUM6^C`Fad=pRxRT$&WW@4 z@DA$Xmw}{oQW|^*&Hhb{*uS~RXZCMGg#L}vz1TayKCNwp?oG3Guf2B@&E6enaPPJf zy_*`=yAvGUo9LK)=wHY3EaHV3tbkfDDz_hL2xn4|w9CA~*`%6z=Uy3C%~>cE`0r8U z$;&lGpVXO8u2+#y4!VE}*f0AI? zVZ?FaZV7~NI@tFlj0V{2j1TEJASdnJbzPd<0*6SIDX`|4Co$%g|s6(XjxliU&~vyWx@*s^iF_ zsz}~Gs*0B)yUT6~+dS)b<}p%d0GM;o)pZyyKLI{=%=UWvB_iTJhPG%SZAOR6=JNnI zg=_q?5Q)u$oun$bE&n1sGkFu%Z;qrG$wsHq82>0}?u2+%Kbip~y52F0kFH`9A4o%m zFujJ(X^M>t^K#sv~VJFQn*v8+cY5TVd0q%abIU)1eD3{8|#~k@u+ql#*L&UB{ioM zyfLmuO82d1;Iw(mfJF~nKo!R@Bd;`GUW}Cd4bpgd2?Nr2naMyiEHHf7hXHB49K?V$ zUJheG8ZXBJfFaEP4%J{H04a5++UuHNuj?tu`^M1`l#_M0Z77a)w=3~N=vI#1o5U-m zMfctXc8hK~Ft{wbJC@M+nE|71yEm^hX6L5Rn#(2HRt~gD=qnf62cw~VTPRJim;NoD z^s+t1Akr|x2DT;UC^b^rHH}foW$H8a1(zkgHkI?UUb~75kowFD226d%5tP=LL*F>; zv%0T#xcz+fX5=sqAc?C&_0#7sh2~_2ztt1=?m3uvg+tHt~(cY%?K`e2jiO`_v)=vaflmPv0E)_rf_b-GL1 z?L?V)YuSm$;Iy96kw0+8B{_;&VcE-mCD!mgB{{-e>9ZfBIx6Rl#)NGxur1hiBulF} znx#$tBTM@Xe~I8kpf_=*fAAj657P0E|CL3?(4q4%1yeWv=UNeCX?zh!`r$}M zb$O}Xey6QZ#sjb~jx?*VxvDT7iKv%(S1E|xfX}LPY@={zU;?hEW;hi9<^64 z000*M1r2S*7hA>SN-Ubc_VxL5SQYg7^Jzw#!36Z#`SguiNh3ZN>yN}k<1KuRTbS%K zEw)kwr@=-M&9g(nPbff*xsTP%^!zKfSNkDCV;jJl25JC%V)Ic9;JZjveM<6-InSKV z*k6V*r}zq9Z`UnDtck?wmL)v!p*0wOI7DajS`XagEyW!p~e zdX?H$yZS_ymUt3A*!W%jXO{LQWE7$=b;K`vo;usS52P_>Ar2cj{b}w)qVlKl;Utyv zr&$SrvPbu)S-{yPH>Pi;aOd==!TkbJ{AuVyBujIq;A@uV{3ED}rIvOAWLHbY{X_3@ z>*PKrIZr9Q^auc#UJA8zC$N}mX(pM!nrH6)BOBt?N2q&oVMlZY?dDbkl52Y*Z4qSD zP+&TC&Y`H06pw@z7>jA{sf8hEUbm+3`lqImHG z)%Ksn=%%w<_3NKxLpRpoNRwObqgyN7TIKgNU6uFCXMmJexeh3}>Ey`vF5U>O@_{O# ze~mKsYmV5g%}K7Cil&LS+$I~D&ut_x9!~eSLZuw8IpE$u6B4?kXdfQJon=;cl{!$W z&#qeC0-P=#X!@hnhzP3HeymsNh4LXDm3r#`o#wVwPgl{Rz6U8`>gi4-I@Qy!`9rCm ze#?MVPeTky_4FVEQawG&fK*TaW3ZaS*@&8nxr3IMdZ!=M%D zJfiO0d352E@VR*nZ=5QX-Q`6`A?Af6-rgvXR9zqz4~fkI?X{j0tr70ZA~!1N9)(DjQ0In;*uf}MRpE!|_Tgbvok7@T!Hx-ChD}>7VvD9c zZ;E@HMV&1e2$van;6Vu{7q{Gt$WzDMPN}PogdLX{<8+70&4)bR7PWrFthMv}h3mXZ zT{}$db#%hE$CNi9Y9s!a0}}h+s;58iB=hzF>eQP=r{atTcWST&gqECO4DRZx@#B8D z1~VoR4a&R8?8@Bn3|~*ZV@67&;dWnp?l=V!DTAC3qOai+{xo;-vv^wS7$>u{@mKhN z)os82@&ReTzIHZ9T@u9z9A20Gnuw0FU&jIX-|g4%M6z*V=nXb5+<|#=4ki{Ej{nd0 z>vMzuoBg_rql! zL)x#aLkH5ym`d}3_F$tKp8pH`b@64O75{hp^>4lG_Ur0duwUPZs5qAWdJhqBtKR=7 z`*kbSSxFj}{W|$waH)S}zyADczuuGXwqJjY6lq8#HykBOG(|@#QG%kH8Gmt3 zwjPt4HCvB2fp0~y9)Hq7ia0$Vu!}g8Xc{~Hfghh6Ma1cXBEtk1s=iZj`H>qW1(yR1 zNWtYW15$7~$$%7GJlr%XxFj$j1s5Fv2`=YSds_f-A8I&)fLDC6 zU#a_F#Yc3BbkcEJp8T<~lCnE2)^_pgD+nm2c-62wx@8Lz;PXJ(gij_F=^B;lM&!s1Wa5+pWDgue+v6w)QE0kJ)}t;Q26q_7+B$ z-|irJqd&CNj&d3Tc(cskyqW^3#A#nUY4+z(+J$ zP6b}6!biIJ;Z2A&6pew+mbAUhF$qR=C2w58-`KJ!dLW9qb-2gY=zaj~joy0L-Do{V z1!=wzkIqIvgU{>Oig*y%RHNBnAY!9`29`P+eHcB92D8g@lt}R5L%Yh}$`!6we*e3> zl~b)&7KgR+c-^gh0@ze5aamWCq3r{_b+qz#On6%>h2<4v4NJ}WL=}Z$-5b2Nxi=P` z=UbsSbgy_>tHt#9(-wGevvkM~-SQ{wmkmOvezR5U2kwqum@zQ(d;v=7u*m+2x6Z2R z^J%-nW|3V%-zbZ0Ww|h3mLk>IjIYTzYZ7&w>qxY50>T9L2fE&8Tc+3YGNt_iUZQHN z2b7gDY)Nh+QaD9#WYP)CW#~AwX{DF*TOgfE@|~!F^(g^2IWsjuNcLY zUv=qS^3`$b(sMQYs}b_mG5TuLDZ5R$mwZL1I*olm=(OG1jlDsti8A&kBetnA14+@X z!+fc?`e08-d_Y@8(MyPzmTv#-Twi+Q{<4}eoARh~-2bwAsL<+0*$kE%yl>l}bpMp`H3wbVUWqyXi}cb5UO z*@eZiVsau zwt)hh*ulE$>ItG#GGM+_Q)~w`k{CofqWKEdi>`eNR3M|54>Eg$3u)KSJ9ku~)U(Lr zGNCH;HH^#?iuf`pk+4-H5Ow2(pt`{7jiW@hAjbCl1>Xd#>9z6B9$+o#mk9c|wqH_C zXkP^d>P`u34AEot!fmvl&Z#WQ|+q8x@$0UP8}*|CKVdNOuP$=OnN@Oi$|r~-3S~?w_8R; zi>MgI!`SZDYdB!Ul2>@i+tYQZBS?=iUNdQ(rCa2~J@n3BNVZ)?!k!wbSvt1i#xMi`wPyp%Dbw2IdAi zg}^2O_OG)+jc_4Z@Pp86x3VdvA^71rchUNv-aN|HMV34>ym&%#5t?FdVh*?ieQ1iT z833g8HZ0V1pG&9-tio$IlS;S~X*MQR)T)J)T|i~X2h?Je_B)RZpprx(l3e<4B6S~x zKJronZ^*?t5-E+sHtpC$w;E*MKfBxeEpo{Ap2DMQ?{orOVxujR&ZR)4+B+a>d&_~Pj?t#;C`Ef&u#VW?n0f-y zJ%A!qd+z{XZ|{Pi-0kgpn`-Y6JgW9?uDiYcfk?IYL1MO2XL9-+v;#_4du=`kNusTE zpBE;zC1f^UL-J?tx5lqj2_<_Tep`s8UxR}g`Ix!Zt1soN)}V*RFVqh8)%)_*+eGgX zlHc}fxqS5uef0+7#YC3@>9So*XuKbxHjK_layl2-7EM^;VH1X`-WUuMwVWkOZ5~k<@H$#hKaK z8~8nYR<>5sGFv+VLG^>eeu}5mBi}B;WWfCb;3-qj%jUxa<8CB+i)&yp&X9Fai+x=< zU-e5C4l$7X$oOHquL&EA&5PrjnUe2q(}^QX;(I-SD3Y)4f3!heM+xq2h}o}>5+pNZ z`svd_HB0cNh`eEfFI7MZzI|CTWx*Hsi2;Zt_XgIYvcb;^zGDRp4pyXqT>;#t%-fvW zjxd5s=GFZ_VlyVP$omnH$;A{&<(OHcH6GV4Ej)6cZA0g2m+YQOOkXM?S^aGOc z-Y)lUHV`fyw|VMP5td*@)~_Pmk6Z>0Us5-8Lw%&Czz5IMaqzq}4qa4CLgrD>0kA&X=p>wFe~0L#h=u?vfm_#x#pTe6^LL3ti}VpV;^%=Odl>FxGw5u))I>TW zixTtK-AhD8I;Pj60~yX$!#?_31hrM*lSc zSaEm}_0>0GwK(#gW5eig4uHpqHh^=shHuf+B7lN39WScfH zAlp>UfNawz49GTp4uINpI!!8CGg5DyiaTle@e3mXRq_TMDrA$7KW63H*Koz!}!KjZSTSJ&uy}`llXm#-!-kXwWPM$TJ-%Q zb)T;|3+i#41r0XkO|E7ICs#e3jh)6%^3p=zjz;(S`o($RR1GB%s^g&MO{9Q^VEV&KG2;Z2BoJA^L3xfXzkKhIn6k;AJ+-D0zqnE$}gFq~%jqVGY z?J7tM8H&vyEqck?4MI21U-mry2FmX%R78F^ zK3cyQbY? z$-*-Nuv~>d#u1)uh;}6gZ$3p3bAMHZwCXw8^p#PD=Lo?)f(cj}6x`$Yu|#r2aF5&` zv=?Cu>@gB?m8=CshtFgL4l=}HnH!z)m%g#ZxhUA|T6FtgVgEf@-!t(=`jR4UT@pXj zo_1b?q9s@2uMBr#9OiXuwemB-)pICTpJr^ub0m9@IuOwb69c!mK|1fz1xxK%=<*wA zrto(ByAYnyl{BaIz}AaFA59C+djRA}JAw?s^U~&fYzw0_mBJ`p0eWxEVG6lq&0z|+ zW6j~MEXS!ZN)*peFI_`&oEb)G98YK|ZvO{>JI>JEsEVnBI-}PfM&(^PsQoO#izo)W z3$#t?^;IGk&uDdpg38Q?C$!skv3E#FH^j~9TWTebtG72?p{EkA;R zSlu!UGm5rup?2DrHg^jv9%i?|;R~&n-I8W?3u-G(M6I`iLR|p%fFuz@32h3| z@j_0x0mUOBoCvhyutjreF4r5jh*iwq7ttJv9Q|l4mTLWI;Nnqhc+fjeqN113m4z}g zk)$lTpUw?fa^$ao6p1+w6g=Q0rhbYyg2Xgm2=pHSbfe!A0X-iFQP%X8$f3}`(AkEb zTtNi-HBSKjLja|BbZ4rP8TKdPQx4HB(TVwwCq*RUpv`y)>6*@AjJ$YYT|4QWK9xee|_oS#MvJKf}IB*{GaO}SXhc@-|!VqA@B<;vs<%OobO z2t-?7oF%H%P-+(*Oct7sn!s+PcgogU;kN_7@A2Dzfwcz_Vf#U`qb8Wg#EkyvnXOT* z^3f-a7M)OA|Ddk?0Ge&<&#6J=XErHAh4u%BSI`s((KOpppXRiPX1Z}+ody?3^q3z@Ea%eG#qtc)MxMKJ<^zE)@^Lf-c6k5eMI?r(^|Vx`SfWf*<8!2Jrowgm1nhD>`h-6n16 zA#ETP*aYt8sicQA!9Ib~LqrS*7Py7j!I$@WJxj<^<6Nh6@SR+z56YLIuzG+tKioTs z!am>%>MD%{ZGp!$64+za>LqI&4XbiAYzLL1;?zz9WNVl(63jwtRl{nS7)VWG6ll6z z!P<+SicBqzqTo8{JgFEUm6#in&kgF&Go&e4To&49?!uee*5GQ;e^OScqeuaBY|X>+ zq6{zR?nJ9>r=#D5t|RIF8Ds3`mJPt9*~0&Rhn7d~(#}8I=Sok8%(>DYSgbDXk3I_P z(g3okE=3#&0J2N_0j_=KFu3jqnCYX_oS?$ zg|9p*TkasAq|n=2xZkAScOFWJrj9ko=k=>&q*ECmhOqL&w7Slz8T<5-KR}!;G+#!E zY5NIL)#uQW;b?@8`PDzF&0EN2{DsXixAyiTUbvM*3gu)ehXFuJd#eQsr5sYAT%o-k zn*;RZ8fBud$}Gp6(M!%j_1)-`kY=H;1qD^;PyS>>|0CWA^iLA{&jFg~&B(hH4AqT( zwgY`GAcejF{itex9o`7^y$JmvfSFobRNDVAY9T~9|Gjv03j{_f5&dJKD`BIvg~K3M z*VVD11MqBvFDc(_7Sdm3LxCxU!crg%a-T!)S32u!=u@TLzK1u)(=<>b{BMA|$JARV zqc$q%?RaC1LW;~uQJYlGQ{UTiK8`oW0i=l2AM<|2Vq)@ZoCzFZYT;d6ic|~lXF#fj z1q?{Fa4rKnh=28j2|07xzDM0H;cfHgFIUM6b9n)MqnwaYUq zHTIUDYj4k5`Z^gYP?bM$>flZPz?Lc*AM;@7F^!M9|1rYvG(dhjf@Y!J_f8rhJ75GN zm^I%%EHSrqVCJ0^C*}vDVWw!hsPdQgP{=nBS~zcnHubSr7=bq26>b*|?PBZ%sDU4D z@2HotB8_ZLkpZO7`oT6Y*b6xUDWsBrW9$JQJn3cojbq1zp1#{S8@&oCM6LTzovJks;scj{y$tcwts$XJ6^dNv$}}@9)2HE? zMu%mhosU8D$H)wd16ASQ znj4=#;H;P`r2SpB+JhjF18{$wJ^o_^1ziQ~%7%PL3VN3TLb`h++ha<1abV+N{#f-m zC%v$mMo)TjJPAPd_;4xR*~Pk%xIPgwM|8RF0KT{6x@Hh1EJJE%2d`e{4WvYr>sr65Jf<@6C^ct2u!XAQ@(`;_u3HpU2)U1iQgidi&yXpET#lw# zLdajJWS9^_TLDT4>4#XlfqQ2%4 zor%B$`OA-~Tvr8F{N{R=e%sLSmw0t`?eT5>qY- zULD`SY=ZuKm0DzQb;PZsLw>^eo80-v44dLjR(!D;XUZ;_K;#xKEz*P4uSh@^%FDOC9JRwWD8-7V%^v^fMIthY0;`0HtI>9?y~) zkR=RT44=dDex0^L_X?;tm};xjAnO7KV@^{>6x31J&p4Ag)Dr z&T??NFuCFnbdWMPpJG7D6%Ej~{EbqsXv#n{AuzOHK*|-Z8IW?tc??Lo;vxVfS5RGy zO8|rw{;yYUg_kiP3*X3qEPOKqvhZyT$ily1Ko-7-0a^IZ0H|>CCN~ZO2p3DH@0Y^E z?%sATKRukwXAY(b7C|hzErRgSme7*$Fvo%RPmm&)wDZ%TC}?}xCD8+~Q71{Kp4PM+ zzvdJc9&FPo6|d}O?CoIhQgV1gJVKn949rg7%6s})GC&?2s*F6eDZ0?`B8eVKXy`*9 zP=rlOs=EZATN&C>hIW+X+%+EbvRkUyOVxK~Ixj5KBxL$;_8T^m07P*;1p}P_06N0l zaJUoCQue+c^FyqgTagIc|Hvz|wL|!wnFU$os%*^_k3$9k<-i8|oGU?shm&*Y-W=f^ zdikYn42Tb{xtu87_?B=Y6BZH*(oyk!a_pA{-@~?tX#x*Jbuuc!^bLI<3aauAT}9fe z!|Bi-X&M5>>nu=2yl)X3)&og|P>zR*44Hg)5txPLcsLMHnB(Ct;-jYH;ZQVGIvz@a z;%abi)BCWhy=^Dr4ZM(sqWQ$#@p3QF2Sx&g6ezxzm$x?Yygn603qz5LR85AWzY&fT z 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 px2$g*t+U(2hRHiY#u3}^*LE7jT* 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�RG#*+SC%z#pTue>u+Qro0s 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�T|iMgRF%uxKH{ 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 z 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

2wLML0e!5NR-<$GV=2q`8te+E5#>t_d6AXtXYB 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

j!v2%=0X5H*s3^rXy` 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 diff --git a/src/apu/apu.h b/src/apu/apu.h index 8645248f..4071fe6d 100644 --- a/src/apu/apu.h +++ b/src/apu/apu.h @@ -27,4 +27,7 @@ static const uint8 iplrom[64]; virtual bool in_opcode(); void disassemble_opcode(char *output); inline uint16 __relb(int8 offset, int op_len); + + APU() {} + virtual ~APU() {} }; diff --git a/src/apu/bapu/bapu_op_fn.cpp b/src/apu/bapu/bapu_op_fn.cpp index 4819c448..71476382 100644 --- a/src/apu/bapu/bapu_op_fn.cpp +++ b/src/apu/bapu/bapu_op_fn.cpp @@ -9,12 +9,11 @@ int16 r = x + y + regs.p.c; } uint16 bAPU::op_addw(uint16 x, uint16 y) { -int32 r = x + y; - regs.p.n = !!(r & 0x8000); - regs.p.v = !!(~(x ^ y) & (y ^ (uint16)r) & 0x8000); - regs.p.h = !!((x ^ y ^ (uint16)r) & 0x10); +int16 r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; regs.p.z = ((uint16)r == 0); - regs.p.c = (r > 0xffff); return r; } @@ -66,12 +65,11 @@ int16 r = x - y - !regs.p.c; } uint16 bAPU::op_subw(uint16 x, uint16 y) { -int32 r = x - y; - regs.p.n = !!(r & 0x8000); - regs.p.v = !!((x ^ y) & (x ^ (uint16)r) & 0x8000); - regs.p.h = !((x ^ y ^ (uint16)r) & 0x10); +int16 r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; regs.p.z = ((uint16)r == 0); - regs.p.c = (r >= 0); return r; } diff --git a/src/base.h b/src/base.h index 7e00bb59..5edbe715 100644 --- a/src/base.h +++ b/src/base.h @@ -1,6 +1,16 @@ +//debugging extensions (~10% speed hit) //#define DEBUGGER + +//snes core polymorphism (allow mem/cpu/apu/ppu overriding, +//~10% speed hit) //#define POLYMORPHISM +//enable GZ, ZIP format support +#define GZIP_SUPPORT + +//enable JMA support +#define JMA_SUPPORT + //this should be declared in the port-specific makefiles //#define ARCH_LSB //#define ARCH_MSB diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp new file mode 100644 index 00000000..e3c729d7 --- /dev/null +++ b/src/cart/cart.cpp @@ -0,0 +1,243 @@ +#include "../base.h" + +void Cartridge::read_header() { + cart.srtc = false; + cart.sdd1 = false; + cart.c4 = false; + + if(cart.header_index == 0x7fc0 && cart.rom_size >= 0x401000) { + cart.mapper = EXLOROM; + } else if(cart.header_index == 0x7fc0 && rom[cart.header_index + MAPPER] == 0x32) { + cart.mapper = EXLOROM; + } else if(cart.header_index == 0x7fc0) { + cart.mapper = LOROM; + } else if(cart.header_index == 0xffc0) { + cart.mapper = HIROM; + } else { //cart.header_index == 0x40ffc0 + cart.mapper = EXHIROM; + } + +uint8 mapper = rom[cart.header_index + MAPPER]; +uint8 rom_type = rom[cart.header_index + ROM_TYPE]; + if(mapper == 0x35 && rom_type == 0x55) { + cart.srtc = true; + } + + if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + cart.sdd1 = true; + } + + if(mapper == 0x20 && rom_type == 0xf3) { + cart.c4 = true; + } + + if(rom[cart.header_index + SRAM_SIZE] & 7) { + cart.sram_size = 1024 << (rom[cart.header_index + SRAM_SIZE] & 7); + } else { + cart.sram_size = 0; + } + + cart.region = (rom[cart.header_index + REGION] < 2) ? NTSC : PAL; + + memcpy(&cart.name, &rom[cart.header_index + CART_NAME], 21); + cart.name[21] = 0; + + for(int i=0;i<22;i++) { + if(cart.name[i] & 0x80) { + cart.name[i] = '?'; + } + } +} + +void Cartridge::find_header() { +int32 score_lo = 0, + score_hi = 0, + score_ex = 0; + + if(rom_size < 0x010000) { + //cart too small to be anything else + cart.header_index = 0x007fc0; + return; + } + + if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20)score_lo++; + if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21)score_hi++; + + if(rom[0x7fc0 + ROM_TYPE] < 0x08)score_lo++; + if(rom[0xffc0 + ROM_TYPE] < 0x08)score_hi++; + + if(rom[0x7fc0 + ROM_SIZE] < 0x10)score_lo++; + if(rom[0xffc0 + ROM_SIZE] < 0x10)score_hi++; + + if(rom[0x7fc0 + SRAM_SIZE] < 0x08)score_lo++; + if(rom[0xffc0 + SRAM_SIZE] < 0x08)score_hi++; + + if(rom[0x7fc0 + REGION] < 14)score_lo++; + if(rom[0xffc0 + REGION] < 14)score_hi++; + + if(rom[0x7fc0 + LICENSE] < 3)score_lo++; + if(rom[0xffc0 + LICENSE] < 3)score_hi++; + +uint16 cksum, icksum; + cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8); + icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8); + if((cksum + icksum) == 0xffff)score_lo += 8; + cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8); + icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8); + if((cksum + icksum) == 0xffff)score_hi += 8; + + if(rom_size < 0x401000) { + score_ex = 0; + } else { + if(rom[0x7fc0 + MAPPER] == 0x32)score_lo++; + else score_ex += 16; + } + + if(score_lo >= score_hi && score_lo >= score_ex) { + cart.header_index = 0x007fc0; + } else if(score_hi >= score_ex) { + cart.header_index = 0x00ffc0; + } else { + cart.header_index = 0x40ffc0; + } +} + +void Cartridge::load_sram() { + if(cart.sram_size == 0) { + sram = 0; + return; + } + +FileReader ff; + if(!ff.open(sram_fn)) { + sram = (uint8*)malloc(cart.sram_size); + memset(sram, 0, cart.sram_size); + return; + } + + sram = ff.read(cart.sram_size); + ff.close(); +} + +void Cartridge::save_sram() { + if(cart.sram_size == 0)return; + +FileWriter ff; + if(!ff.open(sram_fn))return; + + ff.write(sram, cart.sram_size); + ff.close(); +} + +void Cartridge::load_rom(Reader *rf) { + base_rom = rf->read(); + rom_size = rf->size(); + + if((rom_size & 0x7fff) == 0x0200) { + rom = base_rom + 512; + rom_size -= 512; + } else { + rom = base_rom; + } + + cart.rom_size = rom_size; +} + +bool Cartridge::load(const char *fn) { + if(cart_loaded == true)return false; + if(strlen(fn) < 3)return false; + + dprintf("* Loading \"%s\"...", fn); + + strcpy(rom_fn, fn); + + switch(Reader::detect(rom_fn)) { + case Reader::RF_NORMAL: { + FileReader ff; + if(!ff.open(rom_fn)) { + alert("Error loading image file (%s)!", rom_fn); + return false; + } + load_rom(static_cast(&ff)); + ff.close(); + break; + } +#ifdef GZIP_SUPPORT + case Reader::RF_GZ: { + GZReader gf; + if(!gf.open(rom_fn)) { + alert("Error loading image file (%s)!", rom_fn); + return false; + } + load_rom(static_cast(&gf)); + gf.close(); + break; + } + case Reader::RF_ZIP: { + ZipReader zf(rom_fn); + load_rom(static_cast(&zf)); + break; + } +#endif +#ifdef JMA_SUPPORT + case Reader::RF_JMA: { + try { + JMAReader jf(rom_fn); + load_rom(static_cast(&jf)); + } catch(JMA::jma_errors jma_error) { + alert("Error loading image file (%s)!", rom_fn); + return false; + } + break; + } +#endif + } + +int i; + strcpy(sram_fn, fn); + for(i=strlen(fn)-1;i>=0;i--) { + if(fn[i] == '.')break; + } + if(i != 0)sram_fn[i] = 0; + strcat(sram_fn, ".srm"); + + find_header(); + read_header(); + load_sram(); + cart_loaded = true; + r_mem->load_cart(); + return true; +} + +bool Cartridge::unload() { + if(cart_loaded == false)return false; + + r_mem->unload_cart(); + + if(base_rom) { + zerofree(base_rom); + } + + if(sram) { + save_sram(); + zerofree(sram); + } + + cart_loaded = false; + return true; +} + +Cartridge::Cartridge() { + cart_loaded = false; + + base_rom = 0; + rom = 0; + sram = 0; + rom_size = 0; +} + +Cartridge::~Cartridge() { + if(cart_loaded == true) { + unload(); + } +} diff --git a/src/cart/cart.h b/src/cart/cart.h new file mode 100644 index 00000000..c8da8554 --- /dev/null +++ b/src/cart/cart.h @@ -0,0 +1,59 @@ +class Cartridge { +public: +bool cart_loaded; +char rom_fn[4096], sram_fn[4096]; + +uint8 *base_rom, *rom, *sram; +uint32 rom_size; + +enum { +//header fields + CART_NAME = 0x00, + MAPPER = 0x15, + ROM_TYPE = 0x16, + ROM_SIZE = 0x17, + SRAM_SIZE = 0x18, + REGION = 0x19, + LICENSE = 0x1a, + VERSION = 0x1b, + ICKSUM = 0x1c, + CKSUM = 0x1e, + +//regions + NTSC = 0, + PAL = 1, + +//memory mappers + LOROM = 0x20, + HIROM = 0x21, + EXLOROM = 0x22, + EXHIROM = 0x25, +}; + +struct { + uint32 header_index; + + char name[32]; + uint32 rom_size; + uint32 sram_size; + bool region; + + uint32 mapper; + bool srtc; + bool sdd1; + bool c4; +} cart; + + void load_rom(Reader *rf); + void load_sram(); + void save_sram(); + void find_header(); + void read_header(); + bool load(const char *fn); + bool unload(); + + Cartridge(); + ~Cartridge(); +}; + +extern Cartridge cartridge; diff --git a/src/chip/c4/c4.cpp b/src/chip/c4/c4.cpp new file mode 100644 index 00000000..d979fbf1 --- /dev/null +++ b/src/chip/c4/c4.cpp @@ -0,0 +1,197 @@ +/* + C4 emulation + + Used in Rockman X2/X3 (Megaman X2/X3) + Portions (c) anomie, Overload, zsKnight, Nach, byuu +*/ + +#include "../../base.h" + +#include "c4data.cpp" +#include "c4fn.cpp" +#include "c4oam.cpp" +#include "c4ops.cpp" + +void C4::init() {} +void C4::enable() {} + +uint32 C4::ldr(uint8 r) { +uint16 addr = 0x0080 + (r * 3); + return (reg[addr]) | (reg[addr + 1] << 8) | (reg[addr + 2] << 16); +} + +void C4::str(uint8 r, uint32 data) { +uint16 addr = 0x0080 + (r * 3); + reg[addr ] = (data); + reg[addr + 1] = (data >> 8); + reg[addr + 2] = (data >> 16); +} + +void C4::mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh) { +int64 rx = x & 0xffffff; +int64 ry = y & 0xffffff; + if(rx & 0x800000)rx |= ~0x7fffff; + if(ry & 0x800000)ry |= ~0x7fffff; + + rx *= ry; + + rl = (rx) & 0xffffff; + rh = (rx >> 24) & 0xffffff; +} + +uint32 C4::sin(uint32 rx) { + r0 = rx & 0x1ff; + if(r0 & 0x100)r0 ^= 0x1ff; + if(r0 & 0x080)r0 ^= 0x0ff; + if(rx & 0x100) { + return sin_table[r0 + 0x80]; + } else { + return sin_table[r0]; + } +} + +uint32 C4::cos(uint32 rx) { + return sin(rx + 0x080); +} + +void C4::immediate_reg(uint32 start) { + r0 = ldr(0); + for(uint32 i=start;i<48;i++) { + if((r0 & 0x0fff) < 0x0c00) { + ram[r0 & 0x0fff] = immediate_data[i]; + } + r0++; + } + str(0, r0); +} + +void C4::transfer_data() { +uint32 src; +uint16 dest, count; + src = (reg[0x40]) | (reg[0x41] << 8) | (reg[0x42] << 16); + count = (reg[0x43]) | (reg[0x44] << 8); + dest = (reg[0x45]) | (reg[0x46] << 8); + + for(uint32 i=0;iread(src++)); + } +} + +void C4::write(uint16 addr, uint8 data) { + addr &= 0x1fff; + + if(addr < 0x0c00) { + //ram + ram[addr] = data; + return; + } + + if(addr < 0x1f00) { + //unmapped + return; + } + +//command register + reg[addr & 0xff] = data; + + if(addr == 0x1f47) { + //memory transfer + transfer_data(); + return; + } + + if(addr == 0x1f4f) { + //c4 command + if(reg[0x4d] == 0x0e && !(data & 0xc3)) { + //c4 test command + reg[0x80] = data >> 2; + return; + } + + switch(data) { + case 0x00:op00();break; + case 0x01:op01();break; + case 0x05:op05();break; + case 0x0d:op0d();break; + case 0x10:op10();break; + case 0x13:op13();break; + case 0x15:op15();break; + case 0x1f:op1f();break; + case 0x22:op22();break; + case 0x25:op25();break; + case 0x2d:op2d();break; + case 0x40:op40();break; + case 0x54:op54();break; + case 0x5c:op5c();break; + case 0x5e:op5e();break; + case 0x60:op60();break; + case 0x62:op62();break; + case 0x64:op64();break; + case 0x66:op66();break; + case 0x68:op68();break; + case 0x6a:op6a();break; + case 0x6c:op6c();break; + case 0x6e:op6e();break; + case 0x70:op70();break; + case 0x72:op72();break; + case 0x74:op74();break; + case 0x76:op76();break; + case 0x78:op78();break; + case 0x7a:op7a();break; + case 0x7c:op7c();break; + case 0x89:op89();break; + } + } +} + +void C4::writeb(uint16 addr, uint8 data) { + write(addr, data); +} + +void C4::writew(uint16 addr, uint16 data) { + write(addr, data); + write(addr + 1, data >> 8); +} + +void C4::writel(uint16 addr, uint32 data) { + write(addr, data); + write(addr + 1, data >> 8); + write(addr + 2, data >> 16); +} + +uint8 C4::read(uint16 addr) { + addr &= 0x1fff; + + if(addr < 0x0c00) { + return ram[addr]; + } + + if(addr >= 0x1f00) { + return reg[addr & 0xff]; + } + + return r_cpu->regs.mdr; +} + +uint8 C4::readb(uint16 addr) { + return read(addr); +} + +uint16 C4::readw(uint16 addr) { + return read(addr) | (read(addr + 1) << 8); +} + +uint32 C4::readl(uint16 addr) { + return read(addr) | (read(addr + 1) << 8) + (read(addr + 2) << 16); +} + +void C4::power() { + reset(); +} + +void C4::reset() { + memset(ram, 0, 0x0c00); + memset(reg, 0, 0x0100); +} + +C4::C4() {} diff --git a/src/chip/c4/c4.h b/src/chip/c4/c4.h new file mode 100644 index 00000000..6adac97c --- /dev/null +++ b/src/chip/c4/c4.h @@ -0,0 +1,94 @@ +class C4 { +private: +uint8 ram[0x0c00]; +uint8 reg[0x0100]; +uint32 r0, r1, r2, r3, r4, r5, r6, r7, + r8, r9, r10, r11, r12, r13, r14, r15; + +static const uint8 immediate_data[48]; +static const uint16 wave_data[40]; +static const uint32 sin_table[256]; + +static const int16 SinTable[512]; +static const int16 CosTable[512]; + +int16 C4WFXVal, C4WFYVal, C4WFZVal, C4WFX2Val, C4WFY2Val, C4WFDist, C4WFScale; +int16 C41FXVal, C41FYVal, C41FAngleRes, C41FDist, C41FDistVal; + +double tanval; +double c4x,c4y,c4z, c4x2,c4y2,c4z2; + +void C4TransfWireFrame(); +void C4TransfWireFrame2(); +void C4CalcWireFrame(); +void C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color); +void C4DrawWireFrame(); +void C4DoScaleRotate(int row_padding); + +public: + uint32 ldr(uint8 r); + void str(uint8 r, uint32 data); + void mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh); + uint32 sin(uint32 rx); + uint32 cos(uint32 rx); + + void transfer_data(); + void immediate_reg(uint32 num); + + void op00_00(); + void op00_03(); + void op00_05(); + void op00_07(); + void op00_08(); + void op00_0b(); + void op00_0c(); + + void op00(); + void op01(); + void op05(); + void op0d(); + void op10(); + void op13(); + void op15(); + void op1f(); + void op22(); + void op25(); + void op2d(); + void op40(); + void op54(); + void op5c(); + void op5e(); + void op60(); + void op62(); + void op64(); + void op66(); + void op68(); + void op6a(); + void op6c(); + void op6e(); + void op70(); + void op72(); + void op74(); + void op76(); + void op78(); + void op7a(); + void op7c(); + void op89(); + + void init(); + void enable(); + void power(); + void reset(); + + void write (uint16 addr, uint8 data); + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writel(uint16 addr, uint32 data); + + uint8 read (uint16 addr); + uint8 readb(uint16 addr); + uint16 readw(uint16 addr); + uint32 readl(uint16 addr); + + C4(); +}; diff --git a/src/chip/c4/c4data.cpp b/src/chip/c4/c4data.cpp new file mode 100644 index 00000000..b831f72b --- /dev/null +++ b/src/chip/c4/c4data.cpp @@ -0,0 +1,183 @@ +const uint8 C4::immediate_data[48] = { + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f, + 0x00, 0x80, 0x00, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00 +}; + +const uint16 C4::wave_data[40] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, + 0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a, 0x020c, 0x020e, + 0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, + 0x0600, 0x0602, 0x0604, 0x0606, 0x0608, 0x060a, 0x060c, 0x060e, + 0x0800, 0x0802, 0x0804, 0x0806, 0x0808, 0x080a, 0x080c, 0x080e +}; + +const uint32 C4::sin_table[256] = { + 0x000000, 0x000324, 0x000648, 0x00096c, 0x000c8f, 0x000fb2, 0x0012d5, 0x0015f6, + 0x001917, 0x001c37, 0x001f56, 0x002273, 0x002590, 0x0028aa, 0x002bc4, 0x002edb, + 0x0031f1, 0x003505, 0x003817, 0x003b26, 0x003e33, 0x00413e, 0x004447, 0x00474d, + 0x004a50, 0x004d50, 0x00504d, 0x005347, 0x00563e, 0x005931, 0x005c22, 0x005f0e, + 0x0061f7, 0x0064dc, 0x0067bd, 0x006a9b, 0x006d74, 0x007049, 0x007319, 0x0075e5, + 0x0078ad, 0x007b70, 0x007e2e, 0x0080e7, 0x00839c, 0x00864b, 0x0088f5, 0x008b9a, + 0x008e39, 0x0090d3, 0x009368, 0x0095f6, 0x00987f, 0x009b02, 0x009d7f, 0x009ff6, + 0x00a267, 0x00a4d2, 0x00a736, 0x00a994, 0x00abeb, 0x00ae3b, 0x00b085, 0x00b2c8, + 0x00b504, 0x00b73a, 0x00b968, 0x00bb8f, 0x00bdae, 0x00bfc7, 0x00c1d8, 0x00c3e2, + 0x00c5e4, 0x00c7de, 0x00c9d1, 0x00cbbb, 0x00cd9f, 0x00cf7a, 0x00d14d, 0x00d318, + 0x00d4db, 0x00d695, 0x00d848, 0x00d9f2, 0x00db94, 0x00dd2d, 0x00debe, 0x00e046, + 0x00e1c5, 0x00e33c, 0x00e4aa, 0x00e60f, 0x00e76b, 0x00e8bf, 0x00ea09, 0x00eb4b, + 0x00ec83, 0x00edb2, 0x00eed8, 0x00eff5, 0x00f109, 0x00f213, 0x00f314, 0x00f40b, + 0x00f4fa, 0x00f5de, 0x00f6ba, 0x00f78b, 0x00f853, 0x00f912, 0x00f9c7, 0x00fa73, + 0x00fb14, 0x00fbac, 0x00fc3b, 0x00fcbf, 0x00fd3a, 0x00fdab, 0x00fe13, 0x00fe70, + 0x00fec4, 0x00ff0e, 0x00ff4e, 0x00ff84, 0x00ffb1, 0x00ffd3, 0x00ffec, 0x00fffb, + 0x000000, 0xfffcdb, 0xfff9b7, 0xfff693, 0xfff370, 0xfff04d, 0xffed2a, 0xffea09, + 0xffe6e8, 0xffe3c8, 0xffe0a9, 0xffdd8c, 0xffda6f, 0xffd755, 0xffd43b, 0xffd124, + 0xffce0e, 0xffcafa, 0xffc7e8, 0xffc4d9, 0xffc1cc, 0xffbec1, 0xffbbb8, 0xffb8b2, + 0xffb5af, 0xffb2af, 0xffafb2, 0xffacb8, 0xffa9c1, 0xffa6ce, 0xffa3dd, 0xffa0f1, + 0xff9e08, 0xff9b23, 0xff9842, 0xff9564, 0xff928b, 0xff8fb6, 0xff8ce6, 0xff8a1a, + 0xff8752, 0xff848f, 0xff81d1, 0xff7f18, 0xff7c63, 0xff79b4, 0xff770a, 0xff7465, + 0xff71c6, 0xff6f2c, 0xff6c97, 0xff6a09, 0xff6780, 0xff64fd, 0xff6280, 0xff6009, + 0xff5d98, 0xff5b2d, 0xff58c9, 0xff566b, 0xff5414, 0xff51c4, 0xff4f7a, 0xff4d37, + 0xff4afb, 0xff48c5, 0xff4697, 0xff4470, 0xff4251, 0xff4038, 0xff3e27, 0xff3c1e, + 0xff3a1b, 0xff3821, 0xff362e, 0xff3444, 0xff3260, 0xff3085, 0xff2eb2, 0xff2ce7, + 0xff2b24, 0xff296a, 0xff27b7, 0xff260d, 0xff246b, 0xff22d2, 0xff2141, 0xff1fb9, + 0xff1e3a, 0xff1cc3, 0xff1b55, 0xff19f0, 0xff1894, 0xff1740, 0xff15f6, 0xff14b4, + 0xff137c, 0xff124d, 0xff1127, 0xff100a, 0xff0ef6, 0xff0dec, 0xff0ceb, 0xff0bf4, + 0xff0b05, 0xff0a21, 0xff0945, 0xff0874, 0xff07ac, 0xff06ed, 0xff0638, 0xff058d, + 0xff04eb, 0xff0453, 0xff03c4, 0xff0340, 0xff02c5, 0xff0254, 0xff01ec, 0xff018f, + 0xff013b, 0xff00f1, 0xff00b1, 0xff007b, 0xff004e, 0xff002c, 0xff0013, 0xff0004 +}; + +const int16 C4::SinTable[512] = { + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765, + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402 +}; + +const int16 C4::CosTable[512] = { + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402, + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765 +}; diff --git a/src/chip/c4/c4fn.cpp b/src/chip/c4/c4fn.cpp new file mode 100644 index 00000000..f5545ec6 --- /dev/null +++ b/src/chip/c4/c4fn.cpp @@ -0,0 +1,242 @@ +#include +#define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000) +#define sar(b, n) ((b) >> (n)) +#ifdef PI +#undef PI +#endif +#define PI 3.1415926535897932384626433832795 + +//Wireframe Helpers +void C4::C4TransfWireFrame() { + c4x = (double)C4WFXVal; + c4y = (double)C4WFYVal; + c4z = (double)C4WFZVal - 0x95; + +//Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + +//Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + +//Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + +//Scale + C4WFXVal = (int16)(c4x * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); + C4WFYVal = (int16)(c4y * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); +} + +void C4::C4CalcWireFrame() { + C4WFXVal = C4WFX2Val - C4WFXVal; + C4WFYVal = C4WFY2Val - C4WFYVal; + + if(abs(C4WFXVal) > abs(C4WFYVal)) { + C4WFDist = abs(C4WFXVal) + 1; + C4WFYVal = (256 * (long)C4WFYVal) / abs(C4WFXVal); + C4WFXVal = (C4WFXVal < 0) ? -256 : 256; + } else if(C4WFYVal != 0) { + C4WFDist = abs(C4WFYVal) + 1; + C4WFXVal = (256 * (long)C4WFXVal) / abs(C4WFYVal); + C4WFYVal = (C4WFYVal < 0) ? -256 : 256; + } else { + C4WFDist = 0; + } +} + +void C4::C4TransfWireFrame2() { + c4x = (double)C4WFXVal; + c4y = (double)C4WFYVal; + c4z = (double)C4WFZVal; + +//Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + +//Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + +//Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + +//Scale + C4WFXVal = (int16)(c4x * C4WFScale / 0x100); + C4WFYVal = (int16)(c4y * C4WFScale / 0x100); +} + +void C4::C4DrawWireFrame() { +uint32 line = readl(0x1f80); +uint32 point1, point2; +int16 X1, Y1, Z1; +int16 X2, Y2, Z2; +uint8 Color; + for(int32 i = ram[0x0295]; i > 0; i--, line += 5) { + if(r_mem->read(line) == 0xff && r_mem->read(line + 1) == 0xff) { + int32 tmp = line - 5; + while(r_mem->read(tmp + 2) == 0xff && r_mem->read(tmp + 3) == 0xff && (tmp + 2) >= 0) { tmp -= 5; } + point1 = (read(0x1f82) << 16) | (r_mem->read(tmp + 2) << 8) | r_mem->read(tmp + 3); + } else { + point1 = (read(0x1f82) << 16) | (r_mem->read(line) << 8) | r_mem->read(line + 1); + } + point2 = (read(0x1f82) << 16) | (r_mem->read(line + 2) << 8) | r_mem->read(line + 3); + + X1=(r_mem->read(point1 + 0) << 8) | r_mem->read(point1 + 1); + Y1=(r_mem->read(point1 + 2) << 8) | r_mem->read(point1 + 3); + Z1=(r_mem->read(point1 + 4) << 8) | r_mem->read(point1 + 5); + X2=(r_mem->read(point2 + 0) << 8) | r_mem->read(point2 + 1); + Y2=(r_mem->read(point2 + 2) << 8) | r_mem->read(point2 + 3); + Z2=(r_mem->read(point2 + 4) << 8) | r_mem->read(point2 + 5); + Color = r_mem->read(line + 4); + C4DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color); + } +} + +void C4::C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color) { +//Transform coordinates + C4WFXVal = (int16)X1; + C4WFYVal = (int16)Y1; + C4WFZVal = Z1; + C4WFScale = read(0x1f90); + C4WFX2Val = read(0x1f86); + C4WFY2Val = read(0x1f87); + C4WFDist = read(0x1f88); + C4TransfWireFrame2(); + X1 = (C4WFXVal + 48) << 8; + Y1 = (C4WFYVal + 48) << 8; + + C4WFXVal = (int16)X2; + C4WFYVal = (int16)Y2; + C4WFZVal = Z2; + C4TransfWireFrame2(); + X2 = (C4WFXVal + 48) << 8; + Y2 = (C4WFYVal + 48) << 8; + +//Get line info + C4WFXVal = (int16)(X1 >> 8); + C4WFYVal = (int16)(Y1 >> 8); + C4WFX2Val = (int16)(X2 >> 8); + C4WFY2Val = (int16)(Y2 >> 8); + C4CalcWireFrame(); + X2 = (int16)C4WFXVal; + Y2 = (int16)C4WFYVal; + +//Render line + for(int32 i = C4WFDist ? C4WFDist : 1; i > 0; i--) { + if(X1 > 0xff && Y1 > 0xff && X1 < 0x6000 && Y1 < 0x6000) { + uint16 addr = (((Y1 >> 8) >> 3) << 8) - (((Y1 >> 8) >> 3) << 6) + (((X1 >> 8) >> 3) << 4) + ((Y1 >> 8) & 7) * 2; + uint8 bit = 0x80 >> ((X1 >> 8) & 7); + ram[addr + 0x300] &= ~bit; + ram[addr + 0x301] &= ~bit; + if(Color & 1) { ram[addr + 0x300] |= bit; } + if(Color & 2) { ram[addr + 0x301] |= bit; } + } + X1 += X2; + Y1 += Y2; + } +} + +void C4::C4DoScaleRotate(int row_padding) { +int16 A, B, C, D; + +//Calculate matrix +int32 XScale = readw(0x1f8f); +int32 YScale = readw(0x1f92); + if(XScale & 0x8000)XScale = 0x7fff; + if(YScale & 0x8000)YScale = 0x7fff; + + if(readw(0x1f80) == 0) { //no rotation + A = (int16)XScale; + B = 0; + C = 0; + D = (int16)YScale; + } else if(readw(0x1f80) == 128) { //90 degree rotation + A = 0; + B = (int16)(-YScale); + C = (int16)XScale; + D = 0; + } else if(readw(0x1f80) == 256) { //180 degree rotation + A = (int16)(-XScale); + B = 0; + C = 0; + D = (int16)(-YScale); + } else if(readw(0x1f80) == 384) { //270 degree rotation + A = 0; + B = (int16)YScale; + C = (int16)(-XScale); + D = 0; + } else { + A = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * XScale, 15); + B = (int16)(-sar(SinTable[readw(0x1f80) & 0x1ff] * YScale, 15)); + C = (int16) sar(SinTable[readw(0x1f80) & 0x1ff] * XScale, 15); + D = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * YScale, 15); + } + +//Calculate Pixel Resolution +uint8 w = read(0x1f89) & ~7; +uint8 h = read(0x1f8c) & ~7; + +//Clear the output RAM + memset(ram, 0, (w + row_padding / 4) * h / 2); + +int32 Cx = (int16)readw(0x1f83); +int32 Cy = (int16)readw(0x1f86); + +//Calculate start position (i.e. (Ox, Oy) = (0, 0)) +//The low 12 bits are fractional, so (Cx<<12) gives us the Cx we want in +//the function. We do Cx*A etc normally because the matrix parameters +//already have the fractional parts. +int32 LineX = (Cx << 12) - Cx * A - Cx * B; +int32 LineY = (Cy << 12) - Cy * C - Cy * D; + +//Start loop +uint32 X, Y; +uint8 byte; +int32 outidx = 0; +uint8 bit = 0x80; + for(int32 y = 0; y < h; y++) { + X = LineX; + Y = LineY; + for(int32 x = 0; x < w; x++) { + if((X >> 12) >= w || (Y >> 12) >= h) { + byte = 0; + } else { + uint32 addr = (Y >> 12) * w + (X >> 12); + byte = read(0x600 + (addr >> 1)); + if(addr & 1) { byte >>= 4; } + } + + //De-bitplanify + if(byte & 1) { ram[outidx ] |= bit; } + if(byte & 2) { ram[outidx + 1] |= bit; } + if(byte & 4) { ram[outidx + 16] |= bit; } + if(byte & 8) { ram[outidx + 17] |= bit; } + + bit >>= 1; + if(!bit) { + bit = 0x80; + outidx += 32; + } + + X += A; //Add 1 to output x => add an A and a C + Y += C; + } + outidx += 2 + row_padding; + if(outidx & 0x10) { + outidx &= ~0x10; + } else { + outidx -= w * 4 + row_padding; + } + LineX += B; //Add 1 to output y => add a B and a D + LineY += D; + } +} diff --git a/src/chip/c4/c4oam.cpp b/src/chip/c4/c4oam.cpp new file mode 100644 index 00000000..ef253216 --- /dev/null +++ b/src/chip/c4/c4oam.cpp @@ -0,0 +1,224 @@ +//Build OAM +void C4::op00_00() { +uint32 oamptr = ram[0x626] << 2; + for(int32 i=0x1fd;i>oamptr && i>=0;i-=4) { + //clear oam-to-be + if(i >= 0)ram[i] = 0xe0; + } + +uint16 globalx, globaly; +uint32 oamptr2; +int16 sprx, spry; +uint8 sprname, sprattr; +uint8 sprcount; + globalx = readw(0x621); + globaly = readw(0x623); + oamptr2 = 0x200 + (ram[0x626] >> 2); + + if(!ram[0x620])return; + + sprcount = 128 - ram[0x626]; +uint8 offset = (ram[0x626] & 3) * 2; + for(int pri=0x30;pri>=0;pri-=0x10) { + uint32 srcptr = 0x220; + for(int i=ram[0x620];i>0 && sprcount>0;i--, srcptr+=16) { + if((ram[srcptr + 4] & 0x30) != pri)continue; + sprx = readw(srcptr) - globalx; + spry = readw(srcptr + 2) - globaly; + sprname = ram[srcptr + 5]; + sprattr = ram[srcptr + 4] | ram[srcptr + 6]; + + uint32 spraddr = readl(srcptr + 7); + if(r_mem->read(spraddr)) { + int16 x, y; + for(int sprcnt=r_mem->read(spraddr++);sprcnt>0 && sprcount>0;sprcnt--, spraddr+=4) { + x = (int8)r_mem->read(spraddr + 1); + if(sprattr & 0x40) { + x = -x - ((r_mem->read(spraddr) & 0x20) ? 16 : 8); + } + x += sprx; + if(x >= -16 && x <= 272) { + y = (int8)r_mem->read(spraddr + 2); + if(sprattr & 0x80) { + y = -y - ((r_mem->read(spraddr) & 0x20) ? 16 : 8); + } + y += spry; + if(y >= -16 && y <= 224) { + ram[oamptr ] = (uint8)x; + ram[oamptr + 1] = (uint8)y; + ram[oamptr + 2] = sprname + r_mem->read(spraddr + 3); + ram[oamptr + 3] = sprattr ^ (r_mem->read(spraddr) & 0xc0); + ram[oamptr2] &= ~(3 << offset); + if(x & 0x100)ram[oamptr2] |= 1 << offset; + if(r_mem->read(spraddr) & 0x20)ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset)oamptr2++; + } + } + } + } else if(sprcount > 0) { + ram[oamptr ] = (uint8)sprx; + ram[oamptr + 1] = (uint8)spry; + ram[oamptr + 2] = sprname; + ram[oamptr + 3] = sprattr; + ram[oamptr2] &= ~(3 << offset); + if(sprx & 0x100)ram[oamptr2] |= 3 << offset; + else ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset)oamptr2++; + } + } + } +} + +//Scale and Rotate +void C4::op00_03() { + C4DoScaleRotate(0); +} + +//Transform Lines +void C4::op00_05() { + C4WFX2Val = read(0x1f83); + C4WFY2Val = read(0x1f86); + C4WFDist = read(0x1f89); + C4WFScale = read(0x1f8c); + +//Transform Vertices +uint32 ptr = 0; + for(int32 i = readw(0x1f80); i > 0; i--, ptr += 0x10) { + C4WFXVal = readw(ptr + 1); + C4WFYVal = readw(ptr + 5); + C4WFZVal = readw(ptr + 9); + C4TransfWireFrame(); + + //Displace + writew(ptr + 1, C4WFXVal + 0x80); + writew(ptr + 5, C4WFYVal + 0x50); + } + + writew(0x600, 23); + writew(0x602, 0x60); + writew(0x605, 0x40); + writew(0x600 + 8, 23); + writew(0x602 + 8, 0x60); + writew(0x605 + 8, 0x40); + + ptr = 0xb02; +uint32 ptr2 = 0; + for(int32 i = readw(0xb00); i > 0; i--, ptr += 2, ptr2 += 8) { + C4WFXVal = readw((read(ptr + 0) << 4) + 1); + C4WFYVal = readw((read(ptr + 0) << 4) + 5); + C4WFX2Val = readw((read(ptr + 1) << 4) + 1); + C4WFY2Val = readw((read(ptr + 1) << 4) + 5); + C4CalcWireFrame(); + writew(ptr2 + 0x600, C4WFDist ? C4WFDist : 1); + writew(ptr2 + 0x602, C4WFXVal); + writew(ptr2 + 0x605, C4WFYVal); + } +} + +//Scale and Rotate +void C4::op00_07() { + C4DoScaleRotate(64); +} + +//Draw Wireframe +void C4::op00_08() { + C4DrawWireFrame(); +} + +//Disintegrate +void C4::op00_0b() { +uint8 width, height; +uint32 startx, starty; +uint32 srcptr; +uint32 x, y; +int32 scalex, scaley; +int32 cx, cy; +int32 i, j; + width = read(0x1f89); + height = read(0x1f8c); + cx = readw(0x1f80); + cy = readw(0x1f83); + + scalex = (int16)readw(0x1f86); + scaley = (int16)readw(0x1f8f); + startx = -cx * scalex + (cx << 8); + starty = -cy * scaley + (cy << 8); + srcptr = 0x600; + + for(i = 0; i < (width * height) >> 1; i++) { + write(i, 0); + } + + for(y = starty, i = 0;i < height; i++, y += scaley) { + for(x = startx, j = 0;j < width; j++, x += scalex) { + if((x >> 8) < width && (y >> 8) < height && (y >> 8) * width + (x >> 8) < 0x2000) { + uint8 pixel = (j & 1) ? (ram[srcptr] >> 4) : (ram[srcptr]); + int32 index = (y >> 11) * width * 4 + (x >> 11) * 32 + ((y >> 8) & 7) * 2; + uint8 mask = 0x80 >> ((x >> 8) & 7); + if(pixel & 1)ram[index ] |= mask; + if(pixel & 2)ram[index + 1] |= mask; + if(pixel & 4)ram[index + 16] |= mask; + if(pixel & 8)ram[index + 17] |= mask; + } + if(j & 1)srcptr++; + } + } +} + +//Bitplane Wave +void C4::op00_0c() { +uint32 destptr = 0; +uint32 waveptr = read(0x1f83); +uint16 mask1 = 0xc0c0; +uint16 mask2 = 0x3f3f; + +int32 i, j; +int16 height, temp; + for(j = 0; j < 0x10; j++) { + do { + height = -((int8)ram[waveptr + 0xb00]) - 16; + for(i = 0; i < 40; i++) { + temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa00 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } while(mask1 != 0xc0c0); + destptr += 16; + + do { + height = -((int8)ram[waveptr + 0xb00]) - 16; + for(i = 0; i < 40; i++) { + temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa10 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } + } while(mask1 != 0xc0c0); + destptr += 16; + } +} diff --git a/src/chip/c4/c4ops.cpp b/src/chip/c4/c4ops.cpp new file mode 100644 index 00000000..05b7ece4 --- /dev/null +++ b/src/chip/c4/c4ops.cpp @@ -0,0 +1,222 @@ +//Sprite Functions +void C4::op00() { + switch(reg[0x4d]) { + case 0x00:op00_00();break; + case 0x03:op00_03();break; + case 0x05:op00_05();break; + case 0x07:op00_07();break; + case 0x08:op00_08();break; + case 0x0b:op00_0b();break; + case 0x0c:op00_0c();break; + } +} + +//Draw Wireframe +void C4::op01() { + memset(ram + 0x300, 0, 2304); + C4DrawWireFrame(); +} + +//Propulsion +void C4::op05() { +int32 temp = 0x10000; + if(readw(0x1f83)) { + temp = sar((temp / readw(0x1f83)) * readw(0x1f81), 8); + } + writew(0x1f80, temp); +} + +//Set Vector length +void C4::op0d() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDistVal = readw(0x1f86); + tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal)); + tanval = (double)C41FDistVal / tanval; + C41FYVal = (int16)(((double)C41FYVal * tanval) * 0.99); + C41FXVal = (int16)(((double)C41FXVal * tanval) * 0.98); + writew(0x1f89, C41FXVal); + writew(0x1f8c, C41FYVal); +} + +//Triangle +void C4::op10() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + if(r1 & 0x8000)r1 |= ~0x7fff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 16) & 0xff; + r2 = (r2 << 8) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 16) & 0xff; + r3 = (r3 << 8) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Triangle +void C4::op13() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 8) & 0xffff; + r2 = (r2 << 16) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 8) & 0xffff; + r3 = (r3 << 16) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Pythagorean +void C4::op15() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDist = (int16)sqrt((double)C41FXVal * (double)C41FXVal + (double)C41FYVal * (double)C41FYVal); + writew(0x1f80, C41FDist); +} + +//Calculate distance +void C4::op1f() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + if(!C41FXVal) { + C41FAngleRes = (C41FYVal > 0) ? 0x080 : 0x180; + } else { + tanval = ((double)C41FYVal) / ((double)C41FXVal); + C41FAngleRes = (short)(atan(tanval) / (PI * 2) * 512); + C41FAngleRes = C41FAngleRes; + if(C41FXVal < 0) { + C41FAngleRes += 0x100; + } + C41FAngleRes &= 0x1ff; + } + writew(0x1f86, C41FAngleRes); +} + +//Trapezoid +void C4::op22() { +int16 angle1 = readw(0x1f8c) & 0x1ff; +int16 angle2 = readw(0x1f8f) & 0x1ff; +int32 tan1 = Tan(angle1); +int32 tan2 = Tan(angle2); +int16 y = readw(0x1f83) - readw(0x1f89); +int16 left, right; + for(int32 j = 0; j < 225; j++, y++) { + if(y >= 0) { + left = sar((int32)tan1 * y, 16) - readw(0x1f80) + readw(0x1f86); + right = sar((int32)tan2 * y, 16) - readw(0x1f80) + readw(0x1f86) + readw(0x1f93); + + if(left < 0 && right < 0) { + left = 1; + right = 0; + } else if(left < 0) { + left = 0; + } else if(right < 0) { + right = 0; + } + + if(left > 255 && right > 255) { + left = 255; + right = 254; + } else if(left > 255) { + left = 255; + } else if(right > 255) { + right = 255; + } + } else { + left = 1; + right = 0; + } + ram[j + 0x800] = (uint8)left; + ram[j + 0x900] = (uint8)right; + } +} + +//Multiply +void C4::op25() { + r0 = ldr(0); + r1 = ldr(1); + mul(r0, r1, r0, r1); + str(0, r0); + str(1, r1); +} + +//Transform Coords +void C4::op2d() { + C4WFXVal = readw(0x1f81); + C4WFYVal = readw(0x1f84); + C4WFZVal = readw(0x1f87); + C4WFX2Val = read (0x1f89); + C4WFY2Val = read (0x1f8a); + C4WFDist = read (0x1f8b); + C4WFScale = readw(0x1f90); + C4TransfWireFrame2(); + writew(0x1f80, C4WFXVal); + writew(0x1f83, C4WFYVal); +} + +//Sum +void C4::op40() { + r0 = 0; + for(uint32 i=0;i<0x800;i++) { + r0 += ram[i]; + } + str(0, r0); +} + +//Square +void C4::op54() { + r0 = ldr(0); + mul(r0, r0, r1, r2); + str(1, r1); + str(2, r2); +} + +//Immediate Register +void C4::op5c() { + str(0, 0x000000); + immediate_reg(0); +} + +//Immediate Register (Multiple) +void C4::op5e() { immediate_reg( 0); } +void C4::op60() { immediate_reg( 3); } +void C4::op62() { immediate_reg( 6); } +void C4::op64() { immediate_reg( 9); } +void C4::op66() { immediate_reg(12); } +void C4::op68() { immediate_reg(15); } +void C4::op6a() { immediate_reg(18); } +void C4::op6c() { immediate_reg(21); } +void C4::op6e() { immediate_reg(24); } +void C4::op70() { immediate_reg(27); } +void C4::op72() { immediate_reg(30); } +void C4::op74() { immediate_reg(33); } +void C4::op76() { immediate_reg(36); } +void C4::op78() { immediate_reg(39); } +void C4::op7a() { immediate_reg(42); } +void C4::op7c() { immediate_reg(45); } + +//Immediate ROM +void C4::op89() { + str(0, 0x054336); + str(1, 0xffffff); +} diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index e7e16098..29a0a4a9 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,8 +1,7 @@ #include "../../base.h" #include "sdd1emu.cpp" -void SDD1::init() { -} +void SDD1::init() {} void SDD1::enable() { for(int i=0x4800;i<=0x4807;i++) { diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index aa59e006..79dc999a 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -74,8 +74,7 @@ tm *t; srtc.data[12] = t->tm_wday; } -void SRTC::init() { -} +void SRTC::init() {} void SRTC::enable() { r_mem->set_mmio_mapper(0x2800, mmio); diff --git a/src/config/config.cpp b/src/config/config.cpp index a3d82316..4da22045 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -21,8 +21,24 @@ void SNES::VideoColorAdjust::set(uint32 _data) { ::snes->update_color_lookup_table(); } -Setting SNES::mute(&config_file, "snes.mute", - "Mutes SNES audio output when enabled", - true, Setting::TRUE_FALSE); +Setting SNES::mute(&config_file, "snes.mute", "Mutes SNES audio output when enabled", + false, Setting::TRUE_FALSE); + +//do not save these settings to config_file +Setting CPU::hdma_enable(0, "cpu.hdma_enable", "Enable HDMA effects", true, Setting::TRUE_FALSE); +Setting PPU::opt_enable(0, "ppu.opt_enable", "Enable offset-per-tile effects", true, Setting::TRUE_FALSE); + +Setting PPU::bg1_pri0_enable(0, "ppu.bg1_pri0_enable", "Enable BG1 Priority 0", true, Setting::TRUE_FALSE); +Setting PPU::bg1_pri1_enable(0, "ppu.bg1_pri1_enable", "Enable BG1 Priority 1", true, Setting::TRUE_FALSE); +Setting PPU::bg2_pri0_enable(0, "ppu.bg2_pri0_enable", "Enable BG2 Priority 0", true, Setting::TRUE_FALSE); +Setting PPU::bg2_pri1_enable(0, "ppu.bg2_pri1_enable", "Enable BG2 Priority 1", true, Setting::TRUE_FALSE); +Setting PPU::bg3_pri0_enable(0, "ppu.bg3_pri0_enable", "Enable BG3 Priority 0", true, Setting::TRUE_FALSE); +Setting PPU::bg3_pri1_enable(0, "ppu.bg3_pri1_enable", "Enable BG3 Priority 1", true, Setting::TRUE_FALSE); +Setting PPU::bg4_pri0_enable(0, "ppu.bg4_pri0_enable", "Enable BG4 Priority 0", true, Setting::TRUE_FALSE); +Setting PPU::bg4_pri1_enable(0, "ppu.bg4_pri1_enable", "Enable BG4 Priority 1", true, Setting::TRUE_FALSE); +Setting PPU::oam_pri0_enable(0, "ppu.oam_pri0_enable", "Enable OAM Priority 0", true, Setting::TRUE_FALSE); +Setting PPU::oam_pri1_enable(0, "ppu.oam_pri1_enable", "Enable OAM Priority 1", true, Setting::TRUE_FALSE); +Setting PPU::oam_pri2_enable(0, "ppu.oam_pri2_enable", "Enable OAM Priority 2", true, Setting::TRUE_FALSE); +Setting PPU::oam_pri3_enable(0, "ppu.oam_pri3_enable", "Enable OAM Priority 3", true, Setting::TRUE_FALSE); }; diff --git a/src/config/config.h b/src/config/config.h index 9933d6c8..31cfd305 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -12,4 +12,19 @@ extern struct SNES { static Setting mute; } snes; +extern struct CPU { + static Setting hdma_enable; +} cpu; + +extern struct PPU { + static Setting opt_enable; + + static Setting bg1_pri0_enable, bg1_pri1_enable; + static Setting bg2_pri0_enable, bg2_pri1_enable; + static Setting bg3_pri0_enable, bg3_pri1_enable; + static Setting bg4_pri0_enable, bg4_pri1_enable; + static Setting oam_pri0_enable, oam_pri1_enable; + static Setting oam_pri2_enable, oam_pri3_enable; +} ppu; + }; diff --git a/src/cpu/bcpu/bcpu_dma.cpp b/src/cpu/bcpu/bcpu_dma.cpp index 2fad96eb..8580dc48 100644 --- a/src/cpu/bcpu/bcpu_dma.cpp +++ b/src/cpu/bcpu/bcpu_dma.cpp @@ -122,12 +122,17 @@ uint8 bCPU::hdma_read(uint8 i) { } void bCPU::hdma_write(uint8 i, uint8 x) { +uint32 addr; if(channel[i].direction == DMA_CPUTOMMIO) { - r_mem->write(hdma_mmio(i), x); + addr = hdma_mmio(i); } else if(!channel[i].hdma_indirect) { - r_mem->write(hdma_addr(i), x); + addr = hdma_addr(i); } else { - r_mem->write(hdma_iaddr(i), x); + addr = hdma_iaddr(i); + } + + if((bool)config::cpu.hdma_enable == true) { + r_mem->write(addr, x); } add_cycles(8); diff --git a/src/cpu/bcpu/bcpu_opfn.cpp b/src/cpu/bcpu/bcpu_opfn.cpp index b6f13563..18eb3d42 100644 --- a/src/cpu/bcpu/bcpu_opfn.cpp +++ b/src/cpu/bcpu/bcpu_opfn.cpp @@ -1,31 +1,75 @@ //op_read inline void bCPU::op_adc_b() { int32 r = regs.a.l + rd.l + regs.p.c; -//bcd if(regs.p.d) { - if(((r ) & 15) > 9)r += 6; - if(((r >> 4) & 15) > 9)r += 6 << 4; + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 += ((rd.l) & 15) + regs.p.c; + if(n0 > 9) { + n0 -= 10; + n0 &= 15; + n1++; + } + n1 += ((rd.l >> 4) & 15); + if(n1 > 9) { + n1 -= 10; + n1 &= 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n1 << 4) | (n0); + } else { + r = regs.a.l + rd.l + regs.p.c; + regs.p.c = (r > 0xff); } regs.p.n = !!(r & 0x80); regs.p.v = !!(~(regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80); regs.p.z = ((uint8)r == 0); - regs.p.c = (r > 0xff); regs.a.l = r; } inline void bCPU::op_adc_w() { -int32 r = regs.a.w + rd.w + regs.p.c; -//bcd +int32 r; if(regs.p.d) { - if(((r ) & 15) > 9)r += 6; - if(((r >> 4) & 15) > 9)r += 6 << 4; - if(((r >> 8) & 15) > 9)r += 6 << 8; - if(((r >> 12) & 15) > 9)r += 6 << 12; + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 += ((rd.w) & 15) + regs.p.c; + if(n0 > 9) { + n0 -= 10; + n0 &= 15; + n1++; + } + n1 += ((rd.w >> 4) & 15); + if(n1 > 9) { + n1 -= 10; + n1 &= 15; + n2++; + } + n2 += ((rd.w >> 8) & 15); + if(n2 > 9) { + n2 -= 10; + n2 &= 15; + n3++; + } + n3 += ((rd.w >> 12) & 15); + if(n3 > 9) { + n3 -= 10; + n3 &= 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | (n0); + } else { + r = regs.a.w + rd.w + regs.p.c; + regs.p.c = (r > 0xffff); } regs.p.n = !!(r & 0x8000); regs.p.v = !!(~(regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000); regs.p.z = ((uint16)r == 0); - regs.p.c = (r > 0xffff); regs.a.w = r; } @@ -156,32 +200,70 @@ inline void bCPU::op_ora_w() { } inline void bCPU::op_sbc_b() { -int32 r = regs.a.l - rd.l - !regs.p.c; -//bcd +int32 r; if(regs.p.d) { - if(((r ) & 15) > 9)r -= 6; - if(((r >> 4) & 15) > 9)r -= 6 << 4; + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 -= ((rd.l ) & 15) + !regs.p.c; + n1 -= ((rd.l >> 4) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n1 << 4) | (n0); + } else { + r = regs.a.l - rd.l - !regs.p.c; + regs.p.c = (r >= 0); } regs.p.n = !!(r & 0x80); regs.p.v = !!((regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80); - regs.p.z = ((byte)r == 0); - regs.p.c = (r >= 0); + regs.p.z = ((uint8)r == 0); regs.a.l = r; } inline void bCPU::op_sbc_w() { -int32 r = regs.a.w - rd.w - !regs.p.c; -//bcd +int32 r; if(regs.p.d) { - if(((r ) & 15) > 9)r -= 6; - if(((r >> 4) & 15) > 9)r -= 6 << 4; - if(((r >> 8) & 15) > 9)r -= 6 << 8; - if(((r >> 12) & 15) > 9)r -= 6 << 12; + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 -= ((rd.w ) & 15) + !regs.p.c; + n1 -= ((rd.w >> 4) & 15); + n2 -= ((rd.w >> 8) & 15); + n3 -= ((rd.w >> 12) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + n2--; + } + if(n2 > 9) { + n2 += 10; + n3--; + } + if(n3 > 9) { + n3 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | (n0); + } else { + r = regs.a.w - rd.w - !regs.p.c; + regs.p.c = (r >= 0); } regs.p.n = !!(r & 0x8000); regs.p.v = !!((regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000); - regs.p.z = ((word)r == 0); - regs.p.c = (r >= 0); + regs.p.z = ((uint16)r == 0); regs.a.w = r; } diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 77e60098..cffc3ba5 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -70,4 +70,5 @@ enum { uint16 __relw(int16 offset); CPU(); + virtual ~CPU() {} }; diff --git a/src/dsp/bdsp/bdsp.cpp b/src/dsp/bdsp/bdsp.cpp index 18a91872..4a3d77d1 100644 --- a/src/dsp/bdsp/bdsp.cpp +++ b/src/dsp/bdsp/bdsp.cpp @@ -1,10 +1,22 @@ #include "../../base.h" #include "bdsp_tables.cpp" -uint8 bDSP::read_8 (uint16 addr) { return (spcram[addr]); } -uint16 bDSP::read_16 (uint16 addr) { return (spcram[(addr + 1) & 0xffff] << 8) + (spcram[addr]); } -void bDSP::write_8 (uint16 addr, uint8 data) { spcram[addr] = data; } -void bDSP::write_16(uint16 addr, uint16 data) { spcram[addr++] = data; spcram[addr] = data >> 8; } +uint8 bDSP::readb(uint16 addr) { + return spcram[addr]; +} + +void bDSP::writeb(uint16 addr, uint8 data) { + spcram[addr] = data; +} + +uint16 bDSP::readw(uint16 addr) { + return (readb(addr)) | (readb(addr + 1) << 8); +} + +void bDSP::writew(uint16 addr, uint16 data) { + writeb(addr, data); + writeb(addr + 1, data >> 8); +} uint8 bDSP::read(uint8 addr) { int i, v, n; @@ -98,12 +110,14 @@ int i, v, n; break; case 0x04:case 0x14:case 0x24:case 0x34: case 0x44:case 0x54:case 0x64:case 0x74: - //voice[v].SRCN = data; + voice[v].SRCN = data; + break; //below is anomie's code, but TRAC says writing SRCN doesn't affect anything until a //BRR-with-end block is encountered, where it loads the loop address from the new SRCN + //anomie's code breaks MK2 sound completely... if(voice[v].SRCN != data) { - voice[v].SRCN = data; - voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + ((voice[v].brr_looped)?2:0)); + voice[v].SRCN = data; + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2) + ((voice[v].brr_looped) ? 2 : 0)); voice[v].brr_index = 0; } break; @@ -144,18 +158,18 @@ int i, v, n; case 0x3c:status.EVOLR = data;break; case 0x4c: - status.KON = data; -// status.kon = data; + status.KON = data; + status.kon = data; status.key_flag = true; break; case 0x5c: - status.KOFF = data; + status.KOFF = data; status.key_flag = true; break; case 0x6c: - status.FLG = data; + status.FLG = data; status.key_flag = true; status.noise_rate = RateTable[data & 0x1f]; break; @@ -165,15 +179,15 @@ int i, v, n; status.ENDX = 0; break; - case 0x0d:status.EFB = data;break; - case 0x2d:status.PMON = data;break; - case 0x3d:status.NON = data;break; - case 0x4d:status.EON = data;break; - case 0x5d:status.DIR = data;break; - case 0x6d:status.ESA = data;break; + case 0x0d:status.EFB = data;break; + case 0x2d:status.PMON = data;break; + case 0x3d:status.NON = data;break; + case 0x4d:status.EON = data;break; + case 0x5d:status.DIR = data;break; + case 0x6d:status.ESA = data;break; case 0x7d: - status.EDL = data; + status.EDL = data; status.echo_size = (data & 0x0f) << 11; break; } @@ -221,7 +235,7 @@ int v; status.KOFF = 0x00; status.FLG |= 0xe0; -//status.kon = 0x00; + status.kon = 0x00; status.key_flag = false; status.noise_ctr = 0; @@ -238,7 +252,7 @@ int v; voice[v].pitch_ctr = 0; voice[v].brr_index = 0; - voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); voice[v].brr_looped = false; voice[v].brr_data[0] = 0; voice[v].brr_data[1] = 0; @@ -288,7 +302,9 @@ int32 esamplel, esampler; int32 fir_samplel, fir_sampler; pmon = status.PMON & ~status.NON & ~1; - if(!(dsp_counter++ & 1) && status.key_flag) { +//if(!(dsp_counter++ & 1) && status.key_flag) { +//TRAC believes KON/KOFF is polled every sample. further testing is needed + if(status.key_flag) { for(v=0;v<8;v++) { uint8 mask = 1 << v; if(status.soft_reset()) { @@ -301,10 +317,12 @@ int32 fir_samplel, fir_sampler; voice[v].env_state = RELEASE; voice[v].AdjustEnvelope(); } - } else if(status.KON & mask) { //status.kon - status.KON &= ~mask; //new code - status.ENDX &= ~mask; //new code - voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2)); + } else if(status.kon & mask) { + //new KON code + status.ENDX &= ~mask; + status.kon &= ~mask; + + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); voice[v].brr_index = -9; voice[v].brr_looped = false; voice[v].brr_data[0] = 0; @@ -316,7 +334,8 @@ int32 fir_samplel, fir_sampler; voice[v].AdjustEnvelope(); } } -// status.ENDX &= ~status.kon; +//old KON code, breaks sound effects in DL / SFA2 +// status.ENDX &= ~status.kon; // status.kon = 0; status.key_flag = false; } @@ -359,7 +378,7 @@ int32 fir_samplel, fir_sampler; voice[v].brr_data_index &= 3; if(voice[v].brr_index == 0) { - voice[v].brr_header = read_8(voice[v].brr_ptr); + voice[v].brr_header = readb(voice[v].brr_ptr); if(voice[v].brr_header_flags() & BRR_END) { status.ENDX |= (1 << v); @@ -373,7 +392,7 @@ int32 fir_samplel, fir_sampler; #define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3] if(voice[v].env_state != SILENCE) { - sample = read_8(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1)); + sample = readb(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1)); if(voice[v].brr_index & 1) { sample = clip(4, sample); } else { @@ -410,7 +429,7 @@ int32 fir_samplel, fir_sampler; if(++voice[v].brr_index > 15) { voice[v].brr_index = 0; if(voice[v].brr_header_flags() & BRR_END) { - voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + 2); + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2) + 2); voice[v].brr_looped = true; } else { voice[v].brr_ptr += 9; @@ -496,7 +515,7 @@ int32 fir_samplel, fir_sampler; //gaussian interpolation / noise if(status.NON & (1 << v)) { - sample = status.noise_sample; + sample = clip(15, status.noise_sample); } else { d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1 sample = ((GaussTable[ -1-d] * S(-3)) >> 11); @@ -527,8 +546,8 @@ int32 fir_samplel, fir_sampler; //echo (FIR) adjust #define F(c,x) status.fir_buffer[c][(status.fir_buffer_index+(x)) & 7] status.fir_buffer_index++; - F(0,0) = read_16((status.ESA << 8) + status.echo_index); - F(1,0) = read_16((status.ESA << 8) + status.echo_index + 2); + F(0,0) = readw((status.ESA << 8) + status.echo_index); + F(1,0) = readw((status.ESA << 8) + status.echo_index + 2); fir_samplel = (F(0,-0) * status.FIR[7] + F(0,-1) * status.FIR[6] + @@ -557,8 +576,8 @@ int32 fir_samplel, fir_sampler; esamplel = clamp(16, esamplel); esampler = clamp(16, esampler); - write_16((status.ESA << 8) + status.echo_index, esamplel); - write_16((status.ESA << 8) + status.echo_index + 2, esampler); + writew((status.ESA << 8) + status.echo_index, esamplel); + writew((status.ESA << 8) + status.echo_index + 2, esampler); } status.echo_index += 4; diff --git a/src/dsp/bdsp/bdsp.h b/src/dsp/bdsp/bdsp.h index dfed7bac..bc07632a 100644 --- a/src/dsp/bdsp/bdsp.h +++ b/src/dsp/bdsp/bdsp.h @@ -10,10 +10,10 @@ enum { BRR_LOOP = 2 }; -uint8 read_8 (uint16 addr); -uint16 read_16 (uint16 addr); -void write_8 (uint16 addr, uint8 data); -void write_16(uint16 addr, uint16 data); +uint8 readb (uint16 addr); +uint16 readw (uint16 addr); +void writeb(uint16 addr, uint8 data); +void writew(uint16 addr, uint16 data); public: static const uint16 RateTable[32]; @@ -63,7 +63,7 @@ struct Status { int8 FIR[8]; //internal variables -//uint8 kon; + uint8 kon; bool key_flag; int16 noise_ctr, noise_rate; diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h index af495adb..892a4ac1 100644 --- a/src/dsp/dsp.h +++ b/src/dsp/dsp.h @@ -6,4 +6,7 @@ public: virtual void power() = 0; virtual void reset() = 0; virtual uint32 run() = 0; + + DSP() {} + virtual ~DSP() {} }; diff --git a/src/interface.h b/src/interface.h index 28fc434a..d15ed2fb 100644 --- a/src/interface.h +++ b/src/interface.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.014" +#define BSNES_VERSION "0.015" #define BSNES_TITLE "bsnes v" BSNES_VERSION #ifdef POLYMORPHISM @@ -15,6 +15,7 @@ #define r_ppu ref(deref(ppu)) #include "reader/reader.h" +#include "cart/cart.h" #include "memory/memory.h" #include "memory/bmemory/bmemory.h" @@ -35,6 +36,7 @@ #include "chip/srtc/srtc.h" #include "chip/sdd1/sdd1.h" +#include "chip/c4/c4.h" extern MMIO mmio_unmapped; #ifdef POLYMORPHISM @@ -54,10 +56,12 @@ extern SNES *snes; extern SRTC *srtc; extern SDD1 *sdd1; +extern C4 *c4; #include "config/config.h" #ifdef INTERFACE_MAIN +Cartridge cartridge; #include "config/config.cpp" #ifdef POLYMORPHISM @@ -77,5 +81,6 @@ SNES *snes; SRTC *srtc; SDD1 *sdd1; +C4 *c4; #endif diff --git a/src/lib/libbase.h b/src/lib/libbase.h index b203d073..2bfbed6b 100644 --- a/src/lib/libbase.h +++ b/src/lib/libbase.h @@ -20,20 +20,23 @@ #define zerofree(__n) if(__n) { free(__n); __n = 0; } -typedef unsigned int uint; +typedef unsigned int uint; -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long ulong; +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long ulong; +typedef unsigned long long uquad; -typedef unsigned char bool8; -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned long uint32; +typedef unsigned char bool8; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned long uint32; +typedef unsigned long long uint64; -typedef signed char int8; -typedef signed short int16; -typedef signed long int32; +typedef signed char int8; +typedef signed short int16; +typedef signed long int32; +typedef signed long long int64; #ifdef null #undef null diff --git a/src/lib/libconfig.cpp b/src/lib/libconfig.cpp index f772619e..08157d98 100644 --- a/src/lib/libconfig.cpp +++ b/src/lib/libconfig.cpp @@ -19,6 +19,15 @@ void Setting::set(uint _data) { } } +char *Setting::sget() { + return strptr(char_data); +} + +void Setting::sset(const char *_data) { + strcpy(char_data, _data); + strunquote(char_data); +} + Setting::Setting(Config *_parent, char *_name, char *_desc, uint _data, uint _type) { int s; if(_parent) { @@ -43,28 +52,53 @@ int s; type = _type; } +Setting::Setting(Config *_parent, char *_name, char *_desc, char *_data) { +int s; + if(_parent) { + _parent->add(this); + } + + s = strlen(_name); + name = (char*)malloc(s + 1); + strcpy(name, _name); + + if(_desc) { + s = strlen(_desc); + desc = (char*)malloc(s + 1); + strcpy(desc, _desc); + } else { + desc = (char*)malloc(1); + *desc = 0; + } + + strcpy(char_data, _data); + strcpy(char_def, _data); + + type = STR; +} + void Config::add(Setting *setting) { list[list_count++] = setting; } uint Config::string_to_uint(uint type, char *input) { - if(!strcmp(input, "true") || - !strcmp(input, "enabled") || - !strcmp(input, "on") || - !strcmp(input, "yes") + if(strmatch(input, "true") || + strmatch(input, "enabled") || + strmatch(input, "on") || + strmatch(input, "yes") ) { return (uint)true; } - if(!strcmp(input, "false") || - !strcmp(input, "disabled") || - !strcmp(input, "off") || - !strcmp(input, "no") + if(strmatch(input, "false") || + strmatch(input, "disabled") || + strmatch(input, "off") || + strmatch(input, "no") ) { return (uint)false; } - if(!strbegin(input, "0x")) { + if(strbegin(input, "0x")) { return strhex(input + 2); } @@ -75,16 +109,16 @@ char *Config::uint_to_string(uint type, uint input) { static char output[512]; switch(type) { case Setting::TRUE_FALSE: - sprintf(output, "%s", (input & 1)?"true":"false"); + sprintf(output, "%s", (input & 1) ? "true" : "false"); break; case Setting::ENABLED_DISABLED: - sprintf(output, "%s", (input & 1)?"enabled":"disabled"); + sprintf(output, "%s", (input & 1) ? "enabled" : "disabled"); break; case Setting::ON_OFF: - sprintf(output, "%s", (input & 1)?"on":"off"); + sprintf(output, "%s", (input & 1) ? "on" : "off"); break; case Setting::YES_NO: - sprintf(output, "%s", (input & 1)?"yes":"no"); + sprintf(output, "%s", (input & 1) ? "yes" : "no"); break; case Setting::DEC: sprintf(output, "%d", input); @@ -119,14 +153,18 @@ char *buffer = (char*)malloc(fsize + 1); qreplace(data, " ", ""); split(line, "\n", data); - for(int i=0;iname, part[0])) { + if(list[l]->type != Setting::STR) { + list[l]->set(string_to_uint(list[l]->type, strptr(part[1]))); + } else { + list[l]->sset(strptr(part[1])); + } } } } @@ -140,15 +178,20 @@ FILE *fp; fp = fopen(fn, "wb"); if(!fp)return false; - for(int i=0;idesc); replace(data, "\r\n", "\n"); split(line, "\n", data); - for(int l=0;ltype, list[i]->def)); - fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, uint_to_string(list[i]->type, list[i]->data)); + if(list[i]->type != Setting::STR) { + fprintf(fp, "# (default = %s)\r\n", uint_to_string(list[i]->type, list[i]->def)); + fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, uint_to_string(list[i]->type, list[i]->data)); + } else { + fprintf(fp, "# (default = \"%s\")\r\n", strptr(list[i]->char_def)); + fprintf(fp, "%s = \"%s\"\r\n\r\n", list[i]->name, strptr(list[i]->char_data)); + } } return true; diff --git a/src/lib/libconfig.h b/src/lib/libconfig.h index bec3ef9c..2cb2316a 100644 --- a/src/lib/libconfig.h +++ b/src/lib/libconfig.h @@ -1,5 +1,5 @@ /* - libconfig : version 0.05 ~byuu (09/13/05) + libconfig : version 0.05 ~byuu (10/30/05) */ #ifndef __LIBCONFIG @@ -28,6 +28,8 @@ class Config; inline __name &operator=(const int8 _data) { set(_data); return *this; } \ inline __name &operator=(const int16 _data) { set(_data); return *this; } \ inline __name &operator=(const int32 _data) { set(_data); return *this; } \ + inline __name &operator=(const float _data) { set((uint)_data); return *this; } \ + inline __name &operator=(const double _data) { set((uint)_data); return *this; } \ void toggle() { data ^= 1; set(data); } \ __name(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = Setting::DEC) : \ Setting(_parent, _name, _desc, _data, _type) {} @@ -46,14 +48,20 @@ enum { YES_NO, BOOL, DEC, - HEX + HEX, + STR }; char *name, *desc; - virtual void toggle(); - virtual uint get(); - virtual void set(uint _data); +substring char_data, char_def; + virtual void toggle(); + virtual uint get(); + virtual void set(uint _data); - Setting(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = DEC); + virtual char *sget(); + virtual void sset(const char *_data); + + Setting(Config *_parent, char *_name, char *_desc, uint _data, uint _type); + Setting(Config *_parent, char *_name, char *_desc, char *_data); inline operator bool() { return (bool)get(); } inline operator uint() { return get(); } @@ -64,6 +72,8 @@ char *name, *desc; inline operator int8() { return get(); } inline operator int16() { return get(); } inline operator int32() { return get(); } + inline operator float() { return (float) get(); } + inline operator double() { return (double)get(); } inline Setting &operator=(const bool _data) { set((uint)_data); return *this; } inline Setting &operator=(const uint _data) { set(_data); return *this; } @@ -74,6 +84,8 @@ char *name, *desc; inline Setting &operator=(const int8 _data) { set(_data); return *this; } inline Setting &operator=(const int16 _data) { set(_data); return *this; } inline Setting &operator=(const int32 _data) { set(_data); return *this; } + inline Setting &operator=(const float _data) { set((uint)_data); return *this; } + inline Setting &operator=(const double _data) { set((uint)_data); return *this; } }; class Config { diff --git a/src/lib/libstring.cpp b/src/lib/libstring.cpp index 288541d9..1a193684 100644 --- a/src/lib/libstring.cpp +++ b/src/lib/libstring.cpp @@ -64,6 +64,28 @@ int strcmp(substring &dest, const char *src) { return strcmp(strptr(dest), src); int strcmp(const char *dest, substring &src) { return strcmp(dest, strptr(src)); } int strcmp(substring &dest, substring &src) { return strcmp(strptr(dest), strptr(src)); } +int __stricmp(const char *dest, const char *src) { + while(*dest && *src) { + if(chrlower(*dest) != chrlower(*src))break; + dest++; + src++; + } + return (int)chrlower(*dest) - (int)chrlower(*src); +} +int stricmp(substring &dest, const char *src) { return __stricmp(strptr(dest), src); } +int stricmp(const char *dest, substring &src) { return __stricmp(dest, strptr(src)); } +int stricmp(substring &dest, substring &src) { return __stricmp(strptr(dest), strptr(src)); } + +bool strmatch(const char *dest, const char *src) { return !strcmp(dest, src); } +bool strmatch(substring &dest, const char *src) { return strmatch(strptr(dest), src); } +bool strmatch(const char *dest, substring &src) { return strmatch(dest, strptr(src)); } +bool strmatch(substring &dest, substring &src) { return strmatch(strptr(dest), strptr(src)); } + +bool strimatch(const char *dest, const char *src) { return !stricmp(dest, src); } +bool strimatch(substring &dest, const char *src) { return strimatch(strptr(dest), src); } +bool strimatch(const char *dest, substring &src) { return strimatch(dest, strptr(src)); } +bool strimatch(substring &dest, substring &src) { return strimatch(strptr(dest), strptr(src)); } + void strcpy(substring &dest, const char *src) { int srclen = strlen(src); if(srclen > dest.size) { strresize(dest, srclen); } @@ -109,18 +131,6 @@ int i, sl = strlen(dest.s); s[i] = 0; } -int __stricmp(const char *dest, const char *src) { - while(*dest && *src) { - if(chrlower(*dest) != chrlower(*src))break; - dest++; - src++; - } - return (int)chrlower(*dest) - (int)chrlower(*src); -} -int stricmp(substring &dest, const char *src) { return __stricmp(strptr(dest), src); } -int stricmp(const char *dest, substring &src) { return __stricmp(dest, strptr(src)); } -int stricmp(substring &dest, substring &src) { return __stricmp(strptr(dest), strptr(src)); } - void strlower(char *str) { while(*str) { *str = chrlower(*str); @@ -137,40 +147,44 @@ void strupper(char *str) { } void strupper(substring &str) { strupper(strptr(str)); } -uint strpos(const char *str, const char *key) { +bool strpos(const char *str, const char *key, uint &pos) { int i, ssl = strlen(str), ksl = strlen(key); - if(ksl > ssl)return null; - for(i=0;i<=ssl-ksl;i++) { - if(!memcmp(str+i, key, ksl))return i; + if(ksl > ssl)return false; + for(i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + pos = i; + return true; + } } - return null; + return false; } -uint strpos(substring &str, const char *key) { return strpos(strptr(str), key); } -uint strpos(const char *str, substring &key) { return strpos(str, strptr(key)); } -uint strpos(substring &str, substring &key) { return strpos(strptr(str), strptr(key)); } +bool strpos(substring &str, const char *key, uint &pos) { return strpos(strptr(str), key, pos); } +bool strpos(const char *str, substring &key, uint &pos) { return strpos(str, strptr(key), pos); } +bool strpos(substring &str, substring &key, uint &pos) { return strpos(strptr(str), strptr(key), pos); } -uint qstrpos(const char *str, const char *key) { +bool qstrpos(const char *str, const char *key, uint &pos) { int i, z, ssl = strlen(str), ksl = strlen(key); uint8 x; - if(ksl > ssl)return null; - for(i=0;i<=ssl-ksl;) { + if(ksl > ssl)return false; + for(i = 0; i <= ssl - ksl;) { x = str[i]; if(x == '\"' || x == '\'') { z = i++; while(str[i] != x && i < ssl)i++; if(i >= ssl)i = z; } - if(!memcmp(str+i, key, ksl)) { - return i; + if(!memcmp(str + i, key, ksl)) { + pos = i; + return true; } else { i++; } } - return null; + return false; } -uint qstrpos(substring &str, const char *key) { return qstrpos(strptr(str), key); } -uint qstrpos(const char *str, substring &key) { return qstrpos(str, strptr(key)); } -uint qstrpos(substring &str, substring &key) { return qstrpos(strptr(str), strptr(key)); } +bool qstrpos(substring &str, const char *key, uint &pos) { return qstrpos(strptr(str), key, pos); } +bool qstrpos(const char *str, substring &key, uint &pos) { return qstrpos(str, strptr(key), pos); } +bool qstrpos(substring &str, substring &key, uint &pos) { return qstrpos(strptr(str), strptr(key), pos); } void strtr(char *dest, const char *before, const char *after) { int i, l, sl = strlen(dest), bsl = strlen(before), asl = strlen(after); @@ -183,53 +197,51 @@ int i, l, sl = strlen(dest), bsl = strlen(before), asl = strlen(after); } void strtr(substring &dest, const char *before, const char *after) { strtr(strptr(dest), before, after); } -uint strbegin(const char *str, const char *key) { +bool strbegin(const char *str, const char *key) { int i, ssl = strlen(str), ksl = strlen(key); - if(ksl > ssl)return 1; - if(!memcmp(str, key, ksl))return 0; - return 1; + if(ksl > ssl)return false; + return (!memcmp(str, key, ksl)); } -uint strbegin(substring &str, const char *key) { return strbegin(strptr(str), key); } +bool strbegin(substring &str, const char *key) { return strbegin(strptr(str), key); } -uint stribegin(const char *str, const char *key) { +bool stribegin(const char *str, const char *key) { int i, ssl = strlen(str), ksl = strlen(key); - if(ksl > ssl)return 1; + if(ksl > ssl)return false; for(i=0;i= 'A' && str[i] <= 'Z') { - if(str[i] != key[i] && str[i]+0x20 != key[i])return 1; + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; } else if(str[i] >= 'a' && str[i] <= 'z') { - if(str[i] != key[i] && str[i]-0x20 != key[i])return 1; + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; } else { - if(str[i] != key[i])return 1; + if(str[i] != key[i])return false; } } - return 0; + return true; } -uint stribegin(substring &str, const char *key) { return stribegin(strptr(str), key); } +bool stribegin(substring &str, const char *key) { return stribegin(strptr(str), key); } -uint strend(const char *str, const char *key) { +bool strend(const char *str, const char *key) { int i, ssl = strlen(str), ksl = strlen(key); - if(ksl > ssl)return 1; - if(!memcmp(str + ssl - ksl, key, ksl))return 0; - return 1; + if(ksl > ssl)return false; + return (!memcmp(str + ssl - ksl, key, ksl)); } -uint strend(substring &str, const char *key) { return strend(strptr(str), key); } +bool strend(substring &str, const char *key) { return strend(strptr(str), key); } -uint striend(const char *str, const char *key) { +bool striend(const char *str, const char *key) { int i, z, ssl = strlen(str), ksl = strlen(key); - if(ksl > ssl)return 1; + if(ksl > ssl)return false; for(i=ssl-ksl, z=0;i= 'A' && str[i] <= 'Z') { - if(str[i] != key[z] && str[i]+0x20 != key[z])return 1; + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; } else if(str[i] >= 'a' && str[i] <= 'z') { - if(str[i] != key[z] && str[i]-0x20 != key[z])return 1; + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; } else { - if(str[i] != key[z])return 1; + if(str[i] != key[z])return false; } } - return 0; + return true; } -uint striend(substring &str, const char *key) { return striend(strptr(str), key); } +bool striend(substring &str, const char *key) { return striend(strptr(str), key); } void strltrim(char *str, const char *key) { int i, ssl = strlen(str), ksl = strlen(key); diff --git a/src/lib/libstring.h b/src/lib/libstring.h index 508662a6..0348d22d 100644 --- a/src/lib/libstring.h +++ b/src/lib/libstring.h @@ -1,5 +1,5 @@ /* - libstring : version 0.06a ~byuu (08/22/05) + libstring : version 0.07 ~byuu (11/30/05) */ #ifndef __LIBSTRING @@ -25,6 +25,25 @@ int strcmp(substring &dest, const char *src); int strcmp(const char *dest, substring &src); int strcmp(substring &dest, substring &src); +//vc6/win32 and gcc/dos only support stricmp, whereas +//gcc/unix only supports strcasecmp. this is an attempt +//to avoid platform-specific defines... +#define stricmp __stricmp +int __stricmp(const char *dest, const char *src); +int stricmp(substring &dest, const char *src); +int stricmp(const char *dest, substring &src); +int stricmp(substring &dest, substring &src); + +bool strmatch(const char *dest, const char *src); +bool strmatch(substring &dest, const char *src); +bool strmatch(const char *dest, substring &src); +bool strmatch(substring &dest, substring &src); + +bool strimatch(const char *dest, const char *src); +bool strimatch(substring &dest, const char *src); +bool strimatch(const char *dest, substring &src); +bool strimatch(substring &dest, substring &src); + void strcpy(substring &dest, const char *src); void strcpy(substring &dest, substring &src); @@ -38,45 +57,36 @@ void strinsert(substring &dest, substring &src, uint pos); void strremove(substring &dest, uint start, uint length = 0); -//vc6/win32 and gcc/dos only support stricmp, whereas -//gcc/unix only supports strcasecmp. this is an attempt -//to avoid platform-specific defines... -#define stricmp __stricmp -int __stricmp(const char *dest, const char *src); -int stricmp(substring &dest, const char *src); -int stricmp(const char *dest, substring &src); -int stricmp(substring &dest, substring &src); - void strlower(char *str); void strlower(substring &str); void strupper(char *str); void strupper(substring &str); -uint strpos(const char *str, const char *key); -uint strpos(substring &str, const char *key); -uint strpos(const char *str, substring &key); -uint strpos(substring &str, substring &key); +bool strpos(const char *str, const char *key, uint &pos); +bool strpos(substring &str, const char *key, uint &pos); +bool strpos(const char *str, substring &key, uint &pos); +bool strpos(substring &str, substring &key, uint &pos); -uint qstrpos(const char *str, const char *key); -uint qstrpos(substring &str, const char *key); -uint qstrpos(const char *str, substring &key); -uint qstrpos(substring &str, substring &key); +bool qstrpos(const char *str, const char *key, uint &pos); +bool qstrpos(substring &str, const char *key, uint &pos); +bool qstrpos(const char *str, substring &key, uint &pos); +bool qstrpos(substring &str, substring &key, uint &pos); void strtr(char *dest, const char *before, const char *after); void strtr(substring &dest, const char *before, const char *after); -uint strbegin(const char *str, const char *key); -uint strbegin(substring &str, const char *key); +bool strbegin(const char *str, const char *key); +bool strbegin(substring &str, const char *key); -uint stribegin(const char *str, const char *key); -uint stribegin(substring &str, const char *key); +bool stribegin(const char *str, const char *key); +bool stribegin(substring &str, const char *key); -uint strend(const char *str, const char *key); -uint strend(substring &str, const char *key); +bool strend(const char *str, const char *key); +bool strend(substring &str, const char *key); -uint striend(const char *str, const char *key); -uint striend(substring &str, const char *key); +bool striend(const char *str, const char *key); +bool striend(substring &str, const char *key); void strltrim(char *str, const char *key); void strltrim(substring &str, const char *key); diff --git a/src/memory/bmemory/bcart_lorom.cpp b/src/memory/bmemory/bcart_lorom.cpp index 59e43946..c195837b 100644 --- a/src/memory/bmemory/bcart_lorom.cpp +++ b/src/memory/bmemory/bcart_lorom.cpp @@ -6,6 +6,12 @@ uint32 b, w; b = (addr >> 16); w = (addr & 0xffff); + if(cartridge.cart.c4) { + if(!(b & 0x40) && w >= 0x6000 && w <= 0x7fff) { + return c4->read(w); + } + } + //SRAM Region A if((b & 0x7f) >= 0x30 && (b & 0x7f) <= 0x3f && (w & 0xe000) == 0x6000) { b &= 0x7f; @@ -57,6 +63,13 @@ uint32 b, w; b = (addr >> 16); w = (addr & 0xffff); + if(cartridge.cart.c4) { + if(!(b & 0x40) && w >= 0x6000 && w <= 0x7fff) { + c4->write(w, value); + return; + } + } + //SRAM Region A if((b & 0x7f) >= 0x30 && (b & 0x7f) <= 0x3f && (w & 0xe000) == 0x6000) { b &= 0x7f; diff --git a/src/memory/bmemory/bmemory.cpp b/src/memory/bmemory/bmemory.cpp index de003674..da4f816b 100644 --- a/src/memory/bmemory/bmemory.cpp +++ b/src/memory/bmemory/bmemory.cpp @@ -4,82 +4,15 @@ #include "bcart_exlorom.cpp" #include "bcart_exhirom.cpp" -bool bMemBus::load_cart(Reader *rf) { -uint32 cksum, icksum, index; -char cart_title[24]; -uint8 mapper, region; - if(rom_loaded == true)return false; +void bMemBus::load_cart() { + if(rom_loaded == true)return; - rf->read(&rom_image); - rom = rom_image; - rom_size = rf->size(); - - if(rom_size < 32768) { - zerofree(rom_image); - return false; - } - -//check for ROM header (currently unused) - if((rom_size & 0x1fff) == 0x0200) { - rom_size -= 512; - rom += 512; - } - - if(rom_size >= 0x410000) { - if(rom[0x7fd5] == 0x32) { - mapper = EXLOROM; - } else { - mapper = EXHIROM; - } - goto end; - } - - if(rom_size < 65536) { - mapper = LOROM; - goto end; - } - - cksum = rom[0xffdc] | (rom[0xffdd] << 8); - icksum = rom[0xffde] | (rom[0xffdf] << 8); - if(cksum + icksum == 0xffff) { - mapper = HIROM; - } else { - mapper = LOROM; - } - - if(rom[0x7fd5] == 0x32 && rom[0xffd5] == 0x32) { - //SFA2 detected - mapper = EXLOROM; - goto end; - } - -end: - switch(mapper) { - case LOROM: index = 0x007fc0;break; - case HIROM: index = 0x00ffc0;break; - case EXLOROM:index = 0x007fc0;break; - case EXHIROM:index = 0x40ffc0;break; - } - memcpy(cart_title, (char*)rom + index, 21); - cart_title[21] = 0; - - switch(rom[index + 0x18] & 7) { - case 0:sram_size = 0; break; - case 1:sram_size = 2 * 1024;break; - case 2:sram_size = 4 * 1024;break; - case 3:sram_size = 8 * 1024;break; - case 4:sram_size = 16 * 1024;break; - case 5:sram_size = 32 * 1024;break; - case 6:sram_size = 64 * 1024;break; - case 7:sram_size = 128 * 1024;break; - } - - region = rom[index + 0x19]; - - dprintf("* Image Name : \"%s\"", cart_title); - dprintf("* Region : %s", (region <= 1) ? "NTSC" : "PAL"); - dprintf("* MAD : %0.2x", mapper); - dprintf("* SRAM Size : %dkb", sram_size / 1024); +uint8 *rom = cartridge.rom; +uint16 index = cartridge.cart.header_index; + dprintf("* Image Name : \"%s\"", cartridge.cart.name); + dprintf("* Region : %s", (cartridge.cart.region == Cartridge::NTSC) ? "NTSC" : "PAL"); + dprintf("* MAD : %0.2x", cartridge.cart.mapper); + dprintf("* SRAM Size : %dkb", cartridge.cart.sram_size / 1024); dprintf("* Reset:%0.4x NMI:%0.4x IRQ:%0.4x BRK[n]:%0.4x COP[n]:%0.4x BRK[e]:%0.4x COP[e]:%0.4x", rom[index + 0x3c] | (rom[index + 0x3d] << 8), //Reset rom[index + 0x2a] | (rom[index + 0x2b] << 8), //NMI @@ -92,66 +25,42 @@ end: dprintf(""); CartInfo ci; - ci.rom = rom; - ci.sram = sram; - ci.rom_size = rom_size; - ci.sram_size = sram_size; + ci.rom = cartridge.rom; + ci.sram = cartridge.sram; + ci.rom_size = cartridge.cart.rom_size; + ci.sram_size = cartridge.cart.sram_size; - switch(mapper) { - case LOROM: cart = new bCartLoROM(); break; - case HIROM: cart = new bCartHiROM(); break; - case EXLOROM:cart = new bCartExLoROM();break; - case EXHIROM:cart = new bCartExHiROM();break; - default:return false; + switch(cartridge.cart.mapper) { + case Cartridge::LOROM: cart = new bCartLoROM(); break; + case Cartridge::HIROM: cart = new bCartHiROM(); break; + case Cartridge::EXLOROM:cart = new bCartExLoROM();break; + case Cartridge::EXHIROM:cart = new bCartExHiROM();break; + default:return; } cart->set_cartinfo(&ci); - rom_loaded = true; - if(region == 0 || region == 1) { + if(cartridge.cart.region == Cartridge::NTSC) { snes->set_region(SNES::NTSC); } else { snes->set_region(SNES::PAL); } - return true; -} - -bool bMemBus::load_sram(Reader *rf) { - if(rom_loaded == false || sram_size == 0)return false; - rf->read(&sram, sram_size); - -CartInfo ci; - ci.rom = rom; - ci.sram = sram; - ci.rom_size = rom_size; - ci.sram_size = sram_size; - cart->set_cartinfo(&ci); - - return true; -} - -bool bMemBus::save_sram(Writer *wf) { - if(rom_loaded == false || sram_size == 0)return false; - wf->write(sram, sram_size); - return true; + rom_loaded = true; } void bMemBus::unload_cart() { if(rom_loaded == false)return; - if(rom_image)free(rom_image); - if(sram)free(sram); delete(cart); - rom_loaded = false; } void bMemBus::get_cartinfo(CartInfo *ci) { - ci->rom = rom; - ci->sram = sram; - ci->rom_size = rom_size; - ci->sram_size = sram_size; + ci->rom = cartridge.rom; + ci->sram = cartridge.sram; + ci->rom_size = cartridge.cart.rom_size; + ci->sram_size = cartridge.cart.sram_size; } /*********************** @@ -171,7 +80,7 @@ void bMemBus::get_cartinfo(CartInfo *ci) { ***************************************/ uint8 bMemBus::read(uint32 addr) { -static uint32 r; +uint8 r; switch(addr & 0xc00000) { case 0x400000: if((addr & 0xfe0000) == 0x7e0000) { diff --git a/src/memory/bmemory/bmemory.h b/src/memory/bmemory/bmemory.h index 992ac956..2469946e 100644 --- a/src/memory/bmemory/bmemory.h +++ b/src/memory/bmemory/bmemory.h @@ -11,8 +11,7 @@ public: //just a pointer to rom_image (with no header), or //rom_image + 512 (if a header is present). //rom should never be allocated or released directly. -uint8 *rom_image, *rom, *sram, *wram; -uint32 rom_size, sram_size; +uint8 *wram; bool rom_loaded; enum { LOROM = 0x20, HIROM = 0x21, EXLOROM = 0x22, EXHIROM = 0x25 }; @@ -20,10 +19,9 @@ enum { LOROM = 0x20, HIROM = 0x21, EXLOROM = 0x22, EXHIROM = 0x25 }; uint8 read (uint32 addr); void write(uint32 addr, uint8 value); - bool load_cart(Reader *rf); - bool load_sram(Reader *rf); - bool save_sram(Writer *wf); + void load_cart(); void unload_cart(); + bool cart_loaded() { return rom_loaded; } void get_cartinfo(CartInfo *ci); void power(); diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index c305ac2d..d2e8f8c4 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -127,5 +127,3 @@ int i; } speed_table = (uint8*)speed_table_slowrom; } - -MemBus::~MemBus() {} diff --git a/src/memory/memory.h b/src/memory/memory.h index 7c195169..0d0ab80d 100644 --- a/src/memory/memory.h +++ b/src/memory/memory.h @@ -48,15 +48,14 @@ public: } void set_speed(bool fast); - virtual bool load_cart(Reader *rf) = 0; - virtual bool load_sram(Reader *rf) = 0; - virtual bool save_sram(Writer *wf) = 0; + virtual void load_cart() = 0; virtual void unload_cart() = 0; + virtual bool cart_loaded() = 0; virtual void get_cartinfo(CartInfo *ci) = 0; virtual void power() = 0; virtual void reset() = 0; MemBus(); - ~MemBus(); + virtual ~MemBus() {} }; diff --git a/src/ppu/bppu/Copy of bppu_render_addsub.cpp b/src/ppu/bppu/Copy of bppu_render_addsub.cpp deleted file mode 100644 index 2a216632..00000000 --- a/src/ppu/bppu/Copy of bppu_render_addsub.cpp +++ /dev/null @@ -1,64 +0,0 @@ -inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) { - if(!regs.color_mode) { - //add - cdest = ((cdest << 16) | cdest) & 0x03e07c1f; - csrc = ((csrc << 16) | csrc) & 0x03e07c1f; - cdest += csrc; - - if(regs.color_halve) { - cdest >>= 1; - } else { - if(cdest & 0x04000000)cdest |= 0x03e00000; - if(cdest & 0x00008000)cdest |= 0x00007c00; - if(cdest & 0x00000020)cdest |= 0x0000001f; - } - - cdest &= 0x03e07c1f; - return (cdest >> 16) | cdest; - } else { - //subtract - if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00; - else cdest -= (csrc & 0x7c00); - if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0; - else cdest -= (csrc & 0x03e0); - if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f; - else cdest -= (csrc & 0x001f); - - return (!regs.color_halve) ? cdest : ((cdest & 0x7bde) >> 1); - } -} - -inline uint16 bPPU::addsub_pixel(uint32 cdest) { -uint32 csrc = regs.color_rgb; - if(!regs.color_mode) { - //add - cdest = ((cdest << 16) | cdest) & 0x03e07c1f; - csrc = ((csrc << 16) | csrc) & 0x03e07c1f; - cdest += csrc; - - if(regs.color_halve && !regs.addsub_mode) { - cdest >>= 1; - } else { - if(cdest & 0x04000000)cdest |= 0x03e00000; - if(cdest & 0x00008000)cdest |= 0x00007c00; - if(cdest & 0x00000020)cdest |= 0x0000001f; - } - - cdest &= 0x03e07c1f; - return (cdest >> 16) | cdest; - } else { - //subtract - if((cdest & 0x7c00) < (csrc & 0x7c00))cdest &= ~0x7c00; - else cdest -= (csrc & 0x7c00); - if((cdest & 0x03e0) < (csrc & 0x03e0))cdest &= ~0x03e0; - else cdest -= (csrc & 0x03e0); - if((cdest & 0x001f) < (csrc & 0x001f))cdest &= ~0x001f; - else cdest -= (csrc & 0x001f); - - if(regs.color_halve && !regs.addsub_mode) { - return (cdest & 0x7bde) >> 1; - } - - return cdest; - } -} diff --git a/src/ppu/bppu/Copy of bppu_render_line.cpp b/src/ppu/bppu/Copy of bppu_render_line.cpp deleted file mode 100644 index 58ac93f8..00000000 --- a/src/ppu/bppu/Copy of bppu_render_line.cpp +++ /dev/null @@ -1,82 +0,0 @@ -inline uint16 bPPU::get_palette(uint8 index) { - return read16(cgram, index << 1); -} - -inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { -//p = 00000bgr -//t = BBGGGRRR -//r = 0BBb00GGGg0RRRr0 - return ((t & 7) << 2) | ((p & 1) << 1) | - (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | - ((t >> 6) << 13) | ((p >> 2) << 12); -} - -inline uint16 bPPU::get_pixel(int x) { -_pixel *p = &pixel_cache[x]; -uint16 r, src_back = get_palette(0); - if(p->bg_main && p->bg_sub) { - if(!p->color_exempt && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) { - if(regs.addsub_mode) { - r = addsub_pixels(p->src_main, p->src_sub); - } else { - r = addsub_pixel(p->src_main); - } - } else { - r = p->src_main; - } - } else if(p->bg_main) { - if(!p->color_exempt && regs.color_enabled[p->bg_main & 0x7f] && window_cache[COL].sub[x]) { - r = addsub_pixel(p->src_main); - } else { - r = p->src_main; - } - } else if(p->bg_sub) { - if(regs.color_enabled[BACK]) { - if(window_cache[COL].sub[x]) { - if(regs.addsub_mode) { - r = addsub_pixels(src_back, p->src_sub); - } else { - r = addsub_pixel(src_back); - } - } else { - r = src_back; - } - } else { - r = src_back; //was 0x0000 -- possibly another condition here? - } - } else { - if(window_cache[COL].main[x]) { - if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) { - r = addsub_pixel(src_back); - } else { - r = src_back; - } - } else { - if(regs.color_enabled[BACK] && window_cache[COL].sub[x]) { - r = (!regs.color_mode) ? regs.color_rgb : 0x0000; - } else { - r = 0x0000; - } - } - } - return r; -} - -inline void bPPU::render_line_output() { -uint16 r, x; -uint16 *ptr = (uint16*)output + (line.y * 1024) + - ((line.interlace && line.interlace_field) ? 512 : 0); -uint16 *ltable = (uint16*)light_table + (regs.display_brightness << 15); - - if(line.width == 256) { - for(x=0;x<256;x++) { - r = get_pixel(x); - *ptr++ = *(ltable + r); - } - } else { - for(x=0;x<512;x++) { - r = get_pixel(x); - *ptr++ = *(ltable + r); - } - } -} diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index b0d1af9b..cddab1fd 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -6,8 +6,7 @@ void bPPU::run() {} void bPPU::scanline() { line.y = r_cpu->vcounter(); - line.width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256; - line.hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + line.width = (regs.hires) ? 512 : 256; line.interlace = r_cpu->interlace(); line.interlace_field = r_cpu->interlace_field(); @@ -17,6 +16,28 @@ void bPPU::scanline() { regs.range_over = false; } +int32 bg; + if(line.y == 1) { + //mosaic reset + for(bg=BG1;bg<=BG4;bg++) { + regs.bg_y[bg] = 1; + } + + regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } else { + for(bg=BG1;bg<=BG4;bg++) { + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) { + regs.bg_y[bg] = line.y; + } + } + + if(!regs.mosaic_countdown) { + regs.mosaic_countdown = regs.mosaic_size + 1; + } + regs.mosaic_countdown--; + } + if(line.y == 1) { //OAM FirstSprite priority set if(regs.oam_priority == true) { @@ -66,6 +87,12 @@ void bPPU::reset() { regs.ppu1_mdr = 0xff; regs.ppu2_mdr = 0xff; +//bg line counters + regs.bg_y[0] = 0; + regs.bg_y[1] = 0; + regs.bg_y[2] = 0; + regs.bg_y[3] = 0; + //$2100 regs.display_disabled = 0; regs.display_brightness = 0; @@ -90,13 +117,15 @@ void bPPU::reset() { regs.bg_tilesize[BG4] = 0; regs.bg3_priority = 0; regs.bg_mode = 0; + regs.hires = false; //$2106 - regs.mosaic_size = 0; - regs.mosaic_enabled[BG1] = false; - regs.mosaic_enabled[BG2] = false; - regs.mosaic_enabled[BG3] = false; - regs.mosaic_enabled[BG4] = false; + regs.mosaic_size = 0; + regs.mosaic_enabled[BG1] = false; + regs.mosaic_enabled[BG2] = false; + regs.mosaic_enabled[BG3] = false; + regs.mosaic_enabled[BG4] = false; + regs.mosaic_countdown = 0; //$2107-$210a regs.bg_scaddr[BG1] = 0x0000; @@ -236,11 +265,12 @@ void bPPU::reset() { regs.color_rgb = 0x0000; //$2133 - regs.mode7_extbg = false; - regs.overscan = false; - regs.scanlines = 224; - regs.oam_halve = false; - regs.interlace = false; + regs.mode7_extbg = false; + regs.pseudo_hires = false; + regs.overscan = false; + regs.scanlines = 224; + regs.oam_halve = false; + regs.interlace = false; //$2137 regs.hcounter = 0; diff --git a/src/ppu/bppu/bppu.h b/src/ppu/bppu/bppu.h index 6223b8db..f79d6e85 100644 --- a/src/ppu/bppu/bppu.h +++ b/src/ppu/bppu/bppu.h @@ -20,7 +20,6 @@ enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; struct { uint32 y; uint32 width; - bool hires; bool interlace; bool interlace_field; } line; @@ -39,6 +38,9 @@ struct { //open bus support uint8 ppu1_mdr, ppu2_mdr; +//bg line counters + uint16 bg_y[4]; + //$2100 bool display_disabled; uint8 display_brightness; @@ -59,10 +61,12 @@ struct { bool bg_tilesize[4]; bool bg3_priority; uint8 bg_mode; + bool hires; //$2106 uint8 mosaic_size; bool mosaic_enabled[4]; + uint16 mosaic_countdown; //$2107-$210a uint16 bg_scaddr[4]; @@ -131,6 +135,7 @@ struct { //$2133 bool mode7_extbg; + bool pseudo_hires; bool overscan; uint16 scanlines; bool oam_halve; @@ -246,7 +251,7 @@ uint16 *mosaic_table[16]; void power(); void reset(); - bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); } + bool scanline_is_hires() { return (regs.hires); } bPPU(); ~bPPU(); diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index fcd3ddbb..8f53f005 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -117,6 +117,7 @@ void bPPU::mmio_w2105(uint8 value) { regs.bg_tilesize[BG1] = !!(value & 0x10); regs.bg3_priority = !!(value & 0x08); regs.bg_mode = (value & 7); + regs.hires = (regs.bg_mode == 5 || regs.bg_mode == 6); window_cache[BG1].main_dirty = window_cache[BG1].sub_dirty = true; window_cache[BG2].main_dirty = window_cache[BG2].sub_dirty = true; @@ -548,11 +549,12 @@ void bPPU::mmio_w2132(uint8 value) { //SETINI void bPPU::mmio_w2133(uint8 value) { - regs.mode7_extbg = !!(value & 0x40); - regs.overscan = !!(value & 0x04); - regs.scanlines = (value & 0x04)?239:224; - regs.oam_halve = !!(value & 0x02); - regs.interlace = !!(value & 0x01); + regs.mode7_extbg = !!(value & 0x40); + regs.pseudo_hires = !!(value & 0x08); + regs.overscan = !!(value & 0x04); + regs.scanlines = (value & 0x04)?239:224; + regs.oam_halve = !!(value & 0x02); + regs.interlace = !!(value & 0x01); r_cpu->set_overscan(regs.overscan); r_cpu->set_interlace(regs.interlace); diff --git a/src/ppu/bppu/bppu_render.cpp b/src/ppu/bppu/bppu_render.cpp index 8edac47c..5cde2785 100644 --- a/src/ppu/bppu/bppu_render.cpp +++ b/src/ppu/bppu/bppu_render.cpp @@ -6,6 +6,40 @@ #include "bppu_render_addsub.cpp" #include "bppu_render_line.cpp" +bool bPPU::render_enabled(uint8 bg, uint8 pri) { + switch(bg) { + case BG1: + switch(pri) { + case 0: return config::ppu.bg1_pri0_enable; + case 1: return config::ppu.bg1_pri1_enable; + } break; + case BG2: + switch(pri) { + case 0: return config::ppu.bg2_pri0_enable; + case 1: return config::ppu.bg2_pri1_enable; + } break; + case BG3: + switch(pri) { + case 0: return config::ppu.bg3_pri0_enable; + case 1: return config::ppu.bg3_pri1_enable; + } break; + case BG4: + switch(pri) { + case 0: return config::ppu.bg4_pri0_enable; + case 1: return config::ppu.bg4_pri1_enable; + } break; + case OAM: + switch(pri) { + case 0: return config::ppu.oam_pri0_enable; + case 1: return config::ppu.oam_pri1_enable; + case 2: return config::ppu.oam_pri2_enable; + case 3: return config::ppu.oam_pri3_enable; + } break; + } + + return true; +} + /* Mode 0: -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 diff --git a/src/ppu/bppu/bppu_render.h b/src/ppu/bppu/bppu_render.h index 0e9b3c24..7085f8b6 100644 --- a/src/ppu/bppu/bppu_render.h +++ b/src/ppu/bppu/bppu_render.h @@ -1,4 +1,5 @@ //bppu_render.cpp +inline bool render_enabled(uint8 bg, uint8 pri); inline void render_line_mode0(); inline void render_line_mode1(); inline void render_line_mode2(); @@ -12,19 +13,18 @@ inline void render_line_mode7(); enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 }; enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 }; -enum { PC_BG1 = 0x80, PC_BG2 = 0x81, PC_BG3 = 0x82, PC_BG4 = 0x83, PC_OAM = 0x84, PC_BACK = 0x00 }; struct _pixel { //bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0 //needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work uint16 src_main, src_sub; //indicates source of palette # for main/subscreen (BG1-4, OAM, or back) uint8 bg_main, bg_sub; -//true when bg_main == OAM && palette index >= 192, disables color add/sub effects -uint8 color_exempt; +//color_exemption -- true when bg == OAM && palette index >= 192, disables color add/sub effects +uint8 ce_main, ce_sub; //priority level of src_n. to set src_n, //the priority of the pixel must be >pri_n uint8 pri_main, pri_sub; -} pixel_cache[512]; +} pixel_cache[256]; uint8 *bg_tiledata[3]; uint8 *bg_tiledata_state[3]; @@ -37,7 +37,7 @@ inline void clear_tiledata_cache(); //bppu_render_windows.cpp struct _window { bool main_dirty, sub_dirty; - uint8 main[512], sub[512]; + uint8 main[256], sub[256]; } window_cache[6]; void build_window_table(uint8 bg, bool mainscreen); @@ -71,18 +71,20 @@ void load_oam_tiles(); void render_oam_tile(int tile_num); void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); void render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); -void render_line_oam_hires(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); //bppu_render_mode7.cpp void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos); //bppu_render_addsub.cpp -inline uint16 addsub_pixels(uint32 x); +inline uint16 addsub_pixels(uint32 x, uint32 cdest, uint32 csrc, bool halve); //bppu_render_line.cpp enum { BLENDTYPE_BACK = 0, BLENDTYPE_MAIN = 1, BLENDTYPE_SUB = 2, BLENDTYPE_COMBINE = 3 }; inline uint16 get_palette(uint8 index); inline uint16 get_direct_color(uint8 p, uint8 t); -inline uint16 get_pixel(uint32 x); +inline uint16 get_pixel_normal(uint32 x); +inline uint16 get_pixel_swap(uint32 x); +inline uint16 get_pixel_lores(uint32 x); +inline uint16 get_pixel_hires(uint32 x); inline void render_line_output(); diff --git a/src/ppu/bppu/bppu_render_addsub.cpp b/src/ppu/bppu/bppu_render_addsub.cpp index 835d4d8a..18a276f3 100644 --- a/src/ppu/bppu/bppu_render_addsub.cpp +++ b/src/ppu/bppu/bppu_render_addsub.cpp @@ -1,14 +1,4 @@ -inline uint16 bPPU::addsub_pixels(uint32 x) { -uint32 cdest = pixel_cache[x].src_main, - csrc = pixel_cache[x].src_sub; -bool halve = false; - if(regs.color_halve && window_cache[COL].main[x]) { - if(regs.addsub_mode && pixel_cache[x].bg_sub == BACK); - else { - halve = true; - } - } - +inline uint16 bPPU::addsub_pixels(uint32 x, uint32 cdest, uint32 csrc, bool halve) { if(!regs.color_mode) { //add cdest = ((cdest << 16) | cdest) & 0x03e07c1f; diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index 9b0bd8d6..d44fc3f3 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -1,8 +1,8 @@ //called once at the start of every rendered scanline void bPPU::update_bg_info() { - for(int bg=0;bg<4;bg++) { + for(int bg = 0; bg < 4; bg++) { bg_info[bg].th = (regs.bg_tilesize[bg]) ? 4 : 3; - bg_info[bg].tw = (line.hires) ? 4 : bg_info[bg].th; + bg_info[bg].tw = (regs.hires) ? 4 : bg_info[bg].th; bg_info[bg].mx = (bg_info[bg].th == 4) ? (line.width << 1) : line.width; bg_info[bg].my = bg_info[bg].mx; if(regs.bg_scsize[bg] & 0x01)bg_info[bg].mx <<= 1; @@ -13,22 +13,17 @@ void bPPU::update_bg_info() { } uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) { -uint16 map_index, pos; +uint16 pos; x &= bg_info[bg].mx; y &= bg_info[bg].my; -//32 = SC width, height; tilemap data is stored as uint16 -//width(32) * height(32) * sizeof(uint16) = 2048 - switch(regs.bg_scsize[bg]) { - case SC_32x32:map_index = 0; break; - case SC_64x32:map_index = ((x >> bg_info[bg].th) / 32) * 2048; break; - case SC_32x64:map_index = ((y >> bg_info[bg].th) / 32) * 2048; break; - case SC_64x64:map_index = ((x >> bg_info[bg].th) / 32) * 2048 + - ((y >> bg_info[bg].th) / 32) * 4096; break; - } + x >>= bg_info[bg].tw; + y >>= bg_info[bg].th; - pos = ((((y >> bg_info[bg].th) & 31) * 32) + ((x >> bg_info[bg].tw) & 31)) << 1; - return read16(vram, regs.bg_scaddr[bg] + map_index + pos); + pos = (regs.bg_scsize[bg] & 2) ? ((y & 0x20) << ((regs.bg_scsize[bg] & 1) ? 6 : 5)) : 0; + pos += (regs.bg_scsize[bg] & 1) ? ((x & 0x20) << 5) : 0; + pos += ((y & 31) << 5) + (x & 31); + return read16(vram, regs.bg_scaddr[bg] + (pos << 1)); } void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos) { @@ -36,6 +31,12 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri return; } +//are layers disabled by user? + if(render_enabled(bg, 0) == false)pri0_pos = 0; + if(render_enabled(bg, 1) == false)pri1_pos = 0; +//nothing to render? + if(!pri0_pos && !pri1_pos)return; + bool bg_enabled = regs.bg_enabled[bg]; bool bgsub_enabled = regs.bgsub_enabled[bg]; @@ -57,13 +58,13 @@ uint16 mask_x = bg_info[bg].mx; //screen width mask uint16 mask_y = bg_info[bg].my; //screen height mask uint x = 0; -uint y = line.y; - if(line.interlace && line.hires) { +uint y = regs.bg_y[bg]; + if(line.interlace && regs.hires) { y = (y << 1) + line.interlace_field; } -uint16 hscroll = (line.hires) ? (regs.bg_hofs[bg] << 1) : regs.bg_hofs[bg]; -uint16 vscroll = (line.interlace && line.hires) ? (regs.bg_vofs[bg] << 1) : regs.bg_vofs[bg]; +uint16 hscroll = (regs.hires) ? (regs.bg_hofs[bg] << 1) : regs.bg_hofs[bg]; +uint16 vscroll = (line.interlace && regs.hires) ? (regs.bg_vofs[bg] << 1) : regs.bg_vofs[bg]; hscroll &= mask_x; vscroll &= mask_y; @@ -78,14 +79,14 @@ uint8 *tile_ptr; uint xpos, ypos; uint16 hoffset, voffset, opt_x, col; bool mirror_x, mirror_y; -bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6); +bool is_opt_mode = (config::ppu.opt_enable) && (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6); build_window_tables(bg); uint8 *wt_main = window_cache[bg].main; uint8 *wt_sub = window_cache[bg].sub; int32 prev_x = -1, prev_y = -1; - for(x=0;x> 3) != prev_x || (mosaic_y >> 3) != prev_y) { prev_x = (mosaic_x >> 3); @@ -171,21 +172,35 @@ int32 prev_x = -1, prev_y = -1; col = get_palette(col + pal_index); } - if(bg_enabled == true && !wt_main[x]) { - if(pixel_cache[x].pri_main < tile_pri) { - pixel_cache[x].pri_main = tile_pri; - pixel_cache[x].bg_main = 0x80 | bg; - pixel_cache[x].src_main = col; - pixel_cache[x].color_exempt = false; - } - } - if(bgsub_enabled == true && !wt_sub[x]) { - if(pixel_cache[x].pri_sub < tile_pri) { - pixel_cache[x].pri_sub = tile_pri; - pixel_cache[x].bg_sub = 0x80 | bg; - pixel_cache[x].src_sub = col; + #define setpixel_main(x) \ + if(pixel_cache[x].pri_main < tile_pri) { \ + pixel_cache[x].pri_main = tile_pri; \ + pixel_cache[x].bg_main = bg; \ + pixel_cache[x].src_main = col; \ + pixel_cache[x].ce_main = false; \ + } + #define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < tile_pri) { \ + pixel_cache[x].pri_sub = tile_pri; \ + pixel_cache[x].bg_sub = bg; \ + pixel_cache[x].src_sub = col; \ + pixel_cache[x].ce_sub = false; \ + } + + if(!regs.hires) { + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } else { + int hx = x >> 1; + if(x & 1) { + if(bg_enabled == true && !wt_main[hx]) { setpixel_main(hx); } + } else { + if(bgsub_enabled == true && !wt_sub[hx]) { setpixel_sub(hx); } } } + + #undef setpixel_main + #undef setpixel_sub } } } diff --git a/src/ppu/bppu/bppu_render_cache.cpp b/src/ppu/bppu/bppu_render_cache.cpp index 0557f711..dbc8ac4d 100644 --- a/src/ppu/bppu/bppu_render_cache.cpp +++ b/src/ppu/bppu/bppu_render_cache.cpp @@ -103,7 +103,19 @@ uint8 *dest; #undef render_bg_tile_line_256 inline void bPPU::clear_pixel_cache() { - memset(pixel_cache, 0, sizeof(pixel_cache)); +uint16 main = get_palette(0); +uint16 sub = (regs.pseudo_hires || regs.hires) ? main : regs.color_rgb; +uint32 i = 255; + do { + pixel_cache[i].src_main = main; + pixel_cache[i].src_sub = sub; + pixel_cache[i].bg_main = BACK; + pixel_cache[i].bg_sub = BACK; + pixel_cache[i].ce_main = false; + pixel_cache[i].ce_sub = false; + pixel_cache[i].pri_main = 0; + pixel_cache[i].pri_sub = 0; + } while(i--); } void bPPU::init_tiledata_cache() { diff --git a/src/ppu/bppu/bppu_render_line.cpp b/src/ppu/bppu/bppu_render_line.cpp index 3e254660..8cfafced 100644 --- a/src/ppu/bppu/bppu_render_line.cpp +++ b/src/ppu/bppu/bppu_render_line.cpp @@ -11,59 +11,109 @@ inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { ((t >> 6) << 13) | ((p >> 2) << 12); } -inline uint16 bPPU::get_pixel(uint32 x) { +inline uint16 bPPU::get_pixel_normal(uint32 x) { _pixel *p = &pixel_cache[x]; - if(!p->bg_main) { - p->bg_main = BACK; - p->src_main = get_palette(0); - p->color_exempt = false; - } else { - p->bg_main &= 0x7f; - } - - if(!p->bg_sub) { - p->bg_sub = BACK; - p->src_sub = regs.color_rgb; - } else { - p->bg_sub &= 0x7f; - } +uint16 src_main, src_sub; +uint8 bg_sub; + src_main = p->src_main; if(!regs.addsub_mode) { - p->bg_sub = BACK; - p->src_sub = regs.color_rgb; + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p->bg_sub; + src_sub = p->src_sub; } if(!window_cache[COL].main[x]) { if(!window_cache[COL].sub[x]) { return 0x0000; } - //p->bg_main remains the same, even when the main color window - //masks out the color. this is needed for regs.color_enabled[p->bg_main] - //test below. illusion of gaia relies on this behavior for its load menu. - p->src_main = 0x0000; + src_main = 0x0000; } - if(!p->color_exempt && regs.color_enabled[p->bg_main] && window_cache[COL].sub[x]) { - return addsub_pixels(x); + if(!p->ce_main && regs.color_enabled[p->bg_main] && window_cache[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window_cache[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub_pixels(x, src_main, src_sub, halve); + } + + return src_main; +} + +inline uint16 bPPU::get_pixel_swap(uint32 x) { +_pixel *p = &pixel_cache[x]; +uint16 src_main, src_sub; +uint8 bg_sub; + src_main = p->src_sub; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p->bg_main; + src_sub = p->src_main; + } + + if(!window_cache[COL].main[x]) { + if(!window_cache[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p->ce_sub && regs.color_enabled[p->bg_sub] && window_cache[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window_cache[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub_pixels(x, src_main, src_sub, halve); + } + + return src_main; +} + +inline uint16 bPPU::get_pixel_lores(uint32 x) { + return get_pixel_normal(x); +} + +inline uint16 bPPU::get_pixel_hires(uint32 x) { + if(x & 1) { + return get_pixel_normal(x >> 1); + } else { + return get_pixel_swap(x >> 1); } - return p->src_main; } inline void bPPU::render_line_output() { -uint16 r, x; +uint32 r, x; uint16 *ptr = (uint16*)output + (line.y * 1024) + ((line.interlace && line.interlace_field) ? 512 : 0); uint16 *ltable = (uint16*)light_table + (regs.display_brightness << 15); - if(line.width == 256) { + if(!regs.pseudo_hires && !regs.hires) { for(x=0;x<256;x++) { - r = get_pixel(x); + r = get_pixel_lores(x); *ptr++ = *(ltable + r); } } else { for(x=0;x<512;x++) { - r = get_pixel(x); + r = get_pixel_hires(x); *ptr++ = *(ltable + r); } + if(regs.pseudo_hires && !regs.hires) { + ptr -= 512; + for(x=0;x<256;x++) { + *(ptr + x) = ((ptr[x << 1] & 0x7bde) >> 1) + ((ptr[(x << 1) + 1] & 0x7bde) >> 1); + } + } } } diff --git a/src/ppu/bppu/bppu_render_mode7.cpp b/src/ppu/bppu/bppu_render_mode7.cpp index 6e0797eb..9c0bdc52 100644 --- a/src/ppu/bppu/bppu_render_mode7.cpp +++ b/src/ppu/bppu/bppu_render_mode7.cpp @@ -17,6 +17,12 @@ void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return; +//are layers disabled by user? + if(render_enabled(bg, 0) == false)pri0_pos = 0; + if(render_enabled(bg, 1) == false)pri1_pos = 0; +//nothing to render? + if(!pri0_pos && !pri1_pos); + int32 x, y; int32 a, b, c, d, cx, cy; int32 hofs, vofs; @@ -131,16 +137,17 @@ int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d if(regs.bg_enabled[bg] == true && !wt_main[_x]) { if(pixel_cache[_x].pri_main < _pri) { pixel_cache[_x].pri_main = _pri; - pixel_cache[_x].bg_main = 0x80 | bg; + pixel_cache[_x].bg_main = bg; pixel_cache[_x].src_main = col; - pixel_cache[_x].color_exempt = false; + pixel_cache[_x].ce_main = false; } } if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { if(pixel_cache[_x].pri_sub < _pri) { pixel_cache[_x].pri_sub = _pri; - pixel_cache[_x].bg_sub = 0x80 | bg; + pixel_cache[_x].bg_sub = bg; pixel_cache[_x].src_sub = col; + pixel_cache[_x].ce_sub = false; } } } diff --git a/src/ppu/bppu/bppu_render_oam.cpp b/src/ppu/bppu/bppu_render_oam.cpp index a1177b40..1ff23631 100644 --- a/src/ppu/bppu/bppu_render_oam.cpp +++ b/src/ppu/bppu/bppu_render_oam.cpp @@ -220,13 +220,32 @@ int s; if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false)return; - if(line.width == 256) { - render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos); - } else { - render_line_oam_hires(pri0_pos, pri1_pos, pri2_pos, pri3_pos); - } +//are layers disabled by user? + if(render_enabled(OAM, 0) == false)pri0_pos = 0; + if(render_enabled(OAM, 1) == false)pri1_pos = 0; + if(render_enabled(OAM, 2) == false)pri2_pos = 0; + if(render_enabled(OAM, 3) == false)pri3_pos = 0; +//nothing to render? + if(!pri0_pos && !pri1_pos && !pri2_pos && !pri3_pos)return; + + render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos); } +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < pri) { \ + pixel_cache[x].pri_main = pri; \ + pixel_cache[x].bg_main = OAM; \ + pixel_cache[x].src_main = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_main = (oam_line_pal[x] < 192); \ + } +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < pri) { \ + pixel_cache[x].pri_sub = pri; \ + pixel_cache[x].bg_sub = OAM; \ + pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \ + } + void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { bool bg_enabled = regs.bg_enabled[OAM]; bool bgsub_enabled = regs.bgsub_enabled[OAM]; @@ -246,78 +265,10 @@ int pri; case 3:pri = pri3_pos;break; } - if(bg_enabled == true && !wt_main[x]) { - if(pixel_cache[x].pri_main < pri) { - pixel_cache[x].pri_main = pri; - pixel_cache[x].bg_main = PC_OAM; - pixel_cache[x].src_main = get_palette(oam_line_pal[x]); - pixel_cache[x].color_exempt = (oam_line_pal[x] < 192); - } - } - - if(bgsub_enabled == true && !wt_sub[x]) { - if(pixel_cache[x].pri_sub < pri) { - pixel_cache[x].pri_sub = pri; - pixel_cache[x].bg_sub = PC_OAM; - pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); - } - } + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } } } -void bPPU::render_line_oam_hires(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { -bool bg_enabled = regs.bg_enabled[OAM]; -bool bgsub_enabled = regs.bgsub_enabled[OAM]; - - build_window_tables(OAM); -uint8 *wt_main = window_cache[OAM].main; -uint8 *wt_sub = window_cache[OAM].sub; - -int pri, sx; - for(int x=0;x<256;x++) { - if(oam_line_pri[x] == OAM_PRI_NONE)continue; - - switch(oam_line_pri[x]) { - case 0:pri = pri0_pos;break; - case 1:pri = pri1_pos;break; - case 2:pri = pri2_pos;break; - case 3:pri = pri3_pos;break; - } - - sx = x << 1; - if(bg_enabled == true && !wt_main[sx]) { - if(pixel_cache[sx].pri_main < pri) { - pixel_cache[sx].pri_main = pri; - pixel_cache[sx].bg_main = PC_OAM; - pixel_cache[sx].src_main = get_palette(oam_line_pal[x]); - pixel_cache[sx].color_exempt = (oam_line_pal[x] < 192); - } - } - - if(bgsub_enabled == true && !wt_sub[sx]) { - if(pixel_cache[sx].pri_sub < pri) { - pixel_cache[sx].pri_sub = pri; - pixel_cache[sx].bg_sub = PC_OAM; - pixel_cache[sx].src_sub = get_palette(oam_line_pal[x]); - } - } - - sx++; - if(bg_enabled == true && !wt_main[sx]) { - if(pixel_cache[sx].pri_main < pri) { - pixel_cache[sx].pri_main = pri; - pixel_cache[sx].bg_main = PC_OAM; - pixel_cache[sx].src_main = get_palette(oam_line_pal[x]); - pixel_cache[sx].color_exempt = (oam_line_pal[x] < 192); - } - } - - if(bgsub_enabled == true && !wt_sub[sx]) { - if(pixel_cache[sx].pri_sub < pri) { - pixel_cache[sx].pri_sub = pri; - pixel_cache[sx].bg_sub = PC_OAM; - pixel_cache[sx].src_sub = get_palette(oam_line_pal[x]); - } - } - } -} +#undef setpixel_main +#undef setpixel_sub diff --git a/src/ppu/bppu/bppu_render_windows.cpp b/src/ppu/bppu/bppu_render_windows.cpp index 5791fe89..084c1130 100644 --- a/src/ppu/bppu/bppu_render_windows.cpp +++ b/src/ppu/bppu/bppu_render_windows.cpp @@ -9,11 +9,11 @@ uint8 *wtbl; if(bg != COL) { if(mainscreen == true && regs.window_enabled[bg] == false) { - memset(wtbl, 0, line.width); + memset(wtbl, 0, 256); return; } if(mainscreen == false && regs.sub_window_enabled[bg] == false) { - memset(wtbl, 0, line.width); + memset(wtbl, 0, 256); return; } } else { @@ -26,13 +26,13 @@ uint8 *wtbl; if(mask == 0) { //always - memset(wtbl, 1, line.width); + memset(wtbl, 1, 256); return; } if(mask == 3) { //never - memset(wtbl, 0, line.width); + memset(wtbl, 0, 256); return; } @@ -56,37 +56,30 @@ bool r; window2_left = regs.window2_left; window2_right = regs.window2_right; - if(line.width == 512) { - window1_left <<= 1; - window1_right <<= 1; - window2_left <<= 1; - window2_right <<= 1; - } - if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) { - memset(wtbl, clr, line.width); + memset(wtbl, clr, 256); } else if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) { if(regs.window1_invert[bg] == false) { - for(x=0;x= window1_left && x <= window1_right) ? set : clr; } } else { - for(x=0;x window1_right) ? set : clr; } } } else if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) { if(regs.window2_invert[bg] == false) { - for(x=0;x= window2_left && x <= window2_right) ? set : clr; } } else { - for(x=0;x window2_right) ? set : clr; } } } else { //if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) { - for(x=0;x= window1_left && x <= window1_right); } else { diff --git a/src/ppu/ppu.h b/src/ppu/ppu.h index 6def0e52..af65e132 100644 --- a/src/ppu/ppu.h +++ b/src/ppu/ppu.h @@ -51,5 +51,5 @@ struct scanline_info { virtual bool renderer_enabled(); PPU(); - ~PPU(); + virtual ~PPU(); }; diff --git a/src/reader/filereader.cpp b/src/reader/filereader.cpp index 26f796f2..41864921 100644 --- a/src/reader/filereader.cpp +++ b/src/reader/filereader.cpp @@ -6,7 +6,7 @@ uint32 FileReader::size() { //This is needed so that when SRAM files do not exist, the //memory for the SRAM data will be allocated still. //The memory is flushed to 0x00 when no file is opened. -void FileReader::read(uint8 **buffer, uint32 length) { +uint8 *FileReader::read(uint32 length) { uint8 *data; if(length == 0) { //read the entire file into RAM @@ -24,7 +24,7 @@ uint8 *data; memset(data, 0, length); if(fp)fread(data, 1, length, fp); } - *buffer = data; + return data; } bool FileReader::open(char *fn) { diff --git a/src/reader/filereader.h b/src/reader/filereader.h index aab88908..c195ff25 100644 --- a/src/reader/filereader.h +++ b/src/reader/filereader.h @@ -5,7 +5,7 @@ uint32 fsize; public: uint32 size(); - void read(uint8 **buffer, uint32 length = 0); + uint8 *read(uint32 length = 0); bool open(char *fn); void close(); diff --git a/src/reader/gzreader.cpp b/src/reader/gzreader.cpp new file mode 100644 index 00000000..7a66ce43 --- /dev/null +++ b/src/reader/gzreader.cpp @@ -0,0 +1,76 @@ +uint32 GZReader::size() { + return fsize; +} + +//This function will allocate memory even if open() fails. +//This is needed so that when SRAM files do not exist, the +//memory for the SRAM data will be allocated still. +//The memory is flushed to 0x00 when no file is opened. +uint8 *GZReader::read(uint32 length) { +uint8 *data; + if(length == 0) { + //read the entire file into RAM + data = (uint8*)memalloc(fsize); + memset(data, 0, fsize); + if(gp)gzread(gp, data, fsize); + } else if(length > fsize) { + //read the entire file into RAM, pad the rest with 0x00s + data = (uint8*)memalloc(length); + memset(data, 0, length); + if(gp)gzread(gp, data, fsize); + } else { //fsize >= length + //read only what was requested + data = (uint8*)memalloc(length); + memset(data, 0, length); + if(gp)gzread(gp, data, length); + } + return data; +} + +bool GZReader::open(char *fn) { +FILE *fp; + fp = fopen(fn, "rb"); + if(!fp)return false; + + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + + if(fsize < 4) { + fclose(fp); + fp = 0; + return false; + } + +uint32 gzsize; + fseek(fp, -4, SEEK_END); //jump to 4 bytes before end + gzsize = fgetc(fp); + gzsize |= fgetc(fp) << 8; + gzsize |= fgetc(fp) << 16; + gzsize |= fgetc(fp) << 24; + + fclose(fp); + fp = 0; + + gp = gzopen(fn, "rb"); + if(!gp)return false; + + if(!gzdirect(gp)) { + fsize = gzsize; + } + +//empty file? + if(fsize == 0) { + gzclose(gp); + gp = 0; + return false; + } + + return true; +} + +void GZReader::close() { + if(gp) { + gzclose(gp); + gp = 0; + } +} diff --git a/src/reader/gzreader.h b/src/reader/gzreader.h new file mode 100644 index 00000000..308717af --- /dev/null +++ b/src/reader/gzreader.h @@ -0,0 +1,16 @@ +#include "zlib/zlib.h" + +class GZReader : public Reader { +private: +gzFile gp; +uint32 fsize; + +public: + uint32 size(); + uint8 *read(uint32 length = 0); + bool open(char *fn); + void close(); + + GZReader() { gp = 0; fsize = 0; } + ~GZReader() { if(gp)gzclose(gp); } +}; diff --git a/src/reader/jma/7z.h b/src/reader/jma/7z.h new file mode 100644 index 00000000..e91ee55e --- /dev/null +++ b/src/reader/jma/7z.h @@ -0,0 +1,29 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __7Z_H +#define __7Z_H + +#include "iiostrm.h" + +bool decompress_lzma_7z(ISequentialInStream& in, unsigned in_size, ISequentialOutStream& out, unsigned out_size) throw (); +bool decompress_lzma_7z(const unsigned char* in_data, unsigned in_size, unsigned char* out_data, unsigned out_size) throw (); + +#endif + diff --git a/src/reader/jma/7zlzma.cpp b/src/reader/jma/7zlzma.cpp new file mode 100644 index 00000000..fae93f45 --- /dev/null +++ b/src/reader/jma/7zlzma.cpp @@ -0,0 +1,51 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "7z.h" + +#include "lzmadec.h" + +bool decompress_lzma_7z(ISequentialInStream& in, unsigned in_size, ISequentialOutStream& out, unsigned out_size) throw () +{ + try + { + NCompress::NLZMA::CDecoder cc; + + UINT64 in_size_l = in_size; + UINT64 out_size_l = out_size; + + if (cc.ReadCoderProperties(&in) != S_OK) { return(false); } + if (cc.Code(&in, &out, &in_size_l, &out_size_l) != S_OK) { return(false); } + if (out.size_get() != out_size || out.overflow_get()) { return(false); } + + return(true); + } + catch (...) + { + return(false); + } +} + +bool decompress_lzma_7z(const unsigned char* in_data, unsigned int in_size, unsigned char* out_data, unsigned int out_size) throw () +{ + ISequentialInStream_Array in(reinterpret_cast(in_data), in_size); + ISequentialOutStream_Array out(reinterpret_cast(out_data), out_size); + + return(decompress_lzma_7z(in, in_size, out, out_size)); +} diff --git a/src/reader/jma/aribitcd.h b/src/reader/jma/aribitcd.h new file mode 100644 index 00000000..1fb421ba --- /dev/null +++ b/src/reader/jma/aribitcd.h @@ -0,0 +1,73 @@ +#ifndef __COMPRESSION_BITCODER_H +#define __COMPRESSION_BITCODER_H + +#include "rngcoder.h" + +namespace NCompression { +namespace NArithmetic { + +const int kNumBitModelTotalBits = 11; +const UINT32 kBitModelTotal = (1 << kNumBitModelTotalBits); + +const int kNumMoveReducingBits = 2; + +///////////////////////////// +// CBitModel + +template +class CBitModel +{ +public: + UINT32 m_Probability; + void UpdateModel(UINT32 aSymbol) + { + /* + m_Probability -= (m_Probability + ((aSymbol - 1) & ((1 << aNumMoveBits) - 1))) >> aNumMoveBits; + m_Probability += (1 - aSymbol) << (kNumBitModelTotalBits - aNumMoveBits); + */ + if (aSymbol == 0) + m_Probability += (kBitModelTotal - m_Probability) >> aNumMoveBits; + else + m_Probability -= (m_Probability) >> aNumMoveBits; + } +public: + void Init() { m_Probability = kBitModelTotal / 2; } +}; + +template +class CBitDecoder: public CBitModel +{ +public: + UINT32 Decode(CRangeDecoder *aRangeDecoder) + { + UINT32 aNewBound = (aRangeDecoder->m_Range >> kNumBitModelTotalBits) * CBitModel::m_Probability; + if (aRangeDecoder->m_Code < aNewBound) + { + aRangeDecoder->m_Range = aNewBound; + CBitModel::m_Probability += (kBitModelTotal - CBitModel::m_Probability) >> aNumMoveBits; + if (aRangeDecoder->m_Range < kTopValue) + { + aRangeDecoder->m_Code = (aRangeDecoder->m_Code << 8) | aRangeDecoder->m_Stream.ReadByte(); + aRangeDecoder->m_Range <<= 8; + } + return 0; + } + else + { + aRangeDecoder->m_Range -= aNewBound; + aRangeDecoder->m_Code -= aNewBound; + CBitModel::m_Probability -= (CBitModel::m_Probability) >> aNumMoveBits; + if (aRangeDecoder->m_Range < kTopValue) + { + aRangeDecoder->m_Code = (aRangeDecoder->m_Code << 8) | aRangeDecoder->m_Stream.ReadByte(); + aRangeDecoder->m_Range <<= 8; + } + return 1; + } + } +}; + +}} + + +#endif diff --git a/src/reader/jma/ariconst.h b/src/reader/jma/ariconst.h new file mode 100644 index 00000000..8cb0ead6 --- /dev/null +++ b/src/reader/jma/ariconst.h @@ -0,0 +1,30 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __ARICONST_H +#define __ARICONST_H + +#include "aribitcd.h" + + +typedef NCompression::NArithmetic::CRangeDecoder CMyRangeDecoder; +template class CMyBitDecoder: + public NCompression::NArithmetic::CBitDecoder {}; + +#endif diff --git a/src/reader/jma/ariprice.h b/src/reader/jma/ariprice.h new file mode 100644 index 00000000..ccc398e1 --- /dev/null +++ b/src/reader/jma/ariprice.h @@ -0,0 +1,12 @@ +#ifndef __COMPRESSION_ARIPRICE_H +#define __COMPRESSION_ARIPRICE_H + +namespace NCompression { +namespace NArithmetic { + +const UINT32 kNumBitPriceShiftBits = 6; +const UINT32 kBitPrice = 1 << kNumBitPriceShiftBits; + +}} + +#endif diff --git a/src/reader/jma/btreecd.h b/src/reader/jma/btreecd.h new file mode 100644 index 00000000..85f89b29 --- /dev/null +++ b/src/reader/jma/btreecd.h @@ -0,0 +1,127 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __BITTREECODER_H +#define __BITTREECODER_H + +#include "aribitcd.h" +#include "rcdefs.h" + + +////////////////////////// +// CBitTreeDecoder + +template +class CBitTreeDecoder +{ + CMyBitDecoder m_Models[1 << m_NumBitLevels]; +public: + void Init() + { + for(UINT32 i = 1; i < (1 << m_NumBitLevels); i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + RC_INIT_VAR + for(UINT32 aBitIndex = m_NumBitLevels; aBitIndex > 0; aBitIndex--) + { + // aModelIndex = (aModelIndex << 1) + m_Models[aModelIndex].Decode(aRangeDecoder); + RC_GETBIT(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex) + } + RC_FLUSH_VAR + return aModelIndex - (1 << m_NumBitLevels); + }; +}; + +//////////////////////////////// +// CReverseBitTreeDecoder + +template +class CReverseBitTreeDecoder2 +{ + CMyBitDecoder *m_Models; + UINT32 m_NumBitLevels; +public: + CReverseBitTreeDecoder2(): m_Models(0) { } + ~CReverseBitTreeDecoder2() { delete []m_Models; } + bool Create(UINT32 aNumBitLevels) + { + m_NumBitLevels = aNumBitLevels; + m_Models = new CMyBitDecoder[1 << aNumBitLevels]; + return (m_Models != 0); + } + void Init() + { + UINT32 aNumModels = 1 << m_NumBitLevels; + for(UINT32 i = 1; i < aNumModels; i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + UINT32 aSymbol = 0; + RC_INIT_VAR + for(UINT32 aBitIndex = 0; aBitIndex < m_NumBitLevels; aBitIndex++) + { + // UINT32 aBit = m_Models[aModelIndex].Decode(aRangeDecoder); + // aModelIndex <<= 1; + // aModelIndex += aBit; + // aSymbol |= (aBit << aBitIndex); + RC_GETBIT2(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex, ; , aSymbol |= (1 << aBitIndex)) + } + RC_FLUSH_VAR + return aSymbol; + }; +}; +//////////////////////////// +// CReverseBitTreeDecoder2 + +template +class CReverseBitTreeDecoder +{ + CMyBitDecoder m_Models[1 << m_NumBitLevels]; +public: + void Init() + { + for(UINT32 i = 1; i < (1 << m_NumBitLevels); i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + UINT32 aSymbol = 0; + RC_INIT_VAR + for(UINT32 aBitIndex = 0; aBitIndex < m_NumBitLevels; aBitIndex++) + { + // UINT32 aBit = m_Models[aModelIndex].Decode(aRangeDecoder); + // aModelIndex <<= 1; + // aModelIndex += aBit; + // aSymbol |= (aBit << aBitIndex); + RC_GETBIT2(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex, ; , aSymbol |= (1 << aBitIndex)) + } + RC_FLUSH_VAR + return aSymbol; + } +}; + + + +#endif diff --git a/src/reader/jma/crc32.h b/src/reader/jma/crc32.h new file mode 100644 index 00000000..c760dc7b --- /dev/null +++ b/src/reader/jma/crc32.h @@ -0,0 +1,29 @@ +/* +Copyright (C) 2004 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CRC32_H +#define CRC32_H + +namespace CRC32lib +{ + unsigned int CRC32(const unsigned char *, size_t, register unsigned int crc32 = 0xFFFFFFFF); + unsigned short SUM_CRC32(const unsigned char *, size_t, unsigned int &crc32); +} + +#endif diff --git a/src/reader/jma/iiostrm.cpp b/src/reader/jma/iiostrm.cpp new file mode 100644 index 00000000..66cefb0c --- /dev/null +++ b/src/reader/jma/iiostrm.cpp @@ -0,0 +1,133 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "portable.h" +#include "iiostrm.h" +#include "crc32.h" + +HRESULT ISequentialInStream_Array::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > size) + { + aSize = size; + } + + *aProcessedSize = aSize; + memcpy(aData, data, aSize); + size -= aSize; + data += aSize; + return(S_OK); +} + +HRESULT ISequentialOutStream_Array::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > size) + { + overflow = true; + aSize = size; + } + + *aProcessedSize = aSize; + memcpy(data, aData, aSize); + size -= aSize; + data += aSize; + total += aSize; + return(S_OK); +} + +HRESULT ISequentialInStream_String::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > data.size()) + { + aSize = data.size(); + } + + *aProcessedSize = aSize; + memcpy(aData, data.c_str(), aSize); + data.erase(0, aSize); + return(S_OK); +} + +HRESULT ISequentialOutStream_String::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + *aProcessedSize = aSize; + data.append((const char *)aData, aSize); + total += aSize; + return(S_OK); +} + +HRESULT ISequentialInStream_Istream::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + data.read((char *)aData, aSize); + *aProcessedSize = data.gcount(); + return(S_OK); +} + +HRESULT ISequentialOutStream_Ostream::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + *aProcessedSize = aSize; + data.write((char *)aData, aSize); + total += aSize; + return(S_OK); +} + + + +HRESULT ISequentialInStreamCRC32_Array::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_Array::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_Array::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_Array::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialInStreamCRC32_String::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_String::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_String::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_String::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialInStreamCRC32_Istream::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_Istream::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_Ostream::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_Ostream::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} diff --git a/src/reader/jma/iiostrm.h b/src/reader/jma/iiostrm.h new file mode 100644 index 00000000..a5cb19ce --- /dev/null +++ b/src/reader/jma/iiostrm.h @@ -0,0 +1,211 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __IINOUTSTREAMS_H +#define __IINOUTSTREAMS_H + +#include +#include + +#include "portable.h" + + +class ISequentialInStream +{ +public: + virtual HRESULT Read(void *, UINT32, UINT32 *) = 0; + + virtual ~ISequentialInStream() {} +}; + + +class ISequentialInStream_Array : public ISequentialInStream +{ + const char *data; + unsigned int size; +public: + ISequentialInStream_Array(const char *Adata, unsigned Asize) : data(Adata), size(Asize) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_Array() {} +}; + +class ISequentialInStream_String : public ISequentialInStream +{ + std::string& data; +public: + ISequentialInStream_String(std::string& Adata) : data(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_String() {} +}; + +class ISequentialInStream_Istream : public ISequentialInStream +{ + std::istream& data; +public: + ISequentialInStream_Istream(std::istream& Adata) : data(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_Istream() {} +}; + + + +class ISequentialOutStream +{ +public: + virtual bool overflow_get() const = 0; + virtual unsigned int size_get() const = 0; + + virtual HRESULT Write(const void *, UINT32, UINT32 *) = 0; + + virtual ~ISequentialOutStream() {} +}; + + +class ISequentialOutStream_Array : public ISequentialOutStream +{ + char *data; + unsigned int size; + bool overflow; + unsigned int total; +public: + ISequentialOutStream_Array(char *Adata, unsigned Asize) : data(Adata), size(Asize), overflow(false), total(0) { } + + bool overflow_get() const { return(overflow); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_Array() {} +}; + +class ISequentialOutStream_String : public ISequentialOutStream +{ + std::string& data; + unsigned int total; +public: + ISequentialOutStream_String(std::string& Adata) : data(Adata), total(0) { } + + bool overflow_get() const { return(false); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_String() {} +}; + + +class ISequentialOutStream_Ostream : public ISequentialOutStream +{ + std::ostream& data; + unsigned int total; +public: + ISequentialOutStream_Ostream(std::ostream& Adata) : data(Adata), total(0) { } + + bool overflow_get() const { return(false); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_Ostream() {} +}; + + + +class ISequentialStreamCRC32 +{ +protected: + unsigned int crc32; +public: + ISequentialStreamCRC32() : crc32(0) {} + unsigned int crc32_get() const { return(crc32); } + + virtual ~ISequentialStreamCRC32() {} +}; + + +class ISequentialInStreamCRC32_Array : public ISequentialInStream_Array, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_Array(const char *Adata, unsigned Asize) : ISequentialInStream_Array(Adata, Asize) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_Array() {} +}; + +class ISequentialInStreamCRC32_String : public ISequentialInStream_String, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_String(std::string& Adata) : ISequentialInStream_String(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_String() {} +}; + +class ISequentialInStreamCRC32_Istream : public ISequentialInStream_Istream, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_Istream(std::istream& Adata) : ISequentialInStream_Istream(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_Istream() {} +}; + + +class ISequentialOutStreamCRC32_Array : public ISequentialOutStream_Array, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_Array(char *Adata, unsigned Asize) : ISequentialOutStream_Array(Adata, Asize) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_Array() {} +}; + +class ISequentialOutStreamCRC32_String : public ISequentialOutStream_String, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_String(std::string& Adata) : ISequentialOutStream_String(Adata) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_String() {} +}; + + +class ISequentialOutStreamCRC32_Ostream : public ISequentialOutStream_Ostream, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_Ostream(std::ostream& Adata) : ISequentialOutStream_Ostream(Adata) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_Ostream() {} +}; + +#endif diff --git a/src/reader/jma/inbyte.cpp b/src/reader/jma/inbyte.cpp new file mode 100644 index 00000000..436309e2 --- /dev/null +++ b/src/reader/jma/inbyte.cpp @@ -0,0 +1,61 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "inbyte.h" + +namespace NStream{ + +CInByte::CInByte(UINT32 aBufferSize): + m_BufferBase(0), + m_BufferSize(aBufferSize) +{ + m_BufferBase = new BYTE[m_BufferSize]; +} + +CInByte::~CInByte() +{ + delete []m_BufferBase; +} + +void CInByte::Init(ISequentialInStream *aStream) +{ + m_Stream = aStream; + m_ProcessedSize = 0; + m_Buffer = m_BufferBase; + m_BufferLimit = m_Buffer; + m_StreamWasExhausted = false; +} + +bool CInByte::ReadBlock() +{ + if (m_StreamWasExhausted) + return false; + m_ProcessedSize += (m_Buffer - m_BufferBase); + UINT32 aNumProcessedBytes; + HRESULT aResult = m_Stream->Read(m_BufferBase, m_BufferSize, &aNumProcessedBytes); + if (aResult != S_OK) + throw aResult; + m_Buffer = m_BufferBase; + m_BufferLimit = m_Buffer + aNumProcessedBytes; + m_StreamWasExhausted = (aNumProcessedBytes == 0); + return (!m_StreamWasExhausted); +} + +} diff --git a/src/reader/jma/inbyte.h b/src/reader/jma/inbyte.h new file mode 100644 index 00000000..705065fe --- /dev/null +++ b/src/reader/jma/inbyte.h @@ -0,0 +1,77 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __STREAM_INBYTE_H +#define __STREAM_INBYTE_H + +#include "iiostrm.h" + +namespace NStream { + +class CInByte +{ + UINT64 m_ProcessedSize; + BYTE *m_BufferBase; + UINT32 m_BufferSize; + BYTE *m_Buffer; + BYTE *m_BufferLimit; + ISequentialInStream* m_Stream; + bool m_StreamWasExhausted; + + bool ReadBlock(); + +public: + CInByte(UINT32 aBufferSize = 0x100000); + ~CInByte(); + + void Init(ISequentialInStream *aStream); + + bool ReadByte(BYTE &aByte) + { + if(m_Buffer >= m_BufferLimit) + if(!ReadBlock()) + return false; + aByte = *m_Buffer++; + return true; + } + BYTE ReadByte() + { + if(m_Buffer >= m_BufferLimit) + if(!ReadBlock()) + return 0x0; + return *m_Buffer++; + } + void ReadBytes(void *aData, UINT32 aSize, UINT32 &aProcessedSize) + { + for(aProcessedSize = 0; aProcessedSize < aSize; aProcessedSize++) + if (!ReadByte(((BYTE *)aData)[aProcessedSize])) + return; + } + bool ReadBytes(void *aData, UINT32 aSize) + { + UINT32 aProcessedSize; + ReadBytes(aData, aSize, aProcessedSize); + return (aProcessedSize == aSize); + } + UINT64 GetProcessedSize() const { return m_ProcessedSize + (m_Buffer - m_BufferBase); } +}; + +} + +#endif diff --git a/src/reader/jma/jcrc32.cpp b/src/reader/jma/jcrc32.cpp new file mode 100644 index 00000000..efa92680 --- /dev/null +++ b/src/reader/jma/jcrc32.cpp @@ -0,0 +1,82 @@ +/* +Copyright (C) 2004 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +namespace CRC32lib +{ + //Don't ask questions, this is the PKZip CRC32 table + const unsigned int crc32Table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + + + //CRC32 for char arrays + unsigned int CRC32(const unsigned char *array, size_t size, register unsigned int crc32) + { + const unsigned char *end_p = array+size; + for (register const unsigned char *p = array; p < end_p; p++) + { + crc32 = ((crc32 >> 8) & 0x00FFFFFF) ^ crc32Table[(crc32 ^ *p) & 0xFF]; + } + + return(~crc32); + } +} diff --git a/src/reader/jma/jma.cpp b/src/reader/jma/jma.cpp new file mode 100644 index 00000000..be2f00c1 --- /dev/null +++ b/src/reader/jma/jma.cpp @@ -0,0 +1,552 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include "jma.h" +using namespace std; + +#include "portable.h" +#include "7z.h" +#include "crc32.h" + +namespace JMA +{ + const char jma_magic[] = { 'J', 'M', 'A', 0, 'N' }; + const unsigned int jma_header_length = 5; + const unsigned char jma_version = 1; + const unsigned int jma_version_length = 1; + const unsigned int jma_total_header_length = jma_header_length + jma_version_length + UINT_SIZE; + + //Convert DOS/zip/JMA integer time to to time_t + time_t uint_to_time(unsigned short date, unsigned short time) + { + tm formatted_time; + + formatted_time.tm_mday = date & 0x1F; + formatted_time.tm_mon = ((date >> 5) & 0xF) - 1; + formatted_time.tm_year = ((date >> 9) & 0x7f) + 80; + formatted_time.tm_sec = (time & 0x1F) * 2; + formatted_time.tm_min = (time >> 5) & 0x3F; + formatted_time.tm_hour = (time >> 11) & 0x1F; + + return(mktime(&formatted_time)); + } + + + //Retreive the file block, what else? + void jma_open::retrieve_file_block() throw(jma_errors) + { + unsigned char uint_buffer[UINT_SIZE]; + unsigned char ushort_buffer[USHORT_SIZE]; + + //File block size is the last UINT in the file + stream.seekg(-UINT_SIZE,ios::end); + stream.read((char *)uint_buffer, UINT_SIZE); + size_t file_block_size = charp_to_uint(uint_buffer); + + //Currently at the end of the file, so that's the file size + size_t jma_file_size = stream.tellg(); + + //The file block can't be larger than the JMA file without it's header. + //This if can probably be improved + if (file_block_size >= jma_file_size-jma_total_header_length) + { + throw(JMA_BAD_FILE); + } + + //Seek to before file block so we can read the file block + stream.seekg(-((int)file_block_size+UINT_SIZE),ios::end); + + //This is needed if the file block is compressed + stringstream decompressed_file_block; + //Pointer to where to read file block from (file or decompressed buffer) + istream *file_block_stream; + + //Setup file info buffer and byte to read with + jma_file_info file_info; + char byte; + + stream.get(byte); + if (!byte) //If file block is compressed + { + //Compressed size isn't counting the byte we just read or the UINT for compressed size + size_t compressed_size = file_block_size - (1+UINT_SIZE); + + //Read decompressed size / true file block size + stream.read((char *)uint_buffer, UINT_SIZE); + file_block_size = charp_to_uint(uint_buffer); + + //Setup access methods for decompression + ISequentialInStream_Istream compressed_data(stream); + ISequentialOutStream_Ostream decompressed_data(decompressed_file_block); + + //Decompress the data + if (!decompress_lzma_7z(compressed_data, compressed_size, decompressed_data, file_block_size)) + { + throw(JMA_DECOMPRESS_FAILED); + } + + //Go to beginning, setup pointer to buffer + decompressed_file_block.seekg(0, ios::beg); + file_block_stream = &decompressed_file_block; + } + else + { + stream.putback(byte); //Putback byte, byte is part of filename, not compressed indicator + file_block_stream = &stream; + } + + + //Minimum file name length is 2 bytes, a char and a null + //Minimum comment length is 1 byte, a null + //There are currently 2 UINTs and 2 USHORTs per file + while (file_block_size >= 2+1+UINT_SIZE*2+USHORT_SIZE*2) //This does allow for a gap, but that's okay + { + //First stored in the file block is the file name null terminated + file_info.name = ""; + + file_block_stream->get(byte); + while (byte) + { + file_info.name += byte; + file_block_stream->get(byte); + } + + //There must be a file name or the file is bad + if (!file_info.name.length()) + { + throw(JMA_BAD_FILE); + } + + //Same trick as above for the comment + file_info.comment = ""; + + file_block_stream->get(byte); + while (byte) + { + file_info.comment += byte; + file_block_stream->get(byte); + } + + //Next is a UINT representing the file's size + file_block_stream->read((char *)uint_buffer, UINT_SIZE); + file_info.size = charp_to_uint(uint_buffer); + + //Followed by CRC32 + file_block_stream->read((char *)uint_buffer, UINT_SIZE); + file_info.crc32 = charp_to_uint(uint_buffer); + + //Special USHORT representation of file's date + file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); + file_info.date = charp_to_ushort(ushort_buffer); + + //Special USHORT representation of file's time + file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); + file_info.time = charp_to_ushort(ushort_buffer); + + file_info.buffer = 0; //Pointing to null till we decompress files + + files.push_back(file_info); //Put file info into our structure + + //Subtract size of the file info we just read + file_block_size -= file_info.name.length()+file_info.comment.length()+2+UINT_SIZE*2+USHORT_SIZE*2; + } + } + + //Constructor for opening JMA files for reading + jma_open::jma_open(const char *compressed_file_name) throw (jma_errors) + { + decompressed_buffer = 0; + compressed_buffer = 0; + + stream.open(compressed_file_name, ios::in | ios::binary); + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Header is "JMA\0N" + unsigned char header[jma_header_length]; + stream.read((char *)header, jma_header_length); + if (memcmp(jma_magic, header, jma_header_length)) + { + throw(JMA_BAD_FILE); + } + + //Not the cleanest code but logical + stream.read((char *)header, 5); + if (*header <= jma_version) + { + chunk_size = charp_to_uint(header+1); //Chunk size is a UINT that follows version # + retrieve_file_block(); + } + else + { + throw(JMA_UNSUPPORTED_VERSION); + } + } + + //Destructor only has to close the stream if neccesary + jma_open::~jma_open() + { + if (stream.is_open()) + { + stream.close(); + } + } + + //Return a vector containing useful info about the files in the JMA + vector jma_open::get_files_info() + { + vector file_info_vector; + jma_public_file_info file_info; + + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + file_info.name = i->name; + file_info.comment = i->comment; + file_info.size = i->size; + file_info.datetime = uint_to_time(i->date, i->time); + file_info.crc32 = i->crc32; + file_info_vector.push_back(file_info); + } + + return(file_info_vector); + } + + //Skip forward a given number of chunks + void jma_open::chunk_seek(unsigned int chunk_num) throw(jma_errors) + { + //Check the stream is open + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Clear possible errors so the seek will work + stream.clear(); + + //Move forward over header + stream.seekg(jma_total_header_length, ios::beg); + + unsigned char int4_buffer[UINT_SIZE]; + + while (chunk_num--) + { + //Read in size of chunk + stream.read((char *)int4_buffer, UINT_SIZE); + + //Skip chunk plus it's CRC32 + stream.seekg(charp_to_uint(int4_buffer)+UINT_SIZE, ios::cur); + } + } + + //Return a vector of pointers to each file in the JMA, the buffer to hold all the files + //must be initilized outside. + vector jma_open::get_all_files(unsigned char *buffer) throw(jma_errors) + { + //If there's no stream we can't read from it, so exit + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Seek to the first chunk + chunk_seek(0); + + //Set the buffer that decompressed data goes to + decompressed_buffer = buffer; + + //If the JMA is not solid + if (chunk_size) + { + unsigned char int4_buffer[UINT_SIZE]; + size_t size = get_total_size(files); + + //For each chunk in the file... + for (size_t remaining_size = size; remaining_size; remaining_size -= chunk_size) + { + //Read the compressed size + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Allocate memory of the correct size to hold the compressed data in the JMA + //Throw error on failure as that is unrecoverable from + try + { + compressed_buffer = new unsigned char[compressed_size]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + //Read all the compressed data in + stream.read((char *)compressed_buffer, compressed_size); + + //Read the expected CRC of compressed data from the file + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, throw error and cleanup memory + if (CRC32lib::CRC32(compressed_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] compressed_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress the data, cleanup memory on failure + if (!decompress_lzma_7z(compressed_buffer, compressed_size, + decompressed_buffer+size-remaining_size, + (remaining_size > chunk_size) ? chunk_size : remaining_size)) + { + delete[] compressed_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + delete[] compressed_buffer; + + if (remaining_size <= chunk_size) //If we just decompressed the remainder + { + break; + } + } + } + else //Solidly compressed JMA + { + unsigned char int4_buffer[UINT_SIZE]; + + //Read the size of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Get decompressed size + size_t size = get_total_size(files); + + //Setup access methods for decompression + ISequentialInStream_Istream compressed_data(stream); + ISequentialOutStream_Array decompressed_data(reinterpret_cast(decompressed_buffer), size); + + //Decompress the data + if (!decompress_lzma_7z(compressed_data, compressed_size, decompressed_data, size)) + { + throw(JMA_DECOMPRESS_FAILED); + } + + /* + //Allocate memory of the right size to hold the compressed data in the JMA + try + { + compressed_buffer = new unsigned char[compressed_size]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + //Copy the compressed data into memory + stream.read((char *)compressed_buffer, compressed_size); + size_t size = get_total_size(files); + + //Read the CRC of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, complain + if (CRC32lib::CRC32(compressed_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] compressed_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress the data + if (!decompress_lzma_7z(compressed_buffer, compressed_size, decompressed_buffer, size)) + { + delete[] compressed_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + delete[] compressed_buffer; + */ + } + + vector file_pointers; + size_t size = 0; + + //For each file, add it's pointer to the vector, size is pointer offset in the buffer + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + i->buffer = decompressed_buffer+size; + file_pointers.push_back(decompressed_buffer+size); + size += i->size; + } + + //Return the vector of pointers + return(file_pointers); + } + + //Extracts the file with a given name found in the archive to the given buffer + void jma_open::extract_file(string& name, unsigned char *buffer) throw(jma_errors) + { + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + size_t size_to_skip = 0; + size_t our_file_size = 0; + + //Search through the vector of file information + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + if (i->name == name) + { + //Set the variable so we can tell we found it + our_file_size = i->size; + break; + } + + //Keep a running total of size + size_to_skip += i->size; + } + + if (!our_file_size) //File with the specified name was not found in the archive + { + throw(JMA_FILE_NOT_FOUND); + } + + //If the JMA only contains one file, we can skip a lot of overhead + if (files.size() == 1) + { + get_all_files(buffer); + return; + } + + if (chunk_size) //we are using non-solid archive.. + { + unsigned int chunks_to_skip = size_to_skip / chunk_size; + + //skip over requisite number of chunks + chunk_seek(chunks_to_skip); + + //Allocate memory for compressed and decompressed data + unsigned char *comp_buffer = 0, *decomp_buffer = 0; + try + { + //Compressed data size is <= non compressed size + unsigned char *combined_buffer = new unsigned char[chunk_size*2]; + comp_buffer = combined_buffer; + decomp_buffer = combined_buffer+chunk_size; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + size_t first_chunk_offset = size_to_skip % chunk_size; + unsigned char int4_buffer[UINT_SIZE]; + for (size_t i = 0; i < our_file_size;) + { + //Get size + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Read all the compressed data in + stream.read((char *)comp_buffer, compressed_size); + + //Read the CRC of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, complain + if (CRC32lib::CRC32(comp_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] comp_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress chunk + if (!decompress_lzma_7z(comp_buffer, compressed_size, decomp_buffer, chunk_size)) + { + delete[] comp_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + + size_t copy_amount = our_file_size-i > chunk_size-first_chunk_offset ? chunk_size-first_chunk_offset : our_file_size-i; + + memcpy(buffer+i, decomp_buffer+first_chunk_offset, copy_amount); + first_chunk_offset = 0; //Set to zero since this is only for the first iteration + i += copy_amount; + } + delete[] comp_buffer; + } + else //Solid JMA + { + unsigned char *decomp_buffer = 0; + try + { + decomp_buffer = new unsigned char[get_total_size(files)]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + get_all_files(decomp_buffer); + + memcpy(buffer, decomp_buffer+size_to_skip, our_file_size); + + delete[] decomp_buffer; + } + } + + bool jma_open::is_solid() + { + return(chunk_size ? false : true); + } + + const char *jma_error_text(jma_errors error) + { + switch (error) + { + case JMA_NO_CREATE: + return("JMA could not be created"); + + case JMA_NO_MEM_ALLOC: + return("Memory for JMA could be allocated"); + + case JMA_NO_OPEN: + return("JMA could not be opened"); + + case JMA_BAD_FILE: + return("Invalid/Corrupt JMA"); + + case JMA_UNSUPPORTED_VERSION: + return("JMA version not supported"); + + case JMA_COMPRESS_FAILED: + return("JMA compression failed"); + + case JMA_DECOMPRESS_FAILED: + return("JMA decompression failed"); + + case JMA_FILE_NOT_FOUND: + return("File not found in JMA"); + } + return("Unknown error"); + } + +} + + diff --git a/src/reader/jma/jma.h b/src/reader/jma/jma.h new file mode 100644 index 00000000..d5268721 --- /dev/null +++ b/src/reader/jma/jma.h @@ -0,0 +1,90 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef JMA_H +#define JMA_H + +#include +#include +#include +#include + +namespace JMA +{ + enum jma_errors { JMA_NO_CREATE, JMA_NO_MEM_ALLOC, JMA_NO_OPEN, JMA_BAD_FILE, + JMA_UNSUPPORTED_VERSION, JMA_COMPRESS_FAILED, JMA_DECOMPRESS_FAILED, + JMA_FILE_NOT_FOUND }; + + struct jma_file_info_base + { + std::string name; + std::string comment; + size_t size; + unsigned int crc32; + }; + + struct jma_public_file_info : jma_file_info_base + { + time_t datetime; + }; + + struct jma_file_info : jma_file_info_base + { + unsigned short date; + unsigned short time; + const unsigned char *buffer; + }; + + template + inline size_t get_total_size(std::vector& files) + { + size_t size = 0; + for (typename std::vector::iterator i = files.begin(); i != files.end(); i++) + { + size += i->size; //We do have a problem if this wraps around + } + + return(size); + } + + class jma_open + { + public: + jma_open(const char *) throw(jma_errors); + ~jma_open(); + + std::vector get_files_info(); + std::vector get_all_files(unsigned char *) throw(jma_errors); + void extract_file(std::string& name, unsigned char *) throw(jma_errors); + bool is_solid(); + + private: + std::ifstream stream; + std::vector files; + size_t chunk_size; + unsigned char *decompressed_buffer; + unsigned char *compressed_buffer; + + void chunk_seek(unsigned int) throw(jma_errors); + void retrieve_file_block() throw(jma_errors); + }; + + const char *jma_error_text(jma_errors); +} +#endif diff --git a/src/reader/jma/lencoder.h b/src/reader/jma/lencoder.h new file mode 100644 index 00000000..5fe9cc63 --- /dev/null +++ b/src/reader/jma/lencoder.h @@ -0,0 +1,94 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LENCODER_H +#define __LENCODER_H + +#include "btreecd.h" + +namespace NLength { + +const UINT32 kNumPosStatesBitsMax = 4; +const int kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + + +const int kNumPosStatesBitsEncodingMax = 4; +const int kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + +const int kNumMoveBits = 5; + +const int kNumLenBits = 3; +const int kNumLowSymbols = 1 << kNumLenBits; +const int kNumMidBits = 3; +const int kNumMidSymbols = 1 << kNumMidBits; + +const int kNumHighBits = 8; + +const int kNumSymbolsTotal = kNumLowSymbols + kNumMidSymbols + (1 << kNumHighBits); + +const int kNumSpecSymbols = kNumLowSymbols + kNumMidSymbols; + +class CDecoder +{ + CMyBitDecoder m_Choice; + CBitTreeDecoder m_LowCoder[kNumPosStatesMax]; + CMyBitDecoder m_Choice2; + CBitTreeDecoder m_MidCoder[kNumPosStatesMax]; + CBitTreeDecoder m_HighCoder; + UINT32 m_NumPosStates; +public: + void Create(UINT32 aNumPosStates) + { m_NumPosStates = aNumPosStates; } + void Init() + { + m_Choice.Init(); + for (UINT32 aPosState = 0; aPosState < m_NumPosStates; aPosState++) + { + m_LowCoder[aPosState].Init(); + m_MidCoder[aPosState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder, UINT32 aPosState) + { + if(m_Choice.Decode(aRangeDecoder) == 0) + return m_LowCoder[aPosState].Decode(aRangeDecoder); + else + { + UINT32 aSymbol = kNumLowSymbols; + if(m_Choice2.Decode(aRangeDecoder) == 0) + aSymbol += m_MidCoder[aPosState].Decode(aRangeDecoder); + else + { + aSymbol += kNumMidSymbols; + aSymbol += m_HighCoder.Decode(aRangeDecoder); + } + return aSymbol; + } + } + +}; + +} + + +#endif diff --git a/src/reader/jma/litcoder.h b/src/reader/jma/litcoder.h new file mode 100644 index 00000000..c5ad476f --- /dev/null +++ b/src/reader/jma/litcoder.h @@ -0,0 +1,123 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LITERALCODER_H +#define __LITERALCODER_H + +#include "aribitcd.h" +#include "rcdefs.h" + +namespace NLiteral { + +const int kNumMoveBits = 5; + +class CDecoder2 +{ + CMyBitDecoder m_Decoders[3][1 << 8]; +public: + void Init() + { + for (int i = 0; i < 3; i++) + for (int j = 1; j < (1 << 8); j++) + m_Decoders[i][j].Init(); + } + + BYTE DecodeNormal(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aSymbol = 1; + RC_INIT_VAR + do + { + // aSymbol = (aSymbol << 1) | m_Decoders[0][aSymbol].Decode(aRangeDecoder); + RC_GETBIT(kNumMoveBits, m_Decoders[0][aSymbol].m_Probability, aSymbol) + } + while (aSymbol < 0x100); + RC_FLUSH_VAR + return aSymbol; + } + + BYTE DecodeWithMatchByte(CMyRangeDecoder *aRangeDecoder, BYTE aMatchByte) + { + UINT32 aSymbol = 1; + RC_INIT_VAR + do + { + UINT32 aMatchBit = (aMatchByte >> 7) & 1; + aMatchByte <<= 1; + // UINT32 aBit = m_Decoders[1 + aMatchBit][aSymbol].Decode(aRangeDecoder); + // aSymbol = (aSymbol << 1) | aBit; + UINT32 aBit; + RC_GETBIT2(kNumMoveBits, m_Decoders[1 + aMatchBit][aSymbol].m_Probability, aSymbol, + aBit = 0, aBit = 1) + if (aMatchBit != aBit) + { + while (aSymbol < 0x100) + { + // aSymbol = (aSymbol << 1) | m_Decoders[0][aSymbol].Decode(aRangeDecoder); + RC_GETBIT(kNumMoveBits, m_Decoders[0][aSymbol].m_Probability, aSymbol) + } + break; + } + } + while (aSymbol < 0x100); + RC_FLUSH_VAR + return aSymbol; + } +}; + +class CDecoder +{ + CDecoder2 *m_Coders; + UINT32 m_NumPrevBits; + UINT32 m_NumPosBits; + UINT32 m_PosMask; +public: + CDecoder(): m_Coders(0) {} + ~CDecoder() { Free(); } + void Free() + { + delete []m_Coders; + m_Coders = 0; + } + void Create(UINT32 aNumPosBits, UINT32 aNumPrevBits) + { + Free(); + m_NumPosBits = aNumPosBits; + m_PosMask = (1 << aNumPosBits) - 1; + m_NumPrevBits = aNumPrevBits; + UINT32 aNumStates = 1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new CDecoder2[aNumStates]; + } + void Init() + { + UINT32 aNumStates = 1 << (m_NumPrevBits + m_NumPosBits); + for (UINT32 i = 0; i < aNumStates; i++) + m_Coders[i].Init(); + } + UINT32 GetState(UINT32 aPos, BYTE aPrevByte) const + { return ((aPos & m_PosMask) << m_NumPrevBits) + (aPrevByte >> (8 - m_NumPrevBits)); } + BYTE DecodeNormal(CMyRangeDecoder *aRangeDecoder, UINT32 aPos, BYTE aPrevByte) + { return m_Coders[GetState(aPos, aPrevByte)].DecodeNormal(aRangeDecoder); } + BYTE DecodeWithMatchByte(CMyRangeDecoder *aRangeDecoder, UINT32 aPos, BYTE aPrevByte, BYTE aMatchByte) + { return m_Coders[GetState(aPos, aPrevByte)].DecodeWithMatchByte(aRangeDecoder, aMatchByte); } +}; + +} + +#endif diff --git a/src/reader/jma/lzma.cpp b/src/reader/jma/lzma.cpp new file mode 100644 index 00000000..d994be5f --- /dev/null +++ b/src/reader/jma/lzma.cpp @@ -0,0 +1,42 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lzma.h" + +namespace NCompress { +namespace NLZMA { + +UINT32 kDistStart[kDistTableSizeMax]; + +static class CConstInit +{ +public: + CConstInit() + { + UINT32 aStartValue = 0; + int i; + for (i = 0; i < kDistTableSizeMax; i++) + { + kDistStart[i] = aStartValue; + aStartValue += (1 << kDistDirectBits[i]); + } + } +} g_ConstInit; + +}} diff --git a/src/reader/jma/lzma.h b/src/reader/jma/lzma.h new file mode 100644 index 00000000..fc6a2fd7 --- /dev/null +++ b/src/reader/jma/lzma.h @@ -0,0 +1,125 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lencoder.h" + +#ifndef __LZMA_H +#define __LZMA_H + +namespace NCompress { +namespace NLZMA { + +const UINT32 kNumRepDistances = 4; + +const BYTE kNumStates = 12; + +const BYTE kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +const BYTE kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +const BYTE kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +const BYTE kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +class CState +{ +public: + BYTE m_Index; + void Init() + { m_Index = 0; } + void UpdateChar() + { m_Index = kLiteralNextStates[m_Index]; } + void UpdateMatch() + { m_Index = kMatchNextStates[m_Index]; } + void UpdateRep() + { m_Index = kRepNextStates[m_Index]; } + void UpdateShortRep() + { m_Index = kShortRepNextStates[m_Index]; } +}; + +class CBaseCoder +{ +protected: + CState m_State; + BYTE m_PreviousByte; + bool m_PeviousIsMatch; + UINT32 m_RepDistances[kNumRepDistances]; + void Init() + { + m_State.Init(); + m_PreviousByte = 0; + m_PeviousIsMatch = false; + for(UINT32 i = 0 ; i < kNumRepDistances; i++) + m_RepDistances[i] = 0; + } +}; + +const int kNumPosSlotBits = 6; +const int kDicLogSizeMax = 28; +const int kDistTableSizeMax = kDicLogSizeMax * 2; + +extern UINT32 kDistStart[kDistTableSizeMax]; +const BYTE kDistDirectBits[kDistTableSizeMax] = +{ + 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, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26 +}; + +const UINT32 kNumLenToPosStates = 4; +inline UINT32 GetLenToPosState(UINT32 aLen) +{ + aLen -= 2; + if (aLen < kNumLenToPosStates) + return aLen; + return kNumLenToPosStates - 1; +} + +const int kMatchMinLen = 2; + +const int kMatchMaxLen = kMatchMinLen + NLength::kNumSymbolsTotal - 1; + +const int kNumAlignBits = 4; +const int kAlignTableSize = 1 << kNumAlignBits; +const UINT32 kAlignMask = (kAlignTableSize - 1); + +const int kStartPosModelIndex = 4; +const int kEndPosModelIndex = 14; +const int kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + +const int kNumFullDistances = 1 << (kEndPosModelIndex / 2); + + +const int kMainChoiceLiteralIndex = 0; +const int kMainChoiceMatchIndex = 1; + +const int kMatchChoiceDistanceIndex= 0; +const int kMatchChoiceRepetitionIndex = 1; + +const int kNumMoveBitsForMainChoice = 5; +const int kNumMoveBitsForPosCoders = 5; + +const int kNumMoveBitsForAlignCoders = 5; + +const int kNumMoveBitsForPosSlotCoder = 5; + +const int kNumLitPosStatesBitsEncodingMax = 4; +const int kNumLitContextBitsMax = 8; + +}} + +#endif diff --git a/src/reader/jma/lzmadec.cpp b/src/reader/jma/lzmadec.cpp new file mode 100644 index 00000000..2cc75a03 --- /dev/null +++ b/src/reader/jma/lzmadec.cpp @@ -0,0 +1,299 @@ +/* +Copyright (C) 2005 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "portable.h" +#include "lzmadec.h" + +#define RETURN_E_OUTOFMEMORY_IF_FALSE(x) { if (!(x)) return E_OUTOFMEMORY; } + +namespace NCompress { +namespace NLZMA { + +HRESULT CDecoder::SetDictionarySize(UINT32 aDictionarySize) +{ + if (aDictionarySize > (1 << kDicLogSizeMax)) + return E_INVALIDARG; + + UINT32 aWindowReservSize = MyMax(aDictionarySize, UINT32(1 << 21)); + + if (m_DictionarySize != aDictionarySize) + { + m_OutWindowStream.Create(aDictionarySize, kMatchMaxLen, aWindowReservSize); + m_DictionarySize = aDictionarySize; + } + return S_OK; +} + +HRESULT CDecoder::SetLiteralProperties( + UINT32 aLiteralPosStateBits, UINT32 aLiteralContextBits) +{ + if (aLiteralPosStateBits > 8) + return E_INVALIDARG; + if (aLiteralContextBits > 8) + return E_INVALIDARG; + m_LiteralDecoder.Create(aLiteralPosStateBits, aLiteralContextBits); + return S_OK; +} + +HRESULT CDecoder::SetPosBitsProperties(UINT32 aNumPosStateBits) +{ + if (aNumPosStateBits > NLength::kNumPosStatesBitsMax) + return E_INVALIDARG; + UINT32 aNumPosStates = 1 << aNumPosStateBits; + m_LenDecoder.Create(aNumPosStates); + m_RepMatchLenDecoder.Create(aNumPosStates); + m_PosStateMask = aNumPosStates - 1; + return S_OK; +} + +CDecoder::CDecoder(): + m_DictionarySize((UINT32)-1) +{ + Create(); +} + +HRESULT CDecoder::Create() +{ + for(int i = 0; i < kNumPosModels; i++) + { + RETURN_E_OUTOFMEMORY_IF_FALSE( + m_PosDecoders[i].Create(kDistDirectBits[kStartPosModelIndex + i])); + } + return S_OK; +} + + +HRESULT CDecoder::Init(ISequentialInStream *anInStream, + ISequentialOutStream *anOutStream) +{ + m_RangeDecoder.Init(anInStream); + + m_OutWindowStream.Init(anOutStream); + + int i; + for(i = 0; i < kNumStates; i++) + { + for (UINT32 j = 0; j <= m_PosStateMask; j++) + { + m_MainChoiceDecoders[i][j].Init(); + m_MatchRepShortChoiceDecoders[i][j].Init(); + } + m_MatchChoiceDecoders[i].Init(); + m_MatchRepChoiceDecoders[i].Init(); + m_MatchRep1ChoiceDecoders[i].Init(); + m_MatchRep2ChoiceDecoders[i].Init(); + } + + m_LiteralDecoder.Init(); + + // m_RepMatchLenDecoder.Init(); + + for (i = 0; (UINT32) i < kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + + for(i = 0; i < kNumPosModels; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepMatchLenDecoder.Init(); + + m_PosAlignDecoder.Init(); + return S_OK; + +} + +HRESULT CDecoder::CodeReal(ISequentialInStream *anInStream, + ISequentialOutStream *anOutStream, + const UINT64 *anInSize, const UINT64 *anOutSize) +{ + if (anOutSize == NULL) + return E_INVALIDARG; + + Init(anInStream, anOutStream); + + CState aState; + aState.Init(); + bool aPeviousIsMatch = false; + BYTE aPreviousByte = 0; + UINT32 aRepDistances[kNumRepDistances]; + for(UINT32 i = 0 ; i < kNumRepDistances; i++) + aRepDistances[i] = 0; + + UINT64 aNowPos64 = 0; + UINT64 aSize = *anOutSize; + while(aNowPos64 < aSize) + { + UINT64 aNext = MyMin(aNowPos64 + (1 << 18), aSize); + while(aNowPos64 < aNext) + { + UINT32 aPosState = UINT32(aNowPos64) & m_PosStateMask; + if (m_MainChoiceDecoders[aState.m_Index][aPosState].Decode(&m_RangeDecoder) == (UINT32) kMainChoiceLiteralIndex) + { + // aCounts[0]++; + aState.UpdateChar(); + if(aPeviousIsMatch) + { + BYTE aMatchByte = m_OutWindowStream.GetOneByte(0 - aRepDistances[0] - 1); + aPreviousByte = m_LiteralDecoder.DecodeWithMatchByte(&m_RangeDecoder, + UINT32(aNowPos64), aPreviousByte, aMatchByte); + aPeviousIsMatch = false; + } + else + aPreviousByte = m_LiteralDecoder.DecodeNormal(&m_RangeDecoder, + UINT32(aNowPos64), aPreviousByte); + m_OutWindowStream.PutOneByte(aPreviousByte); + aNowPos64++; + } + else + { + aPeviousIsMatch = true; + UINT32 aDistance, aLen; + if(m_MatchChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == + (UINT32) kMatchChoiceRepetitionIndex) + { + if(m_MatchRepChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + if(m_MatchRepShortChoiceDecoders[aState.m_Index][aPosState].Decode(&m_RangeDecoder) == 0) + { + aState.UpdateShortRep(); + aPreviousByte = m_OutWindowStream.GetOneByte(0 - aRepDistances[0] - 1); + m_OutWindowStream.PutOneByte(aPreviousByte); + aNowPos64++; + // aCounts[3 + 4]++; + continue; + } + // aCounts[3 + 0]++; + aDistance = aRepDistances[0]; + } + else + { + if(m_MatchRep1ChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + aDistance = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + // aCounts[3 + 1]++; + } + else + { + if (m_MatchRep2ChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + // aCounts[3 + 2]++; + aDistance = aRepDistances[2]; + } + else + { + // aCounts[3 + 3]++; + aDistance = aRepDistances[3]; + aRepDistances[3] = aRepDistances[2]; + } + aRepDistances[2] = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + } + aRepDistances[0] = aDistance; + } + aLen = m_RepMatchLenDecoder.Decode(&m_RangeDecoder, aPosState) + kMatchMinLen; + // aCounts[aLen]++; + aState.UpdateRep(); + } + else + { + aLen = kMatchMinLen + m_LenDecoder.Decode(&m_RangeDecoder, aPosState); + aState.UpdateMatch(); + UINT32 aPosSlot = m_PosSlotDecoder[GetLenToPosState(aLen)].Decode(&m_RangeDecoder); + // aCounts[aPosSlot]++; + if (aPosSlot >= (UINT32) kStartPosModelIndex) + { + aDistance = kDistStart[aPosSlot]; + if (aPosSlot < (UINT32) kEndPosModelIndex) + aDistance += m_PosDecoders[aPosSlot - kStartPosModelIndex].Decode(&m_RangeDecoder); + else + { + aDistance += (m_RangeDecoder.DecodeDirectBits(kDistDirectBits[aPosSlot] - + kNumAlignBits) << kNumAlignBits); + aDistance += m_PosAlignDecoder.Decode(&m_RangeDecoder); + } + } + else + aDistance = aPosSlot; + + + aRepDistances[3] = aRepDistances[2]; + aRepDistances[2] = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + + aRepDistances[0] = aDistance; + // UpdateStat(aLen, aPosSlot); + } + if (aDistance >= aNowPos64) + throw E_INVALIDDATA; + m_OutWindowStream.CopyBackBlock(aDistance, aLen); + aNowPos64 += aLen; + aPreviousByte = m_OutWindowStream.GetOneByte(0 - 1); + } + } + } + return Flush(); +} + +HRESULT CDecoder::Code(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize) +{ + try { + return CodeReal(anInStream, anOutStream, anInSize, anOutSize); + } catch (HRESULT& e) { + return e; + } catch (...) { + return E_FAIL; + } +} + +HRESULT CDecoder::ReadCoderProperties(ISequentialInStream *anInStream) +{ + UINT32 aNumPosStateBits; + UINT32 aLiteralPosStateBits; + UINT32 aLiteralContextBits; + UINT32 aDictionarySize; + + UINT32 aProcessesedSize; + + BYTE aByte; + RETURN_IF_NOT_S_OK(anInStream->Read(&aByte, sizeof(aByte), &aProcessesedSize)); + if (aProcessesedSize != sizeof(aByte)) + return E_INVALIDARG; + + aLiteralContextBits = aByte % 9; + BYTE aRemainder = aByte / 9; + aLiteralPosStateBits = aRemainder % 5; + aNumPosStateBits = aRemainder / 5; + + UINT8 uint_buffer[UINT_SIZE]; + RETURN_IF_NOT_S_OK(anInStream->Read(uint_buffer, sizeof(aDictionarySize), &aProcessesedSize)); + aDictionarySize = charp_to_uint(uint_buffer); + + if (aProcessesedSize != sizeof(aDictionarySize)) + return E_INVALIDARG; + + RETURN_IF_NOT_S_OK(SetDictionarySize(aDictionarySize)); + RETURN_IF_NOT_S_OK(SetLiteralProperties(aLiteralPosStateBits, aLiteralContextBits)); + RETURN_IF_NOT_S_OK(SetPosBitsProperties(aNumPosStateBits)); + + return S_OK; +} + +}} diff --git a/src/reader/jma/lzmadec.h b/src/reader/jma/lzmadec.h new file mode 100644 index 00000000..88234448 --- /dev/null +++ b/src/reader/jma/lzmadec.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LZARITHMETIC_DECODER_H +#define __LZARITHMETIC_DECODER_H + +#include "winout.h" +#include "lzma.h" +#include "lencoder.h" +#include "litcoder.h" + +namespace NCompress { +namespace NLZMA { + +typedef CMyBitDecoder CMyBitDecoder2; + +class CDecoder +{ + NStream::NWindow::COut m_OutWindowStream; + CMyRangeDecoder m_RangeDecoder; + + CMyBitDecoder2 m_MainChoiceDecoders[kNumStates][NLength::kNumPosStatesMax]; + CMyBitDecoder2 m_MatchChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRepChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRep1ChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRep2ChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRepShortChoiceDecoders[kNumStates][NLength::kNumPosStatesMax]; + + CBitTreeDecoder m_PosSlotDecoder[kNumLenToPosStates]; + + CReverseBitTreeDecoder2 m_PosDecoders[kNumPosModels]; + CReverseBitTreeDecoder m_PosAlignDecoder; + // CBitTreeDecoder2 m_PosDecoders[kNumPosModels]; + // CBitTreeDecoder m_PosAlignDecoder; + + NLength::CDecoder m_LenDecoder; + NLength::CDecoder m_RepMatchLenDecoder; + + NLiteral::CDecoder m_LiteralDecoder; + + UINT32 m_DictionarySize; + + UINT32 m_PosStateMask; + + HRESULT Create(); + + HRESULT Init(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream); + + HRESULT Flush() { return m_OutWindowStream.Flush(); } + + HRESULT CodeReal(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize); + +public: + + CDecoder(); + + HRESULT Code(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize); + HRESULT ReadCoderProperties(ISequentialInStream *anInStream); + + HRESULT SetDictionarySize(UINT32 aDictionarySize); + HRESULT SetLiteralProperties(UINT32 aLiteralPosStateBits, UINT32 aLiteralContextBits); + HRESULT SetPosBitsProperties(UINT32 aNumPosStateBits); +}; + +}} + +#endif diff --git a/src/reader/jma/portable.h b/src/reader/jma/portable.h new file mode 100644 index 00000000..1ed1fb50 --- /dev/null +++ b/src/reader/jma/portable.h @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __PORTABLE_H +#define __PORTABLE_H + +#include + +typedef signed char INT8; +typedef unsigned char UINT8; +typedef short INT16; +typedef unsigned short UINT16; +typedef long INT32; +typedef unsigned long UINT32; +typedef long long INT64; +typedef unsigned long long UINT64; + +typedef UINT8 BYTE; +typedef UINT16 WORD; +typedef UINT32 DWORD; + +typedef unsigned UINT_PTR; + +typedef int BOOL; +#define FALSE 0 +#define TRUE 1 + +#define HRESULT int +#define S_OK 0 +#define E_INVALIDARG -1 +#define E_OUTOFMEMORY -2 +#define E_FAIL -3 +#define E_INTERNAL_ERROR -4 +#define E_INVALIDDATA -5 + +template inline T MyMin(T a, T b) { + return a < b ? a : b; +} + +template inline T MyMax(T a, T b) { + return a > b ? a : b; +} + +#define RETURN_IF_NOT_S_OK(x) { HRESULT __aResult_ = (x); if(__aResult_ != S_OK) return __aResult_; } + + +#define UINT_SIZE (4) +#define USHORT_SIZE (2) + +//Convert an array of 4 bytes back into an integer +inline unsigned int charp_to_uint(const unsigned char buffer[UINT_SIZE]) +{ + unsigned int num = (unsigned int)buffer[3]; + num |= ((unsigned int)buffer[2]) << 8; + num |= ((unsigned int)buffer[1]) << 16; + num |= ((unsigned int)buffer[0]) << 24; + return(num); +} + +//Convert an array of 2 bytes back into a short integer +inline unsigned short charp_to_ushort(const unsigned char buffer[USHORT_SIZE]) +{ + unsigned short num = (unsigned short)buffer[1]; + num |= ((unsigned short)buffer[0]) << 8; + return(num); +} + +#endif diff --git a/src/reader/jma/rcdefs.h b/src/reader/jma/rcdefs.h new file mode 100644 index 00000000..46df11f9 --- /dev/null +++ b/src/reader/jma/rcdefs.h @@ -0,0 +1,61 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __RCDEFS_H +#define __RCDEFS_H + +#include "aribitcd.h" +#include "ariconst.h" + +#define RC_INIT_VAR \ + UINT32 aRange = aRangeDecoder->m_Range; \ + UINT32 aCode = aRangeDecoder->m_Code; + +#define RC_FLUSH_VAR \ + aRangeDecoder->m_Range = aRange; \ + aRangeDecoder->m_Code = aCode; + +#define RC_NORMALIZE \ + if (aRange < NCompression::NArithmetic::kTopValue) \ + { \ + aCode = (aCode << 8) | aRangeDecoder->m_Stream.ReadByte(); \ + aRange <<= 8; } + +#define RC_GETBIT2(aNumMoveBits, aProb, aModelIndex, Action0, Action1) \ + {UINT32 aNewBound = (aRange >> NCompression::NArithmetic::kNumBitModelTotalBits) * aProb; \ + if (aCode < aNewBound) \ + { \ + Action0; \ + aRange = aNewBound; \ + aProb += (NCompression::NArithmetic::kBitModelTotal - aProb) >> aNumMoveBits; \ + aModelIndex <<= 1; \ + } \ + else \ + { \ + Action1; \ + aRange -= aNewBound; \ + aCode -= aNewBound; \ + aProb -= (aProb) >> aNumMoveBits; \ + aModelIndex = (aModelIndex << 1) + 1; \ + }} \ + RC_NORMALIZE + +#define RC_GETBIT(aNumMoveBits, aProb, aModelIndex) RC_GETBIT2(aNumMoveBits, aProb, aModelIndex, ; , ;) + +#endif diff --git a/src/reader/jma/rngcoder.h b/src/reader/jma/rngcoder.h new file mode 100644 index 00000000..59118580 --- /dev/null +++ b/src/reader/jma/rngcoder.h @@ -0,0 +1,144 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __COMPRESSION_RANGECODER_H +#define __COMPRESSION_RANGECODER_H + +#include "inbyte.h" + +namespace NCompression { +namespace NArithmetic { + +const UINT32 kNumTopBits = 24; +const UINT32 kTopValue = (1 << kNumTopBits); + +class CRangeDecoder +{ +public: + NStream::CInByte m_Stream; + UINT32 m_Range; + UINT32 m_Code; + UINT32 m_Word; + void Normalize() + { + while (m_Range < kTopValue) + { + m_Code = (m_Code << 8) | m_Stream.ReadByte(); + m_Range <<= 8; + } + } + + void Init(ISequentialInStream *aStream) + { + m_Stream.Init(aStream); + m_Code = 0; + m_Range = UINT32(-1); + for(int i = 0; i < 5; i++) + m_Code = (m_Code << 8) | m_Stream.ReadByte(); + } + + UINT32 GetThreshold(UINT32 aTotal) + { + return (m_Code) / ( m_Range /= aTotal); + } + + void Decode(UINT32 aStart, UINT32 aSize, UINT32 aTotal) + { + m_Code -= aStart * m_Range; + m_Range *= aSize; + Normalize(); + } + + /* + UINT32 DecodeDirectBitsDiv(UINT32 aNumTotalBits) + { + m_Range >>= aNumTotalBits; + UINT32 aThreshold = m_Code / m_Range; + m_Code -= aThreshold * m_Range; + + Normalize(); + return aThreshold; + } + + UINT32 DecodeDirectBitsDiv2(UINT32 aNumTotalBits) + { + if (aNumTotalBits <= kNumBottomBits) + return DecodeDirectBitsDiv(aNumTotalBits); + UINT32 aResult = DecodeDirectBitsDiv(aNumTotalBits - kNumBottomBits) << kNumBottomBits; + return (aResult | DecodeDirectBitsDiv(kNumBottomBits)); + } + */ + + UINT32 DecodeDirectBits(UINT32 aNumTotalBits) + { + UINT32 aRange = m_Range; + UINT32 aCode = m_Code; + UINT32 aResult = 0; + for (UINT32 i = aNumTotalBits; i > 0; i--) + { + aRange >>= 1; + /* + aResult <<= 1; + if (aCode >= aRange) + { + aCode -= aRange; + aResult |= 1; + } + */ + UINT32 t = (aCode - aRange) >> 31; + aCode -= aRange & (t - 1); + // aRange = aRangeTmp + ((aRange & 1) & (1 - t)); + aResult = (aResult << 1) | (1 - t); + + if (aRange < kTopValue) + { + aCode = (aCode << 8) | m_Stream.ReadByte(); + aRange <<= 8; + } + } + m_Range = aRange; + m_Code = aCode; + return aResult; + } + + UINT32 DecodeBit(UINT32 aSize0, UINT32 aNumTotalBits) + { + UINT32 aNewBound = (m_Range >> aNumTotalBits) * aSize0; + UINT32 aSymbol; + if (m_Code < aNewBound) + { + aSymbol = 0; + m_Range = aNewBound; + } + else + { + aSymbol = 1; + m_Code -= aNewBound; + m_Range -= aNewBound; + } + Normalize(); + return aSymbol; + } + + UINT64 GetProcessedSize() {return m_Stream.GetProcessedSize(); } +}; + +}} + +#endif diff --git a/src/reader/jma/winout.cpp b/src/reader/jma/winout.cpp new file mode 100644 index 00000000..61e9a652 --- /dev/null +++ b/src/reader/jma/winout.cpp @@ -0,0 +1,90 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "winout.h" + +namespace NStream { +namespace NWindow { + +void COut::Create(UINT32 aKeepSizeBefore, UINT32 aKeepSizeAfter, UINT32 aKeepSizeReserv) +{ + m_Pos = 0; + m_PosLimit = aKeepSizeReserv + aKeepSizeBefore; + m_KeepSizeBefore = aKeepSizeBefore; + m_KeepSizeAfter = aKeepSizeAfter; + m_KeepSizeReserv = aKeepSizeReserv; + m_StreamPos = 0; + m_MoveFrom = m_KeepSizeReserv; + m_WindowSize = aKeepSizeBefore; + UINT32 aBlockSize = m_KeepSizeBefore + m_KeepSizeAfter + m_KeepSizeReserv; + delete []m_Buffer; + m_Buffer = new BYTE[aBlockSize]; +} + +COut::~COut() +{ + delete []m_Buffer; +} + +void COut::SetWindowSize(UINT32 aWindowSize) +{ + m_WindowSize = aWindowSize; + m_MoveFrom = m_KeepSizeReserv + m_KeepSizeBefore - aWindowSize; +} + +void COut::Init(ISequentialOutStream *aStream, bool aSolid) +{ + m_Stream = aStream; + + if(aSolid) + m_StreamPos = m_Pos; + else + { + m_Pos = 0; + m_PosLimit = m_KeepSizeReserv + m_KeepSizeBefore; + m_StreamPos = 0; + } +} + +HRESULT COut::Flush() +{ + UINT32 aSize = m_Pos - m_StreamPos; + if(aSize == 0) + return S_OK; + UINT32 aProcessedSize; + HRESULT aResult = m_Stream->Write(m_Buffer + m_StreamPos, aSize, &aProcessedSize); + if (aResult != S_OK) + return aResult; + if (aSize != aProcessedSize) + return E_FAIL; + m_StreamPos = m_Pos; + return S_OK; +} + +void COut::MoveBlockBackward() +{ + HRESULT aResult = Flush(); + if (aResult != S_OK) + throw aResult; + memmove(m_Buffer, m_Buffer + m_MoveFrom, m_WindowSize + m_KeepSizeAfter); + m_Pos -= m_MoveFrom; + m_StreamPos -= m_MoveFrom; +} + +}} diff --git a/src/reader/jma/winout.h b/src/reader/jma/winout.h new file mode 100644 index 00000000..1e64e2e8 --- /dev/null +++ b/src/reader/jma/winout.h @@ -0,0 +1,90 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __STREAM_WINDOWOUT_H +#define __STREAM_WINDOWOUT_H + +#include "iiostrm.h" + +namespace NStream { +namespace NWindow { + +// m_KeepSizeBefore: how mach BYTEs must be in buffer before m_Pos; +// m_KeepSizeAfter: how mach BYTEs must be in buffer after m_Pos; +// m_KeepSizeReserv: how mach BYTEs must be in buffer for Moving Reserv; +// must be >= aKeepSizeAfter; // test it + +class COut +{ + BYTE *m_Buffer; + UINT32 m_Pos; + UINT32 m_PosLimit; + UINT32 m_KeepSizeBefore; + UINT32 m_KeepSizeAfter; + UINT32 m_KeepSizeReserv; + UINT32 m_StreamPos; + + UINT32 m_WindowSize; + UINT32 m_MoveFrom; + + ISequentialOutStream *m_Stream; + + virtual void MoveBlockBackward(); +public: + COut(): m_Buffer(0), m_Stream(0) {} + virtual ~COut(); + void Create(UINT32 aKeepSizeBefore, + UINT32 aKeepSizeAfter, UINT32 aKeepSizeReserv = (1<<17)); + void SetWindowSize(UINT32 aWindowSize); + + void Init(ISequentialOutStream *aStream, bool aSolid = false); + HRESULT Flush(); + + UINT32 GetCurPos() const { return m_Pos; } + const BYTE *GetPointerToCurrentPos() const { return m_Buffer + m_Pos;}; + + void CopyBackBlock(UINT32 aDistance, UINT32 aLen) + { + if (m_Pos >= m_PosLimit) + MoveBlockBackward(); + BYTE *p = m_Buffer + m_Pos; + aDistance++; + for(UINT32 i = 0; i < aLen; i++) + p[i] = p[i - aDistance]; + m_Pos += aLen; + } + + void PutOneByte(BYTE aByte) + { + if (m_Pos >= m_PosLimit) + MoveBlockBackward(); + m_Buffer[m_Pos++] = aByte; + } + + BYTE GetOneByte(UINT32 anIndex) const + { + return m_Buffer[m_Pos + anIndex]; + } + + BYTE *GetBuffer() const { return m_Buffer; } +}; + +}} + +#endif diff --git a/src/reader/jmareader.cpp b/src/reader/jmareader.cpp new file mode 100644 index 00000000..ea5db124 --- /dev/null +++ b/src/reader/jmareader.cpp @@ -0,0 +1,43 @@ +//created by Nach + +//#include "jma/jma.h" + +uint32 JMAReader::size() { + return fsize; +} + +#define MAXROM 0x800000 + +uint8 *JMAReader::read(uint32 length) +{ + uint8 *data; + if (!fsize) { return 0; } + if (length <= fsize) + { + //read the entire file into RAM + data = (uint8*)memalloc(fsize); + JMAFile.extract_file(cname, data); + } + else if (length > fsize) + { + //read the entire file into RAM, pad the rest with 0x00s + data = (uint8*)memalloc(length); + memset(data, 0, length); + JMAFile.extract_file(cname, data); + } + return data; +} + +JMAReader::JMAReader(char *fn) : JMAFile(fn), fsize(0) +{ + std::vector file_info = JMAFile.get_files_info(); + for (std::vector::iterator i = file_info.begin(); i != file_info.end(); i++) + { + //Check for valid ROM based on size + if ((i->size <= MAXROM+512) && (i->size > fsize)) + { + cname = i->name; + fsize = i->size; + } + } +} diff --git a/src/reader/jmareader.h b/src/reader/jmareader.h new file mode 100644 index 00000000..7fc0d4f9 --- /dev/null +++ b/src/reader/jmareader.h @@ -0,0 +1,17 @@ +//created by Nach + +#include "jma/jma.h" + +class JMAReader : public Reader { +private: +JMA::jma_open JMAFile; +uint32 fsize; +std::string cname; + +public: + uint32 size(); + uint8 *read(uint32 length = 0); + + JMAReader(char *fn); + ~JMAReader() { } +}; diff --git a/src/reader/reader.cpp b/src/reader/reader.cpp index 68985773..1d44af80 100644 --- a/src/reader/reader.cpp +++ b/src/reader/reader.cpp @@ -1,2 +1,22 @@ #include "../base.h" #include "filereader.cpp" +#ifdef GZIP_SUPPORT + #include "gzreader.cpp" + #include "zipreader.cpp" +#endif +#ifdef JMA_SUPPORT + #include "jmareader.cpp" +#endif + +uint32 Reader::detect(char *fn) { +int len = strlen(fn); + if(len >= 4 && !stricmp(fn + len - 3, ".gz")) { + return RF_GZ; + } else if(len >= 5 && !stricmp(fn + len - 4, ".zip")) { + return RF_ZIP; + } else if(len >= 5 && !stricmp(fn + len - 4, ".jma")) { + return RF_JMA; + } else { + return RF_NORMAL; + } +} diff --git a/src/reader/reader.h b/src/reader/reader.h index 695f989c..c545a2d3 100644 --- a/src/reader/reader.h +++ b/src/reader/reader.h @@ -1,7 +1,17 @@ class Reader { public: +enum { + RF_NORMAL = 0, + RF_GZ = 1, + RF_ZIP = 2, + RF_JMA = 3 +}; +//attemps to determine filetype by extension, +//RF_NORMAL is returned on failure as a failsafe + static uint32 detect(char *fn); virtual uint32 size() = 0; - virtual void read(uint8 **buffer, uint32 length = 0) = 0; +//return is 0 on failure, caller must deallocate memory manually + virtual uint8 *read(uint32 length = 0) = 0; }; class Writer { @@ -10,3 +20,10 @@ public: }; #include "filereader.h" +#ifdef GZIP_SUPPORT + #include "gzreader.h" + #include "zipreader.h" +#endif +#ifdef JMA_SUPPORT + #include "jmareader.h" +#endif diff --git a/src/reader/zipreader.cpp b/src/reader/zipreader.cpp new file mode 100644 index 00000000..c6218d2d --- /dev/null +++ b/src/reader/zipreader.cpp @@ -0,0 +1,54 @@ +//created by Nach + +uint32 ZipReader::size() { + return fsize; +} + +#define MAXROM 0x800000 + +uint8 *ZipReader::read(uint32 length) +{ + uint8 *data; + if (!fsize) { return 0; } + if (length <= fsize) + { + //read the entire file into RAM + data = (uint8*)memalloc(fsize); + unzReadCurrentFile(zipfile, data, fsize); + } + else if (length > fsize) + { + //read the entire file into RAM, pad the rest with 0x00s + data = (uint8*)memalloc(length); + memset(data, 0, length); + unzReadCurrentFile(zipfile, data, fsize); + } + return data; +} + +ZipReader::ZipReader(char *fn) : fsize(0) +{ + unz_file_info cFileInfo; //Create variable to hold info for a compressed file + char cFileName[sizeof(cname)]; + + if (zipfile = unzOpen(fn)) //Open zip file + { + for (int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) + { + //Gets info on current file, and places it in cFileInfo + unzGetCurrentFileInfo(zipfile, &cFileInfo, cFileName, sizeof(cname), 0, 0, 0, 0); + + if ((cFileInfo.uncompressed_size <= MAXROM+512) && (cFileInfo.uncompressed_size > fsize)) + { + strcpy(cname, cFileName); + fsize = cFileInfo.uncompressed_size; + } + } + + if (fsize) + { + unzLocateFile(zipfile, cname, 1); + unzOpenCurrentFile(zipfile); + } + } +} diff --git a/src/reader/zipreader.h b/src/reader/zipreader.h new file mode 100644 index 00000000..14eacd58 --- /dev/null +++ b/src/reader/zipreader.h @@ -0,0 +1,27 @@ +//created by Nach + +#include "zlib/unzip.h" + +//Could be up to 65536 +#define ZIP_MAX_FILE_NAME 4096 + +class ZipReader : public Reader { +private: +unzFile zipfile; +uint32 fsize; +char cname[4096]; + +public: + uint32 size(); + uint8 *read(uint32 length = 0); + + ZipReader(char *fn); + ~ZipReader() + { + if (zipfile) + { + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + } + } +}; diff --git a/src/reader/zlib/adler32.c b/src/reader/zlib/adler32.c new file mode 100644 index 00000000..58cdf069 --- /dev/null +++ b/src/reader/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/src/reader/zlib/compress.c b/src/reader/zlib/compress.c new file mode 100644 index 00000000..1b7f07ad --- /dev/null +++ b/src/reader/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/src/reader/zlib/crc32.c b/src/reader/zlib/crc32.c new file mode 100644 index 00000000..fe16f0cb --- /dev/null +++ b/src/reader/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id: crc32.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/src/reader/zlib/crc32.h b/src/reader/zlib/crc32.h new file mode 100644 index 00000000..8053b611 --- /dev/null +++ b/src/reader/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/src/reader/zlib/crypt.h b/src/reader/zlib/crypt.h new file mode 100644 index 00000000..622f4bc2 --- /dev/null +++ b/src/reader/zlib/crypt.h @@ -0,0 +1,132 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/reader/zlib/deflate.c b/src/reader/zlib/deflate.c new file mode 100644 index 00000000..d08ce26a --- /dev/null +++ b/src/reader/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/src/reader/zlib/deflate.h b/src/reader/zlib/deflate.h new file mode 100644 index 00000000..be7b1682 --- /dev/null +++ b/src/reader/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/reader/zlib/gzio.c b/src/reader/zlib/gzio.c new file mode 100644 index 00000000..6538d6ce --- /dev/null +++ b/src/reader/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id: gzio.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/src/reader/zlib/inffast.c b/src/reader/zlib/inffast.c new file mode 100644 index 00000000..de927350 --- /dev/null +++ b/src/reader/zlib/inffast.c @@ -0,0 +1,724 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef __i386__ + +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + struct inffast_ar { + void *esp; /* esp save */ + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ + unsigned wsize; /* window size or zero if not using window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned status; /* this is set when state changes */ + } ar; + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + ar.in = strm->next_in; + ar.last = ar.in + (strm->avail_in - 5); + ar.out = strm->next_out; + ar.beg = ar.out - (start - strm->avail_out); + ar.end = ar.out + (strm->avail_out - 257); + ar.wsize = state->wsize; + ar.write = state->write; + ar.window = state->window; + ar.hold = state->hold; + ar.bits = state->bits; + ar.lcode = state->lencode; + ar.dcode = state->distcode; + ar.lmask = (1U << state->lenbits) - 1; + ar.dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + /* align in on 2 byte boundary */ + if (((unsigned long)(void *)ar.in & 0x1) != 0) { + ar.hold += (unsigned long)*ar.in++ << ar.bits; + ar.bits += 8; + } + + __asm__ __volatile__ ( +" leal %0, %%eax\n" +" pushf\n" +" pushl %%ebp\n" +" movl %%esp, (%%eax)\n" +" movl %%eax, %%esp\n" +" movl 4(%%esp), %%esi\n" /* esi = in */ +" movl 12(%%esp), %%edi\n" /* edi = out */ +" movl 36(%%esp), %%edx\n" /* edx = hold */ +" movl 40(%%esp), %%ebx\n" /* ebx = bits */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ + +" cld\n" +" jmp .L_do_loop\n" + +".L_while_test:\n" +" cmpl %%edi, 20(%%esp)\n" +" jbe .L_break_loop\n" +" cmpl %%esi, 8(%%esp)\n" +" jbe .L_break_loop\n" + +".L_do_loop:\n" +" cmpb $15, %%bl\n" +" ja .L_get_length_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_length_code:\n" +" movl 52(%%esp), %%eax\n" /* eax = lmask */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[hold & lmask] */ + +".L_dolen:\n" +" movb %%ah, %%cl\n" /* cl = this.bits */ +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ + +" testb %%al, %%al\n" +" jnz .L_test_for_length_base\n" /* if (op != 0) 45.7% */ + +" shrl $16, %%eax\n" /* output this.val char */ +" stosb\n" +" jmp .L_while_test\n" + +".L_test_for_length_base:\n" +" movl %%eax, %%ecx\n" /* len = this */ +" shrl $16, %%ecx\n" /* len = this.val */ +" movl %%ecx, 60(%%esp)\n" /* len = this */ +" movb %%al, %%cl\n" + +" testb $16, %%al\n" +" jz .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */ +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_decode_distance\n" /* if (!op) */ +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_len\n" /* if (op <= bits) */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_len:\n" +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" subb %%cl, %%bl\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, 60(%%esp)\n" /* len += hold & mask[op] */ + +".L_decode_distance:\n" +" cmpb $15, %%bl\n" +" ja .L_get_distance_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_distance_code:\n" +" movl 56(%%esp), %%eax\n" /* eax = dmask */ +" movl 48(%%esp), %%ecx\n" /* ecx = dcode */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ecx,%%eax,4), %%eax\n"/* eax = dcode[hold & dmask] */ + +".L_dodist:\n" +" movl %%eax, %%ebp\n" /* dist = this */ +" shrl $16, %%ebp\n" /* dist = this.val */ +" movb %%ah, %%cl\n" +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ +" movb %%al, %%cl\n" /* cl = this.op */ + +" testb $16, %%al\n" /* if ((op & 16) == 0) */ +" jz .L_test_for_second_level_dist\n" +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_check_dist_one\n" +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_dist\n" /* if (op <= bits) 97.6% */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_dist:\n" +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" /* (1 << op) - 1 */ +" subb %%cl, %%bl\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, %%ebp\n" /* dist += hold & ((1 << op) - 1) */ + +".L_check_window:\n" +" movl %%esi, 4(%%esp)\n" /* save in so from can use it's reg */ +" movl %%edi, %%eax\n" +" subl 16(%%esp), %%eax\n" /* nbytes = out - beg */ + +" cmpl %%ebp, %%eax\n" +" jb .L_clip_window\n" /* if (dist > nbytes) 4.2% */ + +" movl 60(%%esp), %%ecx\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ + +" subl $3, %%ecx\n" /* copy from to out */ +" movb (%%esi), %%al\n" +" movb %%al, (%%edi)\n" +" movb 1(%%esi), %%al\n" +" movb 2(%%esi), %%ah\n" +" addl $3, %%esi\n" +" movb %%al, 1(%%edi)\n" +" movb %%ah, 2(%%edi)\n" +" addl $3, %%edi\n" +" rep movsb\n" + +" movl 4(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_check_dist_one:\n" +" cmpl $1, %%ebp\n" /* if dist 1, is a memset */ +" jne .L_check_window\n" +" cmpl %%edi, 16(%%esp)\n" +" je .L_check_window\n" + +" decl %%edi\n" +" movl 60(%%esp), %%ecx\n" +" movb (%%edi), %%al\n" +" subl $3, %%ecx\n" + +" movb %%al, 1(%%edi)\n" /* memset out with from[-1] */ +" movb %%al, 2(%%edi)\n" +" movb %%al, 3(%%edi)\n" +" addl $4, %%edi\n" +" rep stosb\n" +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_test_for_second_level_length:\n" +" testb $64, %%al\n" +" jnz .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */ + +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl 60(%%esp), %%eax\n" /* eax += this.val */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/ +" jmp .L_dolen\n" + +".L_test_for_second_level_dist:\n" +" testb $64, %%al\n" +" jnz .L_invalid_distance_code\n" /* if ((op & 64) != 0) */ + +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl %%ebp, %%eax\n" /* eax += this.val */ +" movl 48(%%esp), %%ecx\n" /* ecx = dcode */ +" movl (%%ecx,%%eax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/ +" jmp .L_dodist\n" + +".L_clip_window:\n" +" movl %%eax, %%ecx\n" +" movl 24(%%esp), %%eax\n" /* prepare for dist compare */ +" negl %%ecx\n" /* nbytes = -nbytes */ +" movl 32(%%esp), %%esi\n" /* from = window */ + +" cmpl %%ebp, %%eax\n" +" jb .L_invalid_distance_too_far\n" /* if (dist > wsize) */ + +" addl %%ebp, %%ecx\n" /* nbytes = dist - nbytes */ +" cmpl $0, 28(%%esp)\n" +" jne .L_wrap_around_window\n" /* if (write != 0) */ + +" subl %%ecx, %%eax\n" +" addl %%eax, %%esi\n" /* from += wsize - nbytes */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +".L_wrap_around_window:\n" +" movl 28(%%esp), %%eax\n" +" cmpl %%eax, %%ecx\n" +" jbe .L_contiguous_in_window\n" /* if (write >= nbytes) */ + +" addl 24(%%esp), %%esi\n" +" addl %%eax, %%esi\n" +" subl %%ecx, %%esi\n" /* from += wsize + write - nbytes */ +" subl %%eax, %%ecx\n" /* nbytes -= write */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl 32(%%esp), %%esi\n" /* from = window */ +" movl 28(%%esp), %%ecx\n" /* nbytes = write */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +".L_contiguous_in_window:\n" +" addl %%eax, %%esi\n" +" subl %%ecx, %%esi\n" /* from += write - nbytes */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ + +".L_do_copy1:\n" +" movl %%eax, %%ecx\n" +" rep movsb\n" + +" movl 4(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_test_for_end_of_block:\n" +" testb $32, %%al\n" +" jz .L_invalid_literal_length_code\n" +" movl $1, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_literal_length_code:\n" +" movl $2, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_code:\n" +" movl $3, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_too_far:\n" +" movl 4(%%esp), %%esi\n" +" movl $4, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_break_loop:\n" +" movl $0, 68(%%esp)\n" + +".L_break_loop_with_status:\n" +/* put in, out, bits, and hold back into ar and pop esp */ +" movl %%esi, 4(%%esp)\n" +" movl %%edi, 12(%%esp)\n" +" movl %%ebx, 40(%%esp)\n" +" movl %%edx, 36(%%esp)\n" +" movl (%%esp), %%esp\n" +" popl %%ebp\n" +" popf\n" + : + : "m" (ar) + : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi" + ); + + + if (ar.status > 1) { + if (ar.status == 2) + strm->msg = "invalid literal/length code"; + else if (ar.status == 3) + strm->msg = "invalid distance code"; + else + strm->msg = "invalid distance too far back"; + state->mode = BAD; + } + else if ( ar.status == 1 ) { + state->mode = TYPE; + } + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + ar.len = ar.bits >> 3; + ar.in -= ar.len; + ar.bits -= ar.len << 3; + ar.hold &= (1U << ar.bits) - 1; + + /* update state and return */ + strm->next_in = ar.in; + strm->next_out = ar.out; + strm->avail_in = (unsigned)(ar.in < ar.last ? 5 + (ar.last - ar.in) : + 5 - (ar.in - ar.last)); + strm->avail_out = (unsigned)(ar.out < ar.end ? 257 + (ar.end - ar.out) : + 257 - (ar.out - ar.end)); + state->hold = ar.hold; + state->bits = ar.bits; + return; +} + +#else + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !i386 */ diff --git a/src/reader/zlib/inffast.h b/src/reader/zlib/inffast.h new file mode 100644 index 00000000..1e88d2d9 --- /dev/null +++ b/src/reader/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/reader/zlib/inffixed.h b/src/reader/zlib/inffixed.h new file mode 100644 index 00000000..75ed4b59 --- /dev/null +++ b/src/reader/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/src/reader/zlib/inflate.c b/src/reader/zlib/inflate.c new file mode 100644 index 00000000..792fdee8 --- /dev/null +++ b/src/reader/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/src/reader/zlib/inflate.h b/src/reader/zlib/inflate.h new file mode 100644 index 00000000..07bd3e78 --- /dev/null +++ b/src/reader/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/src/reader/zlib/inftrees.c b/src/reader/zlib/inftrees.c new file mode 100644 index 00000000..8a9c13ff --- /dev/null +++ b/src/reader/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 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 unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 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 unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/reader/zlib/inftrees.h b/src/reader/zlib/inftrees.h new file mode 100644 index 00000000..b1104c87 --- /dev/null +++ b/src/reader/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/reader/zlib/ioapi.c b/src/reader/zlib/ioapi.c new file mode 100644 index 00000000..f1bee23e --- /dev/null +++ b/src/reader/zlib/ioapi.c @@ -0,0 +1,177 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/reader/zlib/ioapi.h b/src/reader/zlib/ioapi.h new file mode 100644 index 00000000..7d457baa --- /dev/null +++ b/src/reader/zlib/ioapi.h @@ -0,0 +1,75 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/reader/zlib/trees.c b/src/reader/zlib/trees.c new file mode 100644 index 00000000..ecd62b51 --- /dev/null +++ b/src/reader/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {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}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {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}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/src/reader/zlib/trees.h b/src/reader/zlib/trees.h new file mode 100644 index 00000000..72facf90 --- /dev/null +++ b/src/reader/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 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, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 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, 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, 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, +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 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 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, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/src/reader/zlib/unzip.c b/src/reader/zlib/unzip.c new file mode 100644 index 00000000..1e05038b --- /dev/null +++ b/src/reader/zlib/unzip.c @@ -0,0 +1,1605 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/src/reader/zlib/unzip.h b/src/reader/zlib/unzip.h new file mode 100644 index 00000000..b247937c --- /dev/null +++ b/src/reader/zlib/unzip.h @@ -0,0 +1,354 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/src/reader/zlib/zconf.h b/src/reader/zlib/zconf.h new file mode 100644 index 00000000..eb5a9ce2 --- /dev/null +++ b/src/reader/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/reader/zlib/zip.c b/src/reader/zlib/zip.c new file mode 100644 index 00000000..2dc74dfc --- /dev/null +++ b/src/reader/zlib/zip.c @@ -0,0 +1,1220 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.01e, February 12th, 2005 + + 27 Dec 2004 Rolf Kalbermatter + Modification to zipOpen2 to support globalComment retrieval. + + Copyright (C) 1998-2005 Gilles Vollant + + Read zip.h for more info +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] = + " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + uLong pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralheader; /* size of the central header for cur file */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile_info; + +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile_info ci; /* info on the file curretly writing */ + + uLong begin_pos; /* position of the beginning of the zipfile */ + uLong add_position_when_writting_offset; + uLong number_entry; +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip_internal; + + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(ldi) + linkedlist_datablock_internal* ldi; +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(ll) + linkedlist_data* ll; +{ + ll->first_block = ll->last_block = NULL; +} + +/* +local void free_linkedlist(ll) + linkedlist_data* ll; +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} +*/ + +local int add_data_in_datablock(ll,buf,len) + linkedlist_data* ll; + const void* buf; + uLong len; +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 or 4 (byte, short or long) +*/ + +local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte)); +local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong x; + int nbByte; +{ + unsigned char buf[4]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); +local void ziplocal_putValue_inmemory (dest, x, nbByte) + void* dest; + uLong x; + int nbByte; +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) + const tm_zip* ptm; + uLong dosDate; +{ + uLong year = (uLong)ptm->tm_year; + if (year>1980) + year-=1980; + else if (year>80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int ziplocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int ziplocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x; + int i = 0; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int ziplocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong ziplocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def) + const char *pathname; + int append; + zipcharpc* globalcomment; + zlib_filefunc_def* pzlib_filefunc_def; +{ + zip_internal ziinit; + zip_internal* zi; + int err=ZIP_OK; + + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&ziinit.z_filefunc); + else + ziinit.z_filefunc = *pzlib_filefunc_def; + + ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) + (ziinit.z_filefunc.opaque, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + zi = (zip_internal*)ALLOC(sizeof(zip_internal)); + if (zi==NULL) + { + ZCLOSE(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory */ + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry; + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong size_comment; + + central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); + if (central_pos==0) + err=ZIP_ERRNO; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* zipfile global comment length */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((central_pos0) + { + ziinit.globalcomment = ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); + ziinit.globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - + (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + { + uLong size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + uLong read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir,buf_read, + (uLong)read_this); + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + ziinit.begin_pos = byte_before_the_zipfile; + ziinit.number_entry = number_entry_CD; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen (pathname, append) + const char *pathname; + int append; +{ + return zipOpen2(pathname,append,NULL,NULL); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; + int windowBits; + int memLevel; + int strategy; + const char* password; + uLong crcForCrypting; +{ + zip_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); + } + + zi->ci.flag = 0; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + + size_extrafield_global + size_comment; + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); + + ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); + ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* write the local header */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); + + if ((err==ZIP_OK) && (size_filename>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + + if ((err==ZIP_OK) && (size_extrafield_local>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) + !=size_extrafield_local) + err = ZIP_ERRNO; + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, + Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = 1; + } +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; +{ + return zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; +{ + return zipOpenNewFileInZip2 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0); +} + +local int zipFlushWriteBuffer(zi) + zip_internal* zi; +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, + zi->ci.buffered_data[i],t); +#endif + } + if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) + !=zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + zi->ci.pos_in_buffered_data = 0; + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (file, buf, len) + zipFile file; + const void* buf; + unsigned len; +{ + zip_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.stream.next_in = (void*)buf; + zi->ci.stream.avail_in = len; + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + for (i=0;ici.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) + zipFile file; + uLong uncompressed_size; + uLong crc32; +{ + zip_internal* zi; + uLong compressed_size; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + err=deflateEnd(&zi->ci.stream); + zi->ci.stream_initialised = 0; + } + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = (uLong)zi->ci.stream.total_in; + } + compressed_size = (uLong)zi->ci.stream.total_out; +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20, + compressed_size,4); /*compr size*/ + if (zi->ci.stream.data_type == Z_ASCII) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + ziplocal_putValue_inmemory(zi->ci.central_header+24, + uncompressed_size,4); /*uncompr size*/ + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, + (uLong)zi->ci.size_centralheader); + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (ZSEEK(zi->z_filefunc,zi->filestream, + zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + + if (ZSEEK(zi->z_filefunc,zi->filestream, + cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (file) + zipFile file; +{ + return zipCloseFileInZipRaw (file,0,0); +} + +extern int ZEXPORT zipClose (file, global_comment) + zipFile file; + const char* global_comment; +{ + zip_internal* zi; + int err = 0; + uLong size_centraldir = 0; + uLong centraldir_pos_inzip; + uInt size_global_comment; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + if (global_comment==NULL) + size_global_comment = 0; + else + size_global_comment = (uInt)strlen(global_comment); + + centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + ldi->data,ldi->filled_in_this_block) + !=ldi->filled_in_this_block ) + err = ZIP_ERRNO; + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_datablock(zi->central_dir.first_block); + + if (err==ZIP_OK) /* Magic End */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* size of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the + starting disk number */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, + (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + + if (err==ZIP_OK) /* zipfile comment length */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if ((err==ZIP_OK) && (size_global_comment>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + global_comment,size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + + if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} diff --git a/src/reader/zlib/zip.h b/src/reader/zlib/zip.h new file mode 100644 index 00000000..acacce83 --- /dev/null +++ b/src/reader/zlib/zip.h @@ -0,0 +1,235 @@ +/* zip.h -- IO for compress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow creates .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + For uncompress .zip file, look at unzip.h + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.html for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _zip_H +#define _zip_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCtypting)); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCtypting : crc of file to compress (needed for crypting) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); +/* + Close the current file in the zipfile, for fiel opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip_H */ diff --git a/src/reader/zlib/zlib.h b/src/reader/zlib/zlib.h new file mode 100644 index 00000000..02281792 --- /dev/null +++ b/src/reader/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/reader/zlib/zutil.c b/src/reader/zlib/zutil.c new file mode 100644 index 00000000..8b278e31 --- /dev/null +++ b/src/reader/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/src/reader/zlib/zutil.h b/src/reader/zlib/zutil.h new file mode 100644 index 00000000..c7ad800a --- /dev/null +++ b/src/reader/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/src/sdl/Makefile b/src/sdl/Makefile index c80c0168..c76e8fca 100644 --- a/src/sdl/Makefile +++ b/src/sdl/Makefile @@ -1,18 +1,23 @@ -CC = c++ +CC = cc +CXX = c++ CFLAGS = -O3 -fomit-frame-pointer -ffast-math +CXXFLAGS = $(CFLAGS) -fno-rtti OBJS = sdlmain.o \ libstring.o libconfig.o \ - reader.o \ + reader.o cart.o \ memory.o bmemory.o \ cpu.o bcpu.o \ apu.o bapu.o \ bdsp.o \ ppu.o bppu.o \ snes.o \ - srtc.o sdd1.o + srtc.o sdd1.o c4.o \ + adler32.o compress.o crc32.o deflate.o gzio.o inffast.o \ + inflate.o inftrees.o ioapi.o trees.o unzip.o zip.o zutil.o \ + jma.o jcrc32.o lzmadec.o 7zlzma.o iiostrm.o inbyte.o lzma.o winout.o all: $(OBJS) - $(CC) $(CFLAGS) $(OBJS) `sdl11-config --cflags --libs` -o bsnes_sdl + $(CXX) $(CXXFLAGS) $(OBJS) `sdl11-config --cflags --libs` -o bsnes_sdl clean: rm *.o @@ -21,78 +26,93 @@ clean: ### sdl-specific ### #################### sdlmain.o: *.cpp *.h - $(CC) $(CFLAGS) -c sdlmain.cpp `sdl11-config --cflags` + $(CXX) $(CXXFLAGS) -c sdlmain.cpp `sdl11-config --cflags` ################# ### libraries ### ################# libstring.o: ../lib/*.cpp ../lib/*.h - $(CC) $(CFLAGS) -c ../lib/libstring.cpp + $(CXX) $(CXXFLAGS) -c ../lib/libstring.cpp libconfig.o: ../lib/*.cpp ../lib/*.h - $(CC) $(CFLAGS) -c ../lib/libconfig.cpp + $(CXX) $(CXXFLAGS) -c ../lib/libconfig.cpp ############## ### memory ### ############## memory.o: ../memory/memory.cpp ../memory/memory.h - $(CC) $(CFLAGS) -c ../memory/memory.cpp + $(CXX) $(CXXFLAGS) -c ../memory/memory.cpp bmemory.o: ../memory/bmemory/* - $(CC) $(CFLAGS) -c ../memory/bmemory/bmemory.cpp + $(CXX) $(CXXFLAGS) -c ../memory/bmemory/bmemory.cpp ########### ### cpu ### ########### cpu.o: ../cpu/*.cpp ../cpu/*.h - $(CC) $(CFLAGS) -c ../cpu/cpu.cpp + $(CXX) $(CXXFLAGS) -c ../cpu/cpu.cpp bcpu.o: ../cpu/bcpu/* - $(CC) $(CFLAGS) -c ../cpu/bcpu/bcpu.cpp + $(CXX) $(CXXFLAGS) -c ../cpu/bcpu/bcpu.cpp ########### ### apu ### ########### apu.o: ../apu/* - $(CC) $(CFLAGS) -c ../apu/apu.cpp + $(CXX) $(CXXFLAGS) -c ../apu/apu.cpp bapu.o: ../apu/bapu/* - $(CC) $(CFLAGS) -c ../apu/bapu/bapu.cpp + $(CXX) $(CXXFLAGS) -c ../apu/bapu/bapu.cpp ########### ### dsp ### ########### bdsp.o: ../dsp/bdsp/* - $(CC) $(CFLAGS) -c ../dsp/bdsp/bdsp.cpp + $(CXX) $(CXXFLAGS) -c ../dsp/bdsp/bdsp.cpp ########### ### ppu ### ########### ppu.o: ../ppu/*.cpp ../ppu/*.h - $(CC) $(CFLAGS) -c ../ppu/ppu.cpp + $(CXX) $(CXXFLAGS) -c ../ppu/ppu.cpp bppu.o: ../ppu/bppu/* - $(CC) $(CFLAGS) -c ../ppu/bppu/bppu.cpp + $(CXX) $(CXXFLAGS) -c ../ppu/bppu/bppu.cpp -############## -### reader ### -############## +################# +### utilities ### +################# reader.o: ../reader/*.cpp ../reader/*.h - $(CC) $(CFLAGS) -c ../reader/reader.cpp + $(CXX) $(CXXFLAGS) -c ../reader/reader.cpp + +cart.o: ../cart/*.cpp ../cart/*.h + $(CXX) $(CXXFLAGS) -c ../cart/cart.cpp ############ ### snes ### ############ snes.o: ../snes/*.cpp ../snes/*.h - $(CC) $(CFLAGS) -c ../snes/snes.cpp + $(CXX) $(CXXFLAGS) -c ../snes/snes.cpp -############ -### srtc ### -############ +##################### +### special chips ### +##################### srtc.o: ../chip/srtc/*.cpp ../chip/srtc/*.h - $(CC) $(CFLAGS) -c ../chip/srtc/srtc.cpp + $(CXX) $(CXXFLAGS) -c ../chip/srtc/srtc.cpp + +sdd1.o: ../chip/sdd1/*.cpp ../chip/sdd1/*.h + $(CXX) $(CXXFLAGS) -c ../chip/sdd1/sdd1.cpp + +c4.o: ../chip/c4/*.cpp ../chip/c4/*.h + $(CXX) $(CXXFLAGS) -c ../chip/c4/c4.cpp ############ -### sdd1 ### +### zlib ### ############ -sdd1.o: ../chip/sdd1/*.cpp ../chip/sdd1/*.h - $(CC) $(CFLAGS) -c ../chip/sdd1/sdd1.cpp +adler32.o: ../reader/zlib/*.c ../reader/zlib/*.h + $(CC) $(CFLAGS) -c ../reader/zlib/*.c + +########### +### jma ### +########### +jma.o: ../reader/jma/*.cpp ../reader/jma/*.h + $(CXX) $(CXXFLAGS) -c ../reader/jma/*.cpp diff --git a/src/sdl/Makefile.win32 b/src/sdl/Makefile.win32 index 3f83052b..e49473fb 100644 --- a/src/sdl/Makefile.win32 +++ b/src/sdl/Makefile.win32 @@ -1,15 +1,18 @@ CC = cl -CFLAGS = /nologo /O2 /wd4996 +CFLAGS = /nologo /O2 /EHsc /wd4996 OBJS = sdlmain.obj \ libstring.obj libconfig.obj \ - reader.obj \ + reader.obj cart.obj \ memory.obj bmemory.obj \ cpu.obj bcpu.obj \ apu.obj bapu.obj \ bdsp.obj \ ppu.obj bppu.obj \ snes.obj \ - srtc.obj sdd1.obj + srtc.obj sdd1.obj c4.obj +# adler32.obj compress.obj crc32.obj deflate.obj gzio.obj inffast.obj \ +# inflate.obj inftrees.obj ioapi.obj trees.obj unzip.obj zip.obj zutil.obj \ +# jma.obj jcrc32.obj lzmadec.obj 7zlzma.obj iiostrm.obj inbyte.obj lzma.obj winout.obj LIBS = kernel32.lib user32.lib gdi32.lib sdlmain.lib sdl.lib all: $(OBJS) @@ -74,26 +77,41 @@ ppu.obj: ../ppu/*.cpp ../ppu/*.h bppu.obj: ../ppu/bppu/* $(CC) $(CFLAGS) /c ../ppu/bppu/bppu.cpp -############## -### reader ### -############## +################# +### utilities ### +################# reader.obj: ../reader/*.cpp ../reader/*.h $(CC) $(CFLAGS) /c ../reader/reader.cpp +cart.obj: ../cart/*.cpp ../cart/*.h + $(CC) $(CFLAGS) /c ../cart/cart.cpp + ############ ### snes ### ############ snes.obj: ../snes/*.cpp ../snes/*.h $(CC) $(CFLAGS) /c ../snes/snes.cpp -############ -### srtc ### -############ +##################### +### special chips ### +##################### srtc.obj: ../chip/srtc/*.cpp ../chip/srtc/*.h $(CC) $(CFLAGS) /c ../chip/srtc/srtc.cpp -############ -### sdd1 ### -############ sdd1.obj: ../chip/sdd1/*.cpp ../chip/sdd1/*.h $(CC) $(CFLAGS) /c ../chip/sdd1/sdd1.cpp + +c4.obj: ../chip/c4/*.cpp ../chip/c4/*.h + $(CC) $(CFLAGS) /c ../chip/c4/c4.cpp + +############ +### zlib ### +############ +adler32.obj: ../reader/zlib/*.c ../reader/zlib/*.h + $(CC) $(CFLAGS) /c ../reader/zlib/*.c + +########### +### jma ### +########### +jma.obj: ../reader/jma/*.cpp ../reader/jma/*.h + $(CC) $(CFLAGS) /c ../reader/jma/*.cpp diff --git a/src/sdl/bsnes.cpp b/src/sdl/bsnes.cpp index 11188844..ca29091f 100644 --- a/src/sdl/bsnes.cpp +++ b/src/sdl/bsnes.cpp @@ -2,8 +2,6 @@ void bSNES::set_status(uint32 new_status) { run_status = new_status; } uint32 bSNES::get_status() { return run_status; } void bSNES::run() { - if(!rom_image->loaded())return; - switch(run_status) { case RUN: SNES::runtoframe(); diff --git a/src/sdl/bsnes_sdl.cfg b/src/sdl/bsnes_sdl.cfg index 5a6ac037..7009e0a4 100644 --- a/src/sdl/bsnes_sdl.cfg +++ b/src/sdl/bsnes_sdl.cfg @@ -13,7 +13,7 @@ snes.video_color_curve = true snes.video_color_adjust_mode = 0 # Mutes SNES audio output when enabled -# (default = true) +# (default = false) snes.mute = true # Enable fullscreen mode at startup diff --git a/src/sdl/rom.cpp b/src/sdl/rom.cpp deleted file mode 100644 index 46897fcb..00000000 --- a/src/sdl/rom.cpp +++ /dev/null @@ -1,99 +0,0 @@ -class ROMImage { -private: -char rom_fn[4096]; -char sram_fn[4096]; -bool file_loaded; - -public: - bool loaded(); - bool load(); - void unload(); - void select(char *fn); - ROMImage(); - ~ROMImage(); -}; - -bool ROMImage::loaded() { - return file_loaded; -} - -bool ROMImage::load() { - if(file_loaded == true)return false; - - dprintf("* Loading \"%s\"...", rom_fn); - -FileReader *rf = new FileReader(); - if(!rf->open(rom_fn)) { - alert("Error loading image file [%s]!", rom_fn); - return false; - } - r_mem->load_cart(static_cast(rf)); - rf->close(); - -CartInfo ci; - r_mem->get_cartinfo(&ci); - if(ci.sram_size != 0) { - rf->open(sram_fn); - r_mem->load_sram(static_cast(rf)); - rf->close(); - } - - delete(rf); - - file_loaded = true; - return true; -} - -void ROMImage::unload() { - if(file_loaded == false)return; - -FileWriter *wf; -CartInfo ci; - r_mem->get_cartinfo(&ci); - if(ci.sram_size != 0) { - wf = new FileWriter(); - wf->open(sram_fn); - r_mem->save_sram(static_cast(wf)); - wf->close(); - delete(wf); - } - - file_loaded = false; - - r_mem->unload_cart(); -} - -void ROMImage::select(char *fn) { -int i; - if(file_loaded == true)return; - -//remove quotes - if(fn[0] == '\"') { - strcpy(rom_fn, fn + 1); - rom_fn[strlen(rom_fn) - 1] = 0; - } else { - strcpy(rom_fn, fn); - } - - for(i=strlen(rom_fn)-1;i>=0;i--) { - if(rom_fn[i] == '.')break; - } - - strcpy(sram_fn, rom_fn); - if(rom_fn[i] == '.')sram_fn[i] = 0; - strcat(sram_fn, ".srm"); -} - -ROMImage::ROMImage() { - *rom_fn = 0; - *sram_fn = 0; - file_loaded = false; -} - -ROMImage::~ROMImage() { - if(file_loaded == true) { - unload(); - } -} - -ROMImage *rom_image; diff --git a/src/sdl/sdlmain.cpp b/src/sdl/sdlmain.cpp index d371da97..19f483ba 100644 --- a/src/sdl/sdlmain.cpp +++ b/src/sdl/sdlmain.cpp @@ -9,7 +9,6 @@ HWND hwnd; #endif #include "bsnes.h" -#include "rom.cpp" #include "render.cpp" #include "bsnes.cpp" @@ -66,13 +65,13 @@ void init_snes() { void term_snes() { snes->term(); #ifdef POLYMORPHISM - if(deref(mem)) { delete static_cast(deref(mem)); deref(mem) = 0; } - if(deref(cpu)) { delete static_cast (deref(cpu)); deref(cpu) = 0; } - if(deref(apu)) { delete static_cast (deref(apu)); deref(apu) = 0; } - if(deref(dsp)) { delete static_cast (deref(dsp)); deref(dsp) = 0; } - if(deref(ppu)) { delete static_cast (deref(ppu)); deref(ppu) = 0; } + if(deref(mem)) { delete deref(mem); deref(mem) = 0; } + if(deref(cpu)) { delete deref(cpu); deref(cpu) = 0; } + if(deref(apu)) { delete deref(apu); deref(apu) = 0; } + if(deref(dsp)) { delete deref(dsp); deref(dsp) = 0; } + if(deref(ppu)) { delete deref(ppu); deref(ppu) = 0; } #endif - if(snes) { delete(static_cast(snes)); snes = 0; } + if(snes) { delete(snes); snes = 0; } } void center_window() { @@ -120,13 +119,9 @@ SDL_Event event; config_file.load("bsnes_sdl.cfg"); - rom_image = new ROMImage(); init_snes(); - rom_image->select(argv[1]); - rom_image->load(); - - if(rom_image->loaded() == false) { + if(cartridge.load(argv[1]) == false) { alert("Failed to load image. Usage: bsnes_sdl "); goto _end; } @@ -178,6 +173,7 @@ int cursor_status; _end: config_file.save("bsnes_sdl.cfg"); + cartridge.unload(); term_snes(); return 0; diff --git a/src/sdl/sdlrun.bat b/src/sdl/sdlrun.bat index d397c2f2..aa59b644 100644 --- a/src/sdl/sdlrun.bat +++ b/src/sdl/sdlrun.bat @@ -1 +1 @@ -bsnes_sdl c:\root\bsnes_testrom\zelda_us.smc \ No newline at end of file +bsnes_sdl c:\root\bsnes_testrom\zelda_us.smc diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index 04ca776e..e30de1f1 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -33,9 +33,11 @@ void SNES::runtoframe() { void SNES::init() { srtc = new SRTC(); sdd1 = new SDD1(); + c4 = new C4(); srtc->init(); sdd1->init(); + c4->init(); video_init(); audio_init(); @@ -52,8 +54,9 @@ void SNES::power() { r_ppu->power(); r_mem->power(); - srtc->power(); - sdd1->power(); + if(cartridge.cart.srtc)srtc->power(); + if(cartridge.cart.sdd1)sdd1->power(); + if(cartridge.cart.c4) c4->power(); int i; r_mem->flush_mmio_mappers(); @@ -66,8 +69,9 @@ int i; for(i=0x4200;i<=0x421f;i++)r_mem->set_mmio_mapper(i, r_cpu->mmio); for(i=0x4300;i<=0x437f;i++)r_mem->set_mmio_mapper(i, r_cpu->mmio); - srtc->enable(); - sdd1->enable(); + if(cartridge.cart.srtc)srtc->enable(); + if(cartridge.cart.sdd1)sdd1->enable(); + if(cartridge.cart.c4) c4->enable(); video_update(); } @@ -81,8 +85,9 @@ void SNES::reset() { r_ppu->reset(); r_mem->reset(); - srtc->reset(); - sdd1->reset(); + if(cartridge.cart.srtc)srtc->reset(); + if(cartridge.cart.sdd1)sdd1->reset(); + if(cartridge.cart.c4) c4->reset(); video_update(); } diff --git a/src/snes/snes.h b/src/snes/snes.h index db2a31f5..cf838a82 100644 --- a/src/snes/snes.h +++ b/src/snes/snes.h @@ -56,4 +56,5 @@ enum { virtual void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); SNES(); + virtual ~SNES() {} }; diff --git a/src/win/Makefile b/src/win/Makefile index ec12082c..ef0efb61 100644 --- a/src/win/Makefile +++ b/src/win/Makefile @@ -1,32 +1,37 @@ CC = cl -CFLAGS = /nologo /O2 /wd4996 -OBJS = winmain.obj \ +CFLAGS = /nologo /O2 /EHsc /wd4996 +OBJS = main.obj \ libstring.obj libconfig.obj \ - reader.obj \ + reader.obj cart.obj \ memory.obj bmemory.obj \ cpu.obj bcpu.obj \ apu.obj bapu.obj \ bdsp.obj \ ppu.obj bppu.obj \ snes.obj \ - srtc.obj sdd1.obj -LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dxguid.lib + srtc.obj sdd1.obj c4.obj \ + adler32.obj compress.obj crc32.obj deflate.obj gzio.obj inffast.obj \ + inflate.obj inftrees.obj ioapi.obj trees.obj unzip.obj zip.obj zutil.obj \ + jma.obj jcrc32.obj lzmadec.obj 7zlzma.obj iiostrm.obj inbyte.obj lzma.obj winout.obj +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib all: $(OBJS) rc /r /fobsnes.res bsnes.rc $(CC) /Febsnes.exe $(CFLAGS) $(OBJS) bsnes.res $(LIBS) -#/link /PGD:bsnes.pgd /LTCG:PGOPTIMIZE +#/link /PGD:bsnes.pgd /LTCG:PGINSTRUMENT clean: del *.obj del *.pgd del *.pgc + del *.ilk + del *.pdb ###################### ### win32-specific ### ###################### -winmain.obj: *.cpp *.h ../config/* - $(CC) $(CFLAGS) /c winmain.cpp +main.obj: *.cpp *.h video/* audio/* input/* ../config/* + $(CC) $(CFLAGS) /c main.cpp ################# ### libraries ### @@ -78,26 +83,41 @@ ppu.obj: ../ppu/*.cpp ../ppu/*.h bppu.obj: ../ppu/bppu/* $(CC) $(CFLAGS) /c ../ppu/bppu/bppu.cpp -############## -### reader ### -############## +################# +### utilities ### +################# reader.obj: ../reader/*.cpp ../reader/*.h $(CC) $(CFLAGS) /c ../reader/reader.cpp +cart.obj: ../cart/*.cpp ../cart/*.h + $(CC) $(CFLAGS) /c ../cart/cart.cpp + ############ ### snes ### ############ snes.obj: ../snes/*.cpp ../snes/*.h $(CC) $(CFLAGS) /c ../snes/snes.cpp -############ -### srtc ### -############ +##################### +### special chips ### +##################### srtc.obj: ../chip/srtc/*.cpp ../chip/srtc/*.h $(CC) $(CFLAGS) /c ../chip/srtc/srtc.cpp -############ -### sdd1 ### -############ sdd1.obj: ../chip/sdd1/*.cpp ../chip/sdd1/*.h $(CC) $(CFLAGS) /c ../chip/sdd1/sdd1.cpp + +c4.obj: ../chip/c4/*.cpp ../chip/c4/*.h + $(CC) $(CFLAGS) /c ../chip/c4/c4.cpp + +############ +### zlib ### +############ +adler32.obj: ../reader/zlib/*.c ../reader/zlib/*.h + $(CC) $(CFLAGS) /c ../reader/zlib/*.c + +########### +### jma ### +########### +jma.obj: ../reader/jma/*.cpp ../reader/jma/*.h + $(CC) $(CFLAGS) /c ../reader/jma/*.cpp diff --git a/src/win/audio/audio.h b/src/win/audio/audio.h new file mode 100644 index 00000000..3d0b0d55 --- /dev/null +++ b/src/win/audio/audio.h @@ -0,0 +1,13 @@ +class Audio { +public: +uint32 frequency; + virtual void run(uint32 sample) = 0; + virtual void set_frequency(uint32 new_freq) = 0; + virtual void clear_audio() = 0; + virtual void init() = 0; + virtual void term() = 0; + + Audio() { frequency = 32000; } +}; + +#include "dsound.h" diff --git a/src/win/audio/dsound.cpp b/src/win/audio/dsound.cpp new file mode 100644 index 00000000..6ebf5f1b --- /dev/null +++ b/src/win/audio/dsound.cpp @@ -0,0 +1,108 @@ +void AudioDS::run(uint32 sample) { + data.buffer[data.buffer_pos++] = sample; + + if(data.buffer_pos >= data.samples_per_frame) { + uint32 pos, size; + void *buffer; + if((bool)config::system.regulate_speed == true) { + do { + //see if video refresh is needed + uiVideo->scanline(); + dsb_b->GetCurrentPosition(&pos, 0); + data.read_buffer = pos / data.buffer_size; + } while(data.read_buffer == data.prev_buffer); + } + + data.prev_buffer = data.read_buffer; + data.read_buffer++; + data.read_buffer &= 7; + + pos = (data.read_buffer + 1) & 7; + + if(dsb_b->Lock(pos * data.buffer_size, + data.buffer_size, &buffer, &size, 0, 0, 0) == DS_OK) { + memcpy(buffer, data.buffer, data.buffer_size); + dsb_b->Unlock(buffer, size, 0, 0); + } + + data.buffer_pos = 0; + } +} + +void AudioDS::set_frequency(uint32 new_freq) { + frequency = new_freq; + init(); +} + +void AudioDS::clear_audio() { + data.read_buffer = 0; + data.prev_buffer = 0; + data.buffer_pos = 0; + memset(data.buffer, 0, 2048 * 4); + + if(!dsb_b)return; + + dsb_b->Stop(); + dsb_b->SetCurrentPosition(0); + +uint32 size; +void *buffer; + dsb_b->Lock(0, data.buffer_size * 8, &buffer, &size, 0, 0, 0); + memset(buffer, 0, data.buffer_size * 8); + dsb_b->Unlock(buffer, size, 0, 0); + + dsb_b->Play(0, 0, DSBPLAY_LOOPING); +} + +void AudioDS::init() { + clear_audio(); + term(); + + data.samples_per_frame = frequency / ((snes->region() == SNES::NTSC) ? 60 : 50); + data.buffer_size = data.samples_per_frame * 4; + + DirectSoundCreate(0, &ds, 0); + ds->SetCooperativeLevel(wMain.hwnd, DSSCL_PRIORITY); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = 0; + ds->CreateSoundBuffer(&dsbd, &dsb_p, 0); + + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = frequency; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + dsb_p->SetFormat(&wfx); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | + DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE; + dsbd.dwBufferBytes = data.buffer_size * 8; + dsbd.guid3DAlgorithm = GUID_NULL; + dsbd.lpwfxFormat = &wfx; + ds->CreateSoundBuffer(&dsbd, &dsb_b, 0); + dsb_b->SetFrequency(frequency); + dsb_b->SetCurrentPosition(0); + +uint32 size; +void *buffer; + dsb_b->Lock(0, data.buffer_size * 8, &buffer, &size, 0, 0, 0); + memset(buffer, 0, data.buffer_size * 8); + dsb_b->Unlock(buffer, size, 0, 0); + + data.read_buffer = 0; + dsb_b->Play(0, 0, DSBPLAY_LOOPING); +} + +void AudioDS::term() { + if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; } + if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; } + if(ds) { ds->Release(); ds = 0; } +} diff --git a/src/win/audio/dsound.h b/src/win/audio/dsound.h new file mode 100644 index 00000000..aaf20d8e --- /dev/null +++ b/src/win/audio/dsound.h @@ -0,0 +1,32 @@ +#include + +class AudioDS : public Audio { +public: +LPDIRECTSOUND ds; +LPDIRECTSOUNDBUFFER dsb_p, dsb_b; +DSBUFFERDESC dsbd; +WAVEFORMATEX wfx; +uint32 buffer_size; + +struct { + uint32 buffer[16384]; + uint8 read_buffer, prev_buffer; + uint32 buffer_pos, buffer_size; + + uint32 samples_per_frame; +} data; + void run(uint32 sample); + void set_frequency(uint32 new_freq); + void clear_audio(); + void init(); + void term(); + + AudioDS() { + ds = 0; + dsb_p = 0; + dsb_b = 0; + buffer_size = 0; + } + + ~AudioDS() { term(); } +}; diff --git a/src/win/bsnes.cpp b/src/win/bsnes.cpp index 826e1128..bea4bb18 100644 --- a/src/win/bsnes.cpp +++ b/src/win/bsnes.cpp @@ -1,5 +1,7 @@ +uint32 bSNES::get_state() { return state; } +void bSNES::set_state(uint32 new_state) { state = new_state; } + void bSNES::power() { - ds_sound->init(); SNES::power(); } @@ -7,116 +9,18 @@ void bSNES::reset() { SNES::reset(); } -void bSNES::set_status(uint32 new_status) { -uint8 cpu_op; - run_status = new_status; - - switch(new_status) { - case STOP: - w_console->is_running(false); - w_console->update_status(); - w_memory->refresh(); - break; - case RUNTOCPUSTEP: - status.cpu_ran = false; - break; - case RUNTOCPUPROCEED: - cpu_op = r_mem->read(r_cpu->regs.pc.d); - - if(cpu_op == 0x10 || //bpl rel - cpu_op == 0x30 || //bmi rel - cpu_op == 0x50 || //bvc rel - cpu_op == 0x70 || //bvs rel - cpu_op == 0x90 || //bcc rel - cpu_op == 0xb0 || //bcs rel - cpu_op == 0xd0 || //bne rel - cpu_op == 0xf0 || //beq rel - cpu_op == 0x20 || //jsr addr - cpu_op == 0x22 || //jsl long - cpu_op == 0xfc //jsr (addr,x) - ) { - w_console->is_running(true); - status.cpu_stop_pos = (r_cpu->regs.pc.b << 16) | ((r_cpu->regs.pc.d + r_cpu->opcode_length()) & 0xffff); - } else { - status.cpu_ran = false; - run_status = RUNTOCPUSTEP; - } - break; - case RUNTOAPUSTEP: - status.apu_ran = false; - break; - } -} - -uint32 bSNES::get_status() { - return run_status; +void bSNES::scanline() { + SNES::scanline(); + uiVideo->scanline(); } void bSNES::run() { - if(!rom_image->loaded())return; - - switch(run_status) { - case RUN: - SNES::runtoframe(); + if(!r_mem->cart_loaded() || state == PAUSE) { + Sleep(1); return; - case STOP: - break; - case RUNONCE: - SNES::run(); - set_status(STOP); - break; - case RUNTOSIGNAL: - SNES::run(); - if(w_bp->hit() == true) { - set_status(STOP); - disassemble_bp_op(); - } - break; - case RUNTOFRAME: - SNES::run(); - if(r_ppu->status.frame_executed == true) { - r_ppu->status.frame_executed = false; - set_status(STOP); - disassemble_apu_op(); - disassemble_cpu_op(); - } else if(w_bp->hit() == true) { - set_status(STOP); - disassemble_bp_op(); - } - return; - case RUNTOCPUSTEP: - SNES::run(); - if(status.cpu_ran == true) { - set_status(STOP); - } else if(w_bp->hit() == true) { - set_status(STOP); - disassemble_bp_op(); - } - break; - case RUNTOCPUPROCEED: - SNES::run(); - if(r_cpu->in_opcode() == false && status.cpu_stop_pos == r_cpu->regs.pc.d) { - set_status(STOP); - disassemble_cpu_op(); - } else if(w_bp->hit() == true) { - set_status(STOP); - disassemble_bp_op(); - } - break; - case RUNTOCPUTRACE: - SNES::run(); - if(status.cpu_trace_pos >= status.cpu_trace_stop) { - set_status(STOP); - disassemble_cpu_op(); - } - break; - case RUNTOAPUSTEP: - SNES::run(); - if(status.apu_ran == true || w_bp->hit() == true) { - set_status(STOP); - } - break; } + + SNES::runtoframe(); } void bSNES::video_run() { @@ -125,446 +29,60 @@ void bSNES::video_run() { r_ppu->status.frames_updated = false; if((bool)config::gui.show_fps == true) { sprintf(s, "%s : %d fps", BSNES_TITLE, r_ppu->status.frames_executed); - if(w_main->frameskip != 0) { + if(wMain.frameskip != 0) { sprintf(t, " (%d frames)", r_ppu->status.frames_rendered); strcat(s, t); } - SetWindowText(w_main->hwnd, s); + SetWindowText(wMain.hwnd, s); } } - w_main->frameskip_pos++; - w_main->frameskip_pos %= (w_main->frameskip + 1); - if(r_ppu->renderer_enabled())dd_renderer->update(); - r_ppu->enable_renderer(w_main->frameskip_pos == 0); + wMain.frameskip_pos++; + wMain.frameskip_pos %= (wMain.frameskip + 1); + if(r_ppu->renderer_enabled())uiVideo->update(); + r_ppu->enable_renderer(wMain.frameskip_pos == 0); } void bSNES::sound_run(uint32 data) { - ds_sound->run(data); + uiAudio->run(data); } -/*********************** - *** Video functions *** - ***********************/ uint16 *bSNES::video_lock(uint32 &pitch) { - return dd_renderer->lock(pitch); + return uiVideo->lock(pitch); } void bSNES::video_unlock() { - dd_renderer->unlock(); -} - -/*********************** - *** Input functions *** - ***********************/ -void bSNES::clear_input() { - joypad1.up = joypad2.up = - joypad1.down = joypad2.down = - joypad1.left = joypad2.left = - joypad1.right = joypad2.right = - joypad1.a = joypad2.a = - joypad1.b = joypad2.b = - joypad1.x = joypad2.x = - joypad1.y = joypad2.y = - joypad1.l = joypad2.l = - joypad1.r = joypad2.r = - joypad1.select = joypad2.select = - joypad1.start = joypad2.start = 0; + uiVideo->unlock(); } void bSNES::poll_input(uint8 type) { -//only capture input when main window has focus - if(GetForegroundWindow() == w_main->hwnd) { - switch(type) { - case SNES::DEV_JOYPAD1: - joypad1.up = KeyDown(config::input.joypad1.up); - joypad1.down = KeyDown(config::input.joypad1.down); - joypad1.left = KeyDown(config::input.joypad1.left); - joypad1.right = KeyDown(config::input.joypad1.right); - joypad1.select = KeyDown(config::input.joypad1.select); - joypad1.start = KeyDown(config::input.joypad1.start); - joypad1.y = KeyDown(config::input.joypad1.y); - joypad1.b = KeyDown(config::input.joypad1.b); - joypad1.x = KeyDown(config::input.joypad1.x); - joypad1.a = KeyDown(config::input.joypad1.a); - joypad1.l = KeyDown(config::input.joypad1.l); - joypad1.r = KeyDown(config::input.joypad1.r); - break; - case SNES::DEV_JOYPAD2: - joypad2.up = KeyDown(config::input.joypad2.up); - joypad2.down = KeyDown(config::input.joypad2.down); - joypad2.left = KeyDown(config::input.joypad2.left); - joypad2.right = KeyDown(config::input.joypad2.right); - joypad2.select = KeyDown(config::input.joypad2.select); - joypad2.start = KeyDown(config::input.joypad2.start); - joypad2.y = KeyDown(config::input.joypad2.y); - joypad2.b = KeyDown(config::input.joypad2.b); - joypad2.x = KeyDown(config::input.joypad2.x); - joypad2.a = KeyDown(config::input.joypad2.a); - joypad2.l = KeyDown(config::input.joypad2.l); - joypad2.r = KeyDown(config::input.joypad2.r); - break; + uiInput->poll(type); + + if((bool)config::input.joypad1.allow_invalid_input == false) { + if(uiInput->get_status(DEV_JOYPAD1, JOYPAD_UP) && + uiInput->get_status(DEV_JOYPAD1, JOYPAD_DOWN)) { + uiInput->set_status(DEV_JOYPAD1, JOYPAD_DOWN, false); } - } else { - switch(type) { - case SNES::DEV_JOYPAD1: - joypad1.up = joypad1.down = joypad1.left = joypad1.right = - joypad1.select = joypad1.start = - joypad1.y = joypad1.b = joypad1.x = joypad1.a = - joypad1.l = joypad1.r = 0; - break; - case SNES::DEV_JOYPAD2: - joypad2.up = joypad2.down = joypad2.left = joypad2.right = - joypad2.select = joypad2.start = - joypad2.y = joypad2.b = joypad2.x = joypad2.a = - joypad1.l = joypad2.r = 0; - break; + + if(uiInput->get_status(DEV_JOYPAD1, JOYPAD_LEFT) && + uiInput->get_status(DEV_JOYPAD1, JOYPAD_RIGHT)) { + uiInput->set_status(DEV_JOYPAD1, JOYPAD_RIGHT, false); } } -//check for debugger-based key locks - if(is_debugger_enabled == true && type == SNES::DEV_JOYPAD1) { - if(w_console->joypad_lock.up )joypad1.up = true; - if(w_console->joypad_lock.down )joypad1.down = true; - if(w_console->joypad_lock.left )joypad1.left = true; - if(w_console->joypad_lock.right )joypad1.right = true; - if(w_console->joypad_lock.a )joypad1.a = true; - if(w_console->joypad_lock.b )joypad1.b = true; - if(w_console->joypad_lock.x )joypad1.x = true; - if(w_console->joypad_lock.y )joypad1.y = true; - if(w_console->joypad_lock.l )joypad1.l = true; - if(w_console->joypad_lock.r )joypad1.r = true; - if(w_console->joypad_lock.select)joypad1.select = true; - if(w_console->joypad_lock.start )joypad1.start = true; + if((bool)config::input.joypad2.allow_invalid_input == false) { + if(uiInput->get_status(DEV_JOYPAD2, JOYPAD_UP) && + uiInput->get_status(DEV_JOYPAD2, JOYPAD_DOWN)) { + uiInput->set_status(DEV_JOYPAD2, JOYPAD_DOWN, false); + } + + if(uiInput->get_status(DEV_JOYPAD2, JOYPAD_LEFT) && + uiInput->get_status(DEV_JOYPAD2, JOYPAD_RIGHT)) { + uiInput->set_status(DEV_JOYPAD2, JOYPAD_RIGHT, false); + } } - -//it's impossible to hold both up+down, or left+right down -//at the same time on a directional pad; and besides, allowing -//this to happen causes glitches in many SNES games. - if(joypad1.up) joypad1.down = 0; - if(joypad1.left)joypad1.right = 0; - - if(joypad2.up) joypad2.down = 0; - if(joypad2.left)joypad2.right = 0; } bool bSNES::get_input_status(uint8 device, uint8 button) { - switch(device) { - case DEV_JOYPAD1: - switch(button) { - case JOYPAD_UP: return joypad1.up; - case JOYPAD_DOWN: return joypad1.down; - case JOYPAD_LEFT: return joypad1.left; - case JOYPAD_RIGHT: return joypad1.right; - case JOYPAD_A: return joypad1.a; - case JOYPAD_B: return joypad1.b; - case JOYPAD_X: return joypad1.x; - case JOYPAD_Y: return joypad1.y; - case JOYPAD_L: return joypad1.l; - case JOYPAD_R: return joypad1.r; - case JOYPAD_SELECT:return joypad1.select; - case JOYPAD_START: return joypad1.start; - } - break; - case DEV_JOYPAD2: - switch(button) { - case JOYPAD_UP: return joypad2.up; - case JOYPAD_DOWN: return joypad2.down; - case JOYPAD_LEFT: return joypad2.left; - case JOYPAD_RIGHT: return joypad2.right; - case JOYPAD_A: return joypad2.a; - case JOYPAD_B: return joypad2.b; - case JOYPAD_X: return joypad2.x; - case JOYPAD_Y: return joypad2.y; - case JOYPAD_L: return joypad2.l; - case JOYPAD_R: return joypad2.r; - case JOYPAD_SELECT:return joypad2.select; - case JOYPAD_START: return joypad2.start; - } - break; - } - - return 0; -} - -bJoypad::bJoypad() { - up = down = left = right = false; - a = b = x = y = false; - l = r = select = start = false; -} - -/*************************** - *** Debugging functions *** - ***************************/ -uint8 bSNES::read(uint8 type, uint32 addr) { -uint32 bank, a; -uint8 r = 0x00; - debug_command = true; - switch(type) { - case DRAM: - addr &= 0xffffff; - bank = (addr >> 16) & 0xff; - a = addr & 0xffff; - if((bank >= 0x00 && bank <= 0x3f) || - (bank >= 0x80 && bank <= 0xbf)) { - //don't let the debugger poll MMIO ports - if(a >= 0x2000 && a <= 0x5fff) { - r = 0x00; - } else { - r = r_mem->read(addr); - } - } else { - r = r_mem->read(addr); - } - break; - case SPCRAM: - addr &= 0xffff; - r = r_apu->spcram_read(addr); - break; - case VRAM: - addr &= 0xffff; - r = r_ppu->vram_read(addr); - break; - case OAM: - addr &= 0x03ff; - r = r_ppu->oam_read(addr); - break; - case CGRAM: - addr &= 0x01ff; - r = r_ppu->cgram_read(addr); - break; - } - debug_command = false; - return r; -} - -void bSNES::write(uint8 type, uint32 addr, uint8 value) { - debug_command = true; - switch(type) { - case DRAM: - addr &= 0xffffff; - r_mem->cart->write_protect(false); - r_mem->write(addr, value); - r_mem->cart->write_protect(true); - break; - case SPCRAM: - addr &= 0xffff; - r_apu->spcram_write(addr, value); - break; - case VRAM: - addr &= 0xffff; - r_ppu->vram_write(addr, value); - break; - case OAM: - addr &= 0x03ff; - r_ppu->oam_write(addr, value); - break; - case CGRAM: - addr &= 0x01ff; - r_ppu->cgram_write(addr, value); - break; - } - debug_command = false; -} - -void bSNES::notify(uint32 message, uint32 param1, uint32 param2) { -//debugging messages - if(is_debugger_enabled == false)return; - - switch(message) { - case CPU_EXEC_OPCODE_BEGIN: - break; - case CPU_EXEC_OPCODE_END: - status.cpu_ran = true; - status.cpu_trace_pos++; - //test next opcode for breakpoint - w_bp->test(message, r_cpu->regs.pc.d, 0); - disassemble_cpu_op(); - break; - case APU_EXEC_OPCODE_BEGIN: - break; - case APU_EXEC_OPCODE_END: - status.apu_ran = true; - w_bp->test(message, r_apu->regs.pc, 0); - disassemble_apu_op(); - break; - case MEM_READ: - case SPCRAM_READ: - case VRAM_READ: - case OAM_READ: - case CGRAM_READ: - if(debug_command == false) { - w_bp->test(message, param1, param2); - } - break; - case MEM_WRITE: - case SPCRAM_WRITE: - case VRAM_WRITE: - case OAM_WRITE: - case CGRAM_WRITE: - if(debug_command == false) { - w_bp->test(message, param1, param2); - } - //this needs to be called after the breakpoint test, - //as it will access read() and clear debug_command - w_memory->refresh(message, param1); - break; - } -} - -void bSNES::disassemble_cpu_op() { -char t[512]; -//don't disassemble opcodes when no ROM is loaded - if(is_debugger_activated == false)return; - -//don't disassemble opcodes that won't be printed to console/traced to log anyway - if(!w_console->can_write(Console::CPU_MESSAGE) && !w_console->tracing_enabled)return; - - r_cpu->disassemble_opcode(t); - w_console->write(t, Console::CPU_MESSAGE); -} - -void bSNES::disassemble_apu_op() { -char t[512]; - if(is_debugger_activated == false)return; - - if(!w_console->can_write(Console::APU_MESSAGE) && !w_console->tracing_enabled)return; - - r_apu->disassemble_opcode(t); - w_console->write(t, Console::APU_MESSAGE); -} - -void bSNES::disassemble_bp_op() { - if(w_bp->bp[w_bp->bp_hit_num].source == SPCRAM) { - disassemble_apu_op(); - } else { - disassemble_cpu_op(); - } -} - -bool bSNES::debugger_enabled() { - return is_debugger_enabled; -} - -void bSNES::debugger_enable() { - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_DEBUGGER, MF_CHECKED); - set_status(bSNES::STOP); - - w_console->is_running(false); - w_console->update_status(); - w_console->show(); - - w_bp->refresh(); - w_bp->show(); - - w_memory->edit_mode = MemoryEditor::MODE_DRAM; - w_memory->edit_addr = 0x7e0000; - w_memory->edit_mask = 0xffffff; - SendDlgItemMessage(w_memory->hwnd, MEMORYEDITOR_MODE, CB_SETCURSEL, 0, 0); - w_memory->refresh(); - w_memory->show(); - - debugger_update(); - is_debugger_enabled = true; -} - -void bSNES::debugger_disable() { - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_DEBUGGER, MF_UNCHECKED); - w_console->hide(); - w_bp->hide(); - w_memory->hide(); - is_debugger_enabled = false; - set_status(bSNES::RUN); -} - -void bSNES::debugger_update() { - w_console->update_status(); - w_bp->refresh(); - w_memory->refresh(); - disassemble_apu_op(); - disassemble_cpu_op(); -} - -bool bSNES::debugger_activated() { - return is_debugger_activated; -} - -void bSNES::debugger_activate() { -HWND hwnd; -uint32 i, style; - for(i=0;i<100;i++) { - if(w_console->ctl_disabled[i] == true)continue; - hwnd = GetDlgItem(w_console->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - InvalidateRect(w_console->hwnd, 0, TRUE); - - for(i=0;i<100;i++) { - if(w_bp->ctl_disabled[i] == true)continue; - hwnd = GetDlgItem(w_bp->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - InvalidateRect(w_bp->hwnd, 0, TRUE); - - for(i=0;i<100;i++) { - if(w_memory->ctl_disabled[i] == true)continue; - hwnd = GetDlgItem(w_memory->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - InvalidateRect(w_memory->hwnd, 0, TRUE); - - is_debugger_activated = true; -} - -void bSNES::debugger_deactivate() { -HWND hwnd; -uint32 i, style; - for(i=0;i<100;i++) { - hwnd = GetDlgItem(w_console->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style |= WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - w_console->clear(); - InvalidateRect(w_console->hwnd, 0, TRUE); - - for(i=0;i<100;i++) { - hwnd = GetDlgItem(w_bp->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style |= WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - w_bp->clear(); - InvalidateRect(w_bp->hwnd, 0, TRUE); - - - for(i=0;i<100;i++) { - hwnd = GetDlgItem(w_memory->hwnd, i + 100); - if(!hwnd)continue; - style = GetWindowLong(hwnd, GWL_STYLE); - style |= WS_DISABLED; - SetWindowLong(hwnd, GWL_STYLE, style); - } - w_memory->clear(); - InvalidateRect(w_memory->hwnd, 0, TRUE); - - is_debugger_activated = false; -} - -bSNES::bSNES() { - run_status = STOP; - debug_command = false; - - debugger_disable(); + return uiInput->get_status(device, button); } diff --git a/src/win/bsnes.h b/src/win/bsnes.h index 054246ca..c9cd3bdc 100644 --- a/src/win/bsnes.h +++ b/src/win/bsnes.h @@ -1,83 +1,30 @@ -class bJoypad { -public: -bool up, down, left, right; -bool a, b, x, y; -bool l, r, select, start; - bJoypad(); -}; - class bSNES : public SNES { -private: -//If true, indicates a ROM is loaded and debugger output -//is possible. -bool is_debugger_activated; - -//When true, notify() ignores read/write requests, as they -//have been generated by the debugging interface and not by -//the emulator. -bool debug_command; -uint32 run_status; - -bJoypad joypad1, joypad2; - public: +uint32 state; enum { - STOP = 0, - RUN, - RUNONCE, - RUNTOSIGNAL, - RUNTOFRAME, - RUNTOCPUSTEP, - RUNTOCPUPROCEED, - RUNTOCPUTRACE, - RUNTOAPUSTEP + RUN, PAUSE }; -enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; - void power(); - void reset(); + uint32 get_state(); + void set_state(uint32 new_state); - void run(); - void video_run(); - void sound_run(uint32 data); + void power(); + void reset(); - void set_status(uint32 new_status); - uint32 get_status(); + void scanline(); + + void run(); + void video_run(); + void sound_run(uint32 data); //video functions uint16 *video_lock(uint32 &pitch); void video_unlock(); //input functions - void clear_input(); - void poll_input(uint8 type); - bool get_input_status(uint8 device, uint8 button); + void poll_input(uint8 type); + bool get_input_status(uint8 device, uint8 button); -//debugging functions -struct { - uint32 cpu_stop_pos; - uint32 cpu_trace_pos, cpu_trace_stop; - bool cpu_ran, apu_ran; -}status; - uint8 read (uint8 type, uint32 addr); - void write(uint8 type, uint32 addr, uint8 value); - void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); - void disassemble_cpu_op(); - void disassemble_apu_op(); - void disassemble_bp_op(); - -//whether or not the user requests the debugger open - bool debugger_enabled(); - void debugger_enable(); - void debugger_disable(); - void debugger_update(); - -//whether or not the debugger *can* be used -//e.g., debugger cannot be used without ROM loaded - bool debugger_activated(); - void debugger_activate(); - void debugger_deactivate(); - - bSNES(); + bSNES() { state = RUN; } }; bSNES *bsnes; diff --git a/src/win/bsnes.lnk b/src/win/bsnes.lnk index b3f084ab280f782f4613c3931e819121152b727e..d997f525a5ee5a07ec7d8d316c1085cbcf1da412 100644 GIT binary patch delta 81 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 diff --git a/src/win/config.cpp b/src/win/config.cpp index 8d238f06..b63dd7bc 100644 --- a/src/win/config.cpp +++ b/src/win/config.cpp @@ -2,22 +2,34 @@ namespace config { struct System { static Setting regulate_speed; + static Setting speed_slowest, speed_slow, speed_normal, speed_fast, speed_fastest; } system; Setting System::regulate_speed(&config_file, "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", true, Setting::TRUE_FALSE); +Setting System::speed_slowest(&config_file, "system.speed_slowest", "Slowest speed setting (in hz)", 16000, Setting::DEC); +Setting System::speed_slow (&config_file, "system.speed_slow", "Slow speed setting", 24000, Setting::DEC); +Setting System::speed_normal (&config_file, "system.speed_normal", "Normal speed setting", 32000, Setting::DEC); +Setting System::speed_fast (&config_file, "system.speed_fast", "Fast speed setting", 48000, Setting::DEC); +Setting System::speed_fastest(&config_file, "system.speed_fastest", "Fastest speed setting", 64000, Setting::DEC); struct Video { - static Setting mode, use_vram, vblank; + static Setting mode; + static Setting mode0, mode1, mode2, mode3, mode4; + static Setting mode5, mode6, mode7, mode8, mode9; + static Setting use_vram, triple_buffering; } video; -Setting Video::mode(&config_file, "video.mode", - "Video mode\n" - " 0 = 256x224w\n" - " 1 = 512x448w\n" - " 2 = 960x720w\n" - " 3 = 640x480f\n" - " 4 = 1024x768f", - 1, Setting::DEC); +Setting Video::mode(&config_file, "video.mode", "Video mode at startup", 2, Setting::DEC); +Setting Video::mode0(&config_file, "video.mode_0", "Video mode 0 (windowed)", "256x223"); +Setting Video::mode1(&config_file, "video.mode_1", "Video mode 1 (windowed)", "512x446"); +Setting Video::mode2(&config_file, "video.mode_2", "Video mode 2 (windowed)", "640x480"); +Setting Video::mode3(&config_file, "video.mode_3", "Video mode 3 (windowed)", "960x720"); +Setting Video::mode4(&config_file, "video.mode_4", "Video mode 4 (windowed)", "1152x864"); +Setting Video::mode5(&config_file, "video.mode_5", "Video mode 5 (fullscreen)", "640x480@60:640x480"); +Setting Video::mode6(&config_file, "video.mode_6", "Video mode 6 (fullscreen)", "800x600@60:800x600"); +Setting Video::mode7(&config_file, "video.mode_7", "Video mode 7 (fullscreen)", "1024x768@60:1024x768"); +Setting Video::mode8(&config_file, "video.mode_8", "Video mode 8 (fullscreen)", "1280x960@60:1280x960"); +Setting Video::mode9(&config_file, "video.mode_9", "Video mode 9 (fullscreen)", "1600x1200@60:1600x1200"); Setting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", true, Setting::TRUE_FALSE); -Setting Video::vblank(&config_file, "video.vblank", "Wait for vertical retrace when updating screen", false, Setting::TRUE_FALSE); +Setting Video::triple_buffering(&config_file, "video.triple_buffering", "Use triple buffering", false, Setting::TRUE_FALSE); struct GUI { static Setting show_fps; @@ -26,35 +38,43 @@ Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window ti struct Input { struct Joypad1 { + static Setting allow_invalid_input; static Setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad1; struct Joypad2 { + static Setting allow_invalid_input; static Setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad2; } input; -Setting Input::Joypad1::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX); -Setting Input::Joypad1::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX); -Setting Input::Joypad1::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX); -Setting Input::Joypad1::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX); -Setting Input::Joypad1::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX); -Setting Input::Joypad1::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX); -Setting Input::Joypad1::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX); -Setting Input::Joypad1::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX); -Setting Input::Joypad1::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX); -Setting Input::Joypad1::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX); -Setting Input::Joypad1::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX); -Setting Input::Joypad1::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX); -Setting Input::Joypad2::up (&config_file, "input.joypad2.up", "Joypad2 up", 'T', Setting::HEX); -Setting Input::Joypad2::down (&config_file, "input.joypad2.down", "Joypad2 down", 'G', Setting::HEX); -Setting Input::Joypad2::left (&config_file, "input.joypad2.left", "Joypad2 left", 'F', Setting::HEX); -Setting Input::Joypad2::right (&config_file, "input.joypad2.right", "Joypad2 right", 'H', Setting::HEX); -Setting Input::Joypad2::a (&config_file, "input.joypad2.a", "Joypad2 A", 'K', Setting::HEX); -Setting Input::Joypad2::b (&config_file, "input.joypad2.b", "Joypad2 B", 'J', Setting::HEX); -Setting Input::Joypad2::x (&config_file, "input.joypad2.x", "Joypad2 X", 'I', Setting::HEX); -Setting Input::Joypad2::y (&config_file, "input.joypad2.y", "Joypad2 Y", 'U', Setting::HEX); -Setting Input::Joypad2::l (&config_file, "input.joypad2.l", "Joypad2 L", 'O', Setting::HEX); -Setting Input::Joypad2::r (&config_file, "input.joypad2.r", "Joypad2 R", 'L', Setting::HEX); -Setting Input::Joypad2::select(&config_file, "input.joypad2.select", "Joypad2 select", '[', Setting::HEX); -Setting Input::Joypad2::start (&config_file, "input.joypad2.start", "Joypad2 start", ']', Setting::HEX); +Setting Input::Joypad1::allow_invalid_input(&config_file, "input.joypad1.allow_invalid_input", + "Allow \"impossible\" key combinations for joypad 1 (not recommended)", false, Setting::TRUE_FALSE); +Setting Input::Joypad1::up (&config_file, "input.joypad1.up", "Joypad1 up", 0x80c8, Setting::HEX); +Setting Input::Joypad1::down (&config_file, "input.joypad1.down", "Joypad1 down", 0x81d0, Setting::HEX); +Setting Input::Joypad1::left (&config_file, "input.joypad1.left", "Joypad1 left", 0x82cb, Setting::HEX); +Setting Input::Joypad1::right (&config_file, "input.joypad1.right", "Joypad1 right", 0x83cd, Setting::HEX); +Setting Input::Joypad1::a (&config_file, "input.joypad1.a", "Joypad1 A", 0x042d, Setting::HEX); +Setting Input::Joypad1::b (&config_file, "input.joypad1.b", "Joypad1 B", 0x032c, Setting::HEX); +Setting Input::Joypad1::x (&config_file, "input.joypad1.x", "Joypad1 X", 0x011f, Setting::HEX); +Setting Input::Joypad1::y (&config_file, "input.joypad1.y", "Joypad1 Y", 0x001e, Setting::HEX); +Setting Input::Joypad1::l (&config_file, "input.joypad1.l", "Joypad1 L", 0x0620, Setting::HEX); +Setting Input::Joypad1::r (&config_file, "input.joypad1.r", "Joypad1 R", 0x072e, Setting::HEX); +Setting Input::Joypad1::select(&config_file, "input.joypad1.select", "Joypad1 select", 0x0836, Setting::HEX); +Setting Input::Joypad1::start (&config_file, "input.joypad1.start", "Joypad1 start", 0x091c, Setting::HEX); + +Setting Input::Joypad2::allow_invalid_input(&config_file, "input.joypad2.allow_invalid_input", + "Allow \"impossible\" key combinations for joypad 2 (not recommended)", false, Setting::TRUE_FALSE); +Setting Input::Joypad2::up (&config_file, "input.joypad2.up", "Joypad2 up", 0xff14, Setting::HEX); +Setting Input::Joypad2::down (&config_file, "input.joypad2.down", "Joypad2 down", 0xff22, Setting::HEX); +Setting Input::Joypad2::left (&config_file, "input.joypad2.left", "Joypad2 left", 0xff21, Setting::HEX); +Setting Input::Joypad2::right (&config_file, "input.joypad2.right", "Joypad2 right", 0xff23, Setting::HEX); +Setting Input::Joypad2::a (&config_file, "input.joypad2.a", "Joypad2 A", 0xff25, Setting::HEX); +Setting Input::Joypad2::b (&config_file, "input.joypad2.b", "Joypad2 B", 0xff24, Setting::HEX); +Setting Input::Joypad2::x (&config_file, "input.joypad2.x", "Joypad2 X", 0xff17, Setting::HEX); +Setting Input::Joypad2::y (&config_file, "input.joypad2.y", "Joypad2 Y", 0xff16, Setting::HEX); +Setting Input::Joypad2::l (&config_file, "input.joypad2.l", "Joypad2 L", 0xff18, Setting::HEX); +Setting Input::Joypad2::r (&config_file, "input.joypad2.r", "Joypad2 R", 0xff26, Setting::HEX); +Setting Input::Joypad2::select(&config_file, "input.joypad2.select", "Joypad2 select", 0xff1a, Setting::HEX); +Setting Input::Joypad2::start (&config_file, "input.joypad2.start", "Joypad2 start", 0xff1b, Setting::HEX); + }; diff --git a/src/win/input/dinput.cpp b/src/win/input/dinput.cpp new file mode 100644 index 00000000..43ddf966 --- /dev/null +++ b/src/win/input/dinput.cpp @@ -0,0 +1,222 @@ +void InputDI::poll_devices() { +HRESULT hr; +DIJOYSTATE2 js; + memset(keystate, 0, 256); + if(di_key) { + hr = di_key->GetDeviceState(256, keystate); + if(FAILED(hr)) { + di_key->Acquire(); + di_key->GetDeviceState(256, keystate); + } + } + + memset(joystate, 0, 256); + memset(js.rgbButtons, 0, 128); + if(di_joy) { + hr = di_joy->Poll(); + if(FAILED(hr)) { + di_joy->Acquire(); + di_joy->Poll(); + } + di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js); + memcpy(joystate, js.rgbButtons, 128); + + //map d-pad axes to joystate[128 - 131] + joystate[128] = (js.lY == 0x0000) ? 0x80 : 0x00; + joystate[129] = (js.lY == 0xffff) ? 0x80 : 0x00; + joystate[130] = (js.lX == 0x0000) ? 0x80 : 0x00; + joystate[131] = (js.lX == 0xffff) ? 0x80 : 0x00; + } +} + +uint32 InputDI::poll() { + if(GetForegroundWindow() != wInputCfg.hwnd)return 0xffff; + + poll_devices(); + for(int i = 0; i < 256; i++) { + if(joystate[i] & 0x80)return (i << 8) | 0xff; + } + for(int i = 0; i < 256; i++) { + if(keystate[i] & 0x80)return 0xff00 | (i); + } + + return 0xffff; +} + +void InputDI::poll(uint8 type) { +HWND fw = GetForegroundWindow(); + poll_devices(); + +#define poll_key(__key) \ + __key = 0; \ + if(fw == wMain.hwnd) { \ + if(di_joy && ((uint16)config::input.##__key## & 0xff00) != 0xff00) { \ + __key |= !!(joystate[(((uint16)config::input.##__key##) >> 8) & 0xff] & 0x80); \ + } \ + if(di_key && ((uint16)config::input.##__key## & 0x00ff) != 0x00ff) { \ + __key |= !!(keystate[((uint16)config::input.##__key##) & 0xff] & 0x80); \ + } \ + } + + switch(type) { + case SNES::DEV_JOYPAD1: + poll_key(joypad1.up); + poll_key(joypad1.down); + poll_key(joypad1.left); + poll_key(joypad1.right); + poll_key(joypad1.a); + poll_key(joypad1.b); + poll_key(joypad1.x); + poll_key(joypad1.y); + poll_key(joypad1.l); + poll_key(joypad1.r); + poll_key(joypad1.select); + poll_key(joypad1.start); + break; + case SNES::DEV_JOYPAD2: + poll_key(joypad2.up); + poll_key(joypad2.down); + poll_key(joypad2.left); + poll_key(joypad2.right); + poll_key(joypad2.a); + poll_key(joypad2.b); + poll_key(joypad2.x); + poll_key(joypad2.y); + poll_key(joypad2.l); + poll_key(joypad2.r); + poll_key(joypad2.select); + poll_key(joypad2.start); + break; + } +#undef poll_key +} + +bool InputDI::get_status(uint8 device, uint8 button) { + switch(device) { + case SNES::DEV_JOYPAD1: + switch(button) { + case SNES::JOYPAD_UP: return joypad1.up; + case SNES::JOYPAD_DOWN: return joypad1.down; + case SNES::JOYPAD_LEFT: return joypad1.left; + case SNES::JOYPAD_RIGHT: return joypad1.right; + case SNES::JOYPAD_A: return joypad1.a; + case SNES::JOYPAD_B: return joypad1.b; + case SNES::JOYPAD_X: return joypad1.x; + case SNES::JOYPAD_Y: return joypad1.y; + case SNES::JOYPAD_L: return joypad1.l; + case SNES::JOYPAD_R: return joypad1.r; + case SNES::JOYPAD_SELECT: return joypad1.select; + case SNES::JOYPAD_START: return joypad1.start; + } + break; + case SNES::DEV_JOYPAD2: + switch(button) { + case SNES::JOYPAD_UP: return joypad2.up; + case SNES::JOYPAD_DOWN: return joypad2.down; + case SNES::JOYPAD_LEFT: return joypad2.left; + case SNES::JOYPAD_RIGHT: return joypad2.right; + case SNES::JOYPAD_A: return joypad2.a; + case SNES::JOYPAD_B: return joypad2.b; + case SNES::JOYPAD_X: return joypad2.x; + case SNES::JOYPAD_Y: return joypad2.y; + case SNES::JOYPAD_L: return joypad2.l; + case SNES::JOYPAD_R: return joypad2.r; + case SNES::JOYPAD_SELECT: return joypad2.select; + case SNES::JOYPAD_START: return joypad2.start; + } + break; + } + + return false; +} + +void InputDI::set_status(uint8 device, uint8 button, bool status) { + switch(device) { + case SNES::DEV_JOYPAD1: + switch(button) { + case SNES::JOYPAD_UP: joypad1.up = status; break; + case SNES::JOYPAD_DOWN: joypad1.down = status; break; + case SNES::JOYPAD_LEFT: joypad1.left = status; break; + case SNES::JOYPAD_RIGHT: joypad1.right = status; break; + case SNES::JOYPAD_A: joypad1.a = status; break; + case SNES::JOYPAD_B: joypad1.b = status; break; + case SNES::JOYPAD_X: joypad1.x = status; break; + case SNES::JOYPAD_Y: joypad1.y = status; break; + case SNES::JOYPAD_L: joypad1.l = status; break; + case SNES::JOYPAD_R: joypad1.r = status; break; + case SNES::JOYPAD_SELECT: joypad1.select = status; break; + case SNES::JOYPAD_START: joypad1.start = status; break; + } + break; + case SNES::DEV_JOYPAD2: + switch(button) { + case SNES::JOYPAD_UP: joypad2.up = status; break; + case SNES::JOYPAD_DOWN: joypad2.down = status; break; + case SNES::JOYPAD_LEFT: joypad2.left = status; break; + case SNES::JOYPAD_RIGHT: joypad2.right = status; break; + case SNES::JOYPAD_A: joypad2.a = status; break; + case SNES::JOYPAD_B: joypad2.b = status; break; + case SNES::JOYPAD_X: joypad2.x = status; break; + case SNES::JOYPAD_Y: joypad2.y = status; break; + case SNES::JOYPAD_L: joypad2.l = status; break; + case SNES::JOYPAD_R: joypad2.r = status; break; + case SNES::JOYPAD_SELECT: joypad2.select = status; break; + case SNES::JOYPAD_START: joypad2.start = status; break; + } + break; + } +} + +void InputDI::clear_input() { + joypad1.up = joypad2.up = + joypad1.down = joypad2.down = + joypad1.left = joypad2.left = + joypad1.right = joypad2.right = + joypad1.a = joypad2.a = + joypad1.b = joypad2.b = + joypad1.x = joypad2.x = + joypad1.y = joypad2.y = + joypad1.l = joypad2.l = + joypad1.r = joypad2.r = + joypad1.select = joypad2.select = + joypad1.start = joypad2.start = false; +} + +BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *context) { + return static_cast(uiInput)->enum_joypads(instance); +} + +bool InputDI::enum_joypads(const DIDEVICEINSTANCE *instance) { +HRESULT hr; + hr = di->CreateDevice(instance->guidInstance, &di_joy, 0); + if(FAILED(hr))return DIENUM_CONTINUE; + return DIENUM_STOP; +} + +void InputDI::init() { +HRESULT hr; + term(); + clear_input(); + + DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, + IID_IDirectInput8, (void**)&di, 0); + di->CreateDevice(GUID_SysKeyboard, &di_key, 0); + + di_key->SetDataFormat(&c_dfDIKeyboard); + di_key->SetCooperativeLevel(wMain.hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + di_key->Acquire(); + + hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_EnumJoypadsCallback, + 0, DIEDFL_ATTACHEDONLY); + + if(!di_joy)return; + + di_joy->SetDataFormat(&c_dfDIJoystick2); + di_joy->SetCooperativeLevel(wMain.hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); +} + +void InputDI::term() { + if(di_key) { di_key->Unacquire(); di_key->Release(); di_key = 0; } + if(di_joy) { di_joy->Unacquire(); di_joy->Release(); di_joy = 0; } + if(di) { di->Release(); di = 0; } +} diff --git a/src/win/input/dinput.h b/src/win/input/dinput.h new file mode 100644 index 00000000..e02529bb --- /dev/null +++ b/src/win/input/dinput.h @@ -0,0 +1,34 @@ +#define DIRECTINPUT_VERSION 0x0800 +#include + +class InputDI : public Input { +public: +LPDIRECTINPUT8 di; +LPDIRECTINPUTDEVICE8 di_key, di_joy; +uint8 keystate[256], joystate[256]; + +struct joypad { + bool up, down, left, right; + bool a, b, x, y, l, r; + bool select, start; +} joypad1, joypad2; + + void poll_devices(); + uint32 poll(); + + void poll(uint8 type); + bool get_status(uint8 device, uint8 button); + void set_status(uint8 device, uint8 button, bool status); + void clear_input(); + bool enum_joypads(const DIDEVICEINSTANCE *instance); + void init(); + void term(); + + InputDI() { + di = 0; + di_key = 0; + di_joy = 0; + } + + ~InputDI() { term(); } +}; diff --git a/src/win/input/input.h b/src/win/input/input.h new file mode 100644 index 00000000..fc0d3cc4 --- /dev/null +++ b/src/win/input/input.h @@ -0,0 +1,14 @@ +class Input { +public: +//used by GUI for controller configuration + virtual uint32 poll() = 0; + + virtual void poll(uint8 type) = 0; + virtual bool get_status(uint8 device, uint8 button) = 0; + virtual void set_status(uint8 device, uint8 button, bool status) = 0; + virtual void clear_input() = 0; + virtual void init() = 0; + virtual void term() = 0; +}; + +#include "dinput.h" diff --git a/src/win/main.cpp b/src/win/main.cpp new file mode 100644 index 00000000..10104511 --- /dev/null +++ b/src/win/main.cpp @@ -0,0 +1,104 @@ +#define INTERFACE_MAIN + +#include "../base.h" +#include "main.h" + +#include "config.cpp" + +#include "bsnes.h" +#include "ui.h" + +#include "bsnes.cpp" +#include "ui.cpp" + +void *memalloc(uint32 size, char *name, ...) { + return (void*)malloc(size); +} + +void memfree(void *mem, char *name, ...) { + free(mem); +} + +void alert(char *s, ...) { +char str[4096]; +va_list args; + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + MessageBox(0, str, "bsnes", MB_OK); +} + +void dprintf(char *s, ...) { +char str[4096]; +va_list args; + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + fprintf(stdout, "%s\r\n", str); +} + +void init_snes() { +#ifdef POLYMORPHISM + deref(mem) = new bMemBus(); + deref(cpu) = new bCPU(); + deref(apu) = new bAPU(); + deref(dsp) = new bDSP(); + deref(ppu) = new bPPU(); +#endif + snes = new bSNES(); + bsnes = static_cast(snes); + + snes->init(); +} + +void term_snes() { + snes->term(); +#ifdef POLYMORPHISM + if(deref(mem)) { delete deref(mem); deref(mem) = 0; } + if(deref(cpu)) { delete deref(cpu); deref(cpu) = 0; } + if(deref(apu)) { delete deref(apu); deref(apu) = 0; } + if(deref(dsp)) { delete deref(dsp); deref(dsp) = 0; } + if(deref(ppu)) { delete deref(ppu); deref(ppu) = 0; } +#endif + if(snes) { delete(snes); snes = 0; } +} + +void get_config_fn(string &str) { +char *t = (char*)malloc(4096); + _getcwd(t, 4095); + strcpy(str, t); + free(t); + strcat(str, "\\bsnes.cfg"); +} + +int __stdcall WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { +MSG msg; +string cfg_fn; + get_config_fn(cfg_fn); + config_file.load(cfg_fn); + init_snes(); + init_ui(); + +int argc = __argc; +char **argv = __argv; + if(argc >= 2) { + if(cartridge.load(argv[1]) == true) { + snes->power(); + } + } + + while(1) { + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(msg.message == WM_QUIT)goto _end; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + bsnes->run(); + } + +_end: + term_snes(); + cartridge.unload(); + config_file.save(cfg_fn); + return 0; +} diff --git a/src/win/main.h b/src/win/main.h new file mode 100644 index 00000000..5b8c3d58 --- /dev/null +++ b/src/win/main.h @@ -0,0 +1,8 @@ +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 +#include +#include +#include "../lib/libbase.h" +#include "../lib/libvector.h" +#include "../lib/libstring.h" +#include "../lib/libconfig.h" diff --git a/src/win/rom.cpp b/src/win/rom.cpp deleted file mode 100644 index 7057affd..00000000 --- a/src/win/rom.cpp +++ /dev/null @@ -1,101 +0,0 @@ -class ROMImage { -private: -char rom_fn[4096]; -char sram_fn[4096]; -bool file_loaded; - -public: - bool loaded(); - bool load(); - void unload(); - void select(char *fn); - ROMImage(); - ~ROMImage(); -}; - -bool ROMImage::loaded() { - return file_loaded; -} - -bool ROMImage::load() { - if(file_loaded == true)return false; - - dprintf("* Loading \"%s\"...", rom_fn); - -FileReader *rf = new FileReader(); - if(!rf->open(rom_fn)) { - alert("Error loading image file [%s]!", rom_fn); - return false; - } - r_mem->load_cart(static_cast(rf)); - rf->close(); - -CartInfo ci; - r_mem->get_cartinfo(&ci); - if(ci.sram_size != 0) { - rf->open(sram_fn); - r_mem->load_sram(static_cast(rf)); - rf->close(); - } - - delete(rf); - - file_loaded = true; - bsnes->debugger_activate(); - return true; -} - -void ROMImage::unload() { - if(file_loaded == false)return; - -FileWriter *wf; -CartInfo ci; - r_mem->get_cartinfo(&ci); - if(ci.sram_size != 0) { - wf = new FileWriter(); - wf->open(sram_fn); - r_mem->save_sram(static_cast(wf)); - wf->close(); - delete(wf); - } - - file_loaded = false; - bsnes->debugger_deactivate(); - - r_mem->unload_cart(); -} - -void ROMImage::select(char *fn) { -int i; - if(file_loaded == true)return; - -/* Remove quotes */ - if(fn[0] == '\"') { - strcpy(rom_fn, fn + 1); - rom_fn[strlen(rom_fn) - 1] = 0; - } else { - strcpy(rom_fn, fn); - } - - for(i=strlen(rom_fn)-1;i>=0;i--) { - if(rom_fn[i] == '.')break; - } - - strcpy(sram_fn, rom_fn); - if(rom_fn[i] == '.')sram_fn[i] = 0; - strcat(sram_fn, ".srm"); -} - -ROMImage::ROMImage() { - *rom_fn = 0; - *sram_fn = 0; - file_loaded = false; -} - -ROMImage::~ROMImage() { - if(file_loaded == true) { - unload(); - } -} - -ROMImage *rom_image; diff --git a/src/win/snes_controller.bmp b/src/win/snes_controller.bmp deleted file mode 100644 index 90b6265e64ef4dad722b2c2450e9ea44e6d152f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240054 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 &2Nd{sy#HE<%Nk~SN>Y6q`)YUE1@ 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 - w_console->create(); - w_bp->create(); - w_memory->create(); - w_inputconfig->create(); - w_about->create(); -} - -void init_ui0() { - dd_renderer = new DDRenderer(); - ds_sound = new DSSound(); - w_main = new MainWindow(); - w_console = new Console(); - w_bp = new BreakpointEditor(); - w_memory = new MemoryEditor(); - w_inputconfig = new InputConfig(); - w_about = new AboutWindow(); -} - -void init_ui1() { - CreateFonts(); - CreateWindows(); - SetFocus(w_main->hwnd); - dd_renderer->set_window(w_main->hwnd); - dd_renderer->to_windowed(); - - w_main->show_menu(); - w_main->set_video_mode(config::video.mode); - w_main->set_frameskip(0); - - bsnes->debugger_deactivate(); + global::black_brush = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0)); + global::window_brush = (HBRUSH)COLOR_WINDOW; + + wMain.init("bsnes", BSNES_TITLE, + 256, 223, global::black_brush, Window::WF_CENTER); + wInputCfg.init("bsnes_inputcfg", "bsnes Input Configuration", + 355, 145, global::window_brush, Window::WF_CENTER | Window::WF_TOPMOST); + wPPUCfg.init("bsnes_ppucfg", "bsnes PPU Configuration", + 250, 165, global::window_brush, Window::WF_CENTER | Window::WF_TOPMOST); + + uiVideo = new VideoDD(); + uiAudio = new AudioDS(); + uiInput = new InputDI(); + uiVideo->init(); + uiAudio->init(); + uiInput->init(); + + wMain.setup(); + wInputCfg.setup(); + wPPUCfg.setup(); + + wMain.show(); } diff --git a/src/win/ui.h b/src/win/ui.h index 8ca69fc5..8ed0c439 100644 --- a/src/win/ui.h +++ b/src/win/ui.h @@ -1,281 +1,210 @@ -#define KeyDown(key) ((GetAsyncKeyState(key) & 0x8000)?1:0) +#define KeyDown(__key) ((GetAsyncKeyState(__key) & 0x8000) ? 1 : 0) -enum { - VIDEOMODE_256x224w = 0, - VIDEOMODE_512x448w, - VIDEOMODE_960x720w, - VIDEOMODE_640x480f, - VIDEOMODE_1024x768f +#include "video/video.h" +#include "audio/audio.h" +#include "input/input.h" +Video *uiVideo; +Audio *uiAudio; +Input *uiInput; + +namespace global { +HFONT vwf, fwf; +HBRUSH black_brush, window_brush; +bool cursor_visible = true; }; -enum { - MENU_FILE_LOAD = 100, - MENU_FILE_UNLOAD, - MENU_FILE_RESET, - MENU_FILE_POWER, - MENU_FILE_EXIT, - MENU_SETTINGS_REGULATE_SPEED, - MENU_SETTINGS_FRAMESKIP_OFF, - MENU_SETTINGS_FRAMESKIP_1, - MENU_SETTINGS_FRAMESKIP_2, - MENU_SETTINGS_FRAMESKIP_3, - MENU_SETTINGS_FRAMESKIP_4, - MENU_SETTINGS_FRAMESKIP_5, - MENU_SETTINGS_FRAMESKIP_6, - MENU_SETTINGS_FRAMESKIP_7, - MENU_SETTINGS_FRAMESKIP_8, - MENU_SETTINGS_FRAMESKIP_9, - MENU_SETTINGS_VIDEOMODE_256x224w, - MENU_SETTINGS_VIDEOMODE_512x448w, - MENU_SETTINGS_VIDEOMODE_960x720w, - MENU_SETTINGS_VIDEOMODE_640x480f, - MENU_SETTINGS_VIDEOMODE_1024x768f, - MENU_SETTINGS_COLORADJUST_COLORCURVE, - MENU_SETTINGS_COLORADJUST_NORMAL, - MENU_SETTINGS_COLORADJUST_GRAYSCALE, - MENU_SETTINGS_COLORADJUST_VGA, - MENU_SETTINGS_COLORADJUST_GENESIS, - MENU_SETTINGS_USEVRAM, - MENU_SETTINGS_VBLANK, - MENU_SETTINGS_SHOWFPS, - MENU_SETTINGS_MUTE, - MENU_SETTINGS_INPUTCFG_JOYPAD1, - MENU_SETTINGS_INPUTCFG_JOYPAD2, - MENU_SETTINGS_DEBUGGER, - MENU_MISC_SCREENSHOT, - MENU_MISC_LOGAUDIO, - MENU_MISC_ABOUT -}; - -enum { - CONSOLE_OUTPUT = 100, - CONSOLE_STATUS, - - CONSOLE_CPUGROUP, - CONSOLE_CPUSTEP, - CONSOLE_CPUPROCEED, - CONSOLE_CPUSKIP, - CONSOLE_CPUTRACENUM, - CONSOLE_CPUTRACE, - CONSOLE_CPUDISABLE, - - CONSOLE_APUGROUP, - CONSOLE_APUSTEP, - CONSOLE_APUPROCEED, - CONSOLE_APUSKIP, - CONSOLE_APUTRACENUM, - CONSOLE_APUTRACE, - CONSOLE_APUDISABLE, - - CONSOLE_SYSGROUP, - CONSOLE_SYSRUN, - CONSOLE_SYSRUNTOFRAME, - CONSOLE_SYSRUNTONMI, - - CONSOLE_CFGGROUP, - CONSOLE_CFGOUTPUTCPU, - CONSOLE_CFGOUTPUTAPU, - CONSOLE_CFGOUTPUTDBG, - CONSOLE_CFGTRACE, - CONSOLE_CFGREGTYPE, - CONSOLE_CFGREGNUM, - CONSOLE_CFGREGVAL, - CONSOLE_CFGREGSET, - CONSOLE_CFGLOCK, - CONSOLE_CFGLOCKUP, - CONSOLE_CFGLOCKDOWN, - CONSOLE_CFGLOCKLEFT, - CONSOLE_CFGLOCKRIGHT, - CONSOLE_CFGLOCKA, - CONSOLE_CFGLOCKB, - CONSOLE_CFGLOCKX, - CONSOLE_CFGLOCKY, - CONSOLE_CFGLOCKL, - CONSOLE_CFGLOCKR, - CONSOLE_CFGLOCKSELECT, - CONSOLE_CFGLOCKSTART -}; - -enum { - BREAKPOINT_LIST = 100, - BREAKPOINT_STATIC1, - BREAKPOINT_NUM, - BREAKPOINT_OFFSET, - BREAKPOINT_R, - BREAKPOINT_W, - BREAKPOINT_X, - BREAKPOINT_V, - BREAKPOINT_STATIC2, - BREAKPOINT_VALUE, - BREAKPOINT_SOURCE, - BREAKPOINT_SET, - BREAKPOINT_CLEAR, - BREAKPOINT_ENABLE, - BREAKPOINT_CLEARALL, - BREAKPOINT_EXPORT, - BREAKPOINT_IMPORT -}; - -enum { - MEMORYEDITOR_VIEW = 100, - - MEMORYEDITOR_MODE, - MEMORYEDITOR_GOTOADDR, - MEMORYEDITOR_GOTO, - - MEMORYEDITOR_STATIC1, - MEMORYEDITOR_OFFSET, - MEMORYEDITOR_VALUE, - MEMORYEDITOR_EDIT, - - MEMORYEDITOR_FSOURCE, - MEMORYEDITOR_FEXPORT, - - MEMORYEDITOR_UPDATE, - MEMORYEDITOR_AUTOUPDATE -}; - -enum { - ABOUT_STATIC = 100, - ABOUT_OK -}; +long __stdcall window_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); class Window { public: HWND hwnd; -HMENU hmenu; -bool fullscreen, visible, cursor_visible; -RECT workarea, wa_offset; -struct { int width, height; }window; - void resize(int width, int height); - void center(); - void show_menu(); - void hide_menu(); - void show(); - void hide(); +HMENU menu; - void to_left(HWND _hwnd = 0); - void to_center(); - void to_right(); - void to_top(); - void to_middle(); - void to_bottom(HWND _hwnd = 0); +enum { + IDTYPE_UNKNOWN, + IDTYPE_MENU, + IDTYPE_BUTTON +}; + +enum { + WF_NONE = 0, + WF_CENTER = 1, + WF_TOPMOST = 2, +}; + +struct { + bool visible; + bool fullscreen; + bool center; + int width, height; +} settings; + + virtual void hide(); + virtual void show(); + virtual bool visible(); + virtual void hide_menu(); + virtual void show_menu(); + virtual uint32 get_id_type(uint32 id); + virtual void check(uint32 id, bool state); + virtual bool checked(uint32 id); + virtual void check_toggle(uint32 id); + virtual void check(uint32 id); + virtual void uncheck(uint32 id); + virtual void resize(int width, int height, bool fullscreen = false); + virtual void center(); + + virtual void set_text(uint32 id, const char *text, ...); + virtual void control(const char *classname, const char *text, uint32 flags, int x, int y, int width, int height, uint32 id); + virtual long wndproc(UINT msg, WPARAM wparam, LPARAM lparam); + virtual void init(const char *classname, const char *title, int width, int height, HBRUSH hbr, uint32 flags = WF_NONE); Window(); }; +enum { +MENU_FILE = 100, + MENU_FILE_LOAD, + MENU_FILE_UNLOAD, + MENU_FILE_RESET, + MENU_FILE_POWER, + MENU_FILE_EXIT, + +MENU_SETTINGS, + MENU_SETTINGS_VIDEOMODE, + MENU_SETTINGS_VIDEOMODE_0, + MENU_SETTINGS_VIDEOMODE_1, + MENU_SETTINGS_VIDEOMODE_2, + MENU_SETTINGS_VIDEOMODE_3, + MENU_SETTINGS_VIDEOMODE_4, + //--- + MENU_SETTINGS_VIDEOMODE_5, + MENU_SETTINGS_VIDEOMODE_6, + MENU_SETTINGS_VIDEOMODE_7, + MENU_SETTINGS_VIDEOMODE_8, + MENU_SETTINGS_VIDEOMODE_9, + MENU_SETTINGS_FRAMESKIP, + MENU_SETTINGS_FRAMESKIP_0, + //--- + MENU_SETTINGS_FRAMESKIP_1, + MENU_SETTINGS_FRAMESKIP_2, + MENU_SETTINGS_FRAMESKIP_3, + MENU_SETTINGS_FRAMESKIP_4, + MENU_SETTINGS_FRAMESKIP_5, + MENU_SETTINGS_FRAMESKIP_6, + MENU_SETTINGS_FRAMESKIP_7, + MENU_SETTINGS_FRAMESKIP_8, + MENU_SETTINGS_FRAMESKIP_9, + MENU_SETTINGS_COLORADJUST, + MENU_SETTINGS_COLORADJUST_COLORCURVE, + MENU_SETTINGS_COLORADJUST_NORMAL, + MENU_SETTINGS_COLORADJUST_GRAYSCALE, + MENU_SETTINGS_COLORADJUST_VGA, + MENU_SETTINGS_COLORADJUST_GENESIS, + MENU_SETTINGS_VIDEO_OPTIONS, + MENU_SETTINGS_VIDEO_OPTIONS_USEVRAM, + MENU_SETTINGS_VIDEO_OPTIONS_TRIPLEBUF, + MENU_SETTINGS_VIDEO_OPTIONS_SHOWFPS, + //--- + MENU_SETTINGS_VIDEO_OPTIONS_PPUCFG, +//--- + MENU_SETTINGS_MUTE, +//--- + MENU_SETTINGS_INPUTCFG, + MENU_SETTINGS_INPUTCFG_JOYPAD1, + MENU_SETTINGS_INPUTCFG_JOYPAD2, +//--- + MENU_SETTINGS_SPEED_REGULATION, + MENU_SETTINGS_SPEED_REGULATION_ENABLE, + //--- + MENU_SETTINGS_SPEED_REGULATION_SLOWEST, + MENU_SETTINGS_SPEED_REGULATION_SLOW, + MENU_SETTINGS_SPEED_REGULATION_NORMAL, + MENU_SETTINGS_SPEED_REGULATION_FAST, + MENU_SETTINGS_SPEED_REGULATION_FASTEST, + +MENU_MISC, + MENU_MISC_SCREENSHOT, + MENU_MISC_LOGAUDIO, +//--- + MENU_MISC_ABOUT, +}; + class MainWindow : public Window { public: uint8 frameskip, frameskip_pos; -int width, height; - void create(); - void to_fullscreen(); - void to_windowed(); - void set_frameskip(uint8 fs); - void adjust_video_mode(bool fullscreen_mode); +uint8 regulation_speed; + +struct { + bool fullscreen; + uint32 x, y, refresh_rate; + uint32 width, height; +} vi; + void get_video_mode_info(uint8 mode); void set_video_mode(uint8 mode); + void set_frameskip(uint8 fs); + void set_regulation_speed(uint8 speed); void menu_load(); void menu_unload(); -}*w_main = 0; -#define CONSOLE_LINES 240 -class Console : public Window { -private: -bool _is_running; //used to swap "Run"/"Stop" text on console window + long wndproc(UINT msg, WPARAM wparam, LPARAM lparam); + void setup(); -public: -bool ctl_disabled[100]; -bool outputcpu, outputapu, outputdbg; + MainWindow() { frameskip = frameskip_pos = 0; } +} wMain; -enum { DEBUG_MESSAGE = 0, CPU_MESSAGE, APU_MESSAGE }; -enum { REGTYPE_CPU = 0, REGTYPE_APU }; -char output_line[CONSOLE_LINES][128]; -uint8 output_linecol[CONSOLE_LINES]; -char output_data[CONSOLE_LINES * 128]; -FILE *log_fp; -bool tracing_enabled; - -struct { - bool up, down, left, right; - bool a, b, x, y; - bool l, r, select, start; -}joypad_lock; - - void create(); - long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - void clear(); - void update_status(); - void set_reg_list_type(uint8 list_type); - bool can_write(uint32 message_type); - void write(char *s, uint32 message_type = DEBUG_MESSAGE); - void is_running(bool n); //API access to _is_running - - Console(); -}*w_console = 0; - -class BreakpointEditor : public Window { -public: -bool ctl_disabled[100]; - -enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; -enum { FLAG_NONE = 0, FLAG_R = 1, FLAG_W = 2, FLAG_X = 4, FLAG_V = 8 }; -struct { - uint32 addr; - uint8 value; - uint8 source; - uint8 flags; - uint32 hit_count; - bool set; -}bp[16]; -bool bp_enabled, bp_hit; -//indicates which breakpoint was hit, used to tell whether to disassemble -//a cpu or apu opcode once the emulation status is set to stop. -uint8 bp_hit_num; - void clear(); - void create(); - bool hit(); - void test(uint32 message, uint32 addr, uint32 value); - void refresh(); - - BreakpointEditor(); -}*w_bp = 0; - -class MemoryEditor : public Window { -public: -bool ctl_disabled[100]; - -enum { MODE_DRAM = 0, MODE_ROM, MODE_SRAM, MODE_SPCRAM, MODE_VRAM, MODE_OAM, MODE_CGRAM }; -uint32 edit_mode, edit_addr, edit_mask; -bool auto_update; //update memory window whenever visible value is written to when true - void clear(); - void create(); - uint8 read_byte(uint32 addr); - void write_byte(uint32 addr, uint8 value); - void refresh(uint32 type = null, uint32 addr = 0); - void export_data(uint32 type); -}*w_memory = 0; - -class InputConfig : public Window { -public: enum { - JOYPAD1 = 0, - JOYPAD2 + INPUTCFG_TEXT = 100, + INPUTCFG_UP, + INPUTCFG_DOWN, + INPUTCFG_LEFT, + INPUTCFG_RIGHT, + INPUTCFG_A, + INPUTCFG_B, + INPUTCFG_X, + INPUTCFG_Y, + INPUTCFG_L, + INPUTCFG_R, + INPUTCFG_SELECT, + INPUTCFG_START, + INPUTCFG_ALLOW_INVALID_INPUT, }; -enum { - ID_COMMAND = 100 -}; -int config_type, config_pos; -bool polling; - long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - void update_polling(int key); - void update_command(); - void set_text(char *str); - void begin_config(int type); - void create(); -}*w_inputconfig = 0; -class AboutWindow : public Window { +class InputCfgWindow : public Window { public: - long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - void create(); -}*w_about = 0; +bool update_active; +uint32 key_to_update, dev_to_update; + void timer_key_update(); + void begin_key_update(uint32 key); + void end_key_update(); + + void show(); + void hide(); + long wndproc(UINT msg, WPARAM wparam, LPARAM lparam); + void setup(); +} wInputCfg; + +enum { + PPUCFG_STATIC0, + PPUCFG_BG1_PRI0, + PPUCFG_BG1_PRI1, + PPUCFG_BG2_PRI0, + PPUCFG_BG2_PRI1, + PPUCFG_BG3_PRI0, + PPUCFG_BG3_PRI1, + PPUCFG_BG4_PRI0, + PPUCFG_BG4_PRI1, + PPUCFG_OAM_PRI0, + PPUCFG_OAM_PRI1, + PPUCFG_OAM_PRI2, + PPUCFG_OAM_PRI3, + + PPUCFG_STATIC1, + PPUCFG_HDMA_ENABLE, + PPUCFG_OPT_ENABLE, +}; + +class PPUCfgWindow : public Window { +public: + long wndproc(UINT msg, WPARAM wparam, LPARAM lparam); + void setup(); +} wPPUCfg; diff --git a/src/win/ui_inputcfg.cpp b/src/win/ui_inputcfg.cpp new file mode 100644 index 00000000..2be022d2 --- /dev/null +++ b/src/win/ui_inputcfg.cpp @@ -0,0 +1,219 @@ +void CALLBACK InputCfgTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time) { + wInputCfg.timer_key_update(); +} + +void InputCfgWindow::timer_key_update() { +uint32 r, n; + r = uiInput->poll(); + if(r == 0xffff)return; + + end_key_update(); + +//dev_to_update is set by ui_main.cpp + switch(dev_to_update) { + case SNES::DEV_JOYPAD1: + switch(key_to_update) { + case INPUTCFG_UP: n = config::input.joypad1.up; break; + case INPUTCFG_DOWN: n = config::input.joypad1.down; break; + case INPUTCFG_LEFT: n = config::input.joypad1.left; break; + case INPUTCFG_RIGHT: n = config::input.joypad1.right; break; + case INPUTCFG_A: n = config::input.joypad1.a; break; + case INPUTCFG_B: n = config::input.joypad1.b; break; + case INPUTCFG_X: n = config::input.joypad1.x; break; + case INPUTCFG_Y: n = config::input.joypad1.y; break; + case INPUTCFG_L: n = config::input.joypad1.l; break; + case INPUTCFG_R: n = config::input.joypad1.r; break; + case INPUTCFG_SELECT: n = config::input.joypad1.select; break; + case INPUTCFG_START: n = config::input.joypad1.start; break; + } + break; + case SNES::DEV_JOYPAD2: + switch(key_to_update) { + case INPUTCFG_UP: n = config::input.joypad2.up; break; + case INPUTCFG_DOWN: n = config::input.joypad2.down; break; + case INPUTCFG_LEFT: n = config::input.joypad2.left; break; + case INPUTCFG_RIGHT: n = config::input.joypad2.right; break; + case INPUTCFG_A: n = config::input.joypad2.a; break; + case INPUTCFG_B: n = config::input.joypad2.b; break; + case INPUTCFG_X: n = config::input.joypad2.x; break; + case INPUTCFG_Y: n = config::input.joypad2.y; break; + case INPUTCFG_L: n = config::input.joypad2.l; break; + case INPUTCFG_R: n = config::input.joypad2.r; break; + case INPUTCFG_SELECT: n = config::input.joypad2.select; break; + case INPUTCFG_START: n = config::input.joypad2.start; break; + } + break; + } + + if(r == 0xff01) { + //escape key was pressed, clear key + n = 0xffff; + } else if((r & 0xff00) != 0xff00) { + //joy update + n = (r & 0xff00) | (n & 0x00ff); + } else if((r & 0x00ff) != 0x00ff) { + //key update + n = (n & 0xff00) | (r & 0x00ff); + } + + switch(dev_to_update) { + case SNES::DEV_JOYPAD1: + switch(key_to_update) { + case INPUTCFG_UP: config::input.joypad1.up = n; break; + case INPUTCFG_DOWN: config::input.joypad1.down = n; break; + case INPUTCFG_LEFT: config::input.joypad1.left = n; break; + case INPUTCFG_RIGHT: config::input.joypad1.right = n; break; + case INPUTCFG_A: config::input.joypad1.a = n; break; + case INPUTCFG_B: config::input.joypad1.b = n; break; + case INPUTCFG_X: config::input.joypad1.x = n; break; + case INPUTCFG_Y: config::input.joypad1.y = n; break; + case INPUTCFG_L: config::input.joypad1.l = n; break; + case INPUTCFG_R: config::input.joypad1.r = n; break; + case INPUTCFG_SELECT: config::input.joypad1.select = n; break; + case INPUTCFG_START: config::input.joypad1.start = n; break; + } + break; + case SNES::DEV_JOYPAD2: + switch(key_to_update) { + case INPUTCFG_UP: config::input.joypad2.up = n; break; + case INPUTCFG_DOWN: config::input.joypad2.down = n; break; + case INPUTCFG_LEFT: config::input.joypad2.left = n; break; + case INPUTCFG_RIGHT: config::input.joypad2.right = n; break; + case INPUTCFG_A: config::input.joypad2.a = n; break; + case INPUTCFG_B: config::input.joypad2.b = n; break; + case INPUTCFG_X: config::input.joypad2.x = n; break; + case INPUTCFG_Y: config::input.joypad2.y = n; break; + case INPUTCFG_L: config::input.joypad2.l = n; break; + case INPUTCFG_R: config::input.joypad2.r = n; break; + case INPUTCFG_SELECT: config::input.joypad2.select = n; break; + case INPUTCFG_START: config::input.joypad2.start = n; break; + } + break; + } +} + +void InputCfgWindow::begin_key_update(uint32 key) { + if(update_active == true)return; + + update_active = true; + key_to_update = key; + +char t[512]; + strcpy(t, "Press button or key for '"); + switch(key) { + case INPUTCFG_UP: strcat(t, "Up"); break; + case INPUTCFG_DOWN: strcat(t, "Down"); break; + case INPUTCFG_LEFT: strcat(t, "Left"); break; + case INPUTCFG_RIGHT: strcat(t, "Right"); break; + case INPUTCFG_A: strcat(t, "A"); break; + case INPUTCFG_B: strcat(t, "B"); break; + case INPUTCFG_X: strcat(t, "X"); break; + case INPUTCFG_Y: strcat(t, "Y"); break; + case INPUTCFG_L: strcat(t, "L"); break; + case INPUTCFG_R: strcat(t, "R"); break; + case INPUTCFG_SELECT: strcat(t, "Select"); break; + case INPUTCFG_START: strcat(t, "Start"); break; + } + strcat(t, "', or press Esc to clear."); + set_text(INPUTCFG_TEXT, t); + + SetTimer(hwnd, 1000, 50, InputCfgTimerProc); +} + +void InputCfgWindow::end_key_update() { + update_active = false; + KillTimer(hwnd, 1000); + set_text(INPUTCFG_TEXT, "Key updated. Select button to update."); +} + +long InputCfgWindow::wndproc(UINT msg, WPARAM wparam, LPARAM lparam) { + switch(msg) { + case WM_COMMAND: + switch(LOWORD(wparam)) { + case INPUTCFG_UP: + case INPUTCFG_DOWN: + case INPUTCFG_LEFT: + case INPUTCFG_RIGHT: + case INPUTCFG_A: + case INPUTCFG_B: + case INPUTCFG_X: + case INPUTCFG_Y: + case INPUTCFG_L: + case INPUTCFG_R: + case INPUTCFG_SELECT: + case INPUTCFG_START: + begin_key_update(LOWORD(wparam)); + break; + case INPUTCFG_ALLOW_INVALID_INPUT: + switch(dev_to_update) { + case SNES::DEV_JOYPAD1: + config::input.joypad1.allow_invalid_input.toggle(); + check(INPUTCFG_ALLOW_INVALID_INPUT, (bool)config::input.joypad1.allow_invalid_input); + break; + case SNES::DEV_JOYPAD2: + config::input.joypad2.allow_invalid_input.toggle(); + check(INPUTCFG_ALLOW_INVALID_INPUT, (bool)config::input.joypad2.allow_invalid_input); + break; + } + break; + } + break; + case WM_CLOSE: + hide(); + return 0; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void InputCfgWindow::show() { + if(visible()) { + SetFocus(hwnd); + return; + } + + switch(dev_to_update) { + case SNES::DEV_JOYPAD1: + SetWindowText(hwnd, "bsnes Input Configuration (Joypad 1)"); + check(INPUTCFG_ALLOW_INVALID_INPUT, config::input.joypad1.allow_invalid_input); + break; + case SNES::DEV_JOYPAD2: + SetWindowText(hwnd, "bsnes Input Configuration (Joypad 2)"); + check(INPUTCFG_ALLOW_INVALID_INPUT, config::input.joypad2.allow_invalid_input); + break; + default: + return; + } + + update_active = false; + set_text(INPUTCFG_TEXT, "Select button to update."); + Window::show(); +} + +void InputCfgWindow::hide() { + Window::hide(); + end_key_update(); +} + +void InputCfgWindow::setup() { + control("STATIC", "", WS_VISIBLE, 5, 5, 335, 20, INPUTCFG_TEXT); + + control("BUTTON", "L", WS_VISIBLE, 105, 30, 40, 25, INPUTCFG_L); + control("BUTTON", "R", WS_VISIBLE, 210, 30, 40, 25, INPUTCFG_R); + + control("BUTTON", "Up", WS_VISIBLE, 45, 45, 40, 25, INPUTCFG_UP); + control("BUTTON", "Left", WS_VISIBLE, 5, 70, 40, 25, INPUTCFG_LEFT); + control("BUTTON", "Right", WS_VISIBLE, 85, 70, 40, 25, INPUTCFG_RIGHT); + control("BUTTON", "Down", WS_VISIBLE, 45, 95, 40, 25, INPUTCFG_DOWN); + + control("BUTTON", "Select", WS_VISIBLE, 135, 85, 40, 25, INPUTCFG_SELECT); + control("BUTTON", "Start", WS_VISIBLE, 180, 85, 40, 25, INPUTCFG_START); + + control("BUTTON", "X", WS_VISIBLE, 270, 45, 40, 25, INPUTCFG_X); + control("BUTTON", "Y", WS_VISIBLE, 230, 70, 40, 25, INPUTCFG_Y); + control("BUTTON", "A", WS_VISIBLE, 310, 70, 40, 25, INPUTCFG_A); + control("BUTTON", "B", WS_VISIBLE, 270, 95, 40, 25, INPUTCFG_B); + + control("BUTTON", "Allow invalid input (not recommended)", + WS_VISIBLE | BS_CHECKBOX, 5, 125, 335, 20, INPUTCFG_ALLOW_INVALID_INPUT); +} diff --git a/src/win/ui_main.cpp b/src/win/ui_main.cpp index 142aa3cd..7486931b 100644 --- a/src/win/ui_main.cpp +++ b/src/win/ui_main.cpp @@ -1,102 +1,88 @@ -void MainWindow::set_frameskip(uint8 fs) { - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_1, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_2, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_3, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_4, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_5, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_6, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_7, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_8, MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_9, MF_UNCHECKED); +void MainWindow::get_video_mode_info(uint8 mode) { +string t; + if(mode > 9)mode = 0; - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF + fs, MF_CHECKED); - w_main->frameskip = fs; - w_main->frameskip_pos = 0; -} - -void MainWindow::to_fullscreen() { - if(bsnes->debugger_enabled() == true) { - bsnes->debugger_disable(); - } - SetWindowLong(hwnd, GWL_STYLE, WS_POPUP); - SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, width, height, 0); - dd_renderer->to_fullscreen(width, height); - hide_menu(); -} - -void MainWindow::to_windowed() { - SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); - SetWindowLong(hwnd, GWL_EXSTYLE, 0); -//always call in case the video mode change is simply changing -//the backbuffer from a VRAM surface to a DRAM surface. - dd_renderer->to_windowed(); - if(dd_renderer->fullscreen == true) { - show_menu(); - } -} - -void MainWindow::adjust_video_mode(bool fullscreen_mode) { - fullscreen = fullscreen_mode; - - if(fullscreen == true) { - to_fullscreen(); - } else { - to_windowed(); + switch(mode) { + case 0: strcpy(t, config::video.mode0.sget()); break; + case 1: strcpy(t, config::video.mode1.sget()); break; + case 2: strcpy(t, config::video.mode2.sget()); break; + case 3: strcpy(t, config::video.mode3.sget()); break; + case 4: strcpy(t, config::video.mode4.sget()); break; + case 5: strcpy(t, config::video.mode5.sget()); break; + case 6: strcpy(t, config::video.mode6.sget()); break; + case 7: strcpy(t, config::video.mode7.sget()); break; + case 8: strcpy(t, config::video.mode8.sget()); break; + case 9: strcpy(t, config::video.mode9.sget()); break; } - resize(width, height); - if(fullscreen == false)center(); - - if(bsnes->debugger_enabled() == true) { - to_bottom(); - to_right(); - } + vi.fullscreen = (mode >= 5); +char *p = strptr(t); + vi.x = strdec(p); + while(*p && *p != 'x')p++; + vi.y = strdec(++p); + if(mode < 5)return; + while(*p && *p != '@')p++; + vi.refresh_rate = strdec(++p); + while(*p && *p != ':')p++; + vi.width = strdec(++p); + while(*p && *p != 'x')p++; + vi.height = strdec(++p); } void MainWindow::set_video_mode(uint8 mode) { - hide(); - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_256x224w, MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_512x448w, MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_960x720w, MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_640x480f, MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_1024x768f, MF_UNCHECKED); - switch(mode) { - case VIDEOMODE_256x224w: - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_256x224w, MF_CHECKED); - width = 256; - height = 223; - adjust_video_mode(false); - break; - case VIDEOMODE_512x448w: - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_512x448w, MF_CHECKED); - width = 512; - height = 446; - adjust_video_mode(false); - break; - case VIDEOMODE_960x720w: - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_960x720w, MF_CHECKED); - width = 960; - height = 720; - adjust_video_mode(false); - break; - case VIDEOMODE_640x480f: - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_640x480f, MF_CHECKED); - width = 640; - height = 480; - adjust_video_mode(true); - break; - case VIDEOMODE_1024x768f: - CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_1024x768f, MF_CHECKED); - width = 1024; - height = 768; - adjust_video_mode(true); - break; - } + if(mode > 9)mode = 0; + get_video_mode_info(mode); config::video.mode = mode; - show(); + + uiVideo->set_video_mode(vi.x, vi.y, vi.fullscreen, + vi.refresh_rate, vi.width, vi.height); + + check(MENU_SETTINGS_VIDEOMODE_0, mode == 0); + check(MENU_SETTINGS_VIDEOMODE_1, mode == 1); + check(MENU_SETTINGS_VIDEOMODE_2, mode == 2); + check(MENU_SETTINGS_VIDEOMODE_3, mode == 3); + check(MENU_SETTINGS_VIDEOMODE_4, mode == 4); + check(MENU_SETTINGS_VIDEOMODE_5, mode == 5); + check(MENU_SETTINGS_VIDEOMODE_6, mode == 6); + check(MENU_SETTINGS_VIDEOMODE_7, mode == 7); + check(MENU_SETTINGS_VIDEOMODE_8, mode == 8); + check(MENU_SETTINGS_VIDEOMODE_9, mode == 9); +} + +void MainWindow::set_frameskip(uint8 fs) { + frameskip = fs % 10; + frameskip_pos = 0; + + check(MENU_SETTINGS_FRAMESKIP_0, fs == 0); + check(MENU_SETTINGS_FRAMESKIP_1, fs == 1); + check(MENU_SETTINGS_FRAMESKIP_2, fs == 2); + check(MENU_SETTINGS_FRAMESKIP_3, fs == 3); + check(MENU_SETTINGS_FRAMESKIP_4, fs == 4); + check(MENU_SETTINGS_FRAMESKIP_5, fs == 5); + check(MENU_SETTINGS_FRAMESKIP_6, fs == 6); + check(MENU_SETTINGS_FRAMESKIP_7, fs == 7); + check(MENU_SETTINGS_FRAMESKIP_8, fs == 8); + check(MENU_SETTINGS_FRAMESKIP_9, fs == 9); +} + +void MainWindow::set_regulation_speed(uint8 speed) { + speed %= 5; + regulation_speed = speed; + + switch(speed) { + case 0: uiAudio->set_frequency(config::system.speed_slowest); break; + case 1: uiAudio->set_frequency(config::system.speed_slow); break; + case 2: uiAudio->set_frequency(config::system.speed_normal); break; + case 3: uiAudio->set_frequency(config::system.speed_fast); break; + case 4: uiAudio->set_frequency(config::system.speed_fastest); break; + } + + check(MENU_SETTINGS_SPEED_REGULATION_SLOWEST, speed == 0); + check(MENU_SETTINGS_SPEED_REGULATION_SLOW, speed == 1); + check(MENU_SETTINGS_SPEED_REGULATION_NORMAL, speed == 2); + check(MENU_SETTINGS_SPEED_REGULATION_FAST, speed == 3); + check(MENU_SETTINGS_SPEED_REGULATION_FASTEST, speed == 4); } void MainWindow::menu_load() { @@ -107,8 +93,22 @@ char t[4096]; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; - ofn.lpstrFilter = "SNES ROM Images (*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078)\0" - "*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078\0" + ofn.lpstrFilter = "SNES ROM Images (*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078" +#ifdef GZIP_SUPPORT + ";.gz;.z;.zip" +#endif +#ifdef JMA_SUPPORT + ";.jma" +#endif + ")\0" + "*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078" +#ifdef GZIP_SUPPORT + ";*.gz;*.z;*.zip" +#endif +#ifdef JMA_SUPPORT + ";*.jma" +#endif + "\0" "All Files (*.*)\0" "*.*\0"; ofn.lpstrFile = t; @@ -118,72 +118,101 @@ char t[4096]; if(!GetOpenFileName(&ofn))return; - rom_image->unload(); + cartridge.unload(); dprintf(""); - rom_image->select(t); - rom_image->load(); + cartridge.load(t); - snes->power(); - bsnes->debugger_update(); + bsnes->power(); } void MainWindow::menu_unload() { - rom_image->unload(); + cartridge.unload(); + uiVideo->clear_video(); + uiAudio->clear_audio(); + uiInput->clear_input(); + SetWindowText(hwnd, BSNES_TITLE); } -long __stdcall wndproc_main(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { -time_t timeout; -int i; +long MainWindow::wndproc(UINT msg, WPARAM wparam, LPARAM lparam) { switch(msg) { case WM_ENTERMENULOOP: - ds_sound->clear(); + uiAudio->clear_audio(); break; - case WM_EXITMENULOOP: - timeout = time(0); + case WM_EXITMENULOOP: { + time_t timeout = time(0); while(difftime(time(0), timeout) < 3) { if(!KeyDown(VK_RETURN))break; } - bsnes->clear_input(); + uiInput->clear_input(); break; + } case WM_KEYDOWN: if(wparam == VK_ESCAPE) { - if(GetMenu(w_main->hwnd) == NULL) { - w_main->show_menu(); + if(GetMenu(hwnd) == NULL) { + show_menu(); } else { - w_main->hide_menu(); + hide_menu(); } - w_main->center(); + center(); + } else if(wparam == VK_PAUSE) { + if(bsnes->get_state() == bSNES::RUN) { + uiAudio->clear_audio(); + bsnes->set_state(bSNES::PAUSE); + SetWindowText(hwnd, BSNES_TITLE " (Paused)"); + } else if(bsnes->get_state() == bSNES::PAUSE) { + bsnes->set_state(bSNES::RUN); + } + } else if(wparam >= '0' && wparam <= '9' && KeyDown(VK_CONTROL)) { + set_video_mode((wparam != '0') ? (wparam - '0' - 1) : 9); + } else if(wparam == VK_SUBTRACT || (wparam == VK_OEM_MINUS && !KeyDown(VK_CONTROL))) { + if(frameskip > 0)set_frameskip(frameskip - 1); + } else if(wparam == VK_ADD || (wparam == VK_OEM_PLUS && !KeyDown(VK_CONTROL))) { + if(frameskip < 9)set_frameskip(frameskip + 1); + } else if(wparam == VK_OEM_MINUS && KeyDown(VK_CONTROL)) { + if(regulation_speed > 0)set_regulation_speed(regulation_speed - 1); + } else if(wparam == VK_OEM_PLUS && KeyDown(VK_CONTROL)) { + if(regulation_speed < 4)set_regulation_speed(regulation_speed + 1); + } + break; + case WM_PAINT: + if(r_mem->cart_loaded() == true) { + uiVideo->redraw(); + } else { + uiVideo->clear_video(); } break; case WM_COMMAND: switch(LOWORD(wparam)) { case MENU_FILE_LOAD: - w_main->menu_load(); + menu_load(); break; case MENU_FILE_UNLOAD: - w_main->menu_unload(); + menu_unload(); break; case MENU_FILE_RESET: - if(rom_image->loaded() == false)break; - dprintf("* Soft Reset"); - snes->reset(); - bsnes->debugger_update(); + bsnes->reset(); + dprintf("* Reset"); break; case MENU_FILE_POWER: - if(rom_image->loaded() == false)break; - dprintf("* Power (Hard Reset)"); - snes->power(); - bsnes->debugger_update(); + bsnes->power(); + dprintf("* Power"); break; case MENU_FILE_EXIT: PostQuitMessage(0); break; - case MENU_SETTINGS_REGULATE_SPEED: - config::system.regulate_speed.toggle(); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_REGULATE_SPEED, - (config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED); + case MENU_SETTINGS_VIDEOMODE_0: + case MENU_SETTINGS_VIDEOMODE_1: + case MENU_SETTINGS_VIDEOMODE_2: + case MENU_SETTINGS_VIDEOMODE_3: + case MENU_SETTINGS_VIDEOMODE_4: + case MENU_SETTINGS_VIDEOMODE_5: + case MENU_SETTINGS_VIDEOMODE_6: + case MENU_SETTINGS_VIDEOMODE_7: + case MENU_SETTINGS_VIDEOMODE_8: + case MENU_SETTINGS_VIDEOMODE_9: + set_video_mode(LOWORD(wparam) - MENU_SETTINGS_VIDEOMODE_0); break; - case MENU_SETTINGS_FRAMESKIP_OFF: + case MENU_SETTINGS_FRAMESKIP_0: case MENU_SETTINGS_FRAMESKIP_1: case MENU_SETTINGS_FRAMESKIP_2: case MENU_SETTINGS_FRAMESKIP_3: @@ -193,201 +222,206 @@ int i; case MENU_SETTINGS_FRAMESKIP_7: case MENU_SETTINGS_FRAMESKIP_8: case MENU_SETTINGS_FRAMESKIP_9: - w_main->set_frameskip(LOWORD(wparam) - MENU_SETTINGS_FRAMESKIP_OFF); - break; - case MENU_SETTINGS_VIDEOMODE_256x224w: - w_main->set_video_mode(VIDEOMODE_256x224w); - break; - case MENU_SETTINGS_VIDEOMODE_512x448w: - w_main->set_video_mode(VIDEOMODE_512x448w); - break; - case MENU_SETTINGS_VIDEOMODE_960x720w: - w_main->set_video_mode(VIDEOMODE_960x720w); - break; - case MENU_SETTINGS_VIDEOMODE_640x480f: - w_main->set_video_mode(VIDEOMODE_640x480f); - break; - case MENU_SETTINGS_VIDEOMODE_1024x768f: - w_main->set_video_mode(VIDEOMODE_1024x768f); + set_frameskip(LOWORD(wparam) - MENU_SETTINGS_FRAMESKIP_0); break; case MENU_SETTINGS_COLORADJUST_COLORCURVE: config::snes.video_color_curve.toggle(); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, - (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); + check(MENU_SETTINGS_COLORADJUST_COLORCURVE, config::snes.video_color_curve); break; case MENU_SETTINGS_COLORADJUST_NORMAL: case MENU_SETTINGS_COLORADJUST_GRAYSCALE: case MENU_SETTINGS_COLORADJUST_VGA: - case MENU_SETTINGS_COLORADJUST_GENESIS: - i = LOWORD(wparam) - MENU_SETTINGS_COLORADJUST_NORMAL; + case MENU_SETTINGS_COLORADJUST_GENESIS: { + uint32 i = LOWORD(wparam) - MENU_SETTINGS_COLORADJUST_NORMAL; config::snes.video_color_adjust_mode = i; - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); + check(MENU_SETTINGS_COLORADJUST_NORMAL, i == 0); + check(MENU_SETTINGS_COLORADJUST_GRAYSCALE, i == 1); + check(MENU_SETTINGS_COLORADJUST_VGA, i == 2); + check(MENU_SETTINGS_COLORADJUST_GENESIS, i == 3); break; - case MENU_SETTINGS_USEVRAM: + } + case MENU_SETTINGS_VIDEO_OPTIONS_USEVRAM: config::video.use_vram.toggle(); - w_main->set_video_mode(config::video.mode); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); + set_video_mode(config::video.mode); + check(MENU_SETTINGS_VIDEO_OPTIONS_USEVRAM, config::video.use_vram); break; - case MENU_SETTINGS_VBLANK: - config::video.vblank.toggle(); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); + case MENU_SETTINGS_VIDEO_OPTIONS_TRIPLEBUF: + config::video.triple_buffering.toggle(); + check(MENU_SETTINGS_VIDEO_OPTIONS_TRIPLEBUF, config::video.triple_buffering); break; - case MENU_SETTINGS_SHOWFPS: + case MENU_SETTINGS_VIDEO_OPTIONS_SHOWFPS: config::gui.show_fps.toggle(); - SetWindowText(w_main->hwnd, BSNES_TITLE); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); + SetWindowText(hwnd, BSNES_TITLE); + check(MENU_SETTINGS_VIDEO_OPTIONS_SHOWFPS, config::gui.show_fps); + break; + case MENU_SETTINGS_VIDEO_OPTIONS_PPUCFG: + wPPUCfg.show(); break; case MENU_SETTINGS_MUTE: config::snes.mute.toggle(); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_MUTE, (config::snes.mute)?MF_CHECKED:MF_UNCHECKED); + check(MENU_SETTINGS_MUTE, config::snes.mute); break; case MENU_SETTINGS_INPUTCFG_JOYPAD1: - w_inputconfig->begin_config(InputConfig::JOYPAD1); + wInputCfg.dev_to_update = SNES::DEV_JOYPAD1; + wInputCfg.show(); break; case MENU_SETTINGS_INPUTCFG_JOYPAD2: - w_inputconfig->begin_config(InputConfig::JOYPAD2); + wInputCfg.dev_to_update = SNES::DEV_JOYPAD2; + wInputCfg.show(); break; - case MENU_SETTINGS_DEBUGGER: - if(bsnes->debugger_enabled() == true) { - bsnes->debugger_disable(); - } else { - bsnes->debugger_enable(); - w_main->set_video_mode(VIDEOMODE_256x224w); - } + case MENU_SETTINGS_SPEED_REGULATION_ENABLE: + config::system.regulate_speed.toggle(); + check(MENU_SETTINGS_SPEED_REGULATION_ENABLE, config::system.regulate_speed); + break; + case MENU_SETTINGS_SPEED_REGULATION_SLOWEST: + case MENU_SETTINGS_SPEED_REGULATION_SLOW: + case MENU_SETTINGS_SPEED_REGULATION_NORMAL: + case MENU_SETTINGS_SPEED_REGULATION_FAST: + case MENU_SETTINGS_SPEED_REGULATION_FASTEST: + set_regulation_speed(LOWORD(wparam) - MENU_SETTINGS_SPEED_REGULATION_SLOWEST); break; case MENU_MISC_SCREENSHOT: snes->capture_screenshot(); break; - case MENU_MISC_LOGAUDIO: - i = CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, 0); - if(i == MF_UNCHECKED) { - CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_CHECKED); + case MENU_MISC_LOGAUDIO: { + if(!checked(MENU_MISC_LOGAUDIO)) { + check(MENU_MISC_LOGAUDIO); snes->log_audio_enable(); } else { - CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_UNCHECKED); + uncheck(MENU_MISC_LOGAUDIO); snes->log_audio_disable(); } break; + } case MENU_MISC_ABOUT: - w_about->center(); - w_about->show(); - SetFocus(w_about->hwnd); + alert(BSNES_TITLE "\n" "Written by: byuu"); break; } break; case WM_DESTROY: PostQuitMessage(0); break; - case WM_PAINT: - dd_renderer->redraw(); - break; } + return DefWindowProc(hwnd, msg, wparam, lparam); } -void MainWindow::create() { -WNDCLASS wc; -int i; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(101)); - wc.hInstance = GetModuleHandle(0); - wc.lpfnWndProc = wndproc_main; - wc.lpszClassName = "bsnes"; - wc.lpszMenuName = 0; - wc.style = CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); +void MainWindow::setup() { +HMENU submenu, branchmenu; + menu = CreateMenu(); - hwnd = CreateWindowEx(0, "bsnes", BSNES_TITLE, - WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, - 0, 0, 512, 446, - 0, 0, GetModuleHandle(0), 0); + submenu = CreatePopupMenu(); + AppendMenu(submenu, MF_STRING, MENU_FILE_LOAD, "&Load ROM"); + AppendMenu(submenu, MF_STRING, MENU_FILE_UNLOAD, "&Unload ROM"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); + AppendMenu(submenu, MF_STRING, MENU_FILE_RESET, "&Reset"); + AppendMenu(submenu, MF_STRING, MENU_FILE_POWER, "&Power (Hard Reset)"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); + AppendMenu(submenu, MF_STRING, MENU_FILE_EXIT, "E&xit"); + AppendMenu(menu, MF_STRING | MF_POPUP, (uint)submenu, "&File"); -HMENU hsubmenu, hbranchmenu; - hmenu = CreateMenu(); + submenu = CreatePopupMenu(); - hsubmenu = CreatePopupMenu(); - AppendMenu(hsubmenu, MF_STRING, MENU_FILE_LOAD, "&Load ROM"); - AppendMenu(hsubmenu, MF_STRING, MENU_FILE_UNLOAD, "&Unload ROM"); - AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hsubmenu, MF_STRING, MENU_FILE_RESET, "&Reset"); - AppendMenu(hsubmenu, MF_STRING, MENU_FILE_POWER, "&Power (Hard Reset)"); - AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hsubmenu, MF_STRING, MENU_FILE_EXIT, "E&xit"); - AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&File"); + branchmenu = CreatePopupMenu(); +char v[512]; + for(int i = 0; i <= 4; i++) { + get_video_mode_info(i); + sprintf(v, "%d. %dx%d", i + 1, vi.x, vi.y); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_0 + i, v); + } + AppendMenu(branchmenu, MF_SEPARATOR, 0, ""); + for(int i = 5; i <= 9; i++) { + get_video_mode_info(i); + sprintf(v, "%d. %dx%d@%dhz <%dx%d>", (i + 1) % 10, vi.x, vi.y, vi.refresh_rate, vi.width, vi.height); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_0 + i, v); + } + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "&Video Mode"); - hsubmenu = CreatePopupMenu(); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_REGULATE_SPEED, "&Regulate Speed"); + branchmenu = CreatePopupMenu(); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_0, "Off"); + AppendMenu(branchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_1, "1"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_2, "2"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_3, "3"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_4, "4"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_5, "5"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_6, "6"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_7, "7"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_8, "8"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_9, "9"); + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "&Frameskip"); - hbranchmenu = CreatePopupMenu(); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_OFF, "Off"); - AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_1, "1"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_2, "2"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_3, "3"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_4, "4"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_5, "5"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_6, "6"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_7, "7"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_8, "8"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_9, "9"); - AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Frameskip"); + branchmenu = CreatePopupMenu(); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_COLORCURVE, "Use &Color Curve"); + AppendMenu(branchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_NORMAL, "Normal (RGB555)"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GRAYSCALE, "Grayscale Mode (L5)"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_VGA, "VGA Mode (RGB332)"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GENESIS, "Genesis Mode (RGB333)"); + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "&Color Adjust"); - hbranchmenu = CreatePopupMenu(); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_256x224w, "256x224 Windowed (16:15)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_512x448w, "512x448 Windowed (16:15)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_960x720w, "960x720 Windowed (4:3)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_640x480f, "640x480 Fullscreen (16:15)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_1024x768f, "1024x768 Fullscreen (4:3)"); - AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Video Mode"); + branchmenu = CreatePopupMenu(); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEO_OPTIONS_USEVRAM, "Use &Video Memory Surface"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEO_OPTIONS_TRIPLEBUF, "Use &Triple Buffering"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEO_OPTIONS_SHOWFPS, "&Show FPS"); + AppendMenu(branchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_VIDEO_OPTIONS_PPUCFG, "&PPU Configuration"); + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "&Video Options"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); + AppendMenu(submenu, MF_STRING, MENU_SETTINGS_MUTE, "&Mute Sound Output"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); - hbranchmenu = CreatePopupMenu(); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_COLORCURVE, "Use &Color Curve"); - AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_NORMAL, "Normal (RGB555)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GRAYSCALE, "Grayscale Mode (L5)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_VGA, "VGA Mode (RGB332)"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GENESIS, "Genesis Mode (RGB333)"); - AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Color Adjust"); + branchmenu = CreatePopupMenu(); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1"); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD2, "Joypad 2"); + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "Configure &Input Devices"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_USEVRAM, "Use &Video Memory Surface"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_VBLANK, "&Wait For Vertical Retrace"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_SHOWFPS, "&Show FPS"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_MUTE, "&Mute Sound Output"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); - hbranchmenu = CreatePopupMenu(); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD2, "Joypad 2"); - AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Configure Input Devices"); + branchmenu = CreatePopupMenu(); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_ENABLE, "&Enable"); + AppendMenu(branchmenu, MF_SEPARATOR, 0, ""); + sprintf(v, "&Slowest (%d%%, %dhz)", uint(100.0 * ((double)config::system.speed_slowest / 32000.0)), + (uint)config::system.speed_slowest); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_SLOWEST, v); + sprintf(v, "&Slow (%d%%, %dhz)", uint(100.0 * ((double)config::system.speed_slow / 32000.0)), + (uint)config::system.speed_slow); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_SLOW, v); + sprintf(v, "&Normal (%d%%, %dhz)", uint(100.0 * ((double)config::system.speed_normal / 32000.0)), + (uint)config::system.speed_normal); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_NORMAL, v); + sprintf(v, "&Fast (%d%%, %dhz)", uint(100.0 * ((double)config::system.speed_fast / 32000.0)), + (uint)config::system.speed_fast); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_FAST, v); + sprintf(v, "&Fastest (%d%%, %dhz)", uint(100.0 * ((double)config::system.speed_fastest / 32000.0)), + (uint)config::system.speed_fastest); + AppendMenu(branchmenu, MF_STRING, MENU_SETTINGS_SPEED_REGULATION_FASTEST, v); + AppendMenu(submenu, MF_STRING | MF_POPUP, (uint)branchmenu, "&Speed Regulation"); -#ifdef DEBUGGER - AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_DEBUGGER, "&Debugger"); -#endif + AppendMenu(menu, MF_STRING | MF_POPUP, (uint)submenu, "&Settings"); - AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Settings"); + submenu = CreatePopupMenu(); + AppendMenu(submenu, MF_STRING, MENU_MISC_SCREENSHOT, "&Capture Screenshot"); + AppendMenu(submenu, MF_STRING, MENU_MISC_LOGAUDIO, "&Log Audio Data"); + AppendMenu(submenu, MF_SEPARATOR, 0, ""); + AppendMenu(submenu, MF_STRING, MENU_MISC_ABOUT, "&About"); + AppendMenu(menu, MF_STRING | MF_POPUP, (uint)submenu, "&Misc"); - hsubmenu = CreatePopupMenu(); - AppendMenu(hsubmenu, MF_STRING, MENU_MISC_SCREENSHOT, "&Capture Screenshot"); - AppendMenu(hsubmenu, MF_STRING, MENU_MISC_LOGAUDIO, "&Log Audio Data"); - AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); - AppendMenu(hsubmenu, MF_STRING, MENU_MISC_ABOUT, "&About..."); - AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Misc"); + show_menu(); - CheckMenuItem(hmenu, MENU_SETTINGS_REGULATE_SPEED, (config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); - i = config::snes.video_color_adjust_mode; - CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_MUTE, (config::snes.mute) ?MF_CHECKED:MF_UNCHECKED); + check(MENU_SETTINGS_COLORADJUST_COLORCURVE, config::snes.video_color_curve); +int32 i = config::snes.video_color_adjust_mode; + check(MENU_SETTINGS_COLORADJUST_NORMAL, i == 0); + check(MENU_SETTINGS_COLORADJUST_GRAYSCALE, i == 1); + check(MENU_SETTINGS_COLORADJUST_VGA, i == 2); + check(MENU_SETTINGS_COLORADJUST_GENESIS, i == 3); + check(MENU_SETTINGS_VIDEO_OPTIONS_USEVRAM, config::video.use_vram); + check(MENU_SETTINGS_VIDEO_OPTIONS_TRIPLEBUF, config::video.triple_buffering); + check(MENU_SETTINGS_VIDEO_OPTIONS_SHOWFPS, config::gui.show_fps); + check(MENU_SETTINGS_MUTE, config::snes.mute); + check(MENU_SETTINGS_SPEED_REGULATION_ENABLE, config::system.regulate_speed); + + set_video_mode(config::video.mode); + set_frameskip(0); + set_regulation_speed(2); } diff --git a/src/win/ui_ppucfg.cpp b/src/win/ui_ppucfg.cpp new file mode 100644 index 00000000..28a13b87 --- /dev/null +++ b/src/win/ui_ppucfg.cpp @@ -0,0 +1,72 @@ +long PPUCfgWindow::wndproc(UINT msg, WPARAM wparam, LPARAM lparam) { + +#define toggle(id) \ + config::##id##_enable.toggle(); \ + check(LOWORD(wparam), config::##id##_enable) + + switch(msg) { + case WM_COMMAND: + switch(LOWORD(wparam)) { + case PPUCFG_BG1_PRI0: toggle(ppu.bg1_pri0); break; + case PPUCFG_BG1_PRI1: toggle(ppu.bg1_pri1); break; + case PPUCFG_BG2_PRI0: toggle(ppu.bg2_pri0); break; + case PPUCFG_BG2_PRI1: toggle(ppu.bg2_pri1); break; + case PPUCFG_BG3_PRI0: toggle(ppu.bg3_pri0); break; + case PPUCFG_BG3_PRI1: toggle(ppu.bg3_pri1); break; + case PPUCFG_BG4_PRI0: toggle(ppu.bg4_pri0); break; + case PPUCFG_BG4_PRI1: toggle(ppu.bg4_pri1); break; + case PPUCFG_OAM_PRI0: toggle(ppu.oam_pri0); break; + case PPUCFG_OAM_PRI1: toggle(ppu.oam_pri1); break; + case PPUCFG_OAM_PRI2: toggle(ppu.oam_pri2); break; + case PPUCFG_OAM_PRI3: toggle(ppu.oam_pri3); break; + case PPUCFG_HDMA_ENABLE: toggle(cpu.hdma); break; + case PPUCFG_OPT_ENABLE: toggle(ppu.opt); break; + } break; + case WM_CLOSE: + hide(); + return 0; + } + +#undef toggle + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void PPUCfgWindow::setup() { + control("STATIC", "BG / OAM display toggles:", WS_VISIBLE, 5, 5, 200, 15, PPUCFG_STATIC0); + + control("BUTTON", "BG1 Priority 0", BS_CHECKBOX | WS_VISIBLE, 5, 20, 95, 15, PPUCFG_BG1_PRI0); + control("BUTTON", "BG1 Priority 1", BS_CHECKBOX | WS_VISIBLE, 105, 20, 95, 15, PPUCFG_BG1_PRI1); + control("BUTTON", "BG2 Priority 0", BS_CHECKBOX | WS_VISIBLE, 5, 35, 95, 15, PPUCFG_BG2_PRI0); + control("BUTTON", "BG2 Priority 1", BS_CHECKBOX | WS_VISIBLE, 105, 35, 95, 15, PPUCFG_BG2_PRI1); + control("BUTTON", "BG3 Priority 0", BS_CHECKBOX | WS_VISIBLE, 5, 50, 95, 15, PPUCFG_BG3_PRI0); + control("BUTTON", "BG3 Priority 1", BS_CHECKBOX | WS_VISIBLE, 105, 50, 95, 15, PPUCFG_BG3_PRI1); + control("BUTTON", "BG4 Priority 0", BS_CHECKBOX | WS_VISIBLE, 5, 65, 95, 15, PPUCFG_BG4_PRI0); + control("BUTTON", "BG4 Priority 1", BS_CHECKBOX | WS_VISIBLE, 105, 65, 95, 15, PPUCFG_BG4_PRI1); + control("BUTTON", "OAM Priority 0", BS_CHECKBOX | WS_VISIBLE, 5, 80, 95, 15, PPUCFG_OAM_PRI0); + control("BUTTON", "OAM Priority 1", BS_CHECKBOX | WS_VISIBLE, 105, 80, 95, 15, PPUCFG_OAM_PRI1); + control("BUTTON", "OAM Priority 2", BS_CHECKBOX | WS_VISIBLE, 5, 95, 95, 15, PPUCFG_OAM_PRI2); + control("BUTTON", "OAM Priority 3", BS_CHECKBOX | WS_VISIBLE, 105, 95, 95, 15, PPUCFG_OAM_PRI3); + + control("STATIC", "Misc. video options:", WS_VISIBLE, 5, 115, 200, 15, PPUCFG_STATIC1); + control("BUTTON", "Enable HDMA (affects CPU)", + BS_CHECKBOX | WS_VISIBLE, 5, 130, 200, 15, PPUCFG_HDMA_ENABLE); + control("BUTTON", "Enable offset-per-tile mode effects", + BS_CHECKBOX | WS_VISIBLE, 5, 145, 200, 15, PPUCFG_OPT_ENABLE); + + check(PPUCFG_BG1_PRI0, config::ppu.bg1_pri0_enable); + check(PPUCFG_BG1_PRI1, config::ppu.bg1_pri1_enable); + check(PPUCFG_BG2_PRI0, config::ppu.bg2_pri0_enable); + check(PPUCFG_BG2_PRI1, config::ppu.bg2_pri1_enable); + check(PPUCFG_BG3_PRI0, config::ppu.bg3_pri0_enable); + check(PPUCFG_BG3_PRI1, config::ppu.bg3_pri1_enable); + check(PPUCFG_BG4_PRI0, config::ppu.bg4_pri0_enable); + check(PPUCFG_BG4_PRI1, config::ppu.bg4_pri1_enable); + check(PPUCFG_OAM_PRI0, config::ppu.oam_pri0_enable); + check(PPUCFG_OAM_PRI1, config::ppu.oam_pri1_enable); + check(PPUCFG_OAM_PRI2, config::ppu.oam_pri2_enable); + check(PPUCFG_OAM_PRI3, config::ppu.oam_pri3_enable); + + check(PPUCFG_HDMA_ENABLE, config::cpu.hdma_enable); + check(PPUCFG_OPT_ENABLE, config::ppu.opt_enable); +} diff --git a/src/win/ui_window.cpp b/src/win/ui_window.cpp index 14695fe2..c4cddb74 100644 --- a/src/win/ui_window.cpp +++ b/src/win/ui_window.cpp @@ -1,167 +1,216 @@ -void Window::resize(int width, int height) { -int style; - window.width = width; - window.height = height; +long __stdcall window_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + if(hwnd == wMain.hwnd) { return wMain.wndproc(msg, wparam, lparam); } + if(hwnd == wInputCfg.hwnd) { return wInputCfg.wndproc(msg, wparam, lparam); } + if(hwnd == wPPUCfg.hwnd) { return wPPUCfg.wndproc(msg, wparam, lparam); } - style = GetWindowLong(hwnd, GWL_STYLE); - if(style & WS_CAPTION) { - width += GetSystemMetrics(SM_CXFIXEDFRAME) << 1; - height += GetSystemMetrics(SM_CYFIXEDFRAME) << 1; - height += GetSystemMetrics(SM_CYCAPTION); - } + return DefWindowProc(hwnd, msg, wparam, lparam); +} - if(width == GetSystemMetrics(SM_CXSCREEN) && height == GetSystemMetrics(SM_CYSCREEN)) { - fullscreen = true; - } else { - fullscreen = false; - } +void Window::hide() { + ShowWindow(hwnd, SW_HIDE); + settings.visible = false; +} - if(fullscreen == false) { - if(GetMenu(hwnd) != 0) { - height += GetSystemMetrics(SM_CYMENU); +void Window::show() { + ShowWindow(hwnd, SW_NORMAL); + SetFocus(hwnd); + settings.visible = true; +} + +bool Window::visible() { + return settings.visible; +} + +void Window::hide_menu() { + if(GetMenu(hwnd) == NULL)return; + SetMenu(hwnd, NULL); + + if(settings.fullscreen == true) { + if(global::cursor_visible == true) { + ShowCursor(FALSE); + global::cursor_visible = false; } - SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE); - } else { - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, width, height, 0); + return; + } + + resize(settings.width, settings.height); +} + +void Window::show_menu() { + if(GetMenu(hwnd) != NULL)return; + SetMenu(hwnd, menu); + + if(global::cursor_visible == false) { + ShowCursor(TRUE); + global::cursor_visible = true; + } + + if(settings.fullscreen == true)return; + resize(settings.width, settings.height); +} + +//determine if id belongs to a menu, button, etc. +//used by check() routines +uint32 Window::get_id_type(uint32 id) { +HWND ctl = GetDlgItem(hwnd, id); + if(!ctl)return IDTYPE_MENU; + +char t[256]; + GetClassName(GetDlgItem(hwnd, id), t, 255); + if(strimatch(t, "button"))return IDTYPE_BUTTON; + + return IDTYPE_UNKNOWN; +} + +void Window::check(uint32 id, bool state) { + switch(get_id_type(id)) { + case IDTYPE_MENU: + CheckMenuItem(menu, id, (state) ? MF_CHECKED : MF_UNCHECKED); + break; + case IDTYPE_BUTTON: + SendDlgItemMessage(hwnd, id, BM_SETCHECK, (WPARAM)state, 0); + break; } } +void Window::check_toggle(uint32 id) { + switch(get_id_type(id)) { + case IDTYPE_MENU: + CheckMenuItem(menu, id, (checked(id)) ? MF_UNCHECKED : MF_CHECKED); + break; + case IDTYPE_BUTTON: + SendDlgItemMessage(hwnd, id, BM_SETCHECK, + (WPARAM)SendDlgItemMessage(hwnd, id, BM_GETCHECK, 0, 0), 0); + break; + } +} + +bool Window::checked(uint32 id) { + switch(get_id_type(id)) { + case IDTYPE_MENU: return CheckMenuItem(menu, id, 0); + case IDTYPE_BUTTON: return SendDlgItemMessage(hwnd, id, BM_GETCHECK, 0, 0); + } + return false; +} + +void Window::check(uint32 id) { + switch(get_id_type(id)) { + case IDTYPE_MENU: CheckMenuItem(menu, id, MF_CHECKED); break; + case IDTYPE_BUTTON: SendDlgItemMessage(hwnd, id, BM_SETCHECK, (WPARAM)TRUE, 0); break; + } +} + +void Window::uncheck(uint32 id) { + switch(get_id_type(id)) { + case IDTYPE_MENU: CheckMenuItem(menu, id, MF_UNCHECKED); break; + case IDTYPE_BUTTON: SendDlgItemMessage(hwnd, id, BM_SETCHECK, (WPARAM)FALSE, 0); break; + } +} + +void Window::resize(int width, int height, bool fullscreen) { +int style = GetWindowLong(hwnd, GWL_STYLE); +bool old_fs = settings.fullscreen; + settings.width = width; + settings.height = height; + settings.fullscreen = fullscreen; + + if(fullscreen == false) { + if(old_fs == true) { + style |= WS_CAPTION; + style |= WS_SYSMENU; + style |= WS_MINIMIZEBOX; + SetWindowLong(hwnd, GWL_STYLE, style); + show_menu(); + } + + //add window borders + menu to size + width += GetSystemMetrics(SM_CXFIXEDFRAME) << 1; + height += GetSystemMetrics(SM_CYFIXEDFRAME) << 1; + height += GetSystemMetrics(SM_CYCAPTION); + if(GetMenu(hwnd) != 0) { + height += GetSystemMetrics(SM_CYMENU); + } + } else { + if(old_fs == false) { + style &= ~WS_CAPTION; + style &= ~WS_SYSMENU; + style &= ~WS_MINIMIZEBOX; + SetWindowLong(hwnd, GWL_STYLE, style); + hide_menu(); + } + } + + if(fullscreen == false) { + SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE); + } else { + SetWindowPos(hwnd, 0, 0, 0, + GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_NOZORDER); + } + + if(settings.center == true)center(); +} + void Window::center() { + if(settings.fullscreen == true)return; + RECT rc; POINT p; - if(fullscreen == true) { - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, window.width, window.height, 0); - return; - } GetWindowRect(hwnd, &rc); p.x = p.y = 0; ClientToScreen(hwnd, &p); OffsetRect(&rc, p.x, p.y); int sw = GetSystemMetrics(SM_CXSCREEN), sh = GetSystemMetrics(SM_CYSCREEN); -int x = (sw - (rc.right - rc.left)) >> 1, - y = (sh - (rc.bottom - rc.top)) >> 1; +int x = (sw - (rc.right - rc.left)) / 2, + y = (sh - (rc.bottom - rc.top)) / 2; SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } -void Window::show_menu() { -RECT rc; -int width, height; - if(GetMenu(hwnd) != NULL)return; - - SetMenu(hwnd, hmenu); - if(cursor_visible == false) { - ShowCursor(TRUE); - cursor_visible = true; - } - if(fullscreen == true) { - return; - } - - resize(window.width, window.height); +void Window::set_text(uint32 id, const char *text, ...) { + SetDlgItemText(hwnd, id, text); } -void Window::hide_menu() { -RECT rc; -int width, height; - if(GetMenu(hwnd) == NULL)return; - - SetMenu(hwnd, NULL); - if(fullscreen == true) { - if(cursor_visible == true) { - ShowCursor(FALSE); - cursor_visible = false; - } - return; - } - - resize(window.width, window.height); +void Window::control(const char *classname, const char *text, uint32 flags, int x, int y, int width, int height, uint32 id) { + CreateWindow(classname, text, flags | WS_CHILD, x, y, width, height, hwnd, (HMENU)id, GetModuleHandle(0), 0); + SendDlgItemMessage(hwnd, id, WM_SETFONT, (WPARAM)global::vwf, 0); } -void Window::show() { -int style; - visible = true; - style = GetWindowLong(hwnd, GWL_STYLE); - if(style & WS_VISIBLE)return; - ShowWindow(hwnd, SW_NORMAL); +long Window::wndproc(UINT msg, WPARAM wparam, LPARAM lparam) { + return DefWindowProc(hwnd, msg, wparam, lparam); } -void Window::hide() { -int style; - visible = false; - style = GetWindowLong(hwnd, GWL_STYLE); - if(!(style & WS_VISIBLE))return; - ShowWindow(hwnd, SW_HIDE); -} +void Window::init(const char *classname, const char *title, int width, int height, HBRUSH hbr, uint32 flags) { +WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)hbr; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(100)); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = window_wndproc; + wc.lpszClassName = classname; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); -void Window::to_left(HWND _hwnd) { -RECT rc; -int offset = 0; - if(_hwnd) { - GetWindowRect(_hwnd, &rc); - offset = rc.right; - } else { - offset = wa_offset.left; - } - GetWindowRect(hwnd, &rc); - rc.left = offset; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); -} + hwnd = CreateWindowEx((flags & WF_TOPMOST) ? WS_EX_TOPMOST : 0, classname, title, + WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + 64, 64, width, height, + 0, 0, wc.hInstance, 0); -void Window::to_center() { -RECT rc; - GetWindowRect(hwnd, &rc); - rc.left = (GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); -} - -void Window::to_right() { -RECT rc; - GetWindowRect(hwnd, &rc); - rc.left = GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left); - rc.left -= wa_offset.right; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); -} - -void Window::to_top() { -RECT rc; - GetWindowRect(hwnd, &rc); - rc.top = wa_offset.top; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); -} - -void Window::to_middle() { -RECT rc; - GetWindowRect(hwnd, &rc); - rc.top = (GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); -} - -void Window::to_bottom(HWND _hwnd) { -RECT rc; -int offset = 0; - if(_hwnd) { - GetWindowRect(_hwnd, &rc); - offset = rc.bottom - rc.top; - } - GetWindowRect(hwnd, &rc); - rc.top = GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top) - offset; - rc.top -= wa_offset.bottom; - SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + settings.visible = false; + settings.width = width; + settings.height = height; + settings.center = !!(flags & WF_CENTER); + resize(width, height); } Window::Window() { - fullscreen = false; - visible = false; - cursor_visible = true; - hmenu = NULL; + hwnd = 0; + menu = 0; - SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea, 0); -int sw = GetSystemMetrics(SM_CXSCREEN), - sh = GetSystemMetrics(SM_CYSCREEN); - - wa_offset.left = workarea.left; - wa_offset.top = workarea.top; - wa_offset.right = sw - workarea.right; - wa_offset.bottom = sh - workarea.bottom; + settings.fullscreen = false; + settings.center = false; + settings.width = 320; + settings.height = 240; } diff --git a/src/win/video/ddraw.cpp b/src/win/video/ddraw.cpp new file mode 100644 index 00000000..622e47be --- /dev/null +++ b/src/win/video/ddraw.cpp @@ -0,0 +1,232 @@ +void VideoDD::create_surface(uint8 i) { +int depth; + lpdds->GetSurfaceDesc(&ddsd); + depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + if(depth == 15 || depth == 16 || (bool)config::video.use_vram == false) { + goto try_native_surface; + } + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + if((bool)config::video.use_vram == true) { + ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + } else { + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + } + + ddsd.dwWidth = 512 + 4; + ddsd.dwHeight = 480 + 4; + + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 16; + ddsd.ddpfPixelFormat.dwRBitMask = 0xf800; + ddsd.ddpfPixelFormat.dwGBitMask = 0x07e0; + ddsd.ddpfPixelFormat.dwBBitMask = 0x001f; + + if(lpdd7->CreateSurface(&ddsd, &surface[i].lpdds, 0) == DD_OK)goto clear_surface; + +try_native_surface: + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + if((bool)config::video.use_vram == true) { + ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + } else { + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + } + + ddsd.dwWidth = 512 + 4; + ddsd.dwHeight = 480 + 4; + + lpdd7->CreateSurface(&ddsd, &surface[i].lpdds, 0); + +clear_surface: + if(!surface[i].lpdds) { + alert("Error: Failed to create DirectDraw backbuffer, cannot continue"); + exit(0); + } + +DDBLTFX fx; + fx.dwSize = sizeof(DDBLTFX); + fx.dwFillColor = 0x00000000; + surface[i].lpdds->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + + surface[i].valid = false; +} + +void VideoDD::clear_video() { +DDBLTFX fx; + fx.dwSize = sizeof(DDBLTFX); + fx.dwFillColor = 0x00000000; + lpdds->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); +} + +void VideoDD::set_mode_windowed() { + settings.fullscreen = false; + DirectDrawCreate(0, &lpdd, 0); + lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7); + lpdd->Release(); + lpdd = 0; + lpdd7->SetCooperativeLevel(wMain.hwnd, DDSCL_NORMAL); +} + +void VideoDD::set_mode_fullscreen() { + settings.fullscreen = true; + DirectDrawCreate(0, &lpdd, 0); + lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7); + lpdd->Release(); + lpdd = 0; + + lpdd7->SetCooperativeLevel(wMain.hwnd, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE); + lpdd7->SetDisplayMode(settings.width, settings.height, 16, settings.refresh_rate, 0); +} + +void VideoDD::set_source_window() { +//skip first scanline +int i = (vi.height == 224) ? 1 : 2; + SetRect(&lpddrc, 0, i, vi.width, vi.height); +} + +void VideoDD::redraw() { +RECT rd; + if(settings.fullscreen == false) { + POINT p = { 0, 0 }; + ClientToScreen(wMain.hwnd, &p); + GetClientRect(wMain.hwnd, &rd); + OffsetRect(&rd, p.x, p.y); + } else { + int rx = (settings.width - settings.output_width) >> 1; + int ry = (settings.height - settings.output_height) >> 1; + SetRect(&rd, rx, ry, rx + settings.output_width, ry + settings.output_height); + } + +HRESULT hr = lpdds->Blt(&rd, surface[active_surface ^ 1].lpdds, &lpddrc, DDBLT_WAIT, 0); + if(hr == DDERR_SURFACELOST) { + lpdds->Restore(); + surface[0].lpdds->Restore(); + surface[1].lpdds->Restore(); + } + + surface[active_surface ^ 1].valid = false; +} + +uint16 *VideoDD::lock(uint32 &pitch) { + if(surface[active_surface].lpdds->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK)return 0; + pitch = ddsd.lPitch; + return (uint16*)ddsd.lpSurface; +} + +void VideoDD::unlock() { + surface[active_surface].lpdds->Unlock(0); +} + +/* + Run periodically to see if the screen needs to be refreshed due to entering + vblank period. This will only happen when triple buffering is enabled. +*/ +void VideoDD::scanline() { + if((bool)config::video.triple_buffering == false)return; + +#if 1 +uint32 current_scanline; + lpdd7->GetScanLine(¤t_scanline); + + if(current_scanline >= settings.screen_height && last_scanline < settings.screen_height) { + if(surface[active_surface ^ 1].valid == true) { + redraw(); + } + } + + last_scanline = current_scanline; +#else +BOOL status; +bool vblank_status; + lpdd7->GetVerticalBlankStatus(&status); + vblank_status = (status == TRUE); + + if(vblank_status == true && in_vblank == false) { + if(surface[active_surface ^ 1].valid == true) { + redraw(); + } + } + + in_vblank = vblank_status; +#endif +} + +void VideoDD::update() { + snes->get_video_info(&vi); + set_source_window(); + + surface[active_surface].valid = true; + active_surface ^= 1; + + if((bool)config::video.triple_buffering == false)redraw(); +} + +bool VideoDD::set_video_mode(int width, int height, bool fullscreen, +int refresh_rate, int output_width, int output_height) { + settings.width = width; + settings.height = height; + settings.output_width = output_width; + settings.output_height = output_height; + settings.fullscreen = fullscreen; + settings.refresh_rate = refresh_rate; + + term(); + + if(settings.fullscreen) { + set_mode_fullscreen(); + } else { + set_mode_windowed(); + } + + wMain.hide(); + wMain.resize(width, height, fullscreen); + wMain.show(); + + settings.screen_height = GetSystemMetrics(SM_CYSCREEN); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpdd7->CreateSurface(&ddsd, &lpdds, 0); + + lpdd7->CreateClipper(0, &lpddc, 0); + lpddc->SetHWnd(0, wMain.hwnd); + lpdds->SetClipper(lpddc); + + active_surface = 0; + last_scanline = 0; + in_vblank = false; + create_surface(0); + create_surface(1); + clear_video(); + + return true; +} + +void VideoDD::init() {} + +void VideoDD::term() { + if(lpddc) { lpddc->Release(); lpddc = 0; } + if(surface[0].lpdds) { surface[0].lpdds->Release(); surface[0].lpdds = 0; } + if(surface[1].lpdds) { surface[1].lpdds->Release(); surface[1].lpdds = 0; } + if(lpdds) { lpdds->Release(); lpdds = 0; } + if(lpdd7) { lpdd7->Release(); lpdd7 = 0; } + if(lpdd) { lpdd->Release(); lpdd = 0; } +} + +VideoDD::VideoDD() { + lpdd = 0; + lpdd7 = 0; + lpdds = 0; + surface[0].lpdds = 0; + surface[1].lpdds = 0; + lpddc = 0; +} diff --git a/src/win/video/ddraw.h b/src/win/video/ddraw.h new file mode 100644 index 00000000..9a523d03 --- /dev/null +++ b/src/win/video/ddraw.h @@ -0,0 +1,43 @@ +#include + +class VideoDD : public Video { +private: +LPDIRECTDRAW lpdd; +LPDIRECTDRAW7 lpdd7; +LPDIRECTDRAWSURFACE7 lpdds; +LPDIRECTDRAWCLIPPER lpddc; + +struct { + LPDIRECTDRAWSURFACE7 lpdds; + bool valid; +} surface[2]; +uint8 active_surface; +bool in_vblank; +uint32 last_scanline; + +DDSURFACEDESC2 ddsd; +DDSCAPS ddscaps; + +RECT lpddrc; +bool fullscreen; + +public: + uint16 *lock(uint32 &pitch); + void unlock(); + + void clear_video(); + bool set_video_mode(int width, int height, bool fullscreen, + int refresh_rate, int output_width, int output_height); + void init(); + void term(); + + void create_surface(uint8 i); + void set_mode_windowed(); + void set_mode_fullscreen(); + void set_source_window(); + void redraw(); + void scanline(); + void update(); + + VideoDD(); +}; diff --git a/src/win/video/video.h b/src/win/video/video.h new file mode 100644 index 00000000..1ff98091 --- /dev/null +++ b/src/win/video/video.h @@ -0,0 +1,24 @@ +class Video { +public: +SNES::video_info vi; +struct { + int width, height, refresh_rate; + int output_width, output_height; + int screen_height; + bool fullscreen; +} settings; + virtual uint16 *lock(uint32 &pitch) = 0; + virtual void unlock() = 0; + + virtual void clear_video() = 0; + virtual bool set_video_mode(int width, int height, bool fullscreen, + int refresh_rate, int output_width, int output_height) = 0; + + virtual void scanline() {} + virtual void redraw() = 0; + virtual void update() = 0; + virtual void init() = 0; + virtual void term() = 0; +}; + +#include "ddraw.h" diff --git a/src/win_legacy/Makefile b/src/win_legacy/Makefile new file mode 100644 index 00000000..d6b485f6 --- /dev/null +++ b/src/win_legacy/Makefile @@ -0,0 +1,123 @@ +CC = cl +CFLAGS = /nologo /O2 /wd4996 +OBJS = winmain.obj \ + libstring.obj libconfig.obj \ + reader.obj cart.obj \ + memory.obj bmemory.obj \ + cpu.obj bcpu.obj \ + apu.obj bapu.obj \ + bdsp.obj \ + ppu.obj bppu.obj \ + snes.obj \ + srtc.obj sdd1.obj c4.obj +# adler32.obj compress.obj crc32.obj deflate.obj gzio.obj inffast.obj \ +# inflate.obj inftrees.obj ioapi.obj trees.obj unzip.obj zip.obj zutil.obj \ +# jma.obj jcrc32.obj lzmadec.obj 7zlzma.obj iiostrm.obj inbyte.obj lzma.obj winout.obj +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dxguid.lib + +all: $(OBJS) + rc /r /fobsnes.res bsnes.rc + $(CC) /Febsnes.exe $(CFLAGS) $(OBJS) bsnes.res $(LIBS) +#/link /PGD:bsnes.pgd /LTCG:PGOPTIMIZE + +clean: + del *.obj + del *.pgd + del *.pgc + del *.ilk + del *.pdb + +###################### +### win32-specific ### +###################### +winmain.obj: *.cpp *.h ../config/* + $(CC) $(CFLAGS) /c winmain.cpp + +################# +### libraries ### +################# +libstring.obj: ../lib/*.cpp ../lib/*.h + $(CC) $(CFLAGS) /c ../lib/libstring.cpp +libconfig.obj: ../lib/*.cpp ../lib/*.h + $(CC) $(CFLAGS) /c ../lib/libconfig.cpp + +############## +### memory ### +############## +memory.obj: ../memory/memory.cpp ../memory/memory.h + $(CC) $(CFLAGS) /c ../memory/memory.cpp + +bmemory.obj: ../memory/bmemory/* + $(CC) $(CFLAGS) /c ../memory/bmemory/bmemory.cpp + +########### +### cpu ### +########### +cpu.obj: ../cpu/*.cpp ../cpu/*.h + $(CC) $(CFLAGS) /c ../cpu/cpu.cpp + +bcpu.obj: ../cpu/bcpu/* + $(CC) $(CFLAGS) /c ../cpu/bcpu/bcpu.cpp + +########### +### apu ### +########### +apu.obj: ../apu/* + $(CC) $(CFLAGS) /c ../apu/apu.cpp + +bapu.obj: ../apu/bapu/* + $(CC) $(CFLAGS) /c ../apu/bapu/bapu.cpp + +########### +### dsp ### +########### +bdsp.obj: ../dsp/bdsp/* + $(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp + +########### +### ppu ### +########### +ppu.obj: ../ppu/*.cpp ../ppu/*.h + $(CC) $(CFLAGS) /c ../ppu/ppu.cpp + +bppu.obj: ../ppu/bppu/* + $(CC) $(CFLAGS) /c ../ppu/bppu/bppu.cpp + +################# +### utilities ### +################# +reader.obj: ../reader/*.cpp ../reader/*.h + $(CC) $(CFLAGS) /c ../reader/reader.cpp + +cart.obj: ../cart/*.cpp ../cart/*.h + $(CC) $(CFLAGS) /c ../cart/cart.cpp + +############ +### snes ### +############ +snes.obj: ../snes/*.cpp ../snes/*.h + $(CC) $(CFLAGS) /c ../snes/snes.cpp + +##################### +### special chips ### +##################### +srtc.obj: ../chip/srtc/*.cpp ../chip/srtc/*.h + $(CC) $(CFLAGS) /c ../chip/srtc/srtc.cpp + +sdd1.obj: ../chip/sdd1/*.cpp ../chip/sdd1/*.h + $(CC) $(CFLAGS) /c ../chip/sdd1/sdd1.cpp + +c4.obj: ../chip/c4/*.cpp ../chip/c4/*.h + $(CC) $(CFLAGS) /c ../chip/c4/c4.cpp + +############ +### zlib ### +############ +adler32.obj: ../reader/zlib/*.c ../reader/zlib/*.h + $(CC) $(CFLAGS) /c ../reader/zlib/*.c + +########### +### jma ### +########### +jma.obj: ../reader/jma/*.cpp ../reader/jma/*.h + $(CC) $(CFLAGS) /c ../reader/jma/*.cpp diff --git a/src/win_legacy/bsnes.cpp b/src/win_legacy/bsnes.cpp new file mode 100644 index 00000000..cce37c24 --- /dev/null +++ b/src/win_legacy/bsnes.cpp @@ -0,0 +1,570 @@ +void bSNES::power() { + ds_sound->init(); + SNES::power(); +} + +void bSNES::reset() { + SNES::reset(); +} + +void bSNES::set_status(uint32 new_status) { +uint8 cpu_op; + run_status = new_status; + + switch(new_status) { + case STOP: + w_console->is_running(false); + w_console->update_status(); + w_memory->refresh(); + break; + case RUNTOCPUSTEP: + status.cpu_ran = false; + break; + case RUNTOCPUPROCEED: + cpu_op = r_mem->read(r_cpu->regs.pc.d); + + if(cpu_op == 0x10 || //bpl rel + cpu_op == 0x30 || //bmi rel + cpu_op == 0x50 || //bvc rel + cpu_op == 0x70 || //bvs rel + cpu_op == 0x90 || //bcc rel + cpu_op == 0xb0 || //bcs rel + cpu_op == 0xd0 || //bne rel + cpu_op == 0xf0 || //beq rel + cpu_op == 0x20 || //jsr addr + cpu_op == 0x22 || //jsl long + cpu_op == 0xfc //jsr (addr,x) + ) { + w_console->is_running(true); + status.cpu_stop_pos = (r_cpu->regs.pc.b << 16) | ((r_cpu->regs.pc.d + r_cpu->opcode_length()) & 0xffff); + } else { + status.cpu_ran = false; + run_status = RUNTOCPUSTEP; + } + break; + case RUNTOAPUSTEP: + status.apu_ran = false; + break; + } +} + +uint32 bSNES::get_status() { + return run_status; +} + +void bSNES::run() { + if(!r_mem->cart_loaded())return; + + switch(run_status) { + case RUN: + SNES::runtoframe(); + return; + case STOP: + break; + case RUNONCE: + SNES::run(); + set_status(STOP); + break; + case RUNTOSIGNAL: + SNES::run(); + if(w_bp->hit() == true) { + set_status(STOP); + disassemble_bp_op(); + } + break; + case RUNTOFRAME: + SNES::run(); + if(r_ppu->status.frame_executed == true) { + r_ppu->status.frame_executed = false; + set_status(STOP); + disassemble_apu_op(); + disassemble_cpu_op(); + } else if(w_bp->hit() == true) { + set_status(STOP); + disassemble_bp_op(); + } + return; + case RUNTOCPUSTEP: + SNES::run(); + if(status.cpu_ran == true) { + set_status(STOP); + } else if(w_bp->hit() == true) { + set_status(STOP); + disassemble_bp_op(); + } + break; + case RUNTOCPUPROCEED: + SNES::run(); + if(r_cpu->in_opcode() == false && status.cpu_stop_pos == r_cpu->regs.pc.d) { + set_status(STOP); + disassemble_cpu_op(); + } else if(w_bp->hit() == true) { + set_status(STOP); + disassemble_bp_op(); + } + break; + case RUNTOCPUTRACE: + SNES::run(); + if(status.cpu_trace_pos >= status.cpu_trace_stop) { + set_status(STOP); + disassemble_cpu_op(); + } + break; + case RUNTOAPUSTEP: + SNES::run(); + if(status.apu_ran == true || w_bp->hit() == true) { + set_status(STOP); + } + break; + } +} + +void bSNES::video_run() { + if(r_ppu->status.frames_updated) { + char s[512], t[512]; + r_ppu->status.frames_updated = false; + if((bool)config::gui.show_fps == true) { + sprintf(s, "%s : %d fps", BSNES_TITLE, r_ppu->status.frames_executed); + if(w_main->frameskip != 0) { + sprintf(t, " (%d frames)", r_ppu->status.frames_rendered); + strcat(s, t); + } + SetWindowText(w_main->hwnd, s); + } + } + + w_main->frameskip_pos++; + w_main->frameskip_pos %= (w_main->frameskip + 1); + if(r_ppu->renderer_enabled())dd_renderer->update(); + r_ppu->enable_renderer(w_main->frameskip_pos == 0); +} + +void bSNES::sound_run(uint32 data) { + ds_sound->run(data); +} + +/*********************** + *** Video functions *** + ***********************/ +uint16 *bSNES::video_lock(uint32 &pitch) { + return dd_renderer->lock(pitch); +} + +void bSNES::video_unlock() { + dd_renderer->unlock(); +} + +/*********************** + *** Input functions *** + ***********************/ +void bSNES::clear_input() { + joypad1.up = joypad2.up = + joypad1.down = joypad2.down = + joypad1.left = joypad2.left = + joypad1.right = joypad2.right = + joypad1.a = joypad2.a = + joypad1.b = joypad2.b = + joypad1.x = joypad2.x = + joypad1.y = joypad2.y = + joypad1.l = joypad2.l = + joypad1.r = joypad2.r = + joypad1.select = joypad2.select = + joypad1.start = joypad2.start = 0; +} + +void bSNES::poll_input(uint8 type) { +//only capture input when main window has focus + if(GetForegroundWindow() == w_main->hwnd) { + switch(type) { + case SNES::DEV_JOYPAD1: + joypad1.up = KeyDown(config::input.joypad1.up); + joypad1.down = KeyDown(config::input.joypad1.down); + joypad1.left = KeyDown(config::input.joypad1.left); + joypad1.right = KeyDown(config::input.joypad1.right); + joypad1.select = KeyDown(config::input.joypad1.select); + joypad1.start = KeyDown(config::input.joypad1.start); + joypad1.y = KeyDown(config::input.joypad1.y); + joypad1.b = KeyDown(config::input.joypad1.b); + joypad1.x = KeyDown(config::input.joypad1.x); + joypad1.a = KeyDown(config::input.joypad1.a); + joypad1.l = KeyDown(config::input.joypad1.l); + joypad1.r = KeyDown(config::input.joypad1.r); + break; + case SNES::DEV_JOYPAD2: + joypad2.up = KeyDown(config::input.joypad2.up); + joypad2.down = KeyDown(config::input.joypad2.down); + joypad2.left = KeyDown(config::input.joypad2.left); + joypad2.right = KeyDown(config::input.joypad2.right); + joypad2.select = KeyDown(config::input.joypad2.select); + joypad2.start = KeyDown(config::input.joypad2.start); + joypad2.y = KeyDown(config::input.joypad2.y); + joypad2.b = KeyDown(config::input.joypad2.b); + joypad2.x = KeyDown(config::input.joypad2.x); + joypad2.a = KeyDown(config::input.joypad2.a); + joypad2.l = KeyDown(config::input.joypad2.l); + joypad2.r = KeyDown(config::input.joypad2.r); + break; + } + } else { + switch(type) { + case SNES::DEV_JOYPAD1: + joypad1.up = joypad1.down = joypad1.left = joypad1.right = + joypad1.select = joypad1.start = + joypad1.y = joypad1.b = joypad1.x = joypad1.a = + joypad1.l = joypad1.r = 0; + break; + case SNES::DEV_JOYPAD2: + joypad2.up = joypad2.down = joypad2.left = joypad2.right = + joypad2.select = joypad2.start = + joypad2.y = joypad2.b = joypad2.x = joypad2.a = + joypad1.l = joypad2.r = 0; + break; + } + } + +//check for debugger-based key locks + if(is_debugger_enabled == true && type == SNES::DEV_JOYPAD1) { + if(w_console->joypad_lock.up )joypad1.up = true; + if(w_console->joypad_lock.down )joypad1.down = true; + if(w_console->joypad_lock.left )joypad1.left = true; + if(w_console->joypad_lock.right )joypad1.right = true; + if(w_console->joypad_lock.a )joypad1.a = true; + if(w_console->joypad_lock.b )joypad1.b = true; + if(w_console->joypad_lock.x )joypad1.x = true; + if(w_console->joypad_lock.y )joypad1.y = true; + if(w_console->joypad_lock.l )joypad1.l = true; + if(w_console->joypad_lock.r )joypad1.r = true; + if(w_console->joypad_lock.select)joypad1.select = true; + if(w_console->joypad_lock.start )joypad1.start = true; + } + +//it's impossible to hold both up+down, or left+right down +//at the same time on a directional pad; and besides, allowing +//this to happen causes glitches in many SNES games. + if(joypad1.up) joypad1.down = 0; + if(joypad1.left)joypad1.right = 0; + + if(joypad2.up) joypad2.down = 0; + if(joypad2.left)joypad2.right = 0; +} + +bool bSNES::get_input_status(uint8 device, uint8 button) { + switch(device) { + case DEV_JOYPAD1: + switch(button) { + case JOYPAD_UP: return joypad1.up; + case JOYPAD_DOWN: return joypad1.down; + case JOYPAD_LEFT: return joypad1.left; + case JOYPAD_RIGHT: return joypad1.right; + case JOYPAD_A: return joypad1.a; + case JOYPAD_B: return joypad1.b; + case JOYPAD_X: return joypad1.x; + case JOYPAD_Y: return joypad1.y; + case JOYPAD_L: return joypad1.l; + case JOYPAD_R: return joypad1.r; + case JOYPAD_SELECT:return joypad1.select; + case JOYPAD_START: return joypad1.start; + } + break; + case DEV_JOYPAD2: + switch(button) { + case JOYPAD_UP: return joypad2.up; + case JOYPAD_DOWN: return joypad2.down; + case JOYPAD_LEFT: return joypad2.left; + case JOYPAD_RIGHT: return joypad2.right; + case JOYPAD_A: return joypad2.a; + case JOYPAD_B: return joypad2.b; + case JOYPAD_X: return joypad2.x; + case JOYPAD_Y: return joypad2.y; + case JOYPAD_L: return joypad2.l; + case JOYPAD_R: return joypad2.r; + case JOYPAD_SELECT:return joypad2.select; + case JOYPAD_START: return joypad2.start; + } + break; + } + + return 0; +} + +bJoypad::bJoypad() { + up = down = left = right = false; + a = b = x = y = false; + l = r = select = start = false; +} + +/*************************** + *** Debugging functions *** + ***************************/ +uint8 bSNES::read(uint8 type, uint32 addr) { +uint32 bank, a; +uint8 r = 0x00; + debug_command = true; + switch(type) { + case DRAM: + addr &= 0xffffff; + bank = (addr >> 16) & 0xff; + a = addr & 0xffff; + if((bank >= 0x00 && bank <= 0x3f) || + (bank >= 0x80 && bank <= 0xbf)) { + //don't let the debugger poll MMIO ports + if(a >= 0x2000 && a <= 0x5fff) { + r = 0x00; + } else { + r = r_mem->read(addr); + } + } else { + r = r_mem->read(addr); + } + break; + case SPCRAM: + addr &= 0xffff; + r = r_apu->spcram_read(addr); + break; + case VRAM: + addr &= 0xffff; + r = r_ppu->vram_read(addr); + break; + case OAM: + addr &= 0x03ff; + r = r_ppu->oam_read(addr); + break; + case CGRAM: + addr &= 0x01ff; + r = r_ppu->cgram_read(addr); + break; + } + debug_command = false; + return r; +} + +void bSNES::write(uint8 type, uint32 addr, uint8 value) { + debug_command = true; + switch(type) { + case DRAM: + addr &= 0xffffff; + r_mem->cart->write_protect(false); + r_mem->write(addr, value); + r_mem->cart->write_protect(true); + break; + case SPCRAM: + addr &= 0xffff; + r_apu->spcram_write(addr, value); + break; + case VRAM: + addr &= 0xffff; + r_ppu->vram_write(addr, value); + break; + case OAM: + addr &= 0x03ff; + r_ppu->oam_write(addr, value); + break; + case CGRAM: + addr &= 0x01ff; + r_ppu->cgram_write(addr, value); + break; + } + debug_command = false; +} + +void bSNES::notify(uint32 message, uint32 param1, uint32 param2) { +//debugging messages + if(is_debugger_enabled == false)return; + + switch(message) { + case CPU_EXEC_OPCODE_BEGIN: + break; + case CPU_EXEC_OPCODE_END: + status.cpu_ran = true; + status.cpu_trace_pos++; + //test next opcode for breakpoint + w_bp->test(message, r_cpu->regs.pc.d, 0); + disassemble_cpu_op(); + break; + case APU_EXEC_OPCODE_BEGIN: + break; + case APU_EXEC_OPCODE_END: + status.apu_ran = true; + w_bp->test(message, r_apu->regs.pc, 0); + disassemble_apu_op(); + break; + case MEM_READ: + case SPCRAM_READ: + case VRAM_READ: + case OAM_READ: + case CGRAM_READ: + if(debug_command == false) { + w_bp->test(message, param1, param2); + } + break; + case MEM_WRITE: + case SPCRAM_WRITE: + case VRAM_WRITE: + case OAM_WRITE: + case CGRAM_WRITE: + if(debug_command == false) { + w_bp->test(message, param1, param2); + } + //this needs to be called after the breakpoint test, + //as it will access read() and clear debug_command + w_memory->refresh(message, param1); + break; + } +} + +void bSNES::disassemble_cpu_op() { +char t[512]; +//don't disassemble opcodes when no ROM is loaded + if(is_debugger_activated == false)return; + +//don't disassemble opcodes that won't be printed to console/traced to log anyway + if(!w_console->can_write(Console::CPU_MESSAGE) && !w_console->tracing_enabled)return; + + r_cpu->disassemble_opcode(t); + w_console->write(t, Console::CPU_MESSAGE); +} + +void bSNES::disassemble_apu_op() { +char t[512]; + if(is_debugger_activated == false)return; + + if(!w_console->can_write(Console::APU_MESSAGE) && !w_console->tracing_enabled)return; + + r_apu->disassemble_opcode(t); + w_console->write(t, Console::APU_MESSAGE); +} + +void bSNES::disassemble_bp_op() { + if(w_bp->bp[w_bp->bp_hit_num].source == SPCRAM) { + disassemble_apu_op(); + } else { + disassemble_cpu_op(); + } +} + +bool bSNES::debugger_enabled() { + return is_debugger_enabled; +} + +void bSNES::debugger_enable() { + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_DEBUGGER, MF_CHECKED); + set_status(bSNES::STOP); + + w_console->is_running(false); + w_console->update_status(); + w_console->show(); + + w_bp->refresh(); + w_bp->show(); + + w_memory->edit_mode = MemoryEditor::MODE_DRAM; + w_memory->edit_addr = 0x7e0000; + w_memory->edit_mask = 0xffffff; + SendDlgItemMessage(w_memory->hwnd, MEMORYEDITOR_MODE, CB_SETCURSEL, 0, 0); + w_memory->refresh(); + w_memory->show(); + + debugger_update(); + is_debugger_enabled = true; +} + +void bSNES::debugger_disable() { + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_DEBUGGER, MF_UNCHECKED); + w_console->hide(); + w_bp->hide(); + w_memory->hide(); + is_debugger_enabled = false; + set_status(bSNES::RUN); +} + +void bSNES::debugger_update() { + w_console->update_status(); + w_bp->refresh(); + w_memory->refresh(); + disassemble_apu_op(); + disassemble_cpu_op(); +} + +bool bSNES::debugger_activated() { + return is_debugger_activated; +} + +void bSNES::debugger_activate() { +HWND hwnd; +uint32 i, style; + for(i=0;i<100;i++) { + if(w_console->ctl_disabled[i] == true)continue; + hwnd = GetDlgItem(w_console->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + InvalidateRect(w_console->hwnd, 0, TRUE); + + for(i=0;i<100;i++) { + if(w_bp->ctl_disabled[i] == true)continue; + hwnd = GetDlgItem(w_bp->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + InvalidateRect(w_bp->hwnd, 0, TRUE); + + for(i=0;i<100;i++) { + if(w_memory->ctl_disabled[i] == true)continue; + hwnd = GetDlgItem(w_memory->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + InvalidateRect(w_memory->hwnd, 0, TRUE); + + is_debugger_activated = true; +} + +void bSNES::debugger_deactivate() { +HWND hwnd; +uint32 i, style; + for(i=0;i<100;i++) { + hwnd = GetDlgItem(w_console->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style |= WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + w_console->clear(); + InvalidateRect(w_console->hwnd, 0, TRUE); + + for(i=0;i<100;i++) { + hwnd = GetDlgItem(w_bp->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style |= WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + w_bp->clear(); + InvalidateRect(w_bp->hwnd, 0, TRUE); + + + for(i=0;i<100;i++) { + hwnd = GetDlgItem(w_memory->hwnd, i + 100); + if(!hwnd)continue; + style = GetWindowLong(hwnd, GWL_STYLE); + style |= WS_DISABLED; + SetWindowLong(hwnd, GWL_STYLE, style); + } + w_memory->clear(); + InvalidateRect(w_memory->hwnd, 0, TRUE); + + is_debugger_activated = false; +} + +bSNES::bSNES() { + run_status = STOP; + debug_command = false; + + debugger_disable(); +} diff --git a/src/win_legacy/bsnes.h b/src/win_legacy/bsnes.h new file mode 100644 index 00000000..054246ca --- /dev/null +++ b/src/win_legacy/bsnes.h @@ -0,0 +1,83 @@ +class bJoypad { +public: +bool up, down, left, right; +bool a, b, x, y; +bool l, r, select, start; + bJoypad(); +}; + +class bSNES : public SNES { +private: +//If true, indicates a ROM is loaded and debugger output +//is possible. +bool is_debugger_activated; + +//When true, notify() ignores read/write requests, as they +//have been generated by the debugging interface and not by +//the emulator. +bool debug_command; +uint32 run_status; + +bJoypad joypad1, joypad2; + +public: +enum { + STOP = 0, + RUN, + RUNONCE, + RUNTOSIGNAL, + RUNTOFRAME, + RUNTOCPUSTEP, + RUNTOCPUPROCEED, + RUNTOCPUTRACE, + RUNTOAPUSTEP +}; +enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; + void power(); + void reset(); + + void run(); + void video_run(); + void sound_run(uint32 data); + + void set_status(uint32 new_status); + uint32 get_status(); + +//video functions + uint16 *video_lock(uint32 &pitch); + void video_unlock(); + +//input functions + void clear_input(); + void poll_input(uint8 type); + bool get_input_status(uint8 device, uint8 button); + +//debugging functions +struct { + uint32 cpu_stop_pos; + uint32 cpu_trace_pos, cpu_trace_stop; + bool cpu_ran, apu_ran; +}status; + uint8 read (uint8 type, uint32 addr); + void write(uint8 type, uint32 addr, uint8 value); + void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); + void disassemble_cpu_op(); + void disassemble_apu_op(); + void disassemble_bp_op(); + +//whether or not the user requests the debugger open + bool debugger_enabled(); + void debugger_enable(); + void debugger_disable(); + void debugger_update(); + +//whether or not the debugger *can* be used +//e.g., debugger cannot be used without ROM loaded + bool debugger_activated(); + void debugger_activate(); + void debugger_deactivate(); + + bSNES(); +}; + +bSNES *bsnes; diff --git a/src/win_legacy/bsnes.ico b/src/win_legacy/bsnes.ico new file mode 100644 index 0000000000000000000000000000000000000000..a396add1a72bc149ec45f1cf5b39cd34a25bd022 GIT binary patch literal 4286 zcmeH}Pe@cz6o>DmIa1|IK*cf;+GiDl>7rK^g>-FbOZ<7({donjPT57$hK>YjthjzyNe)eeGEW@f1kIM>v}4@yr%7j_T1EF(_TZ=9eyD-NGne5{zWzEA^rpb?QR@1w zo4>G5@)i8PbL;TT1^j0mYtX#w>W^qA9EZKIF<+Am(K}t>OUkS|^7o!;LsM%gwFlb& z651KO3`QXh@8N#dU%zXTD(W@2hInS2XByEx0{VREbExxFzc@_55OhPM=Hqt|UW1*~ z7}tlm-3ssWVcT7_?a9{de#U15yz+UgxUPf7*atZY35Oa!ku$IN6Z*RDpCM>Mlf*C5 z#c!3|fMjr5X6SeNoZn06)?AjsRsZqwgHigFG{!9Md^&kwi;OI%kRy*2O@bd9Y~ zv9_O_ZFz0c{n!tepc_`?`$}J-wGz%l5{h}+c#52x&{|sAEd#B^a0Oia$H}{@W8Bo; zve0V-{eI1WovXjqeThL7+TB#$v(OB|VHk$O=TB>j=lXcZ{JjH@eef8bgU(rb0QaEm z^A4H`J`JFC^uY<(9hA2aTUddAWd-;fJ^M(7L}L8TL{0B+V&+41RmCGOA|ocuQy=D) uQpMlonr0K-z9}S$x{$=GD~g^7BhT)-p4& zFudOf5=$~>&=3KtC}PM5LZBqjcr&P;8AcK71Q?Qv^HPf$l%RS*VlX{P48;t245>g8 zq|1rHh=Bp<=w#zIZUu-gz0`_S1}&)0WaB1oOr3f_{S`pWz#sv{Za^#p#2`nul +@pause \ No newline at end of file diff --git a/src/win_legacy/clean.bat b/src/win_legacy/clean.bat new file mode 100644 index 00000000..e0f3d11c --- /dev/null +++ b/src/win_legacy/clean.bat @@ -0,0 +1 @@ +@nmake clean /NOLOGO diff --git a/src/win_legacy/config.cpp b/src/win_legacy/config.cpp new file mode 100644 index 00000000..8d238f06 --- /dev/null +++ b/src/win_legacy/config.cpp @@ -0,0 +1,60 @@ +namespace config { + +struct System { + static Setting regulate_speed; +} system; +Setting System::regulate_speed(&config_file, "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", true, Setting::TRUE_FALSE); + +struct Video { + static Setting mode, use_vram, vblank; +} video; +Setting Video::mode(&config_file, "video.mode", + "Video mode\n" + " 0 = 256x224w\n" + " 1 = 512x448w\n" + " 2 = 960x720w\n" + " 3 = 640x480f\n" + " 4 = 1024x768f", + 1, Setting::DEC); +Setting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", true, Setting::TRUE_FALSE); +Setting Video::vblank(&config_file, "video.vblank", "Wait for vertical retrace when updating screen", false, Setting::TRUE_FALSE); + +struct GUI { + static Setting show_fps; +} gui; +Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window title", true, Setting::TRUE_FALSE); + +struct Input { + struct Joypad1 { + static Setting up, down, left, right, a, b, x, y, l, r, select, start; + } joypad1; + struct Joypad2 { + static Setting up, down, left, right, a, b, x, y, l, r, select, start; + } joypad2; +} input; +Setting Input::Joypad1::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX); +Setting Input::Joypad1::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX); +Setting Input::Joypad1::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX); +Setting Input::Joypad1::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX); +Setting Input::Joypad1::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX); +Setting Input::Joypad1::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX); +Setting Input::Joypad1::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX); +Setting Input::Joypad1::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX); +Setting Input::Joypad1::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX); +Setting Input::Joypad1::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX); +Setting Input::Joypad1::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX); +Setting Input::Joypad1::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX); + +Setting Input::Joypad2::up (&config_file, "input.joypad2.up", "Joypad2 up", 'T', Setting::HEX); +Setting Input::Joypad2::down (&config_file, "input.joypad2.down", "Joypad2 down", 'G', Setting::HEX); +Setting Input::Joypad2::left (&config_file, "input.joypad2.left", "Joypad2 left", 'F', Setting::HEX); +Setting Input::Joypad2::right (&config_file, "input.joypad2.right", "Joypad2 right", 'H', Setting::HEX); +Setting Input::Joypad2::a (&config_file, "input.joypad2.a", "Joypad2 A", 'K', Setting::HEX); +Setting Input::Joypad2::b (&config_file, "input.joypad2.b", "Joypad2 B", 'J', Setting::HEX); +Setting Input::Joypad2::x (&config_file, "input.joypad2.x", "Joypad2 X", 'I', Setting::HEX); +Setting Input::Joypad2::y (&config_file, "input.joypad2.y", "Joypad2 Y", 'U', Setting::HEX); +Setting Input::Joypad2::l (&config_file, "input.joypad2.l", "Joypad2 L", 'O', Setting::HEX); +Setting Input::Joypad2::r (&config_file, "input.joypad2.r", "Joypad2 R", 'L', Setting::HEX); +Setting Input::Joypad2::select(&config_file, "input.joypad2.select", "Joypad2 select", '[', Setting::HEX); +Setting Input::Joypad2::start (&config_file, "input.joypad2.start", "Joypad2 start", ']', Setting::HEX); +}; diff --git a/src/win/dd_renderer.cpp b/src/win_legacy/dd_renderer.cpp similarity index 100% rename from src/win/dd_renderer.cpp rename to src/win_legacy/dd_renderer.cpp diff --git a/src/win/dd_renderer.h b/src/win_legacy/dd_renderer.h similarity index 100% rename from src/win/dd_renderer.h rename to src/win_legacy/dd_renderer.h diff --git a/src/win/ds_sound.cpp b/src/win_legacy/ds_sound.cpp similarity index 100% rename from src/win/ds_sound.cpp rename to src/win_legacy/ds_sound.cpp diff --git a/src/win/ds_sound.h b/src/win_legacy/ds_sound.h similarity index 70% rename from src/win/ds_sound.h rename to src/win_legacy/ds_sound.h index 4e2f9f5d..faaa3db2 100644 --- a/src/win/ds_sound.h +++ b/src/win_legacy/ds_sound.h @@ -15,6 +15,17 @@ public: void clear(); void init(); void term(); + + DSSound() { + ds = 0; + dsb_p = 0; + dsb_b = 0; + buffer_size = 0; + } + + ~DSSound() { + term(); + } }; struct { diff --git a/src/win/lib.cpp b/src/win_legacy/lib.cpp similarity index 100% rename from src/win/lib.cpp rename to src/win_legacy/lib.cpp diff --git a/src/win_legacy/ui.cpp b/src/win_legacy/ui.cpp new file mode 100644 index 00000000..281c4c98 --- /dev/null +++ b/src/win_legacy/ui.cpp @@ -0,0 +1,58 @@ +HFONT hFont, hMonofont; +HBRUSH hbr_backbrush; + +#include "dd_renderer.cpp" +#include "ds_sound.cpp" +#include "uictl_editex.cpp" +#include "ui_window.cpp" +#include "ui_main.cpp" +#include "ui_console.cpp" +#include "ui_bp.cpp" +#include "ui_memory.cpp" +#include "ui_inputconfig.cpp" +#include "ui_about.cpp" + +void CreateFonts() { +HDC hdc = GetDC(0); +long height = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC(0, hdc); + hFont = CreateFont(height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Tahoma"); + hMonofont = CreateFont(height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New"); +} + +void CreateWindows() { + hbr_backbrush = CreateSolidBrush(RGB(32, 32, 32)); + w_main->create(); + w_console->create(); + w_bp->create(); + w_memory->create(); + w_inputconfig->create(); + w_about->create(); +} + +void init_ui0() { + LoadLibrary("riched20.dll"); + + dd_renderer = new DDRenderer(); + ds_sound = new DSSound(); + w_main = new MainWindow(); + w_console = new Console(); + w_bp = new BreakpointEditor(); + w_memory = new MemoryEditor(); + w_inputconfig = new InputConfig(); + w_about = new AboutWindow(); +} + +void init_ui1() { + CreateFonts(); + CreateWindows(); + SetFocus(w_main->hwnd); + dd_renderer->set_window(w_main->hwnd); + dd_renderer->to_windowed(); + + w_main->show_menu(); + w_main->set_video_mode(config::video.mode); + w_main->set_frameskip(0); + + bsnes->debugger_deactivate(); +} diff --git a/src/win_legacy/ui.h b/src/win_legacy/ui.h new file mode 100644 index 00000000..8ca69fc5 --- /dev/null +++ b/src/win_legacy/ui.h @@ -0,0 +1,281 @@ +#define KeyDown(key) ((GetAsyncKeyState(key) & 0x8000)?1:0) + +enum { + VIDEOMODE_256x224w = 0, + VIDEOMODE_512x448w, + VIDEOMODE_960x720w, + VIDEOMODE_640x480f, + VIDEOMODE_1024x768f +}; + +enum { + MENU_FILE_LOAD = 100, + MENU_FILE_UNLOAD, + MENU_FILE_RESET, + MENU_FILE_POWER, + MENU_FILE_EXIT, + MENU_SETTINGS_REGULATE_SPEED, + MENU_SETTINGS_FRAMESKIP_OFF, + MENU_SETTINGS_FRAMESKIP_1, + MENU_SETTINGS_FRAMESKIP_2, + MENU_SETTINGS_FRAMESKIP_3, + MENU_SETTINGS_FRAMESKIP_4, + MENU_SETTINGS_FRAMESKIP_5, + MENU_SETTINGS_FRAMESKIP_6, + MENU_SETTINGS_FRAMESKIP_7, + MENU_SETTINGS_FRAMESKIP_8, + MENU_SETTINGS_FRAMESKIP_9, + MENU_SETTINGS_VIDEOMODE_256x224w, + MENU_SETTINGS_VIDEOMODE_512x448w, + MENU_SETTINGS_VIDEOMODE_960x720w, + MENU_SETTINGS_VIDEOMODE_640x480f, + MENU_SETTINGS_VIDEOMODE_1024x768f, + MENU_SETTINGS_COLORADJUST_COLORCURVE, + MENU_SETTINGS_COLORADJUST_NORMAL, + MENU_SETTINGS_COLORADJUST_GRAYSCALE, + MENU_SETTINGS_COLORADJUST_VGA, + MENU_SETTINGS_COLORADJUST_GENESIS, + MENU_SETTINGS_USEVRAM, + MENU_SETTINGS_VBLANK, + MENU_SETTINGS_SHOWFPS, + MENU_SETTINGS_MUTE, + MENU_SETTINGS_INPUTCFG_JOYPAD1, + MENU_SETTINGS_INPUTCFG_JOYPAD2, + MENU_SETTINGS_DEBUGGER, + MENU_MISC_SCREENSHOT, + MENU_MISC_LOGAUDIO, + MENU_MISC_ABOUT +}; + +enum { + CONSOLE_OUTPUT = 100, + CONSOLE_STATUS, + + CONSOLE_CPUGROUP, + CONSOLE_CPUSTEP, + CONSOLE_CPUPROCEED, + CONSOLE_CPUSKIP, + CONSOLE_CPUTRACENUM, + CONSOLE_CPUTRACE, + CONSOLE_CPUDISABLE, + + CONSOLE_APUGROUP, + CONSOLE_APUSTEP, + CONSOLE_APUPROCEED, + CONSOLE_APUSKIP, + CONSOLE_APUTRACENUM, + CONSOLE_APUTRACE, + CONSOLE_APUDISABLE, + + CONSOLE_SYSGROUP, + CONSOLE_SYSRUN, + CONSOLE_SYSRUNTOFRAME, + CONSOLE_SYSRUNTONMI, + + CONSOLE_CFGGROUP, + CONSOLE_CFGOUTPUTCPU, + CONSOLE_CFGOUTPUTAPU, + CONSOLE_CFGOUTPUTDBG, + CONSOLE_CFGTRACE, + CONSOLE_CFGREGTYPE, + CONSOLE_CFGREGNUM, + CONSOLE_CFGREGVAL, + CONSOLE_CFGREGSET, + CONSOLE_CFGLOCK, + CONSOLE_CFGLOCKUP, + CONSOLE_CFGLOCKDOWN, + CONSOLE_CFGLOCKLEFT, + CONSOLE_CFGLOCKRIGHT, + CONSOLE_CFGLOCKA, + CONSOLE_CFGLOCKB, + CONSOLE_CFGLOCKX, + CONSOLE_CFGLOCKY, + CONSOLE_CFGLOCKL, + CONSOLE_CFGLOCKR, + CONSOLE_CFGLOCKSELECT, + CONSOLE_CFGLOCKSTART +}; + +enum { + BREAKPOINT_LIST = 100, + BREAKPOINT_STATIC1, + BREAKPOINT_NUM, + BREAKPOINT_OFFSET, + BREAKPOINT_R, + BREAKPOINT_W, + BREAKPOINT_X, + BREAKPOINT_V, + BREAKPOINT_STATIC2, + BREAKPOINT_VALUE, + BREAKPOINT_SOURCE, + BREAKPOINT_SET, + BREAKPOINT_CLEAR, + BREAKPOINT_ENABLE, + BREAKPOINT_CLEARALL, + BREAKPOINT_EXPORT, + BREAKPOINT_IMPORT +}; + +enum { + MEMORYEDITOR_VIEW = 100, + + MEMORYEDITOR_MODE, + MEMORYEDITOR_GOTOADDR, + MEMORYEDITOR_GOTO, + + MEMORYEDITOR_STATIC1, + MEMORYEDITOR_OFFSET, + MEMORYEDITOR_VALUE, + MEMORYEDITOR_EDIT, + + MEMORYEDITOR_FSOURCE, + MEMORYEDITOR_FEXPORT, + + MEMORYEDITOR_UPDATE, + MEMORYEDITOR_AUTOUPDATE +}; + +enum { + ABOUT_STATIC = 100, + ABOUT_OK +}; + +class Window { +public: +HWND hwnd; +HMENU hmenu; +bool fullscreen, visible, cursor_visible; +RECT workarea, wa_offset; +struct { int width, height; }window; + void resize(int width, int height); + void center(); + void show_menu(); + void hide_menu(); + void show(); + void hide(); + + void to_left(HWND _hwnd = 0); + void to_center(); + void to_right(); + void to_top(); + void to_middle(); + void to_bottom(HWND _hwnd = 0); + + Window(); +}; + +class MainWindow : public Window { +public: +uint8 frameskip, frameskip_pos; +int width, height; + void create(); + void to_fullscreen(); + void to_windowed(); + void set_frameskip(uint8 fs); + void adjust_video_mode(bool fullscreen_mode); + void set_video_mode(uint8 mode); + void menu_load(); + void menu_unload(); +}*w_main = 0; + +#define CONSOLE_LINES 240 +class Console : public Window { +private: +bool _is_running; //used to swap "Run"/"Stop" text on console window + +public: +bool ctl_disabled[100]; +bool outputcpu, outputapu, outputdbg; + +enum { DEBUG_MESSAGE = 0, CPU_MESSAGE, APU_MESSAGE }; +enum { REGTYPE_CPU = 0, REGTYPE_APU }; +char output_line[CONSOLE_LINES][128]; +uint8 output_linecol[CONSOLE_LINES]; +char output_data[CONSOLE_LINES * 128]; +FILE *log_fp; +bool tracing_enabled; + +struct { + bool up, down, left, right; + bool a, b, x, y; + bool l, r, select, start; +}joypad_lock; + + void create(); + long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + void clear(); + void update_status(); + void set_reg_list_type(uint8 list_type); + bool can_write(uint32 message_type); + void write(char *s, uint32 message_type = DEBUG_MESSAGE); + void is_running(bool n); //API access to _is_running + + Console(); +}*w_console = 0; + +class BreakpointEditor : public Window { +public: +bool ctl_disabled[100]; + +enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; +enum { FLAG_NONE = 0, FLAG_R = 1, FLAG_W = 2, FLAG_X = 4, FLAG_V = 8 }; +struct { + uint32 addr; + uint8 value; + uint8 source; + uint8 flags; + uint32 hit_count; + bool set; +}bp[16]; +bool bp_enabled, bp_hit; +//indicates which breakpoint was hit, used to tell whether to disassemble +//a cpu or apu opcode once the emulation status is set to stop. +uint8 bp_hit_num; + void clear(); + void create(); + bool hit(); + void test(uint32 message, uint32 addr, uint32 value); + void refresh(); + + BreakpointEditor(); +}*w_bp = 0; + +class MemoryEditor : public Window { +public: +bool ctl_disabled[100]; + +enum { MODE_DRAM = 0, MODE_ROM, MODE_SRAM, MODE_SPCRAM, MODE_VRAM, MODE_OAM, MODE_CGRAM }; +uint32 edit_mode, edit_addr, edit_mask; +bool auto_update; //update memory window whenever visible value is written to when true + void clear(); + void create(); + uint8 read_byte(uint32 addr); + void write_byte(uint32 addr, uint8 value); + void refresh(uint32 type = null, uint32 addr = 0); + void export_data(uint32 type); +}*w_memory = 0; + +class InputConfig : public Window { +public: +enum { + JOYPAD1 = 0, + JOYPAD2 +}; +enum { + ID_COMMAND = 100 +}; +int config_type, config_pos; +bool polling; + long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + void update_polling(int key); + void update_command(); + void set_text(char *str); + void begin_config(int type); + void create(); +}*w_inputconfig = 0; + +class AboutWindow : public Window { +public: + long wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + void create(); +}*w_about = 0; diff --git a/src/win/ui_about.cpp b/src/win_legacy/ui_about.cpp similarity index 100% rename from src/win/ui_about.cpp rename to src/win_legacy/ui_about.cpp diff --git a/src/win/ui_bp.cpp b/src/win_legacy/ui_bp.cpp similarity index 97% rename from src/win/ui_bp.cpp rename to src/win_legacy/ui_bp.cpp index ed47955d..e2fb3049 100644 --- a/src/win/ui_bp.cpp +++ b/src/win_legacy/ui_bp.cpp @@ -167,7 +167,7 @@ void BreakpointEditor::clear() { void BreakpointEditor::refresh() { char s[256 * 16], t[256], source[256], v[16]; - if(rom_image->loaded() == false)return; + if(r_mem->cart_loaded() == false)return; if(visible == false)return; strcpy(s, ""); diff --git a/src/win/ui_console.cpp b/src/win_legacy/ui_console.cpp similarity index 97% rename from src/win/ui_console.cpp rename to src/win_legacy/ui_console.cpp index 09b01dd0..f9d98a8f 100644 --- a/src/win/ui_console.cpp +++ b/src/win_legacy/ui_console.cpp @@ -279,7 +279,7 @@ void Console::is_running(bool n) { void Console::update_status() { char s[4096], t[512]; static uint8 linecol[4] = { 1, 2, 3 }; - if(rom_image->loaded() == false)return; + if(r_mem->cart_loaded() == false)return; strcpy(s, ""); sprintf(t, "V:%3d H:%3d HC:%4d I:%d IF:%d O:%d", diff --git a/src/win/ui_inputconfig.cpp b/src/win_legacy/ui_inputconfig.cpp similarity index 95% rename from src/win/ui_inputconfig.cpp rename to src/win_legacy/ui_inputconfig.cpp index 05af8d69..97e11cc0 100644 --- a/src/win/ui_inputconfig.cpp +++ b/src/win_legacy/ui_inputconfig.cpp @@ -3,16 +3,18 @@ HDC hdc, hdcsrc; PAINTSTRUCT ps; HBITMAP hbm; switch(msg) { + /* case WM_PAINT: hdc = BeginPaint(hwnd, &ps); hdcsrc = CreateCompatibleDC(hdc); - hbm = LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(100)); + hbm = LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(101)); SelectObject(hdcsrc, hbm); BitBlt(hdc, 5, 25, 400, 200, hdcsrc, 0, 0, SRCCOPY); DeleteDC(hdcsrc); DeleteObject(hbm); EndPaint(hwnd, &ps); break; + */ case WM_KEYUP: update_polling((int)wparam); break; diff --git a/src/win_legacy/ui_main.cpp b/src/win_legacy/ui_main.cpp new file mode 100644 index 00000000..a6bab347 --- /dev/null +++ b/src/win_legacy/ui_main.cpp @@ -0,0 +1,408 @@ +void MainWindow::set_frameskip(uint8 fs) { + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_1, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_2, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_3, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_4, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_5, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_6, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_7, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_8, MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_9, MF_UNCHECKED); + + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_FRAMESKIP_OFF + fs, MF_CHECKED); + w_main->frameskip = fs; + w_main->frameskip_pos = 0; +} + +void MainWindow::to_fullscreen() { + if(bsnes->debugger_enabled() == true) { + bsnes->debugger_disable(); + } + SetWindowLong(hwnd, GWL_STYLE, WS_POPUP); + SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, width, height, 0); + dd_renderer->to_fullscreen(width, height); + hide_menu(); +} + +void MainWindow::to_windowed() { + SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); + SetWindowLong(hwnd, GWL_EXSTYLE, 0); +//always call in case the video mode change is simply changing +//the backbuffer from a VRAM surface to a DRAM surface. + dd_renderer->to_windowed(); + if(dd_renderer->fullscreen == true) { + show_menu(); + } +} + +void MainWindow::adjust_video_mode(bool fullscreen_mode) { + fullscreen = fullscreen_mode; + + if(fullscreen == true) { + to_fullscreen(); + } else { + to_windowed(); + } + + resize(width, height); + if(fullscreen == false)center(); + + if(bsnes->debugger_enabled() == true) { + to_bottom(); + to_right(); + } +} + +void MainWindow::set_video_mode(uint8 mode) { + hide(); + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_256x224w, MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_512x448w, MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_960x720w, MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_640x480f, MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_1024x768f, MF_UNCHECKED); + switch(mode) { + case VIDEOMODE_256x224w: + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_256x224w, MF_CHECKED); + width = 256; + height = 223; + adjust_video_mode(false); + break; + case VIDEOMODE_512x448w: + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_512x448w, MF_CHECKED); + width = 512; + height = 446; + adjust_video_mode(false); + break; + case VIDEOMODE_960x720w: + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_960x720w, MF_CHECKED); + width = 960; + height = 720; + adjust_video_mode(false); + break; + case VIDEOMODE_640x480f: + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_640x480f, MF_CHECKED); + width = 640; + height = 480; + adjust_video_mode(true); + break; + case VIDEOMODE_1024x768f: + CheckMenuItem(hmenu, MENU_SETTINGS_VIDEOMODE_1024x768f, MF_CHECKED); + width = 1024; + height = 768; + adjust_video_mode(true); + break; + } + + config::video.mode = mode; + show(); +} + +void MainWindow::menu_load() { +OPENFILENAME ofn; +char t[4096]; + strcpy(t, ""); + memset(&ofn, 0, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.lpstrFilter = "SNES ROM Images (*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078" +#ifdef GZIP_SUPPORT + ";.gz;.z;.zip" +#endif +#ifdef JMA_SUPPORT + ";.jma" +#endif + ")\0" + "*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078" +#ifdef GZIP_SUPPORT + ";*.gz;*.z;*.zip" +#endif +#ifdef JMA_SUPPORT + ";*.jma" +#endif + "\0" + "All Files (*.*)\0" + "*.*\0"; + ofn.lpstrFile = t; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST; + ofn.lpstrDefExt = "smc"; + + if(!GetOpenFileName(&ofn))return; + + cartridge.unload(); + bsnes->debugger_activate(); + dprintf(""); + cartridge.load(t); + + snes->power(); + bsnes->debugger_update(); +} + +void MainWindow::menu_unload() { + cartridge.unload(); + bsnes->debugger_deactivate(); +} + +long __stdcall wndproc_main(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +time_t timeout; +int i; + switch(msg) { + case WM_ENTERMENULOOP: + ds_sound->clear(); + break; + case WM_EXITMENULOOP: + timeout = time(0); + while(difftime(time(0), timeout) < 3) { + if(!KeyDown(VK_RETURN))break; + } + bsnes->clear_input(); + break; + case WM_KEYDOWN: + if(wparam == VK_ESCAPE) { + if(GetMenu(w_main->hwnd) == NULL) { + w_main->show_menu(); + } else { + w_main->hide_menu(); + } + w_main->center(); + } + break; + case WM_COMMAND: + switch(LOWORD(wparam)) { + case MENU_FILE_LOAD: + w_main->menu_load(); + break; + case MENU_FILE_UNLOAD: + w_main->menu_unload(); + break; + case MENU_FILE_RESET: + if(r_mem->cart_loaded() == false)break; + dprintf("* Soft Reset"); + snes->reset(); + bsnes->debugger_update(); + break; + case MENU_FILE_POWER: + if(r_mem->cart_loaded() == false)break; + dprintf("* Power (Hard Reset)"); + snes->power(); + bsnes->debugger_update(); + break; + case MENU_FILE_EXIT: + PostQuitMessage(0); + break; + case MENU_SETTINGS_REGULATE_SPEED: + config::system.regulate_speed.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_REGULATE_SPEED, + (config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_FRAMESKIP_OFF: + case MENU_SETTINGS_FRAMESKIP_1: + case MENU_SETTINGS_FRAMESKIP_2: + case MENU_SETTINGS_FRAMESKIP_3: + case MENU_SETTINGS_FRAMESKIP_4: + case MENU_SETTINGS_FRAMESKIP_5: + case MENU_SETTINGS_FRAMESKIP_6: + case MENU_SETTINGS_FRAMESKIP_7: + case MENU_SETTINGS_FRAMESKIP_8: + case MENU_SETTINGS_FRAMESKIP_9: + w_main->set_frameskip(LOWORD(wparam) - MENU_SETTINGS_FRAMESKIP_OFF); + break; + case MENU_SETTINGS_VIDEOMODE_256x224w: + w_main->set_video_mode(VIDEOMODE_256x224w); + break; + case MENU_SETTINGS_VIDEOMODE_512x448w: + w_main->set_video_mode(VIDEOMODE_512x448w); + break; + case MENU_SETTINGS_VIDEOMODE_960x720w: + w_main->set_video_mode(VIDEOMODE_960x720w); + break; + case MENU_SETTINGS_VIDEOMODE_640x480f: + w_main->set_video_mode(VIDEOMODE_640x480f); + break; + case MENU_SETTINGS_VIDEOMODE_1024x768f: + w_main->set_video_mode(VIDEOMODE_1024x768f); + break; + case MENU_SETTINGS_COLORADJUST_COLORCURVE: + config::snes.video_color_curve.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, + (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_COLORADJUST_NORMAL: + case MENU_SETTINGS_COLORADJUST_GRAYSCALE: + case MENU_SETTINGS_COLORADJUST_VGA: + case MENU_SETTINGS_COLORADJUST_GENESIS: + i = LOWORD(wparam) - MENU_SETTINGS_COLORADJUST_NORMAL; + config::snes.video_color_adjust_mode = i; + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_USEVRAM: + config::video.use_vram.toggle(); + w_main->set_video_mode(config::video.mode); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_VBLANK: + config::video.vblank.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_SHOWFPS: + config::gui.show_fps.toggle(); + SetWindowText(w_main->hwnd, BSNES_TITLE); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_MUTE: + config::snes.mute.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_MUTE, (config::snes.mute)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_INPUTCFG_JOYPAD1: + w_inputconfig->begin_config(InputConfig::JOYPAD1); + break; + case MENU_SETTINGS_INPUTCFG_JOYPAD2: + w_inputconfig->begin_config(InputConfig::JOYPAD2); + break; + case MENU_SETTINGS_DEBUGGER: + if(bsnes->debugger_enabled() == true) { + bsnes->debugger_disable(); + } else { + bsnes->debugger_enable(); + w_main->set_video_mode(VIDEOMODE_256x224w); + } + break; + case MENU_MISC_SCREENSHOT: + snes->capture_screenshot(); + break; + case MENU_MISC_LOGAUDIO: + i = CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, 0); + if(i == MF_UNCHECKED) { + CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_CHECKED); + snes->log_audio_enable(); + } else { + CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_UNCHECKED); + snes->log_audio_disable(); + } + break; + case MENU_MISC_ABOUT: + w_about->center(); + w_about->show(); + SetFocus(w_about->hwnd); + break; + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_PAINT: + dd_renderer->redraw(); + break; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void MainWindow::create() { +WNDCLASS wc; +int i; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(100)); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = wndproc_main; + wc.lpszClassName = "bsnes"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + hwnd = CreateWindowEx(0, "bsnes", BSNES_TITLE, + WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + 0, 0, 512, 446, + 0, 0, GetModuleHandle(0), 0); + +HMENU hsubmenu, hbranchmenu; + hmenu = CreateMenu(); + + hsubmenu = CreatePopupMenu(); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_LOAD, "&Load ROM"); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_UNLOAD, "&Unload ROM"); + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_RESET, "&Reset"); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_POWER, "&Power (Hard Reset)"); + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_EXIT, "E&xit"); + AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&File"); + + hsubmenu = CreatePopupMenu(); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_REGULATE_SPEED, "&Regulate Speed"); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_OFF, "Off"); + AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_1, "1"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_2, "2"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_3, "3"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_4, "4"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_5, "5"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_6, "6"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_7, "7"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_8, "8"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_9, "9"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Frameskip"); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_256x224w, "256x224 Windowed (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_512x448w, "512x448 Windowed (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_960x720w, "960x720 Windowed (4:3)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_640x480f, "640x480 Fullscreen (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_1024x768f, "1024x768 Fullscreen (4:3)"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Video Mode"); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_COLORCURVE, "Use &Color Curve"); + AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_NORMAL, "Normal (RGB555)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GRAYSCALE, "Grayscale Mode (L5)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_VGA, "VGA Mode (RGB332)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GENESIS, "Genesis Mode (RGB333)"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Color Adjust"); + + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_USEVRAM, "Use &Video Memory Surface"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_VBLANK, "&Wait For Vertical Retrace"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_SHOWFPS, "&Show FPS"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_MUTE, "&Mute Sound Output"); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD2, "Joypad 2"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Configure Input Devices"); + +#ifdef DEBUGGER + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_DEBUGGER, "&Debugger"); +#endif + + AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Settings"); + + hsubmenu = CreatePopupMenu(); + AppendMenu(hsubmenu, MF_STRING, MENU_MISC_SCREENSHOT, "&Capture Screenshot"); + AppendMenu(hsubmenu, MF_STRING, MENU_MISC_LOGAUDIO, "&Log Audio Data"); + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING, MENU_MISC_ABOUT, "&About..."); + AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Misc"); + + CheckMenuItem(hmenu, MENU_SETTINGS_REGULATE_SPEED, (config::system.regulate_speed)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); + i = config::snes.video_color_adjust_mode; + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_MUTE, (config::snes.mute) ?MF_CHECKED:MF_UNCHECKED); +} diff --git a/src/win/ui_memory.cpp b/src/win_legacy/ui_memory.cpp similarity index 96% rename from src/win/ui_memory.cpp rename to src/win_legacy/ui_memory.cpp index a877778b..c614990d 100644 --- a/src/win/ui_memory.cpp +++ b/src/win_legacy/ui_memory.cpp @@ -1,4 +1,4 @@ -WNDPROC wndproc_oldmemoryeditbox; +WNDPROC wndproc_oldmemoryeditbox = 0; long __stdcall wndproc_memoryeditbox(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { int pos, len, xpos, ypos, t, z, read; @@ -81,7 +81,7 @@ int pos, len, xpos, ypos, t, z, read; SendMessage(hwnd, EM_SETSEL, pos, pos); break; } - return wndproc_oldmemoryeditbox(hwnd, msg, wparam, lparam); + return CallWindowProc(wndproc_oldmemoryeditbox, hwnd, msg, wparam, lparam); } long __stdcall wndproc_memoryeditor(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { @@ -270,7 +270,7 @@ void MemoryEditor::clear() { void MemoryEditor::refresh(uint32 type, uint32 addr) { char s[4096], t[256]; int x, y; - if(rom_image->loaded() == false)return; + if(r_mem->cart_loaded() == false)return; if(visible == false)return; if(type != null) { diff --git a/src/win_legacy/ui_window.cpp b/src/win_legacy/ui_window.cpp new file mode 100644 index 00000000..4e4dcf11 --- /dev/null +++ b/src/win_legacy/ui_window.cpp @@ -0,0 +1,163 @@ +void Window::resize(int width, int height) { +int style; + window.width = width; + window.height = height; + + style = GetWindowLong(hwnd, GWL_STYLE); + if(style & WS_CAPTION) { + width += GetSystemMetrics(SM_CXFIXEDFRAME) << 1; + height += GetSystemMetrics(SM_CYFIXEDFRAME) << 1; + height += GetSystemMetrics(SM_CYCAPTION); + } + + if(width == GetSystemMetrics(SM_CXSCREEN) && height == GetSystemMetrics(SM_CYSCREEN)) { + fullscreen = true; + } else { + fullscreen = false; + } + + if(fullscreen == false) { + if(GetMenu(hwnd) != 0) { + height += GetSystemMetrics(SM_CYMENU); + } + SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE); + } else { + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, width, height, 0); + } +} + +void Window::center() { +RECT rc; +POINT p; + if(fullscreen == true) { + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, window.width, window.height, 0); + return; + } + GetWindowRect(hwnd, &rc); + p.x = p.y = 0; + ClientToScreen(hwnd, &p); + OffsetRect(&rc, p.x, p.y); +int sw = GetSystemMetrics(SM_CXSCREEN), + sh = GetSystemMetrics(SM_CYSCREEN); +int x = (sw - (rc.right - rc.left)) >> 1, + y = (sh - (rc.bottom - rc.top)) >> 1; + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::show_menu() { + if(GetMenu(hwnd) != NULL)return; + + SetMenu(hwnd, hmenu); + if(cursor_visible == false) { + ShowCursor(TRUE); + cursor_visible = true; + } + if(fullscreen == true) { + return; + } + + resize(window.width, window.height); +} + +void Window::hide_menu() { + if(GetMenu(hwnd) == NULL)return; + + SetMenu(hwnd, NULL); + if(fullscreen == true) { + if(cursor_visible == true) { + ShowCursor(FALSE); + cursor_visible = false; + } + return; + } + + resize(window.width, window.height); +} + +void Window::show() { +int style; + visible = true; + style = GetWindowLong(hwnd, GWL_STYLE); + if(style & WS_VISIBLE)return; + ShowWindow(hwnd, SW_NORMAL); +} + +void Window::hide() { +int style; + visible = false; + style = GetWindowLong(hwnd, GWL_STYLE); + if(!(style & WS_VISIBLE))return; + ShowWindow(hwnd, SW_HIDE); +} + +void Window::to_left(HWND _hwnd) { +RECT rc; +int offset = 0; + if(_hwnd) { + GetWindowRect(_hwnd, &rc); + offset = rc.right; + } else { + offset = wa_offset.left; + } + GetWindowRect(hwnd, &rc); + rc.left = offset; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::to_center() { +RECT rc; + GetWindowRect(hwnd, &rc); + rc.left = (GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::to_right() { +RECT rc; + GetWindowRect(hwnd, &rc); + rc.left = GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left); + rc.left -= wa_offset.right; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::to_top() { +RECT rc; + GetWindowRect(hwnd, &rc); + rc.top = wa_offset.top; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::to_middle() { +RECT rc; + GetWindowRect(hwnd, &rc); + rc.top = (GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +void Window::to_bottom(HWND _hwnd) { +RECT rc; +int offset = 0; + if(_hwnd) { + GetWindowRect(_hwnd, &rc); + offset = rc.bottom - rc.top; + } + GetWindowRect(hwnd, &rc); + rc.top = GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top) - offset; + rc.top -= wa_offset.bottom; + SetWindowPos(hwnd, 0, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); +} + +Window::Window() { + fullscreen = false; + visible = false; + cursor_visible = true; + hmenu = NULL; + + SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea, 0); +int sw = GetSystemMetrics(SM_CXSCREEN), + sh = GetSystemMetrics(SM_CYSCREEN); + + wa_offset.left = workarea.left; + wa_offset.top = workarea.top; + wa_offset.right = sw - workarea.right; + wa_offset.bottom = sh - workarea.bottom; +} diff --git a/src/win/uictl_editex.cpp b/src/win_legacy/uictl_editex.cpp similarity index 91% rename from src/win/uictl_editex.cpp rename to src/win_legacy/uictl_editex.cpp index e05dd5a9..79f3fedb 100644 --- a/src/win/uictl_editex.cpp +++ b/src/win_legacy/uictl_editex.cpp @@ -1,3 +1,8 @@ +//for MinGW +#ifndef CFM_BACKCOLOR + #define CFM_BACKCOLOR 0x04000000 +#endif + void editex_setbackcolor(HWND hwnd, COLORREF color) { SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, color); } @@ -57,7 +62,7 @@ int pos = 0; SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr); SendMessage(hwnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); } - SendMessage(hwnd, EM_SETSEL, -1, -1); + SendMessage(hwnd, EM_SETSEL, (WPARAM)-1, (LPARAM)-1); //setting the cursor position has no effect on richedit controls, //until focus is given to the window. even then, behavior is diff --git a/src/win/winmain.cpp b/src/win_legacy/winmain.cpp similarity index 89% rename from src/win/winmain.cpp rename to src/win_legacy/winmain.cpp index 45d8f349..011e3f9c 100644 --- a/src/win/winmain.cpp +++ b/src/win_legacy/winmain.cpp @@ -11,7 +11,6 @@ #include "ds_sound.h" #include "lib.cpp" -#include "rom.cpp" #include "bsnes.cpp" #include "ui.cpp" @@ -76,7 +75,6 @@ string cfg_fn; get_config_fn(cfg_fn); config_file.load(cfg_fn); meminit(); - rom_image = new ROMImage(); init_ui0(); init_snes(); @@ -86,8 +84,8 @@ int argc = __argc; char **argv = __argv; if(argc >= 2) { - rom_image->select(argv[1]); - rom_image->load(); + cartridge.load(argv[1]); + bsnes->debugger_activate(); snes->power(); bsnes->debugger_update(); } @@ -103,8 +101,8 @@ char **argv = __argv; _end: config_file.save(cfg_fn); - delete(rom_image); term_snes(); + cartridge.unload(); memterm(); return 0; } diff --git a/src/win/winmain.h b/src/win_legacy/winmain.h similarity index 100% rename from src/win/winmain.h rename to src/win_legacy/winmain.h