From 27c24bc8a6f45ab5593f0a8a89983ddd9e140818 Mon Sep 17 00:00:00 2001 From: byuu Date: Sun, 28 Mar 2010 15:46:44 +0000 Subject: [PATCH] Update to bsnes v063 release. Time for another (hopefully) stable release. The changelog has all updates since the last stable release. Most notably, this release features substantial accuracy improvements all around. Almost all of them represent brand new findings never before seen in any SNES emulator. Changelog: - fixed off-by-one buffer size issue in S-PPU RTO calculations [PiCiJi] - added XML parser - added XML-based memory mapping system - moved header-based memory mapping code into snesreader library - added some linker flags for Fedora [belegdol] - added cheat code database; with codes for over 1,500 games [mightymo] - fixed a bug where S-CPU IRQs were being tested one cycle early on direct page indexed read opcodes - added global cheat system enable/disable checkbox to cheat code editor - fixed bug in overflow calculation of S-CPU ADC and SBC opcodes in BCD mode [blargg] - emulated the S-CPU ALU MUL and DIV hardware delays with partial result calculation steps [blargg] - controller port read now returns real-time results of B button when strobe latch is raised - major improvements to emulation of the S-SMP TEST register [blargg, byuu] - fixed DSP2 memory map [Overload] - "Apply Patch" checkbox will now scan UPS patch folder if one is set in the paths section - fixed S-CPU TSC negative flag calculation in emulation mode [address] - added "make uninstall" command to Makefile for Linux users - S-CPU (H)DMA now updates the S-CPU MDR; fixes a freeze in Speedy Gonzales - Stage 6-1 - very substantial code cleanups and optimizations as a result of moving from C++98 to C++0x --- pixelshaders/Curvature/fragment | 17 + pixelshaders/HDRTV/fragment | 14 + pixelshaders/HDRTV/vertex | 9 + pixelshaders/HLSL/sepia.fx | 25 + pixelshaders/HQ2x/fragment | 49 + pixelshaders/HQ2x/vertex | 26 + pixelshaders/Pixellate/fragment | 36 + pixelshaders/Pixellate/vertex | 8 + pixelshaders/Scale2x/fragment | 28 + pixelshaders/Scale2x/vertex | 28 + snesfilter/2xsai/2xsai.cpp | 132 + snesfilter/2xsai/2xsai.hpp | 35 + snesfilter/2xsai/implementation.cpp | 1171 +++++ snesfilter/Makefile | 89 + snesfilter/cc.bat | 2 + snesfilter/clean.bat | 1 + snesfilter/direct/direct.cpp | 32 + snesfilter/direct/direct.hpp | 5 + snesfilter/hq2x/hq2x.cpp | 203 + snesfilter/hq2x/hq2x.hpp | 30 + snesfilter/lq2x/lq2x.cpp | 61 + snesfilter/lq2x/lq2x.hpp | 5 + snesfilter/nall/Makefile | 107 + snesfilter/nall/algorithm.hpp | 23 + snesfilter/nall/any.hpp | 74 + snesfilter/nall/array.hpp | 120 + snesfilter/nall/base64.hpp | 90 + snesfilter/nall/bit.hpp | 51 + snesfilter/nall/concept.hpp | 15 + snesfilter/nall/config.hpp | 124 + snesfilter/nall/crc32.hpp | 66 + snesfilter/nall/detect.hpp | 30 + snesfilter/nall/dictionary.hpp | 76 + snesfilter/nall/dl.hpp | 119 + snesfilter/nall/endian.hpp | 38 + snesfilter/nall/file.hpp | 259 ++ snesfilter/nall/filemap.hpp | 190 + snesfilter/nall/foreach.hpp | 31 + snesfilter/nall/function.hpp | 102 + snesfilter/nall/input.hpp | 386 ++ snesfilter/nall/lzss.hpp | 81 + snesfilter/nall/moduloarray.hpp | 40 + snesfilter/nall/platform.hpp | 80 + snesfilter/nall/priorityqueue.hpp | 109 + snesfilter/nall/property.hpp | 91 + snesfilter/nall/qt/Makefile | 55 + snesfilter/nall/qt/check-action.moc.hpp | 41 + snesfilter/nall/qt/concept.hpp | 10 + snesfilter/nall/qt/file-dialog.moc.hpp | 392 ++ snesfilter/nall/qt/hex-editor.moc.hpp | 173 + snesfilter/nall/qt/radio-action.moc.hpp | 41 + snesfilter/nall/qt/window.moc.hpp | 105 + snesfilter/nall/serial.hpp | 80 + snesfilter/nall/serializer.hpp | 145 + snesfilter/nall/sha256.hpp | 143 + snesfilter/nall/sort.hpp | 62 + snesfilter/nall/static.hpp | 20 + snesfilter/nall/stdint.hpp | 44 + snesfilter/nall/string.hpp | 26 + snesfilter/nall/string/base.hpp | 137 + snesfilter/nall/string/cast.hpp | 32 + snesfilter/nall/string/compare.hpp | 104 + snesfilter/nall/string/convert.hpp | 153 + snesfilter/nall/string/core.hpp | 133 + snesfilter/nall/string/filename.hpp | 61 + snesfilter/nall/string/match.hpp | 76 + snesfilter/nall/string/math.hpp | 164 + snesfilter/nall/string/replace.hpp | 103 + snesfilter/nall/string/split.hpp | 56 + snesfilter/nall/string/strl.hpp | 52 + snesfilter/nall/string/trim.hpp | 54 + snesfilter/nall/string/utility.hpp | 169 + snesfilter/nall/string/variadic.hpp | 27 + snesfilter/nall/string/xml.hpp | 257 ++ snesfilter/nall/ups.hpp | 190 + snesfilter/nall/utf8.hpp | 72 + snesfilter/nall/utility.hpp | 30 + snesfilter/nall/varint.hpp | 92 + snesfilter/nall/vector.hpp | 240 + snesfilter/ntsc/ntsc.cpp | 396 ++ snesfilter/ntsc/ntsc.moc.hpp | 91 + snesfilter/ntsc/snes_ntsc/snes_ntsc.c | 251 ++ snesfilter/ntsc/snes_ntsc/snes_ntsc.h | 228 + snesfilter/ntsc/snes_ntsc/snes_ntsc_config.h | 26 + snesfilter/ntsc/snes_ntsc/snes_ntsc_impl.h | 439 ++ snesfilter/pixellate2x/pixellate2x.cpp | 39 + snesfilter/pixellate2x/pixellate2x.hpp | 5 + snesfilter/scale2x/scale2x.cpp | 61 + snesfilter/scale2x/scale2x.hpp | 5 + snesfilter/snesfilter.cpp | 84 + snesfilter/snesfilter.hpp | 16 + snesfilter/sync.sh | 2 + snesreader/7z_C/7zAlloc.c | 77 + snesreader/7z_C/7zAlloc.h | 23 + snesreader/7z_C/7zBuf.c | 36 + snesreader/7z_C/7zBuf.h | 31 + snesreader/7z_C/7zC.txt | 194 + snesreader/7z_C/7zCrc.c | 35 + snesreader/7z_C/7zCrc.h | 32 + snesreader/7z_C/7zDecode.c | 257 ++ snesreader/7z_C/7zDecode.h | 13 + snesreader/7z_C/7zExtract.c | 93 + snesreader/7z_C/7zExtract.h | 49 + snesreader/7z_C/7zHeader.c | 6 + snesreader/7z_C/7zHeader.h | 57 + snesreader/7z_C/7zIn.c | 1204 ++++++ snesreader/7z_C/7zIn.h | 49 + snesreader/7z_C/7zItem.c | 129 + snesreader/7z_C/7zItem.h | 83 + snesreader/7z_C/7zStream.c | 184 + snesreader/7z_C/Bcj2.c | 132 + snesreader/7z_C/Bcj2.h | 30 + snesreader/7z_C/Bra.h | 60 + snesreader/7z_C/Bra86.c | 85 + snesreader/7z_C/CpuArch.h | 69 + snesreader/7z_C/LzmaDec.c | 1010 +++++ snesreader/7z_C/LzmaDec.h | 223 + snesreader/7z_C/Types.h | 206 + snesreader/7z_C/lzma.txt | 594 +++ snesreader/7z_C/readme.txt | 19 + snesreader/Makefile | 187 + snesreader/cc.bat | 2 + snesreader/clean.bat | 1 + snesreader/fex/Binary_Extractor.cpp | 77 + snesreader/fex/Binary_Extractor.h | 26 + snesreader/fex/Data_Reader.cpp | 551 +++ snesreader/fex/Data_Reader.h | 264 ++ snesreader/fex/File_Extractor.cpp | 341 ++ snesreader/fex/File_Extractor.h | 191 + snesreader/fex/Gzip_Extractor.cpp | 98 + snesreader/fex/Gzip_Extractor.h | 34 + snesreader/fex/Gzip_Reader.cpp | 85 + snesreader/fex/Gzip_Reader.h | 46 + snesreader/fex/Rar_Extractor.cpp | 197 + snesreader/fex/Rar_Extractor.h | 43 + snesreader/fex/Zip7_Extractor.cpp | 252 ++ snesreader/fex/Zip7_Extractor.h | 34 + snesreader/fex/Zip_Extractor.cpp | 390 ++ snesreader/fex/Zip_Extractor.h | 45 + snesreader/fex/Zlib_Inflater.cpp | 257 ++ snesreader/fex/Zlib_Inflater.h | 70 + snesreader/fex/blargg_common.cpp | 51 + snesreader/fex/blargg_common.h | 206 + snesreader/fex/blargg_config.h | 34 + snesreader/fex/blargg_endian.h | 185 + snesreader/fex/blargg_errors.cpp | 113 + snesreader/fex/blargg_errors.h | 80 + snesreader/fex/blargg_source.h | 125 + snesreader/fex/fex.cpp | 323 ++ snesreader/fex/fex.h | 206 + snesreader/filechooser.cpp | 57 + snesreader/filechooser.moc.hpp | 20 + snesreader/libjma/7z.h | 28 + snesreader/libjma/7zlzma.cpp | 50 + snesreader/libjma/aribitcd.h | 73 + snesreader/libjma/ariconst.h | 29 + snesreader/libjma/ariprice.h | 12 + snesreader/libjma/btreecd.h | 126 + snesreader/libjma/crc32.h | 26 + snesreader/libjma/iiostrm.cpp | 132 + snesreader/libjma/iiostrm.h | 210 + snesreader/libjma/inbyte.cpp | 60 + snesreader/libjma/inbyte.h | 76 + snesreader/libjma/jcrc32.cpp | 80 + snesreader/libjma/jma.cpp | 550 +++ snesreader/libjma/jma.h | 88 + snesreader/libjma/lencoder.h | 93 + snesreader/libjma/litcoder.h | 122 + snesreader/libjma/lzma.cpp | 41 + snesreader/libjma/lzma.h | 124 + snesreader/libjma/lzmadec.h | 82 + snesreader/libjma/lzmadecode.cpp | 298 ++ snesreader/libjma/portable.h | 83 + snesreader/libjma/rcdefs.h | 60 + snesreader/libjma/rngcoder.h | 143 + snesreader/libjma/winout.cpp | 89 + snesreader/libjma/winout.h | 90 + snesreader/micro-bunzip/micro-bunzip.c | 515 +++ snesreader/nall/Makefile | 107 + snesreader/nall/algorithm.hpp | 23 + snesreader/nall/any.hpp | 74 + snesreader/nall/array.hpp | 120 + snesreader/nall/base64.hpp | 90 + snesreader/nall/bit.hpp | 51 + snesreader/nall/concept.hpp | 15 + snesreader/nall/config.hpp | 124 + snesreader/nall/crc32.hpp | 66 + snesreader/nall/detect.hpp | 30 + snesreader/nall/dictionary.hpp | 76 + snesreader/nall/dl.hpp | 119 + snesreader/nall/endian.hpp | 38 + snesreader/nall/file.hpp | 259 ++ snesreader/nall/filemap.hpp | 190 + snesreader/nall/foreach.hpp | 31 + snesreader/nall/function.hpp | 102 + snesreader/nall/input.hpp | 386 ++ snesreader/nall/lzss.hpp | 81 + snesreader/nall/moduloarray.hpp | 40 + snesreader/nall/platform.hpp | 80 + snesreader/nall/priorityqueue.hpp | 109 + snesreader/nall/property.hpp | 91 + snesreader/nall/qt/Makefile | 55 + snesreader/nall/qt/check-action.moc.hpp | 41 + snesreader/nall/qt/concept.hpp | 10 + snesreader/nall/qt/file-dialog.moc.hpp | 392 ++ snesreader/nall/qt/hex-editor.moc.hpp | 173 + snesreader/nall/qt/radio-action.moc.hpp | 41 + snesreader/nall/qt/window.moc.hpp | 105 + snesreader/nall/serial.hpp | 80 + snesreader/nall/serializer.hpp | 145 + snesreader/nall/sha256.hpp | 143 + snesreader/nall/sort.hpp | 62 + snesreader/nall/static.hpp | 20 + snesreader/nall/stdint.hpp | 44 + snesreader/nall/string.hpp | 26 + snesreader/nall/string/base.hpp | 137 + snesreader/nall/string/cast.hpp | 32 + snesreader/nall/string/compare.hpp | 104 + snesreader/nall/string/convert.hpp | 153 + snesreader/nall/string/core.hpp | 133 + snesreader/nall/string/filename.hpp | 61 + snesreader/nall/string/match.hpp | 76 + snesreader/nall/string/math.hpp | 164 + snesreader/nall/string/replace.hpp | 103 + snesreader/nall/string/split.hpp | 56 + snesreader/nall/string/strl.hpp | 52 + snesreader/nall/string/trim.hpp | 54 + snesreader/nall/string/utility.hpp | 169 + snesreader/nall/string/variadic.hpp | 27 + snesreader/nall/string/xml.hpp | 257 ++ snesreader/nall/ups.hpp | 190 + snesreader/nall/utf8.hpp | 72 + snesreader/nall/utility.hpp | 30 + snesreader/nall/varint.hpp | 92 + snesreader/nall/vector.hpp | 240 + snesreader/snesreader.cpp | 235 + snesreader/snesreader.hpp | 8 + snesreader/sync.sh | 2 + snesreader/unrar/archive.cpp | 97 + snesreader/unrar/archive.hpp | 45 + snesreader/unrar/arcread.cpp | 314 ++ snesreader/unrar/array.hpp | 135 + snesreader/unrar/changes.txt | 141 + snesreader/unrar/coder.cpp | 49 + snesreader/unrar/coder.hpp | 24 + snesreader/unrar/compress.hpp | 36 + snesreader/unrar/crc.cpp | 69 + snesreader/unrar/encname.cpp | 57 + snesreader/unrar/encname.hpp | 20 + snesreader/unrar/extract.cpp | 110 + snesreader/unrar/getbits.cpp | 34 + snesreader/unrar/getbits.hpp | 40 + snesreader/unrar/headers.hpp | 145 + snesreader/unrar/license.txt | 40 + snesreader/unrar/model.cpp | 612 +++ snesreader/unrar/model.hpp | 133 + snesreader/unrar/rar.hpp | 209 + snesreader/unrar/rarvm.cpp | 1158 +++++ snesreader/unrar/rarvm.hpp | 112 + snesreader/unrar/rarvmtbl.cpp | 57 + snesreader/unrar/rawread.cpp | 86 + snesreader/unrar/rawread.hpp | 25 + snesreader/unrar/readme.txt | 63 + snesreader/unrar/suballoc.cpp | 261 ++ snesreader/unrar/suballoc.hpp | 88 + snesreader/unrar/technote.txt | 275 ++ snesreader/unrar/unicode.cpp | 106 + snesreader/unrar/unicode.hpp | 10 + snesreader/unrar/unpack.cpp | 1065 +++++ snesreader/unrar/unpack.hpp | 227 + snesreader/unrar/unpack15.cpp | 532 +++ snesreader/unrar/unpack20.cpp | 394 ++ snesreader/unrar/unrar.cpp | 350 ++ snesreader/unrar/unrar.h | 164 + snesreader/unrar/unrar_misc.cpp | 170 + snesreader/unrar/unrar_open.cpp | 45 + snesreader/unrar/whatsnew.txt | 267 ++ snesreader/xml.cpp | 752 ++++ snesreader/xml.hpp | 103 + snesreader/zlib/adler32.c | 149 + snesreader/zlib/crc32.c | 423 ++ snesreader/zlib/crc32.h | 441 ++ snesreader/zlib/inffast.c | 318 ++ snesreader/zlib/inffast.h | 11 + snesreader/zlib/inffixed.h | 94 + snesreader/zlib/inflate.c | 1368 ++++++ snesreader/zlib/inflate.h | 115 + snesreader/zlib/inftrees.c | 329 ++ snesreader/zlib/inftrees.h | 55 + snesreader/zlib/readme.txt | 10 + snesreader/zlib/zconf.h | 335 ++ snesreader/zlib/zlib.h | 1357 ++++++ snesreader/zlib/zlib.txt | 125 + snesreader/zlib/zutil.c | 318 ++ snesreader/zlib/zutil.h | 269 ++ src/base.hpp | 2 +- src/cpu/scpu/dma/dma.cpp | 4 +- src/ui_qt/debugger/tools/disassembler.cpp | 2 +- src/ui_qt/debugger/tools/memory.cpp | 4 +- src/ui_qt/debugger/tools/memory.moc.hpp | 4 +- src/ui_qt/debugger/tracer.cpp | 4 +- src/ui_qt/debugger/tracer.moc.hpp | 4 +- supergameboy/Makefile | 126 + supergameboy/cc.bat | 2 + supergameboy/clean.bat | 1 + supergameboy/common/adaptivesleep.cpp | 56 + supergameboy/common/adaptivesleep.h | 34 + supergameboy/common/array.h | 40 + supergameboy/common/rateest.cpp | 96 + supergameboy/common/rateest.h | 73 + supergameboy/common/resample/blackmansinc.h | 100 + .../common/resample/chainresampler.cpp | 118 + supergameboy/common/resample/chainresampler.h | 189 + supergameboy/common/resample/cic2.h | 198 + supergameboy/common/resample/cic3.h | 382 ++ supergameboy/common/resample/cic4.h | 237 + supergameboy/common/resample/convoluter.h | 156 + supergameboy/common/resample/hammingsinc.h | 100 + supergameboy/common/resample/linint.h | 129 + supergameboy/common/resample/makesinckernel.h | 152 + supergameboy/common/resample/rectsinc.h | 99 + supergameboy/common/resample/resampler.h | 43 + .../common/resample/resamplerinfo.cpp | 61 + supergameboy/common/resample/resamplerinfo.h | 36 + supergameboy/common/resample/subresampler.h | 33 + supergameboy/common/resample/u48div.cpp | 54 + supergameboy/common/resample/u48div.h | 24 + supergameboy/common/resample/upsampler.h | 51 + supergameboy/common/ringbuffer.h | 112 + supergameboy/common/usec.h | 31 + supergameboy/interface/interface.cpp | 373 ++ supergameboy/interface/interface.hpp | 80 + supergameboy/libgambatte/SConstruct | 64 + supergameboy/libgambatte/include/filterinfo.h | 32 + supergameboy/libgambatte/include/gambatte.h | 82 + supergameboy/libgambatte/include/inputstate.h | 30 + .../libgambatte/include/inputstategetter.h | 30 + supergameboy/libgambatte/include/int.h | 29 + .../libgambatte/include/videoblitter.h | 44 + supergameboy/libgambatte/src/bitmap_font.cpp | 328 ++ supergameboy/libgambatte/src/bitmap_font.h | 87 + .../libgambatte/src/colorconversion.cpp | 96 + .../libgambatte/src/colorconversion.h | 46 + supergameboy/libgambatte/src/cpu.cpp | 2842 ++++++++++++ supergameboy/libgambatte/src/cpu.h | 115 + supergameboy/libgambatte/src/event_queue.h | 160 + supergameboy/libgambatte/src/file/file.cpp | 73 + supergameboy/libgambatte/src/file/file.h | 42 + .../libgambatte/src/file/file_zip.cpp | 167 + .../libgambatte/src/file/unzip/crypt.h | 132 + .../libgambatte/src/file/unzip/ioapi.c | 177 + .../libgambatte/src/file/unzip/ioapi.h | 75 + .../libgambatte/src/file/unzip/unzip.c | 1605 +++++++ .../libgambatte/src/file/unzip/unzip.h | 354 ++ supergameboy/libgambatte/src/gambatte.cpp | 184 + supergameboy/libgambatte/src/initstate.cpp | 281 ++ supergameboy/libgambatte/src/initstate.h | 26 + supergameboy/libgambatte/src/insertion_sort.h | 51 + supergameboy/libgambatte/src/interrupter.cpp | 44 + supergameboy/libgambatte/src/interrupter.h | 38 + supergameboy/libgambatte/src/memory.cpp | 1867 ++++++++ supergameboy/libgambatte/src/memory.h | 238 + supergameboy/libgambatte/src/osd_element.h | 65 + supergameboy/libgambatte/src/rtc.cpp | 157 + supergameboy/libgambatte/src/rtc.h | 97 + supergameboy/libgambatte/src/savestate.h | 184 + supergameboy/libgambatte/src/sound.cpp | 155 + supergameboy/libgambatte/src/sound.h | 95 + .../libgambatte/src/sound/channel1.cpp | 257 ++ supergameboy/libgambatte/src/sound/channel1.h | 91 + .../libgambatte/src/sound/channel2.cpp | 161 + supergameboy/libgambatte/src/sound/channel2.h | 70 + .../libgambatte/src/sound/channel3.cpp | 207 + supergameboy/libgambatte/src/sound/channel3.h | 100 + .../libgambatte/src/sound/channel4.cpp | 300 ++ supergameboy/libgambatte/src/sound/channel4.h | 99 + .../libgambatte/src/sound/duty_unit.cpp | 148 + .../libgambatte/src/sound/duty_unit.h | 64 + .../libgambatte/src/sound/envelope_unit.cpp | 101 + .../libgambatte/src/sound/envelope_unit.h | 50 + .../libgambatte/src/sound/length_counter.cpp | 87 + .../libgambatte/src/sound/length_counter.h | 44 + .../libgambatte/src/sound/master_disabler.h | 31 + .../libgambatte/src/sound/sound_unit.h | 35 + .../src/sound/static_output_tester.h | 41 + .../libgambatte/src/state_osd_elements.cpp | 169 + .../libgambatte/src/state_osd_elements.h | 29 + supergameboy/libgambatte/src/statesaver.cpp | 407 ++ supergameboy/libgambatte/src/statesaver.h | 37 + supergameboy/libgambatte/src/video.cpp | 1474 +++++++ supergameboy/libgambatte/src/video.h | 293 ++ .../libgambatte/src/video/basic_add_event.cpp | 75 + .../libgambatte/src/video/basic_add_event.h | 56 + .../libgambatte/src/video/break_event.cpp | 35 + .../libgambatte/src/video/break_event.h | 59 + .../src/video/filters/catrom2x.cpp | 194 + .../libgambatte/src/video/filters/catrom2x.h | 40 + .../src/video/filters/catrom3x.cpp | 360 ++ .../libgambatte/src/video/filters/catrom3x.h | 40 + .../libgambatte/src/video/filters/filter.h | 39 + .../src/video/filters/kreed2xsai.cpp | 243 ++ .../src/video/filters/kreed2xsai.h | 40 + .../src/video/filters/maxsthq2x.cpp | 2875 ++++++++++++ .../libgambatte/src/video/filters/maxsthq2x.h | 41 + .../src/video/filters/maxsthq3x.cpp | 3845 +++++++++++++++++ .../libgambatte/src/video/filters/maxsthq3x.h | 40 + .../libgambatte/src/video/irq_event.cpp | 36 + .../libgambatte/src/video/irq_event.h | 52 + .../libgambatte/src/video/ly_counter.cpp | 62 + .../libgambatte/src/video/ly_counter.h | 69 + .../libgambatte/src/video/lyc_irq.cpp | 42 + supergameboy/libgambatte/src/video/lyc_irq.h | 67 + .../libgambatte/src/video/m3_extra_cycles.cpp | 101 + .../libgambatte/src/video/m3_extra_cycles.h | 56 + .../libgambatte/src/video/mode0_irq.cpp | 95 + .../libgambatte/src/video/mode0_irq.h | 42 + .../libgambatte/src/video/mode1_irq.cpp | 33 + .../libgambatte/src/video/mode1_irq.h | 56 + .../libgambatte/src/video/mode2_irq.cpp | 63 + .../libgambatte/src/video/mode2_irq.h | 40 + .../libgambatte/src/video/mode3_event.cpp | 62 + .../libgambatte/src/video/mode3_event.h | 47 + .../libgambatte/src/video/sc_reader.cpp | 62 + .../libgambatte/src/video/sc_reader.h | 77 + .../libgambatte/src/video/scx_reader.cpp | 71 + .../libgambatte/src/video/scx_reader.h | 85 + .../libgambatte/src/video/sprite_mapper.cpp | 187 + .../libgambatte/src/video/sprite_mapper.h | 148 + .../libgambatte/src/video/video_event.h | 50 + .../src/video/video_event_comparer.h | 31 + supergameboy/libgambatte/src/video/we.cpp | 59 + supergameboy/libgambatte/src/video/we.h | 118 + .../src/video/we_master_checker.cpp | 58 + .../libgambatte/src/video/we_master_checker.h | 73 + supergameboy/libgambatte/src/video/window.h | 47 + .../libgambatte/src/video/wx_reader.cpp | 65 + .../libgambatte/src/video/wx_reader.h | 83 + supergameboy/libgambatte/src/video/wy.cpp | 105 + supergameboy/libgambatte/src/video/wy.h | 187 + supergameboy/nall/Makefile | 107 + supergameboy/nall/algorithm.hpp | 23 + supergameboy/nall/any.hpp | 74 + supergameboy/nall/array.hpp | 120 + supergameboy/nall/base64.hpp | 90 + supergameboy/nall/bit.hpp | 51 + supergameboy/nall/concept.hpp | 15 + supergameboy/nall/config.hpp | 124 + supergameboy/nall/crc32.hpp | 66 + supergameboy/nall/detect.hpp | 30 + supergameboy/nall/dictionary.hpp | 76 + supergameboy/nall/dl.hpp | 119 + supergameboy/nall/endian.hpp | 38 + supergameboy/nall/file.hpp | 259 ++ supergameboy/nall/filemap.hpp | 190 + supergameboy/nall/foreach.hpp | 31 + supergameboy/nall/function.hpp | 102 + supergameboy/nall/input.hpp | 386 ++ supergameboy/nall/lzss.hpp | 81 + supergameboy/nall/moduloarray.hpp | 40 + supergameboy/nall/platform.hpp | 80 + supergameboy/nall/priorityqueue.hpp | 109 + supergameboy/nall/property.hpp | 91 + supergameboy/nall/qt/Makefile | 55 + supergameboy/nall/qt/check-action.moc.hpp | 41 + supergameboy/nall/qt/concept.hpp | 10 + supergameboy/nall/qt/file-dialog.moc.hpp | 392 ++ supergameboy/nall/qt/hex-editor.moc.hpp | 173 + supergameboy/nall/qt/radio-action.moc.hpp | 41 + supergameboy/nall/qt/window.moc.hpp | 105 + supergameboy/nall/serial.hpp | 80 + supergameboy/nall/serializer.hpp | 145 + supergameboy/nall/sha256.hpp | 143 + supergameboy/nall/sort.hpp | 62 + supergameboy/nall/static.hpp | 20 + supergameboy/nall/stdint.hpp | 44 + supergameboy/nall/string.hpp | 26 + supergameboy/nall/string/base.hpp | 137 + supergameboy/nall/string/cast.hpp | 32 + supergameboy/nall/string/compare.hpp | 104 + supergameboy/nall/string/convert.hpp | 153 + supergameboy/nall/string/core.hpp | 133 + supergameboy/nall/string/filename.hpp | 61 + supergameboy/nall/string/match.hpp | 76 + supergameboy/nall/string/math.hpp | 164 + supergameboy/nall/string/replace.hpp | 103 + supergameboy/nall/string/split.hpp | 56 + supergameboy/nall/string/strl.hpp | 52 + supergameboy/nall/string/trim.hpp | 54 + supergameboy/nall/string/utility.hpp | 169 + supergameboy/nall/string/variadic.hpp | 27 + supergameboy/nall/string/xml.hpp | 257 ++ supergameboy/nall/ups.hpp | 190 + supergameboy/nall/utf8.hpp | 72 + supergameboy/nall/utility.hpp | 30 + supergameboy/nall/varint.hpp | 92 + supergameboy/nall/vector.hpp | 240 + supergameboy/supergameboy.cpp | 68 + supergameboy/supergameboy.hpp | 32 + supergameboy/sync.sh | 2 + 499 files changed, 76703 insertions(+), 12 deletions(-) create mode 100644 pixelshaders/Curvature/fragment create mode 100644 pixelshaders/HDRTV/fragment create mode 100644 pixelshaders/HDRTV/vertex create mode 100644 pixelshaders/HLSL/sepia.fx create mode 100644 pixelshaders/HQ2x/fragment create mode 100644 pixelshaders/HQ2x/vertex create mode 100644 pixelshaders/Pixellate/fragment create mode 100644 pixelshaders/Pixellate/vertex create mode 100644 pixelshaders/Scale2x/fragment create mode 100644 pixelshaders/Scale2x/vertex create mode 100644 snesfilter/2xsai/2xsai.cpp create mode 100644 snesfilter/2xsai/2xsai.hpp create mode 100644 snesfilter/2xsai/implementation.cpp create mode 100644 snesfilter/Makefile create mode 100644 snesfilter/cc.bat create mode 100644 snesfilter/clean.bat create mode 100644 snesfilter/direct/direct.cpp create mode 100644 snesfilter/direct/direct.hpp create mode 100644 snesfilter/hq2x/hq2x.cpp create mode 100644 snesfilter/hq2x/hq2x.hpp create mode 100644 snesfilter/lq2x/lq2x.cpp create mode 100644 snesfilter/lq2x/lq2x.hpp create mode 100644 snesfilter/nall/Makefile create mode 100644 snesfilter/nall/algorithm.hpp create mode 100644 snesfilter/nall/any.hpp create mode 100644 snesfilter/nall/array.hpp create mode 100644 snesfilter/nall/base64.hpp create mode 100644 snesfilter/nall/bit.hpp create mode 100644 snesfilter/nall/concept.hpp create mode 100644 snesfilter/nall/config.hpp create mode 100644 snesfilter/nall/crc32.hpp create mode 100644 snesfilter/nall/detect.hpp create mode 100644 snesfilter/nall/dictionary.hpp create mode 100644 snesfilter/nall/dl.hpp create mode 100644 snesfilter/nall/endian.hpp create mode 100644 snesfilter/nall/file.hpp create mode 100644 snesfilter/nall/filemap.hpp create mode 100644 snesfilter/nall/foreach.hpp create mode 100644 snesfilter/nall/function.hpp create mode 100644 snesfilter/nall/input.hpp create mode 100644 snesfilter/nall/lzss.hpp create mode 100644 snesfilter/nall/moduloarray.hpp create mode 100644 snesfilter/nall/platform.hpp create mode 100644 snesfilter/nall/priorityqueue.hpp create mode 100644 snesfilter/nall/property.hpp create mode 100644 snesfilter/nall/qt/Makefile create mode 100644 snesfilter/nall/qt/check-action.moc.hpp create mode 100644 snesfilter/nall/qt/concept.hpp create mode 100644 snesfilter/nall/qt/file-dialog.moc.hpp create mode 100644 snesfilter/nall/qt/hex-editor.moc.hpp create mode 100644 snesfilter/nall/qt/radio-action.moc.hpp create mode 100644 snesfilter/nall/qt/window.moc.hpp create mode 100644 snesfilter/nall/serial.hpp create mode 100644 snesfilter/nall/serializer.hpp create mode 100644 snesfilter/nall/sha256.hpp create mode 100644 snesfilter/nall/sort.hpp create mode 100644 snesfilter/nall/static.hpp create mode 100644 snesfilter/nall/stdint.hpp create mode 100644 snesfilter/nall/string.hpp create mode 100644 snesfilter/nall/string/base.hpp create mode 100644 snesfilter/nall/string/cast.hpp create mode 100644 snesfilter/nall/string/compare.hpp create mode 100644 snesfilter/nall/string/convert.hpp create mode 100644 snesfilter/nall/string/core.hpp create mode 100644 snesfilter/nall/string/filename.hpp create mode 100644 snesfilter/nall/string/match.hpp create mode 100644 snesfilter/nall/string/math.hpp create mode 100644 snesfilter/nall/string/replace.hpp create mode 100644 snesfilter/nall/string/split.hpp create mode 100644 snesfilter/nall/string/strl.hpp create mode 100644 snesfilter/nall/string/trim.hpp create mode 100644 snesfilter/nall/string/utility.hpp create mode 100644 snesfilter/nall/string/variadic.hpp create mode 100644 snesfilter/nall/string/xml.hpp create mode 100644 snesfilter/nall/ups.hpp create mode 100644 snesfilter/nall/utf8.hpp create mode 100644 snesfilter/nall/utility.hpp create mode 100644 snesfilter/nall/varint.hpp create mode 100644 snesfilter/nall/vector.hpp create mode 100644 snesfilter/ntsc/ntsc.cpp create mode 100644 snesfilter/ntsc/ntsc.moc.hpp create mode 100644 snesfilter/ntsc/snes_ntsc/snes_ntsc.c create mode 100644 snesfilter/ntsc/snes_ntsc/snes_ntsc.h create mode 100644 snesfilter/ntsc/snes_ntsc/snes_ntsc_config.h create mode 100644 snesfilter/ntsc/snes_ntsc/snes_ntsc_impl.h create mode 100644 snesfilter/pixellate2x/pixellate2x.cpp create mode 100644 snesfilter/pixellate2x/pixellate2x.hpp create mode 100644 snesfilter/scale2x/scale2x.cpp create mode 100644 snesfilter/scale2x/scale2x.hpp create mode 100644 snesfilter/snesfilter.cpp create mode 100644 snesfilter/snesfilter.hpp create mode 100644 snesfilter/sync.sh create mode 100644 snesreader/7z_C/7zAlloc.c create mode 100644 snesreader/7z_C/7zAlloc.h create mode 100644 snesreader/7z_C/7zBuf.c create mode 100644 snesreader/7z_C/7zBuf.h create mode 100644 snesreader/7z_C/7zC.txt create mode 100644 snesreader/7z_C/7zCrc.c create mode 100644 snesreader/7z_C/7zCrc.h create mode 100644 snesreader/7z_C/7zDecode.c create mode 100644 snesreader/7z_C/7zDecode.h create mode 100644 snesreader/7z_C/7zExtract.c create mode 100644 snesreader/7z_C/7zExtract.h create mode 100644 snesreader/7z_C/7zHeader.c create mode 100644 snesreader/7z_C/7zHeader.h create mode 100644 snesreader/7z_C/7zIn.c create mode 100644 snesreader/7z_C/7zIn.h create mode 100644 snesreader/7z_C/7zItem.c create mode 100644 snesreader/7z_C/7zItem.h create mode 100644 snesreader/7z_C/7zStream.c create mode 100644 snesreader/7z_C/Bcj2.c create mode 100644 snesreader/7z_C/Bcj2.h create mode 100644 snesreader/7z_C/Bra.h create mode 100644 snesreader/7z_C/Bra86.c create mode 100644 snesreader/7z_C/CpuArch.h create mode 100644 snesreader/7z_C/LzmaDec.c create mode 100644 snesreader/7z_C/LzmaDec.h create mode 100644 snesreader/7z_C/Types.h create mode 100644 snesreader/7z_C/lzma.txt create mode 100644 snesreader/7z_C/readme.txt create mode 100644 snesreader/Makefile create mode 100644 snesreader/cc.bat create mode 100644 snesreader/clean.bat create mode 100644 snesreader/fex/Binary_Extractor.cpp create mode 100644 snesreader/fex/Binary_Extractor.h create mode 100644 snesreader/fex/Data_Reader.cpp create mode 100644 snesreader/fex/Data_Reader.h create mode 100644 snesreader/fex/File_Extractor.cpp create mode 100644 snesreader/fex/File_Extractor.h create mode 100644 snesreader/fex/Gzip_Extractor.cpp create mode 100644 snesreader/fex/Gzip_Extractor.h create mode 100644 snesreader/fex/Gzip_Reader.cpp create mode 100644 snesreader/fex/Gzip_Reader.h create mode 100644 snesreader/fex/Rar_Extractor.cpp create mode 100644 snesreader/fex/Rar_Extractor.h create mode 100644 snesreader/fex/Zip7_Extractor.cpp create mode 100644 snesreader/fex/Zip7_Extractor.h create mode 100644 snesreader/fex/Zip_Extractor.cpp create mode 100644 snesreader/fex/Zip_Extractor.h create mode 100644 snesreader/fex/Zlib_Inflater.cpp create mode 100644 snesreader/fex/Zlib_Inflater.h create mode 100644 snesreader/fex/blargg_common.cpp create mode 100644 snesreader/fex/blargg_common.h create mode 100644 snesreader/fex/blargg_config.h create mode 100644 snesreader/fex/blargg_endian.h create mode 100644 snesreader/fex/blargg_errors.cpp create mode 100644 snesreader/fex/blargg_errors.h create mode 100644 snesreader/fex/blargg_source.h create mode 100644 snesreader/fex/fex.cpp create mode 100644 snesreader/fex/fex.h create mode 100644 snesreader/filechooser.cpp create mode 100644 snesreader/filechooser.moc.hpp create mode 100644 snesreader/libjma/7z.h create mode 100644 snesreader/libjma/7zlzma.cpp create mode 100644 snesreader/libjma/aribitcd.h create mode 100644 snesreader/libjma/ariconst.h create mode 100644 snesreader/libjma/ariprice.h create mode 100644 snesreader/libjma/btreecd.h create mode 100644 snesreader/libjma/crc32.h create mode 100644 snesreader/libjma/iiostrm.cpp create mode 100644 snesreader/libjma/iiostrm.h create mode 100644 snesreader/libjma/inbyte.cpp create mode 100644 snesreader/libjma/inbyte.h create mode 100644 snesreader/libjma/jcrc32.cpp create mode 100644 snesreader/libjma/jma.cpp create mode 100644 snesreader/libjma/jma.h create mode 100644 snesreader/libjma/lencoder.h create mode 100644 snesreader/libjma/litcoder.h create mode 100644 snesreader/libjma/lzma.cpp create mode 100644 snesreader/libjma/lzma.h create mode 100644 snesreader/libjma/lzmadec.h create mode 100644 snesreader/libjma/lzmadecode.cpp create mode 100644 snesreader/libjma/portable.h create mode 100644 snesreader/libjma/rcdefs.h create mode 100644 snesreader/libjma/rngcoder.h create mode 100644 snesreader/libjma/winout.cpp create mode 100644 snesreader/libjma/winout.h create mode 100644 snesreader/micro-bunzip/micro-bunzip.c create mode 100644 snesreader/nall/Makefile create mode 100644 snesreader/nall/algorithm.hpp create mode 100644 snesreader/nall/any.hpp create mode 100644 snesreader/nall/array.hpp create mode 100644 snesreader/nall/base64.hpp create mode 100644 snesreader/nall/bit.hpp create mode 100644 snesreader/nall/concept.hpp create mode 100644 snesreader/nall/config.hpp create mode 100644 snesreader/nall/crc32.hpp create mode 100644 snesreader/nall/detect.hpp create mode 100644 snesreader/nall/dictionary.hpp create mode 100644 snesreader/nall/dl.hpp create mode 100644 snesreader/nall/endian.hpp create mode 100644 snesreader/nall/file.hpp create mode 100644 snesreader/nall/filemap.hpp create mode 100644 snesreader/nall/foreach.hpp create mode 100644 snesreader/nall/function.hpp create mode 100644 snesreader/nall/input.hpp create mode 100644 snesreader/nall/lzss.hpp create mode 100644 snesreader/nall/moduloarray.hpp create mode 100644 snesreader/nall/platform.hpp create mode 100644 snesreader/nall/priorityqueue.hpp create mode 100644 snesreader/nall/property.hpp create mode 100644 snesreader/nall/qt/Makefile create mode 100644 snesreader/nall/qt/check-action.moc.hpp create mode 100644 snesreader/nall/qt/concept.hpp create mode 100644 snesreader/nall/qt/file-dialog.moc.hpp create mode 100644 snesreader/nall/qt/hex-editor.moc.hpp create mode 100644 snesreader/nall/qt/radio-action.moc.hpp create mode 100644 snesreader/nall/qt/window.moc.hpp create mode 100644 snesreader/nall/serial.hpp create mode 100644 snesreader/nall/serializer.hpp create mode 100644 snesreader/nall/sha256.hpp create mode 100644 snesreader/nall/sort.hpp create mode 100644 snesreader/nall/static.hpp create mode 100644 snesreader/nall/stdint.hpp create mode 100644 snesreader/nall/string.hpp create mode 100644 snesreader/nall/string/base.hpp create mode 100644 snesreader/nall/string/cast.hpp create mode 100644 snesreader/nall/string/compare.hpp create mode 100644 snesreader/nall/string/convert.hpp create mode 100644 snesreader/nall/string/core.hpp create mode 100644 snesreader/nall/string/filename.hpp create mode 100644 snesreader/nall/string/match.hpp create mode 100644 snesreader/nall/string/math.hpp create mode 100644 snesreader/nall/string/replace.hpp create mode 100644 snesreader/nall/string/split.hpp create mode 100644 snesreader/nall/string/strl.hpp create mode 100644 snesreader/nall/string/trim.hpp create mode 100644 snesreader/nall/string/utility.hpp create mode 100644 snesreader/nall/string/variadic.hpp create mode 100644 snesreader/nall/string/xml.hpp create mode 100644 snesreader/nall/ups.hpp create mode 100644 snesreader/nall/utf8.hpp create mode 100644 snesreader/nall/utility.hpp create mode 100644 snesreader/nall/varint.hpp create mode 100644 snesreader/nall/vector.hpp create mode 100644 snesreader/snesreader.cpp create mode 100644 snesreader/snesreader.hpp create mode 100644 snesreader/sync.sh create mode 100644 snesreader/unrar/archive.cpp create mode 100644 snesreader/unrar/archive.hpp create mode 100644 snesreader/unrar/arcread.cpp create mode 100644 snesreader/unrar/array.hpp create mode 100644 snesreader/unrar/changes.txt create mode 100644 snesreader/unrar/coder.cpp create mode 100644 snesreader/unrar/coder.hpp create mode 100644 snesreader/unrar/compress.hpp create mode 100644 snesreader/unrar/crc.cpp create mode 100644 snesreader/unrar/encname.cpp create mode 100644 snesreader/unrar/encname.hpp create mode 100644 snesreader/unrar/extract.cpp create mode 100644 snesreader/unrar/getbits.cpp create mode 100644 snesreader/unrar/getbits.hpp create mode 100644 snesreader/unrar/headers.hpp create mode 100644 snesreader/unrar/license.txt create mode 100644 snesreader/unrar/model.cpp create mode 100644 snesreader/unrar/model.hpp create mode 100644 snesreader/unrar/rar.hpp create mode 100644 snesreader/unrar/rarvm.cpp create mode 100644 snesreader/unrar/rarvm.hpp create mode 100644 snesreader/unrar/rarvmtbl.cpp create mode 100644 snesreader/unrar/rawread.cpp create mode 100644 snesreader/unrar/rawread.hpp create mode 100644 snesreader/unrar/readme.txt create mode 100644 snesreader/unrar/suballoc.cpp create mode 100644 snesreader/unrar/suballoc.hpp create mode 100644 snesreader/unrar/technote.txt create mode 100644 snesreader/unrar/unicode.cpp create mode 100644 snesreader/unrar/unicode.hpp create mode 100644 snesreader/unrar/unpack.cpp create mode 100644 snesreader/unrar/unpack.hpp create mode 100644 snesreader/unrar/unpack15.cpp create mode 100644 snesreader/unrar/unpack20.cpp create mode 100644 snesreader/unrar/unrar.cpp create mode 100644 snesreader/unrar/unrar.h create mode 100644 snesreader/unrar/unrar_misc.cpp create mode 100644 snesreader/unrar/unrar_open.cpp create mode 100644 snesreader/unrar/whatsnew.txt create mode 100644 snesreader/xml.cpp create mode 100644 snesreader/xml.hpp create mode 100644 snesreader/zlib/adler32.c create mode 100644 snesreader/zlib/crc32.c create mode 100644 snesreader/zlib/crc32.h create mode 100644 snesreader/zlib/inffast.c create mode 100644 snesreader/zlib/inffast.h create mode 100644 snesreader/zlib/inffixed.h create mode 100644 snesreader/zlib/inflate.c create mode 100644 snesreader/zlib/inflate.h create mode 100644 snesreader/zlib/inftrees.c create mode 100644 snesreader/zlib/inftrees.h create mode 100644 snesreader/zlib/readme.txt create mode 100644 snesreader/zlib/zconf.h create mode 100644 snesreader/zlib/zlib.h create mode 100644 snesreader/zlib/zlib.txt create mode 100644 snesreader/zlib/zutil.c create mode 100644 snesreader/zlib/zutil.h create mode 100644 supergameboy/Makefile create mode 100644 supergameboy/cc.bat create mode 100644 supergameboy/clean.bat create mode 100644 supergameboy/common/adaptivesleep.cpp create mode 100644 supergameboy/common/adaptivesleep.h create mode 100644 supergameboy/common/array.h create mode 100644 supergameboy/common/rateest.cpp create mode 100644 supergameboy/common/rateest.h create mode 100644 supergameboy/common/resample/blackmansinc.h create mode 100644 supergameboy/common/resample/chainresampler.cpp create mode 100644 supergameboy/common/resample/chainresampler.h create mode 100644 supergameboy/common/resample/cic2.h create mode 100644 supergameboy/common/resample/cic3.h create mode 100644 supergameboy/common/resample/cic4.h create mode 100644 supergameboy/common/resample/convoluter.h create mode 100644 supergameboy/common/resample/hammingsinc.h create mode 100644 supergameboy/common/resample/linint.h create mode 100644 supergameboy/common/resample/makesinckernel.h create mode 100644 supergameboy/common/resample/rectsinc.h create mode 100644 supergameboy/common/resample/resampler.h create mode 100644 supergameboy/common/resample/resamplerinfo.cpp create mode 100644 supergameboy/common/resample/resamplerinfo.h create mode 100644 supergameboy/common/resample/subresampler.h create mode 100644 supergameboy/common/resample/u48div.cpp create mode 100644 supergameboy/common/resample/u48div.h create mode 100644 supergameboy/common/resample/upsampler.h create mode 100644 supergameboy/common/ringbuffer.h create mode 100644 supergameboy/common/usec.h create mode 100644 supergameboy/interface/interface.cpp create mode 100644 supergameboy/interface/interface.hpp create mode 100644 supergameboy/libgambatte/SConstruct create mode 100644 supergameboy/libgambatte/include/filterinfo.h create mode 100644 supergameboy/libgambatte/include/gambatte.h create mode 100644 supergameboy/libgambatte/include/inputstate.h create mode 100644 supergameboy/libgambatte/include/inputstategetter.h create mode 100644 supergameboy/libgambatte/include/int.h create mode 100644 supergameboy/libgambatte/include/videoblitter.h create mode 100644 supergameboy/libgambatte/src/bitmap_font.cpp create mode 100644 supergameboy/libgambatte/src/bitmap_font.h create mode 100644 supergameboy/libgambatte/src/colorconversion.cpp create mode 100644 supergameboy/libgambatte/src/colorconversion.h create mode 100644 supergameboy/libgambatte/src/cpu.cpp create mode 100644 supergameboy/libgambatte/src/cpu.h create mode 100644 supergameboy/libgambatte/src/event_queue.h create mode 100644 supergameboy/libgambatte/src/file/file.cpp create mode 100644 supergameboy/libgambatte/src/file/file.h create mode 100644 supergameboy/libgambatte/src/file/file_zip.cpp create mode 100644 supergameboy/libgambatte/src/file/unzip/crypt.h create mode 100644 supergameboy/libgambatte/src/file/unzip/ioapi.c create mode 100644 supergameboy/libgambatte/src/file/unzip/ioapi.h create mode 100644 supergameboy/libgambatte/src/file/unzip/unzip.c create mode 100644 supergameboy/libgambatte/src/file/unzip/unzip.h create mode 100644 supergameboy/libgambatte/src/gambatte.cpp create mode 100644 supergameboy/libgambatte/src/initstate.cpp create mode 100644 supergameboy/libgambatte/src/initstate.h create mode 100644 supergameboy/libgambatte/src/insertion_sort.h create mode 100644 supergameboy/libgambatte/src/interrupter.cpp create mode 100644 supergameboy/libgambatte/src/interrupter.h create mode 100644 supergameboy/libgambatte/src/memory.cpp create mode 100644 supergameboy/libgambatte/src/memory.h create mode 100644 supergameboy/libgambatte/src/osd_element.h create mode 100644 supergameboy/libgambatte/src/rtc.cpp create mode 100644 supergameboy/libgambatte/src/rtc.h create mode 100644 supergameboy/libgambatte/src/savestate.h create mode 100644 supergameboy/libgambatte/src/sound.cpp create mode 100644 supergameboy/libgambatte/src/sound.h create mode 100644 supergameboy/libgambatte/src/sound/channel1.cpp create mode 100644 supergameboy/libgambatte/src/sound/channel1.h create mode 100644 supergameboy/libgambatte/src/sound/channel2.cpp create mode 100644 supergameboy/libgambatte/src/sound/channel2.h create mode 100644 supergameboy/libgambatte/src/sound/channel3.cpp create mode 100644 supergameboy/libgambatte/src/sound/channel3.h create mode 100644 supergameboy/libgambatte/src/sound/channel4.cpp create mode 100644 supergameboy/libgambatte/src/sound/channel4.h create mode 100644 supergameboy/libgambatte/src/sound/duty_unit.cpp create mode 100644 supergameboy/libgambatte/src/sound/duty_unit.h create mode 100644 supergameboy/libgambatte/src/sound/envelope_unit.cpp create mode 100644 supergameboy/libgambatte/src/sound/envelope_unit.h create mode 100644 supergameboy/libgambatte/src/sound/length_counter.cpp create mode 100644 supergameboy/libgambatte/src/sound/length_counter.h create mode 100644 supergameboy/libgambatte/src/sound/master_disabler.h create mode 100644 supergameboy/libgambatte/src/sound/sound_unit.h create mode 100644 supergameboy/libgambatte/src/sound/static_output_tester.h create mode 100644 supergameboy/libgambatte/src/state_osd_elements.cpp create mode 100644 supergameboy/libgambatte/src/state_osd_elements.h create mode 100644 supergameboy/libgambatte/src/statesaver.cpp create mode 100644 supergameboy/libgambatte/src/statesaver.h create mode 100644 supergameboy/libgambatte/src/video.cpp create mode 100644 supergameboy/libgambatte/src/video.h create mode 100644 supergameboy/libgambatte/src/video/basic_add_event.cpp create mode 100644 supergameboy/libgambatte/src/video/basic_add_event.h create mode 100644 supergameboy/libgambatte/src/video/break_event.cpp create mode 100644 supergameboy/libgambatte/src/video/break_event.h create mode 100644 supergameboy/libgambatte/src/video/filters/catrom2x.cpp create mode 100644 supergameboy/libgambatte/src/video/filters/catrom2x.h create mode 100644 supergameboy/libgambatte/src/video/filters/catrom3x.cpp create mode 100644 supergameboy/libgambatte/src/video/filters/catrom3x.h create mode 100644 supergameboy/libgambatte/src/video/filters/filter.h create mode 100644 supergameboy/libgambatte/src/video/filters/kreed2xsai.cpp create mode 100644 supergameboy/libgambatte/src/video/filters/kreed2xsai.h create mode 100644 supergameboy/libgambatte/src/video/filters/maxsthq2x.cpp create mode 100644 supergameboy/libgambatte/src/video/filters/maxsthq2x.h create mode 100644 supergameboy/libgambatte/src/video/filters/maxsthq3x.cpp create mode 100644 supergameboy/libgambatte/src/video/filters/maxsthq3x.h create mode 100644 supergameboy/libgambatte/src/video/irq_event.cpp create mode 100644 supergameboy/libgambatte/src/video/irq_event.h create mode 100644 supergameboy/libgambatte/src/video/ly_counter.cpp create mode 100644 supergameboy/libgambatte/src/video/ly_counter.h create mode 100644 supergameboy/libgambatte/src/video/lyc_irq.cpp create mode 100644 supergameboy/libgambatte/src/video/lyc_irq.h create mode 100644 supergameboy/libgambatte/src/video/m3_extra_cycles.cpp create mode 100644 supergameboy/libgambatte/src/video/m3_extra_cycles.h create mode 100644 supergameboy/libgambatte/src/video/mode0_irq.cpp create mode 100644 supergameboy/libgambatte/src/video/mode0_irq.h create mode 100644 supergameboy/libgambatte/src/video/mode1_irq.cpp create mode 100644 supergameboy/libgambatte/src/video/mode1_irq.h create mode 100644 supergameboy/libgambatte/src/video/mode2_irq.cpp create mode 100644 supergameboy/libgambatte/src/video/mode2_irq.h create mode 100644 supergameboy/libgambatte/src/video/mode3_event.cpp create mode 100644 supergameboy/libgambatte/src/video/mode3_event.h create mode 100644 supergameboy/libgambatte/src/video/sc_reader.cpp create mode 100644 supergameboy/libgambatte/src/video/sc_reader.h create mode 100644 supergameboy/libgambatte/src/video/scx_reader.cpp create mode 100644 supergameboy/libgambatte/src/video/scx_reader.h create mode 100644 supergameboy/libgambatte/src/video/sprite_mapper.cpp create mode 100644 supergameboy/libgambatte/src/video/sprite_mapper.h create mode 100644 supergameboy/libgambatte/src/video/video_event.h create mode 100644 supergameboy/libgambatte/src/video/video_event_comparer.h create mode 100644 supergameboy/libgambatte/src/video/we.cpp create mode 100644 supergameboy/libgambatte/src/video/we.h create mode 100644 supergameboy/libgambatte/src/video/we_master_checker.cpp create mode 100644 supergameboy/libgambatte/src/video/we_master_checker.h create mode 100644 supergameboy/libgambatte/src/video/window.h create mode 100644 supergameboy/libgambatte/src/video/wx_reader.cpp create mode 100644 supergameboy/libgambatte/src/video/wx_reader.h create mode 100644 supergameboy/libgambatte/src/video/wy.cpp create mode 100644 supergameboy/libgambatte/src/video/wy.h create mode 100644 supergameboy/nall/Makefile create mode 100644 supergameboy/nall/algorithm.hpp create mode 100644 supergameboy/nall/any.hpp create mode 100644 supergameboy/nall/array.hpp create mode 100644 supergameboy/nall/base64.hpp create mode 100644 supergameboy/nall/bit.hpp create mode 100644 supergameboy/nall/concept.hpp create mode 100644 supergameboy/nall/config.hpp create mode 100644 supergameboy/nall/crc32.hpp create mode 100644 supergameboy/nall/detect.hpp create mode 100644 supergameboy/nall/dictionary.hpp create mode 100644 supergameboy/nall/dl.hpp create mode 100644 supergameboy/nall/endian.hpp create mode 100644 supergameboy/nall/file.hpp create mode 100644 supergameboy/nall/filemap.hpp create mode 100644 supergameboy/nall/foreach.hpp create mode 100644 supergameboy/nall/function.hpp create mode 100644 supergameboy/nall/input.hpp create mode 100644 supergameboy/nall/lzss.hpp create mode 100644 supergameboy/nall/moduloarray.hpp create mode 100644 supergameboy/nall/platform.hpp create mode 100644 supergameboy/nall/priorityqueue.hpp create mode 100644 supergameboy/nall/property.hpp create mode 100644 supergameboy/nall/qt/Makefile create mode 100644 supergameboy/nall/qt/check-action.moc.hpp create mode 100644 supergameboy/nall/qt/concept.hpp create mode 100644 supergameboy/nall/qt/file-dialog.moc.hpp create mode 100644 supergameboy/nall/qt/hex-editor.moc.hpp create mode 100644 supergameboy/nall/qt/radio-action.moc.hpp create mode 100644 supergameboy/nall/qt/window.moc.hpp create mode 100644 supergameboy/nall/serial.hpp create mode 100644 supergameboy/nall/serializer.hpp create mode 100644 supergameboy/nall/sha256.hpp create mode 100644 supergameboy/nall/sort.hpp create mode 100644 supergameboy/nall/static.hpp create mode 100644 supergameboy/nall/stdint.hpp create mode 100644 supergameboy/nall/string.hpp create mode 100644 supergameboy/nall/string/base.hpp create mode 100644 supergameboy/nall/string/cast.hpp create mode 100644 supergameboy/nall/string/compare.hpp create mode 100644 supergameboy/nall/string/convert.hpp create mode 100644 supergameboy/nall/string/core.hpp create mode 100644 supergameboy/nall/string/filename.hpp create mode 100644 supergameboy/nall/string/match.hpp create mode 100644 supergameboy/nall/string/math.hpp create mode 100644 supergameboy/nall/string/replace.hpp create mode 100644 supergameboy/nall/string/split.hpp create mode 100644 supergameboy/nall/string/strl.hpp create mode 100644 supergameboy/nall/string/trim.hpp create mode 100644 supergameboy/nall/string/utility.hpp create mode 100644 supergameboy/nall/string/variadic.hpp create mode 100644 supergameboy/nall/string/xml.hpp create mode 100644 supergameboy/nall/ups.hpp create mode 100644 supergameboy/nall/utf8.hpp create mode 100644 supergameboy/nall/utility.hpp create mode 100644 supergameboy/nall/varint.hpp create mode 100644 supergameboy/nall/vector.hpp create mode 100644 supergameboy/supergameboy.cpp create mode 100644 supergameboy/supergameboy.hpp create mode 100644 supergameboy/sync.sh diff --git a/pixelshaders/Curvature/fragment b/pixelshaders/Curvature/fragment new file mode 100644 index 00000000..5c311da9 --- /dev/null +++ b/pixelshaders/Curvature/fragment @@ -0,0 +1,17 @@ +//CRT curvature shader +//license: GPL +//author: DOLLS + +uniform sampler2D rubyTexture; + +#define distortion 0.2 + +vec2 barrelDistortion(vec2 coord) { + vec2 cc = coord - 0.5; + float dist = dot(cc, cc); + return coord + cc * (dist + distortion * dist * dist) * distortion; +} + +void main(void) { + gl_FragColor = texture2D(rubyTexture, barrelDistortion(gl_TexCoord[0].xy)); +} diff --git a/pixelshaders/HDRTV/fragment b/pixelshaders/HDRTV/fragment new file mode 100644 index 00000000..16098b90 --- /dev/null +++ b/pixelshaders/HDRTV/fragment @@ -0,0 +1,14 @@ +//HDRTV GLSL shader +//license: GPL +//original version by SimoneT +//ruby port by byuu + +uniform sampler2D rubyTexture; + +void main(void) { + vec4 rgb = texture2D(rubyTexture, gl_TexCoord[0].xy); + vec4 intens = smoothstep(0.2,0.8,rgb) + normalize(vec4(rgb.xyz, 1.0)); + + if(fract(gl_FragCoord.y * 0.5) > 0.5) intens = rgb * 0.8; + gl_FragColor = intens; +} diff --git a/pixelshaders/HDRTV/vertex b/pixelshaders/HDRTV/vertex new file mode 100644 index 00000000..70591d55 --- /dev/null +++ b/pixelshaders/HDRTV/vertex @@ -0,0 +1,9 @@ +//HDRTV GLSL shader +//license: GPL +//original version by SimoneT +//ruby port by byuu + +void main(void) { + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; +} diff --git a/pixelshaders/HLSL/sepia.fx b/pixelshaders/HLSL/sepia.fx new file mode 100644 index 00000000..a9295d06 --- /dev/null +++ b/pixelshaders/HLSL/sepia.fx @@ -0,0 +1,25 @@ +texture rubyTexture; + +float4 vec; + +sampler s0 = sampler_state { texture = ; }; +float3 LightColor = { 1.0, 0.7, 0.5 }; +float3 DarkColor = { 0.2, 0.05, 0.0 }; + +float4 DiffColorPass(in float2 Tex : TEXCOORD0) : COLOR0 +{ + vec.x = 0.5; + vec.y = 1.0; + float3 scnColor = LightColor * tex2D(s0, Tex).xyz; + float3 grayXfer = float3(0.3, 0.59, 0.11); + float gray = dot(grayXfer, scnColor); + float3 muted = lerp(scnColor, gray.xxx, vec.x); + float3 sepia = lerp(DarkColor, LightColor, gray); + float3 result = lerp(muted, sepia, vec.y); + return float4(result, 1); +} + +Technique T0 +{ + pass p0 { PixelShader = compile ps_2_0 DiffColorPass(); } +} diff --git a/pixelshaders/HQ2x/fragment b/pixelshaders/HQ2x/fragment new file mode 100644 index 00000000..0bf24db3 --- /dev/null +++ b/pixelshaders/HQ2x/fragment @@ -0,0 +1,49 @@ +//HQ2x GLSL shader +//license: GPL +//original version by guest(r) +//ruby port by byuu + +uniform sampler2D rubyTexture; + +const float mx = 0.325; // start smoothing wt. +const float k = -0.250; // wt. decrease factor +const float max_w = 0.25; // max filter weigth +const float min_w =-0.05; // min filter weigth +const float lum_add = 0.25; // effects smoothing + +void main() { + vec3 c00 = texture2D(rubyTexture, gl_TexCoord[1].xy).xyz; + vec3 c10 = texture2D(rubyTexture, gl_TexCoord[1].zw).xyz; + vec3 c20 = texture2D(rubyTexture, gl_TexCoord[2].xy).xyz; + vec3 c01 = texture2D(rubyTexture, gl_TexCoord[4].zw).xyz; + vec3 c11 = texture2D(rubyTexture, gl_TexCoord[0].xy).xyz; + vec3 c21 = texture2D(rubyTexture, gl_TexCoord[2].zw).xyz; + vec3 c02 = texture2D(rubyTexture, gl_TexCoord[4].xy).xyz; + vec3 c12 = texture2D(rubyTexture, gl_TexCoord[3].zw).xyz; + vec3 c22 = texture2D(rubyTexture, gl_TexCoord[3].xy).xyz; + vec3 dt = vec3(1.0, 1.0, 1.0); + + float md1 = dot(abs(c00 - c22), dt); + float md2 = dot(abs(c02 - c20), dt); + + float w1 = dot(abs(c22 - c11), dt) * md2; + float w2 = dot(abs(c02 - c11), dt) * md1; + float w3 = dot(abs(c00 - c11), dt) * md2; + float w4 = dot(abs(c20 - c11), dt) * md1; + + float t1 = w1 + w3; + float t2 = w2 + w4; + float ww = max(t1, t2) + 0.0001; + + c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww); + + float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add); + float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add); + + w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w); + w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w); + w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w); + w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w); + + gl_FragColor.xyz = w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11; +} diff --git a/pixelshaders/HQ2x/vertex b/pixelshaders/HQ2x/vertex new file mode 100644 index 00000000..488a870e --- /dev/null +++ b/pixelshaders/HQ2x/vertex @@ -0,0 +1,26 @@ +//HQ2x GLSL shader +//license: GPL +//original version by guest(r) +//ruby port by byuu + +uniform vec2 rubyTextureSize; + +void main() { + float x = 0.5 * (1.0 / rubyTextureSize.x); + float y = 0.5 * (1.0 / rubyTextureSize.y); + vec2 dg1 = vec2( x, y); + vec2 dg2 = vec2(-x, y); + vec2 dx = vec2(x, 0.0); + vec2 dy = vec2(0.0, y); + + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1].xy = gl_TexCoord[0].xy - dg1; + gl_TexCoord[1].zw = gl_TexCoord[0].xy - dy; + gl_TexCoord[2].xy = gl_TexCoord[0].xy - dg2; + gl_TexCoord[2].zw = gl_TexCoord[0].xy + dx; + gl_TexCoord[3].xy = gl_TexCoord[0].xy + dg1; + gl_TexCoord[3].zw = gl_TexCoord[0].xy + dy; + gl_TexCoord[4].xy = gl_TexCoord[0].xy + dg2; + gl_TexCoord[4].zw = gl_TexCoord[0].xy - dx; +} diff --git a/pixelshaders/Pixellate/fragment b/pixelshaders/Pixellate/fragment new file mode 100644 index 00000000..ff94deea --- /dev/null +++ b/pixelshaders/Pixellate/fragment @@ -0,0 +1,36 @@ +//Pixellate shader +//license: GPL +//author: Fes + +uniform sampler2D rubyTexture; +uniform vec2 rubyTextureSize; + +void main() { + vec2 texelSize = 1.0 / rubyTextureSize; + + vec2 range; + range.x = dFdx(gl_TexCoord[0].x) / 2.0 * 0.99; + range.y = dFdy(gl_TexCoord[0].y) / 2.0 * 0.99; + + float left = gl_TexCoord[0].x - range.x; + float top = gl_TexCoord[0].y + range.y; + float right = gl_TexCoord[0].x + range.x; + float bottom = gl_TexCoord[0].y - range.y; + + vec4 topLeftColor = texture2D(rubyTexture, (floor(vec2(left, top) / texelSize) + 0.5) * texelSize); + vec4 bottomRightColor = texture2D(rubyTexture, (floor(vec2(right, bottom) / texelSize) + 0.5) * texelSize); + vec4 bottomLeftColor = texture2D(rubyTexture, (floor(vec2(left, bottom) / texelSize) + 0.5) * texelSize); + vec4 topRightColor = texture2D(rubyTexture, (floor(vec2(right, top) / texelSize) + 0.5) * texelSize); + + vec2 border = clamp(round(gl_TexCoord[0] / texelSize) * texelSize, vec2(left, bottom), vec2(right, top)); + + float totalArea = 4.0 * range.x * range.y; + + vec4 averageColor; + averageColor = ((border.x - left) * (top - border.y) / totalArea) * topLeftColor; + averageColor += ((right - border.x) * (border.y - bottom) / totalArea) * bottomRightColor; + averageColor += ((border.x - left) * (border.y - bottom) / totalArea) * bottomLeftColor; + averageColor += ((right - border.x) * (top - border.y) / totalArea) * topRightColor; + + gl_FragColor = averageColor; +} diff --git a/pixelshaders/Pixellate/vertex b/pixelshaders/Pixellate/vertex new file mode 100644 index 00000000..898112ee --- /dev/null +++ b/pixelshaders/Pixellate/vertex @@ -0,0 +1,8 @@ +//Pixellate shader +//license: GPL +//author: Fes + +void main() { + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; +} diff --git a/pixelshaders/Scale2x/fragment b/pixelshaders/Scale2x/fragment new file mode 100644 index 00000000..c72625ae --- /dev/null +++ b/pixelshaders/Scale2x/fragment @@ -0,0 +1,28 @@ +//Scale2x GLSL shader +//license: GPL +//original version by Pete Bernert +//ruby port by byuu + uniform sampler2D rubyTexture; +uniform vec2 rubyTextureSize; + +void main() { + vec4 colD, colF, colB, colH, col, tmp; + vec2 sel; + + col = texture2DProj(rubyTexture, gl_TexCoord[0]); //central (can be E0-E3) + colD = texture2DProj(rubyTexture, gl_TexCoord[1]); //D (left) + colF = texture2DProj(rubyTexture, gl_TexCoord[2]); //F (right) + colB = texture2DProj(rubyTexture, gl_TexCoord[3]); //B (top) + colH = texture2DProj(rubyTexture, gl_TexCoord[4]); //H (bottom) + + sel = fract(gl_TexCoord[0].xy * rubyTextureSize.xy); //where are we (E0-E3)? + //E0 is default + if(sel.y >= 0.5) { tmp = colB; colB = colH; colH = tmp; } //E1 (or E3): swap B and H + if(sel.x >= 0.5) { tmp = colF; colF = colD; colD = tmp; } //E2 (or E3): swap D and F + + if(colB == colD && colB != colF && colD != colH) { //do the Scale2x rule + col = colD; + } + + gl_FragColor = col; +} diff --git a/pixelshaders/Scale2x/vertex b/pixelshaders/Scale2x/vertex new file mode 100644 index 00000000..6e172cd1 --- /dev/null +++ b/pixelshaders/Scale2x/vertex @@ -0,0 +1,28 @@ +//Scale2x GLSL shader +//license: GPL +//original version by Pete Bernert +//ruby port by byuu + +uniform vec2 rubyTextureSize; + +void main() { + vec4 offsetx; + vec4 offsety; + + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + + offsetx.x = 1.0 / rubyTextureSize.x; + offsetx.y = 0.0; + offsetx.w = 0.0; + offsetx.z = 0.0; + offsety.y = 1.0 / rubyTextureSize.y; + offsety.x = 0.0; + offsety.w = 0.0; + offsety.z = 0.0; + + gl_TexCoord[0] = gl_MultiTexCoord0; //center + gl_TexCoord[1] = gl_TexCoord[0] - offsetx; //left + gl_TexCoord[2] = gl_TexCoord[0] + offsetx; //right + gl_TexCoord[3] = gl_TexCoord[0] - offsety; //top + gl_TexCoord[4] = gl_TexCoord[0] + offsety; //bottom +} diff --git a/snesfilter/2xsai/2xsai.cpp b/snesfilter/2xsai/2xsai.cpp new file mode 100644 index 00000000..75923a0e --- /dev/null +++ b/snesfilter/2xsai/2xsai.cpp @@ -0,0 +1,132 @@ +//2xSaI / Super 2xSaI / Super Eagle filter +//authors: kode54 and Kreed +//license: GPL + +#include "2xsai.hpp" +#include "implementation.cpp" + +//===== +//2xSaI +//===== + +void _2xSaIFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = width; + outheight = height; + + if(width <= 256 && height <= 240) { + outwidth *= 2; + outheight *= 2; + } +} + +void _2xSaIFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + for(unsigned y = 0; y < height; y++) { + const uint16_t *line_in = (const uint16_t *) (((const uint8_t*)input) + pitch * y); + uint32_t *line_out = temp + y * 256; + for(unsigned x = 0; x < width; x++) { + line_out[x] = colortable[line_in[x]]; + } + } + + _2xSaI32( (unsigned char *) temp, 1024, 0, (unsigned char *) output, outpitch, width, height ); +} + +_2xSaIFilter::_2xSaIFilter() { + temp = new uint32_t[256*240]; +} + +_2xSaIFilter::~_2xSaIFilter() { + delete[] temp; +} + +//=========== +//Super 2xSaI +//=========== + +void Super2xSaIFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = width; + outheight = height; + + if(width <= 256 && height <= 240) { + outwidth *= 2; + outheight *= 2; + } +} + +void Super2xSaIFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + for(unsigned y = 0; y < height; y++) { + const uint16_t *line_in = (const uint16_t *) (((const uint8_t*)input) + pitch * y); + uint32_t *line_out = temp + y * 256; + for(unsigned x = 0; x < width; x++) { + line_out[x] = colortable[line_in[x]]; + } + } + + Super2xSaI32( (unsigned char *) temp, 1024, 0, (unsigned char *) output, outpitch, width, height ); +} + +Super2xSaIFilter::Super2xSaIFilter() { + temp = new uint32_t[256*240]; +} + +Super2xSaIFilter::~Super2xSaIFilter() { + delete[] temp; +} + +//=========== +//Super Eagle +//=========== + +void SuperEagleFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = width; + outheight = height; + + if(width <= 256 && height <= 240) { + outwidth *= 2; + outheight *= 2; + } +} + +void SuperEagleFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + for(unsigned y = 0; y < height; y++) { + const uint16_t *line_in = (const uint16_t *) (((const uint8_t*)input) + pitch * y); + uint32_t *line_out = temp + y * 256; + for(unsigned x = 0; x < width; x++) { + line_out[x] = colortable[line_in[x]]; + } + } + + SuperEagle32( (unsigned char *) temp, 1024, 0, (unsigned char *) output, outpitch, width, height ); +} + +SuperEagleFilter::SuperEagleFilter() { + temp = new uint32_t[256*240]; +} + +SuperEagleFilter::~SuperEagleFilter() { + delete[] temp; +} diff --git a/snesfilter/2xsai/2xsai.hpp b/snesfilter/2xsai/2xsai.hpp new file mode 100644 index 00000000..361f47e9 --- /dev/null +++ b/snesfilter/2xsai/2xsai.hpp @@ -0,0 +1,35 @@ +class _2xSaIFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + + _2xSaIFilter(); + ~_2xSaIFilter(); + +private: + uint32_t *temp; +} filter_2xsai; + +class Super2xSaIFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + + Super2xSaIFilter(); + ~Super2xSaIFilter(); + +private: + uint32_t *temp; +} filter_super2xsai; + +class SuperEagleFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + + SuperEagleFilter(); + ~SuperEagleFilter(); + +private: + uint32_t *temp; +} filter_supereagle; diff --git a/snesfilter/2xsai/implementation.cpp b/snesfilter/2xsai/implementation.cpp new file mode 100644 index 00000000..cebca7ef --- /dev/null +++ b/snesfilter/2xsai/implementation.cpp @@ -0,0 +1,1171 @@ +static uint32_t colorMask = 0xFEFEFE; +static uint32_t lowPixelMask = 0x010101; +static uint32_t qcolorMask = 0xFCFCFC; +static uint32_t qlowpixelMask = 0x030303; +static uint32_t redblueMask = 0xFF00FF; +static uint32_t greenMask = 0xFF00; + +uint32_t qRGB_COLOR_MASK[2] = { 0xFEFEFE, 0xFEFEFE }; + +static inline int GetResult1 (uint32_t A, uint32_t B, uint32_t C, uint32_t D, + uint32_t /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline int GetResult2 (uint32_t A, uint32_t B, uint32_t C, uint32_t D, + uint32_t /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r -= 1; + if (y <= 1) + r += 1; + return r; +} + +static inline int GetResult (uint32_t A, uint32_t B, uint32_t C, uint32_t D) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline uint32_t INTERPOLATE (uint32_t A, uint32_t B) +{ + if (A != B) { + return (((A & colorMask) >> 1) + ((B & colorMask) >> 1) + + (A & B & lowPixelMask)); + } else + return A; +} + +static inline uint32_t Q_INTERPOLATE (uint32_t A, uint32_t B, uint32_t C, uint32_t D) +{ + register uint32_t x = ((A & qcolorMask) >> 2) + + ((B & qcolorMask) >> 2) + + ((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2); + register uint32_t y = (A & qlowpixelMask) + + (B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask); + + y = (y >> 2) & qlowpixelMask; + return x + y; +} + +static inline int GetResult1_32 (uint32_t A, uint32_t B, uint32_t C, uint32_t D, + uint32_t /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static inline int GetResult2_32 (uint32_t A, uint32_t B, uint32_t C, uint32_t D, + uint32_t /* E */) +{ + int x = 0; + int y = 0; + int r = 0; + + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r -= 1; + if (y <= 1) + r += 1; + return r; +} + +#define BLUE_MASK565 0x001F001F +#define RED_MASK565 0xF800F800 +#define GREEN_MASK565 0x07E007E0 + +#define BLUE_MASK555 0x001F001F +#define RED_MASK555 0x7C007C00 +#define GREEN_MASK555 0x03E003E0 + +void Super2xSaI (uint8_t *srcPtr, uint32_t srcPitch, + uint8_t *deltaPtr, uint8_t *dstPtr, uint32_t dstPitch, + int width, int height) +{ + uint16_t *bP; + uint8_t *dP; + uint32_t inc_bP; + uint32_t Nextline = srcPitch >> 1; + { + inc_bP = 1; + + for (; height; height--) { + bP = (uint16_t *) srcPtr; + dP = (uint8_t *) dstPtr; + + for (uint32_t finish = width; finish; finish -= inc_bP) { + uint32_t color4, color5, color6; + uint32_t color1, color2, color3; + uint32_t colorA0, colorA1, colorA2, colorA3, + colorB0, colorB1, colorB2, colorB3, colorS1, colorS2; + uint32_t product1a, product1b, product2a, product2b; + + //--------------------------------------- B1 B2 + // 4 5 6 S2 + // 1 2 3 S1 + // A1 A2 + + colorB0 = *(bP - Nextline - 1); + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + colorB3 = *(bP - Nextline + 2); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA0 = *(bP + Nextline + Nextline - 1); + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + colorA3 = *(bP + Nextline + Nextline + 2); + + //-------------------------------------- + if (color2 == color6 && color5 != color3) { + product2b = product1b = color2; + } else if (color5 == color3 && color2 != color6) { + product2b = product1b = color5; + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) + product2b = product1b = color6; + else if (r < 0) + product2b = product1b = color5; + else { + product2b = product1b = INTERPOLATE (color5, color6); + } + } else { + if (color6 == color3 && color3 == colorA1 + && color2 != colorA2 && color3 != colorA0) + product2b = + Q_INTERPOLATE (color3, color3, color3, color2); + else if (color5 == color2 && color2 == colorA2 + && colorA1 != color3 && color2 != colorA3) + product2b = + Q_INTERPOLATE (color2, color2, color2, color3); + else + product2b = INTERPOLATE (color2, color3); + + if (color6 == color3 && color6 == colorB1 + && color5 != colorB2 && color6 != colorB0) + product1b = + Q_INTERPOLATE (color6, color6, color6, color5); + else if (color5 == color2 && color5 == colorB2 + && colorB1 != color6 && color5 != colorB3) + product1b = + Q_INTERPOLATE (color6, color5, color5, color5); + else + product1b = INTERPOLATE (color5, color6); + } + + if (color5 == color3 && color2 != color6 && color4 == color5 + && color5 != colorA2) + product2a = INTERPOLATE (color2, color5); + else + if (color5 == color1 && color6 == color5 + && color4 != color2 && color5 != colorA0) + product2a = INTERPOLATE (color2, color5); + else + product2a = color2; + + if (color2 == color6 && color5 != color3 && color1 == color2 + && color2 != colorB2) + product1a = INTERPOLATE (color2, color5); + else + if (color4 == color2 && color3 == color2 + && color1 != color5 && color2 != colorB0) + product1a = INTERPOLATE (color2, color5); + else + product1a = color5; + +#ifdef WORDS_BIGENDIAN + product1a = (product1a << 16) | product1b; + product2a = (product2a << 16) | product2b; +#else + product1a = product1a | (product1b << 16); + product2a = product2a | (product2b << 16); +#endif + + *((uint32_t *) dP) = product1a; + *((uint32_t *) (dP + dstPitch)) = product2a; + + bP += inc_bP; + dP += sizeof (uint32_t); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (; height; height--) + } +} + +void Super2xSaI32 (uint8_t *srcPtr, uint32_t srcPitch, + uint8_t * /* deltaPtr */, uint8_t *dstPtr, uint32_t dstPitch, + int width, int height) +{ + uint32_t *bP; + uint32_t *dP; + uint32_t inc_bP; + uint32_t Nextline = srcPitch >> 2; + inc_bP = 1; + + for (; height; height--) { + bP = (uint32_t *) srcPtr; + dP = (uint32_t *) dstPtr; + + for (uint32_t finish = width; finish; finish -= inc_bP) { + uint32_t color4, color5, color6; + uint32_t color1, color2, color3; + uint32_t colorA0, colorA1, colorA2, colorA3, + colorB0, colorB1, colorB2, colorB3, colorS1, colorS2; + uint32_t product1a, product1b, product2a, product2b; + + //--------------------------------------- B1 B2 + // 4 5 6 S2 + // 1 2 3 S1 + // A1 A2 + + colorB0 = *(bP - Nextline - 1); + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + colorB3 = *(bP - Nextline + 2); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA0 = *(bP + Nextline + Nextline - 1); + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + colorA3 = *(bP + Nextline + Nextline + 2); + + //-------------------------------------- + if (color2 == color6 && color5 != color3) { + product2b = product1b = color2; + } else if (color5 == color3 && color2 != color6) { + product2b = product1b = color5; + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) + product2b = product1b = color6; + else if (r < 0) + product2b = product1b = color5; + else { + product2b = product1b = INTERPOLATE (color5, color6); + } + } else { + if (color6 == color3 && color3 == colorA1 + && color2 != colorA2 && color3 != colorA0) + product2b = + Q_INTERPOLATE (color3, color3, color3, color2); + else if (color5 == color2 && color2 == colorA2 + && colorA1 != color3 && color2 != colorA3) + product2b = + Q_INTERPOLATE (color2, color2, color2, color3); + else + product2b = INTERPOLATE (color2, color3); + + if (color6 == color3 && color6 == colorB1 + && color5 != colorB2 && color6 != colorB0) + product1b = + Q_INTERPOLATE (color6, color6, color6, color5); + else if (color5 == color2 && color5 == colorB2 + && colorB1 != color6 && color5 != colorB3) + product1b = + Q_INTERPOLATE (color6, color5, color5, color5); + else + product1b = INTERPOLATE (color5, color6); + } + + if (color5 == color3 && color2 != color6 && color4 == color5 + && color5 != colorA2) + product2a = INTERPOLATE (color2, color5); + else + if (color5 == color1 && color6 == color5 + && color4 != color2 && color5 != colorA0) + product2a = INTERPOLATE (color2, color5); + else + product2a = color2; + + if (color2 == color6 && color5 != color3 && color1 == color2 + && color2 != colorB2) + product1a = INTERPOLATE (color2, color5); + else + if (color4 == color2 && color3 == color2 + && color1 != color5 && color2 != colorB0) + product1a = INTERPOLATE (color2, color5); + else + product1a = color5; + *(dP) = product1a; + *(dP+1) = product1b; + *(dP + (dstPitch >> 2)) = product2a; + *(dP + (dstPitch >> 2) + 1) = product2b; + + bP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + // deltaPtr += srcPitch; + } // endof: for (; height; height--) +} + +void SuperEagle (uint8_t *srcPtr, uint32_t srcPitch, uint8_t *deltaPtr, + uint8_t *dstPtr, uint32_t dstPitch, int width, int height) +{ + uint8_t *dP; + uint16_t *bP; + uint16_t *xP; + uint32_t inc_bP; + + { + inc_bP = 1; + + uint32_t Nextline = srcPitch >> 1; + + for (; height; height--) { + bP = (uint16_t *) srcPtr; + xP = (uint16_t *) deltaPtr; + dP = dstPtr; + for (uint32_t finish = width; finish; finish -= inc_bP) { + uint32_t color4, color5, color6; + uint32_t color1, color2, color3; + uint32_t colorA1, colorA2, colorB1, colorB2, colorS1, colorS2; + uint32_t product1a, product1b, product2a, product2b; + + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + + // -------------------------------------- + if (color2 == color6 && color5 != color3) { + product1b = product2a = color2; + if ((color1 == color2) || (color6 == colorB2)) { + product1a = INTERPOLATE (color2, color5); + product1a = INTERPOLATE (color2, product1a); + // product1a = color2; + } else { + product1a = INTERPOLATE (color5, color6); + } + + if ((color6 == colorS2) || (color2 == colorA1)) { + product2b = INTERPOLATE (color2, color3); + product2b = INTERPOLATE (color2, product2b); + // product2b = color2; + } else { + product2b = INTERPOLATE (color2, color3); + } + } else if (color5 == color3 && color2 != color6) { + product2b = product1a = color5; + + if ((colorB1 == color5) || (color3 == colorS1)) { + product1b = INTERPOLATE (color5, color6); + product1b = INTERPOLATE (color5, product1b); + // product1b = color5; + } else { + product1b = INTERPOLATE (color5, color6); + } + + if ((color3 == colorA2) || (color4 == color5)) { + product2a = INTERPOLATE (color5, color2); + product2a = INTERPOLATE (color5, product2a); + // product2a = color5; + } else { + product2a = INTERPOLATE (color2, color3); + } + + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) { + product1b = product2a = color2; + product1a = product2b = INTERPOLATE (color5, color6); + } else if (r < 0) { + product2b = product1a = color5; + product1b = product2a = INTERPOLATE (color5, color6); + } else { + product2b = product1a = color5; + product1b = product2a = color2; + } + } else { + product2b = product1a = INTERPOLATE (color2, color6); + product2b = + Q_INTERPOLATE (color3, color3, color3, product2b); + product1a = + Q_INTERPOLATE (color5, color5, color5, product1a); + + product2a = product1b = INTERPOLATE (color5, color3); + product2a = + Q_INTERPOLATE (color2, color2, color2, product2a); + product1b = + Q_INTERPOLATE (color6, color6, color6, product1b); + + // product1a = color5; + // product1b = color6; + // product2a = color2; + // product2b = color3; + } +#ifdef WORDS_BIGENDIAN + product1a = (product1a << 16) | product1b; + product2a = (product2a << 16) | product2b; +#else + product1a = product1a | (product1b << 16); + product2a = product2a | (product2b << 16); +#endif + + *((uint32_t *) dP) = product1a; + *((uint32_t *) (dP + dstPitch)) = product2a; + *xP = color5; + + bP += inc_bP; + xP += inc_bP; + dP += sizeof (uint32_t); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (height; height; height--) + } +} + +void SuperEagle32 (uint8_t *srcPtr, uint32_t srcPitch, uint8_t */*deltaPtr*/, + uint8_t *dstPtr, uint32_t dstPitch, int width, int height) +{ + uint32_t *dP; + uint32_t *bP; + //uint32_t *xP; + uint32_t inc_bP; + + inc_bP = 1; + + uint32_t Nextline = srcPitch >> 2; + + for (; height; height--) { + bP = (uint32_t *) srcPtr; + //xP = (uint32_t *) deltaPtr; + dP = (uint32_t *)dstPtr; + for (uint32_t finish = width; finish; finish -= inc_bP) { + uint32_t color4, color5, color6; + uint32_t color1, color2, color3; + uint32_t colorA1, colorA2, colorB1, colorB2, colorS1, colorS2; + uint32_t product1a, product1b, product2a, product2b; + + colorB1 = *(bP - Nextline); + colorB2 = *(bP - Nextline + 1); + + color4 = *(bP - 1); + color5 = *(bP); + color6 = *(bP + 1); + colorS2 = *(bP + 2); + + color1 = *(bP + Nextline - 1); + color2 = *(bP + Nextline); + color3 = *(bP + Nextline + 1); + colorS1 = *(bP + Nextline + 2); + + colorA1 = *(bP + Nextline + Nextline); + colorA2 = *(bP + Nextline + Nextline + 1); + + // -------------------------------------- + if (color2 == color6 && color5 != color3) { + product1b = product2a = color2; + if ((color1 == color2) || (color6 == colorB2)) { + product1a = INTERPOLATE (color2, color5); + product1a = INTERPOLATE (color2, product1a); + // product1a = color2; + } else { + product1a = INTERPOLATE (color5, color6); + } + + if ((color6 == colorS2) || (color2 == colorA1)) { + product2b = INTERPOLATE (color2, color3); + product2b = INTERPOLATE (color2, product2b); + // product2b = color2; + } else { + product2b = INTERPOLATE (color2, color3); + } + } else if (color5 == color3 && color2 != color6) { + product2b = product1a = color5; + + if ((colorB1 == color5) || (color3 == colorS1)) { + product1b = INTERPOLATE (color5, color6); + product1b = INTERPOLATE (color5, product1b); + // product1b = color5; + } else { + product1b = INTERPOLATE (color5, color6); + } + + if ((color3 == colorA2) || (color4 == color5)) { + product2a = INTERPOLATE (color5, color2); + product2a = INTERPOLATE (color5, product2a); + // product2a = color5; + } else { + product2a = INTERPOLATE (color2, color3); + } + + } else if (color5 == color3 && color2 == color6) { + register int r = 0; + + r += GetResult (color6, color5, color1, colorA1); + r += GetResult (color6, color5, color4, colorB1); + r += GetResult (color6, color5, colorA2, colorS1); + r += GetResult (color6, color5, colorB2, colorS2); + + if (r > 0) { + product1b = product2a = color2; + product1a = product2b = INTERPOLATE (color5, color6); + } else if (r < 0) { + product2b = product1a = color5; + product1b = product2a = INTERPOLATE (color5, color6); + } else { + product2b = product1a = color5; + product1b = product2a = color2; + } + } else { + product2b = product1a = INTERPOLATE (color2, color6); + product2b = + Q_INTERPOLATE (color3, color3, color3, product2b); + product1a = + Q_INTERPOLATE (color5, color5, color5, product1a); + + product2a = product1b = INTERPOLATE (color5, color3); + product2a = + Q_INTERPOLATE (color2, color2, color2, product2a); + product1b = + Q_INTERPOLATE (color6, color6, color6, product1b); + + // product1a = color5; + // product1b = color6; + // product2a = color2; + // product2b = color3; + } + *(dP) = product1a; + *(dP+1) = product1b; + *(dP + (dstPitch >> 2)) = product2a; + *(dP + (dstPitch >> 2) +1) = product2b; + //*xP = color5; + + bP += inc_bP; + //xP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + //deltaPtr += srcPitch; + } // endof: for (height; height; height--) +} + +void _2xSaI (uint8_t *srcPtr, uint32_t srcPitch, uint8_t *deltaPtr, + uint8_t *dstPtr, uint32_t dstPitch, int width, int height) +{ + uint8_t *dP; + uint16_t *bP; + uint32_t inc_bP; + + { + inc_bP = 1; + + uint32_t Nextline = srcPitch >> 1; + + for (; height; height--) { + bP = (uint16_t *) srcPtr; + dP = dstPtr; + + for (uint32_t finish = width; finish; finish -= inc_bP) { + + register uint32_t colorA, colorB; + uint32_t colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + uint32_t product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - Nextline - 1); + colorE = *(bP - Nextline); + colorF = *(bP - Nextline + 1); + colorJ = *(bP - Nextline + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + Nextline - 1); + colorC = *(bP + Nextline); + colorD = *(bP + Nextline + 1); + colorL = *(bP + Nextline + 2); + + colorM = *(bP + Nextline + Nextline - 1); + colorN = *(bP + Nextline + Nextline); + colorO = *(bP + Nextline + Nextline + 1); + colorP = *(bP + Nextline + Nextline + 2); + + if ((colorA == colorD) && (colorB != colorC)) { + if (((colorA == colorE) && (colorB == colorL)) || + ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ))) { + product = colorA; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorA == colorG) && (colorC == colorO)) || + ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM))) { + product1 = colorA; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorA; + } else if ((colorB == colorC) && (colorA != colorD)) { + if (((colorB == colorF) && (colorA == colorH)) || + ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI))) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorC == colorH) && (colorA == colorF)) || + ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI))) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorB; + } else if ((colorA == colorD) && (colorB == colorC)) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = INTERPOLATE (colorA, colorC); + product = INTERPOLATE (colorA, colorB); + + r += + GetResult1 (colorA, colorB, colorG, colorE, + colorI); + r += + GetResult2 (colorB, colorA, colorK, colorF, + colorJ); + r += + GetResult2 (colorB, colorA, colorH, colorN, + colorM); + r += + GetResult1 (colorA, colorB, colorL, colorO, + colorP); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = + Q_INTERPOLATE (colorA, colorB, colorC, + colorD); + } + } + } else { + product2 = Q_INTERPOLATE (colorA, colorB, colorC, colorD); + + if ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ)) { + product = colorA; + } else if ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI)) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM)) { + product1 = colorA; + } else if ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI)) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + } + +#ifdef WORDS_BIGENDIAN + product = (colorA << 16) | product ; + product1 = (product1 << 16) | product2 ; +#else + product = colorA | (product << 16); + product1 = product1 | (product2 << 16); +#endif + *((int32_t *) dP) = product; + *((uint32_t *) (dP + dstPitch)) = product1; + + bP += inc_bP; + dP += sizeof (uint32_t); + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + deltaPtr += srcPitch; + } // endof: for (height; height; height--) + } +} + +void _2xSaI32 (uint8_t *srcPtr, uint32_t srcPitch, uint8_t * /* deltaPtr */, + uint8_t *dstPtr, uint32_t dstPitch, int width, int height) +{ + uint32_t *dP; + uint32_t *bP; + uint32_t inc_bP = 1; + + uint32_t Nextline = srcPitch >> 2; + + for (; height; height--) { + bP = (uint32_t *) srcPtr; + dP = (uint32_t *) dstPtr; + + for (uint32_t finish = width; finish; finish -= inc_bP) { + register uint32_t colorA, colorB; + uint32_t colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + uint32_t product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - Nextline - 1); + colorE = *(bP - Nextline); + colorF = *(bP - Nextline + 1); + colorJ = *(bP - Nextline + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + Nextline - 1); + colorC = *(bP + Nextline); + colorD = *(bP + Nextline + 1); + colorL = *(bP + Nextline + 2); + + colorM = *(bP + Nextline + Nextline - 1); + colorN = *(bP + Nextline + Nextline); + colorO = *(bP + Nextline + Nextline + 1); + colorP = *(bP + Nextline + Nextline + 2); + + if ((colorA == colorD) && (colorB != colorC)) { + if (((colorA == colorE) && (colorB == colorL)) || + ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ))) { + product = colorA; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorA == colorG) && (colorC == colorO)) || + ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM))) { + product1 = colorA; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorA; + } else if ((colorB == colorC) && (colorA != colorD)) { + if (((colorB == colorF) && (colorA == colorH)) || + ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI))) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if (((colorC == colorH) && (colorA == colorF)) || + ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI))) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + product2 = colorB; + } else if ((colorA == colorD) && (colorB == colorC)) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = INTERPOLATE (colorA, colorC); + product = INTERPOLATE (colorA, colorB); + + r += + GetResult1 (colorA, colorB, colorG, colorE, + colorI); + r += + GetResult2 (colorB, colorA, colorK, colorF, + colorJ); + r += + GetResult2 (colorB, colorA, colorH, colorN, + colorM); + r += + GetResult1 (colorA, colorB, colorL, colorO, + colorP); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = + Q_INTERPOLATE (colorA, colorB, colorC, + colorD); + } + } + } else { + product2 = Q_INTERPOLATE (colorA, colorB, colorC, colorD); + + if ((colorA == colorC) && (colorA == colorF) + && (colorB != colorE) && (colorB == colorJ)) { + product = colorA; + } else if ((colorB == colorE) && (colorB == colorD) + && (colorA != colorF) && (colorA == colorI)) { + product = colorB; + } else { + product = INTERPOLATE (colorA, colorB); + } + + if ((colorA == colorB) && (colorA == colorH) + && (colorG != colorC) && (colorC == colorM)) { + product1 = colorA; + } else if ((colorC == colorG) && (colorC == colorD) + && (colorA != colorH) && (colorA == colorI)) { + product1 = colorC; + } else { + product1 = INTERPOLATE (colorA, colorC); + } + } + *(dP) = colorA; + *(dP + 1) = product; + *(dP + (dstPitch >> 2)) = product1; + *(dP + (dstPitch >> 2) + 1) = product2; + + bP += inc_bP; + dP += 2; + } // end of for ( finish= width etc..) + + srcPtr += srcPitch; + dstPtr += dstPitch << 1; + // deltaPtr += srcPitch; + } // endof: for (height; height; height--) +} + +static uint32_t Bilinear (uint32_t A, uint32_t B, uint32_t x) +{ + unsigned long areaA, areaB; + unsigned long result; + + if (A == B) + return A; + + areaB = (x >> 11) & 0x1f; // reduce 16 bit fraction to 5 bits + areaA = 0x20 - areaB; + + A = (A & redblueMask) | ((A & greenMask) << 16); + B = (B & redblueMask) | ((B & greenMask) << 16); + + result = ((areaA * A) + (areaB * B)) >> 5; + + return (result & redblueMask) | ((result >> 16) & greenMask); +} + +static uint32_t Bilinear4 (uint32_t A, uint32_t B, uint32_t C, uint32_t D, uint32_t x, + uint32_t y) +{ + unsigned long areaA, areaB, areaC, areaD; + unsigned long result, xy; + + x = (x >> 11) & 0x1f; + y = (y >> 11) & 0x1f; + xy = (x * y) >> 5; + + A = (A & redblueMask) | ((A & greenMask) << 16); + B = (B & redblueMask) | ((B & greenMask) << 16); + C = (C & redblueMask) | ((C & greenMask) << 16); + D = (D & redblueMask) | ((D & greenMask) << 16); + + areaA = 0x20 + xy - x - y; + areaB = x - xy; + areaC = y - xy; + areaD = xy; + + result = ((areaA * A) + (areaB * B) + (areaC * C) + (areaD * D)) >> 5; + + return (result & redblueMask) | ((result >> 16) & greenMask); +} + +void Scale_2xSaI (uint8_t *srcPtr, uint32_t srcPitch, uint8_t * /* deltaPtr */, + uint8_t *dstPtr, uint32_t dstPitch, + uint32_t dstWidth, uint32_t dstHeight, int width, int height) +{ + uint8_t *dP; + uint16_t *bP; + + uint32_t w; + uint32_t h; + uint32_t dw; + uint32_t dh; + uint32_t hfinish; + uint32_t wfinish; + + uint32_t Nextline = srcPitch >> 1; + + wfinish = (width - 1) << 16; // convert to fixed point + dw = wfinish / (dstWidth - 1); + hfinish = (height - 1) << 16; // convert to fixed point + dh = hfinish / (dstHeight - 1); + + for (h = 0; h < hfinish; h += dh) { + uint32_t y1, y2; + + y1 = h & 0xffff; // fraction part of fixed point + bP = (uint16_t *) (srcPtr + ((h >> 16) * srcPitch)); + dP = dstPtr; + y2 = 0x10000 - y1; + + w = 0; + + for (; w < wfinish;) { + uint32_t A, B, C, D; + uint32_t E, F, G, H; + uint32_t I, J, K, L; + uint32_t x1, x2, a1, f1, f2; + uint32_t position, product1; + + position = w >> 16; + A = bP[position]; // current pixel + B = bP[position + 1]; // next pixel + C = bP[position + Nextline]; + D = bP[position + Nextline + 1]; + E = bP[position - Nextline]; + F = bP[position - Nextline + 1]; + G = bP[position - 1]; + H = bP[position + Nextline - 1]; + I = bP[position + 2]; + J = bP[position + Nextline + 2]; + K = bP[position + Nextline + Nextline]; + L = bP[position + Nextline + Nextline + 1]; + + x1 = w & 0xffff; // fraction part of fixed point + x2 = 0x10000 - x1; + + /*0*/ + if (A == B && C == D && A == C) + product1 = A; + else /*1*/ if (A == D && B != C) { + f1 = (x1 >> 1) + (0x10000 >> 2); + f2 = (y1 >> 1) + (0x10000 >> 2); + if (y1 <= f1 && A == J && A != E) // close to B + { + a1 = f1 - y1; + product1 = Bilinear (A, B, a1); + } else if (y1 >= f1 && A == G && A != L) // close to C + { + a1 = y1 - f1; + product1 = Bilinear (A, C, a1); + } + else if (x1 >= f2 && A == E && A != J) // close to B + { + a1 = x1 - f2; + product1 = Bilinear (A, B, a1); + } + else if (x1 <= f2 && A == L && A != G) // close to C + { + a1 = f2 - x1; + product1 = Bilinear (A, C, a1); + } + else if (y1 >= x1) // close to C + { + a1 = y1 - x1; + product1 = Bilinear (A, C, a1); + } + else if (y1 <= x1) // close to B + { + a1 = x1 - y1; + product1 = Bilinear (A, B, a1); + } + } + else + /*2*/ + if (B == C && A != D) + { + f1 = (x1 >> 1) + (0x10000 >> 2); + f2 = (y1 >> 1) + (0x10000 >> 2); + if (y2 >= f1 && B == H && B != F) // close to A + { + a1 = y2 - f1; + product1 = Bilinear (B, A, a1); + } + else if (y2 <= f1 && B == I && B != K) // close to D + { + a1 = f1 - y2; + product1 = Bilinear (B, D, a1); + } + else if (x2 >= f2 && B == F && B != H) // close to A + { + a1 = x2 - f2; + product1 = Bilinear (B, A, a1); + } + else if (x2 <= f2 && B == K && B != I) // close to D + { + a1 = f2 - x2; + product1 = Bilinear (B, D, a1); + } + else if (y2 >= x1) // close to A + { + a1 = y2 - x1; + product1 = Bilinear (B, A, a1); + } + else if (y2 <= x1) // close to D + { + a1 = x1 - y2; + product1 = Bilinear (B, D, a1); + } + } + /*3*/ + else + { + product1 = Bilinear4 (A, B, C, D, x1, y1); + } + + //end First Pixel + *(uint32_t *) dP = product1; + dP += 2; + w += dw; + } + dstPtr += dstPitch; + } +} diff --git a/snesfilter/Makefile b/snesfilter/Makefile new file mode 100644 index 00000000..089b86ee --- /dev/null +++ b/snesfilter/Makefile @@ -0,0 +1,89 @@ +include nall/Makefile + +qtlibs := QtCore QtGui +include nall/qt/Makefile + +c := $(compiler) -std=gnu99 +cpp := $(subst cc,++,$(compiler)) -std=gnu++0x +flags := -O3 -I. -Iobj -fomit-frame-pointer $(qtinc) +link := + +ifeq ($(platform),x) + flags := -fPIC -fopenmp $(flags) + link += -s -fopenmp -lpthread -lgomp +else ifeq ($(platform),osx) + flags := -fPIC -fopenmp $(flags) + link += -fopenmp -lpthread -lgomp +else ifeq ($(platform),win) + flags := -fopenmp $(flags) + link += -fopenmp -lpthread +endif + +objects := snesfilter + +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 -c $< -o $@ \ + ) \ + ) \ + ) + +%.o: $<; $(call compile) + +all: build; + +objects := $(patsubst %,obj/%.o,$(objects)) +moc_headers := $(call rwildcard,./,%.moc.hpp) +moc_objects := $(foreach f,$(moc_headers),obj/$(notdir $(patsubst %.moc.hpp,%.moc,$f))) + +# automatically run moc on all .moc.hpp (MOC header) files +%.moc: $<; $(moc) -i $< -o $@ + +# automatically generate %.moc build rules +__list = $(moc_headers) +$(foreach f,$(moc_objects), \ + $(eval __file = $(word 1,$(__list))) \ + $(eval __list = $(wordlist 2,$(words $(__list)),$(__list))) \ + $(eval $f: $(__file)) \ +) + +################## +### snesfilter ### +################## + +obj/snesfilter.o: snesfilter.cpp * + +############### +### targets ### +############### + +build: $(moc_objects) $(objects) +ifeq ($(platform),x) + ar rcs libsnesfilter.a $(objects) + $(cpp) $(link) -o libsnesfilter.so -shared -Wl,-soname,libsnesfilter.so.1 $(objects) $(qtlib) +else ifeq ($(platform),osx) + ar rcs libsnesfilter.a $(objects) + $(cpp) $(link) -o libsnesfilter.dylib -shared -dynamiclib $(objects) $(qtlib) +else ifeq ($(platform),win) + $(cpp) $(link) -o snesfilter.dll -shared -Wl,--out-implib,libsnesfilter.a $(objects) $(qtlib) +endif + +install: +ifeq ($(platform),x) + install -D -m 755 libsnesfilter.a $(DESTDIR)$(prefix)/lib + install -D -m 755 libsnesfilter.so $(DESTDIR)$(prefix)/lib + ldconfig -n $(DESTDIR)$(prefix)/lib +else ifeq ($(platform),osx) + cp libsnesfilter.dylib /usr/local/lib/libsnesfilter.dylib +endif + +clean: + -@$(call delete,obj/*.o) + -@$(call delete,obj/*.moc) + -@$(call delete,libsnesfilter.a) + -@$(call delete,libsnesfilter.so) + -@$(call delete,libsnesfilter.dylib) + -@$(call delete,snesfilter.dll) diff --git a/snesfilter/cc.bat b/snesfilter/cc.bat new file mode 100644 index 00000000..8359a530 --- /dev/null +++ b/snesfilter/cc.bat @@ -0,0 +1,2 @@ +@mingw32-make +@pause \ No newline at end of file diff --git a/snesfilter/clean.bat b/snesfilter/clean.bat new file mode 100644 index 00000000..d8bb7e0b --- /dev/null +++ b/snesfilter/clean.bat @@ -0,0 +1 @@ +@mingw32-make clean diff --git a/snesfilter/direct/direct.cpp b/snesfilter/direct/direct.cpp new file mode 100644 index 00000000..d5214367 --- /dev/null +++ b/snesfilter/direct/direct.cpp @@ -0,0 +1,32 @@ +#include "direct.hpp" + +void DirectFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = width; + outheight = height; +} + +void DirectFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + *output++ = colortable[p]; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + } + } + input += pitch - width; + output += outpitch - width; + } +} diff --git a/snesfilter/direct/direct.hpp b/snesfilter/direct/direct.hpp new file mode 100644 index 00000000..d990b4fa --- /dev/null +++ b/snesfilter/direct/direct.hpp @@ -0,0 +1,5 @@ +class DirectFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); +} filter_direct; diff --git a/snesfilter/hq2x/hq2x.cpp b/snesfilter/hq2x/hq2x.cpp new file mode 100644 index 00000000..7a285e8c --- /dev/null +++ b/snesfilter/hq2x/hq2x.cpp @@ -0,0 +1,203 @@ +//HQ2x filter +//authors: byuu and blargg +//license: public domain +// +//note: this is a clean reimplementation of the original HQ2x filter, which was +//written by Maxim Stepin (MaxSt). it is not 100% identical, but very similar. + +#include "hq2x.hpp" + +void HQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + if(height > 240) return filter_direct.size(outwidth, outheight, width, height); + outwidth = (width <= 256) ? width * 2 : width; + outheight = (height <= 240) ? height * 2 : height; +} + +void HQ2xFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + #pragma omp parallel for + for(unsigned y = 0; y < height; y++) { + const uint16_t *in = input + y * pitch; + uint32_t *out0 = output + y * pitch; + uint32_t *out1 = output + y * pitch + outpitch; + + unsigned linewidth = line[y]; + + if(linewidth == 256) { + int prevline = (y == 0) || (linewidth != line[y - 1]) ? 0 : pitch; + int nextline = (y == height - 1) || (linewidth != line[y + 1]) ? 0 : pitch; + + in++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + + for(unsigned x = 1; x < 256 - 1; x++) { + uint16_t A = *(in - prevline - 1); + uint16_t B = *(in - prevline + 0); + uint16_t C = *(in - prevline + 1); + uint16_t D = *(in - 1); + uint16_t E = *(in + 0); + uint16_t F = *(in + 1); + uint16_t G = *(in + nextline - 1); + uint16_t H = *(in + nextline + 0); + uint16_t I = *(in + nextline + 1); + uint32_t e = yuvTable[E] + diff_offset; + + uint8_t pattern; + pattern = diff(e, A) << 0; + pattern |= diff(e, B) << 1; + pattern |= diff(e, C) << 2; + pattern |= diff(e, D) << 3; + pattern |= diff(e, F) << 4; + pattern |= diff(e, G) << 5; + pattern |= diff(e, H) << 6; + pattern |= diff(e, I) << 7; + + *(out0 + 0) = colortable[blend(hqTable[pattern], E, A, B, D, F, H)]; pattern = rotate[pattern]; + *(out0 + 1) = colortable[blend(hqTable[pattern], E, C, F, B, H, D)]; pattern = rotate[pattern]; + *(out1 + 1) = colortable[blend(hqTable[pattern], E, I, H, F, D, B)]; pattern = rotate[pattern]; + *(out1 + 0) = colortable[blend(hqTable[pattern], E, G, D, H, B, F)]; + + in++; + out0 += 2; + out1 += 2; + } + + in++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + } else { + for(unsigned x = 0; x < 512; x++) { + *out0++ = *out1++ = colortable[*in++]; + } + } + } +} + +HQ2xFilter::HQ2xFilter() { + yuvTable = new uint32_t[32768]; + + for(unsigned i = 0; i < 32768; i++) { + uint8_t R = (i >> 0) & 31; + uint8_t G = (i >> 5) & 31; + uint8_t B = (i >> 10) & 31; + + //bgr555->bgr888 + double r = (R << 3) | (R >> 2); + double g = (G << 3) | (G >> 2); + double b = (B << 3) | (B >> 2); + + //bgr888->yuv888 + double y = (r + g + b) * (0.25f * (63.5f / 48.0f)); + double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f); + double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f); + + yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v); + } + + for(unsigned n = 0; n < 256; n++) { + rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88) + | ((n & 0x01) << 5) | ((n & 0x08) << 3) + | ((n & 0x10) >> 3) | ((n & 0x80) >> 5); + } +} + +HQ2xFilter::~HQ2xFilter() { + delete[] yuvTable; +} + +bool HQ2xFilter::same(uint16_t x, uint16_t y) { + return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask); +} + +bool HQ2xFilter::diff(uint32_t x, uint16_t y) { + return ((x - yuvTable[y]) & diff_mask); +} + +void HQ2xFilter::grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; } +uint16_t HQ2xFilter::pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); } + +uint16_t HQ2xFilter::blend1(uint32_t A, uint32_t B) { + grow(A); grow(B); + A = (A * 3 + B) >> 2; + return pack(A); +} + +uint16_t HQ2xFilter::blend2(uint32_t A, uint32_t B, uint32_t C) { + grow(A); grow(B); grow(C); + return pack((A * 2 + B + C) >> 2); +} + +uint16_t HQ2xFilter::blend3(uint32_t A, uint32_t B, uint32_t C) { + grow(A); grow(B); grow(C); + return pack((A * 5 + B * 2 + C) >> 3); +} + +uint16_t HQ2xFilter::blend4(uint32_t A, uint32_t B, uint32_t C) { + grow(A); grow(B); grow(C); + return pack((A * 6 + B + C) >> 3); +} + +uint16_t HQ2xFilter::blend5(uint32_t A, uint32_t B, uint32_t C) { + grow(A); grow(B); grow(C); + return pack((A * 2 + (B + C) * 3) >> 3); +} + +uint16_t HQ2xFilter::blend6(uint32_t A, uint32_t B, uint32_t C) { + grow(A); grow(B); grow(C); + return pack((A * 14 + B + C) >> 4); +} + +uint16_t HQ2xFilter::blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) { + switch(rule) { default: + case 0: return E; + case 1: return blend1(E, A); + case 2: return blend1(E, D); + case 3: return blend1(E, B); + case 4: return blend2(E, D, B); + case 5: return blend2(E, A, B); + case 6: return blend2(E, A, D); + case 7: return blend3(E, B, D); + case 8: return blend3(E, D, B); + case 9: return blend4(E, D, B); + case 10: return blend5(E, D, B); + case 11: return blend6(E, D, B); + case 12: return same(B, D) ? blend2(E, D, B) : E; + case 13: return same(B, D) ? blend5(E, D, B) : E; + case 14: return same(B, D) ? blend6(E, D, B) : E; + case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A); + case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A); + case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A); + case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D); + case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B); + } +} + +const uint8_t HQ2xFilter::hqTable[256] = { + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13, + 4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14, + 4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14, +}; diff --git a/snesfilter/hq2x/hq2x.hpp b/snesfilter/hq2x/hq2x.hpp new file mode 100644 index 00000000..5aaab806 --- /dev/null +++ b/snesfilter/hq2x/hq2x.hpp @@ -0,0 +1,30 @@ +class HQ2xFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + + HQ2xFilter(); + ~HQ2xFilter(); + +private: + enum { + diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407, + diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0, + }; + + static const uint8_t hqTable[256]; + uint32_t *yuvTable; + uint8_t rotate[256]; + + alwaysinline bool same(uint16_t x, uint16_t y); + alwaysinline bool diff(uint32_t x, uint16_t y); + alwaysinline void grow(uint32_t &n); + alwaysinline uint16_t pack(uint32_t n); + alwaysinline uint16_t blend1(uint32_t A, uint32_t B); + alwaysinline uint16_t blend2(uint32_t A, uint32_t B, uint32_t C); + alwaysinline uint16_t blend3(uint32_t A, uint32_t B, uint32_t C); + alwaysinline uint16_t blend4(uint32_t A, uint32_t B, uint32_t C); + alwaysinline uint16_t blend5(uint32_t A, uint32_t B, uint32_t C); + alwaysinline uint16_t blend6(uint32_t A, uint32_t B, uint32_t C); + alwaysinline uint16_t blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H); +} filter_hq2x; diff --git a/snesfilter/lq2x/lq2x.cpp b/snesfilter/lq2x/lq2x.cpp new file mode 100644 index 00000000..ead1cf58 --- /dev/null +++ b/snesfilter/lq2x/lq2x.cpp @@ -0,0 +1,61 @@ +#include "lq2x.hpp" + +void LQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + if(height > 240) return filter_direct.size(outwidth, outheight, width, height); + outwidth = (width <= 256) ? width * 2 : width; + outheight = (height <= 240) ? height * 2 : height; +} + +void LQ2xFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t *out0 = output; + uint32_t *out1 = output + outpitch; + + for(unsigned y = 0; y < height; y++) { + unsigned linewidth = line[y]; + + if(linewidth == 256) { + int prevline = (y == 0) || (linewidth != line[y - 1]) ? 0 : pitch; + int nextline = (y == height - 1) || (linewidth != line[y + 1]) ? 0 : pitch; + + for(unsigned x = 0; x < 256; x++) { + uint16_t A = *(input - prevline); + uint16_t B = (x > 0) ? *(input - 1) : *input; + uint16_t C = *input; + uint16_t D = (x < 255) ? *(input + 1) : *input; + uint16_t E = *(input++ + nextline); + uint32_t c = colortable[C]; + + if(A != E && B != D) { + *out0++ = (A == B ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c); + *out0++ = (A == D ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c); + *out1++ = (E == B ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c); + *out1++ = (E == D ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c); + } else { + *out0++ = c; + *out0++ = c; + *out1++ = c; + *out1++ = c; + } + } + } else { + for(unsigned x = 0; x < 512; x++) { + *out0++ = *out1++ = colortable[*input++]; + } + } + + input += pitch - linewidth; + out0 += outpitch + outpitch - 512; + out1 += outpitch + outpitch - 512; + } +} diff --git a/snesfilter/lq2x/lq2x.hpp b/snesfilter/lq2x/lq2x.hpp new file mode 100644 index 00000000..07d95639 --- /dev/null +++ b/snesfilter/lq2x/lq2x.hpp @@ -0,0 +1,5 @@ +class LQ2xFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); +} filter_lq2x; diff --git a/snesfilter/nall/Makefile b/snesfilter/nall/Makefile new file mode 100644 index 00000000..8149bf15 --- /dev/null +++ b/snesfilter/nall/Makefile @@ -0,0 +1,107 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),osx) + compiler := gcc-4.2 + else + compiler := gcc + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snesfilter/nall/algorithm.hpp b/snesfilter/nall/algorithm.hpp new file mode 100644 index 00000000..cdc48dcf --- /dev/null +++ b/snesfilter/nall/algorithm.hpp @@ -0,0 +1,23 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } +} + +#endif diff --git a/snesfilter/nall/any.hpp b/snesfilter/nall/any.hpp new file mode 100644 index 00000000..b31cff3c --- /dev/null +++ b/snesfilter/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snesfilter/nall/array.hpp b/snesfilter/nall/array.hpp new file mode 100644 index 00000000..c1d33fd1 --- /dev/null +++ b/snesfilter/nall/array.hpp @@ -0,0 +1,120 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void add(const T data) { + operator[](buffersize) = data; + } + + signed find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i; + return -1; //not found + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snesfilter/nall/base64.hpp b/snesfilter/nall/base64.hpp new file mode 100644 index 00000000..e41c87b7 --- /dev/null +++ b/snesfilter/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snesfilter/nall/bit.hpp b/snesfilter/nall/bit.hpp new file mode 100644 index 00000000..169fc144 --- /dev/null +++ b/snesfilter/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snesfilter/nall/concept.hpp b/snesfilter/nall/concept.hpp new file mode 100644 index 00000000..2949cd5e --- /dev/null +++ b/snesfilter/nall/concept.hpp @@ -0,0 +1,15 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; +} + +#endif diff --git a/snesfilter/nall/config.hpp b/snesfilter/nall/config.hpp new file mode 100644 index 00000000..31ae4e00 --- /dev/null +++ b/snesfilter/nall/config.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + part.qsplit(" = ", line[i]); + trim(part[0]); + trim(part[1]); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snesfilter/nall/crc32.hpp b/snesfilter/nall/crc32.hpp new file mode 100644 index 00000000..ad36fbf6 --- /dev/null +++ b/snesfilter/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[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 + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/snesfilter/nall/detect.hpp b/snesfilter/nall/detect.hpp new file mode 100644 index 00000000..b4991aaf --- /dev/null +++ b/snesfilter/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snesfilter/nall/dictionary.hpp b/snesfilter/nall/dictionary.hpp new file mode 100644 index 00000000..f14e2095 --- /dev/null +++ b/snesfilter/nall/dictionary.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + trim(part[0]); + trim(part[1]); + + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snesfilter/nall/dl.hpp b/snesfilter/nall/dl.hpp new file mode 100644 index 00000000..22acf51f --- /dev/null +++ b/snesfilter/nall/dl.hpp @@ -0,0 +1,119 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 8]; + strcpy(t, name); + strcat(t, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(t)); + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snesfilter/nall/endian.hpp b/snesfilter/nall/endian.hpp new file mode 100644 index 00000000..40d15633 --- /dev/null +++ b/snesfilter/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snesfilter/nall/file.hpp b/snesfilter/nall/file.hpp new file mode 100644 index 00000000..4c8ca8ee --- /dev/null +++ b/snesfilter/nall/file.hpp @@ -0,0 +1,259 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snesfilter/nall/filemap.hpp b/snesfilter/nall/filemap.hpp new file mode 100644 index 00000000..a05f0eb7 --- /dev/null +++ b/snesfilter/nall/filemap.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode) { + default: return false; + case mode_read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode_write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode mode) { + int open_flags, mmap_flags; + + switch(mode) { + default: return false; + case mode_read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode_write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode_readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode_writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snesfilter/nall/foreach.hpp b/snesfilter/nall/foreach.hpp new file mode 100644 index 00000000..ea975b84 --- /dev/null +++ b/snesfilter/nall/foreach.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = foreach_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#include +#include +#include + +namespace nall { + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snesfilter/nall/function.hpp b/snesfilter/nall/function.hpp new file mode 100644 index 00000000..3f0f704e --- /dev/null +++ b/snesfilter/nall/function.hpp @@ -0,0 +1,102 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include +#include + +namespace nall { + template class function; + + template + class function { + private: + struct base1 { virtual void func1(P...) {} }; + struct base2 { virtual void func2(P...) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*callback)(const data_t&, P...); + union { + R (*callback_global)(P...); + struct { + R (derived::*callback_member)(P...); + void *object; + }; + }; + } data; + + static R callback_global(const data_t &data, P... p) { + return data.callback_global(p...); + } + + template + static R callback_member(const data_t &data, P... p) { + return (((C*)data.object)->*((R (C::*&)(P...))data.callback_member))(p...); + } + + public: + R operator()(P... p) const { return data.callback(data, p...); } + operator bool() const { return data.callback; } + void reset() { data.callback = 0; } + + function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { operator=(source); } + + //no pointer + function() { + data.callback = 0; + } + + //symbolic link pointer (nall/dl.hpp::sym, etc) + function(void *callback) { + data.callback = callback ? &callback_global : 0; + data.callback_global = (R (*)(P...))callback; + } + + //global function pointer + function(R (*callback)(P...)) { + data.callback = &callback_global; + data.callback_global = callback; + } + + //member function pointer + template + function(R (C::*callback)(P...), C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = callback; + data.object = object; + } + + //const member function pointer + template + function(R (C::*callback)(P...) const, C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = (R (C::*&)(P...))callback; + data.object = object; + } + + //lambda function pointer + template + function(T callback) { + static_assert(std::is_same::type>::value, "lambda mismatch"); + data.callback = &callback_global; + data.callback_global = (R (*)(P...))callback; + } + }; + + //bind functions to ease construction and assignment of function() with more than one argument + + template + function bind(R (C::*callback)(P...), C *object) { + return function(callback, object); + } + + template + function bind(R (C::*callback)(P...) const, C *object) { + return function(callback, object); + } +} + +#endif diff --git a/snesfilter/nall/input.hpp b/snesfilter/nall/input.hpp new file mode 100644 index 00000000..b3ce9ebf --- /dev/null +++ b/snesfilter/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + ltrim(s, "KB"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + ltrim(s, "MS"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + ltrim(s, "JP"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snesfilter/nall/lzss.hpp b/snesfilter/nall/lzss.hpp new file mode 100644 index 00000000..202bc814 --- /dev/null +++ b/snesfilter/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snesfilter/nall/moduloarray.hpp b/snesfilter/nall/moduloarray.hpp new file mode 100644 index 00000000..be549ae9 --- /dev/null +++ b/snesfilter/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snesfilter/nall/platform.hpp b/snesfilter/nall/platform.hpp new file mode 100644 index 00000000..68ed37ce --- /dev/null +++ b/snesfilter/nall/platform.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface +#else + #include + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +#endif + diff --git a/snesfilter/nall/priorityqueue.hpp b/snesfilter/nall/priorityqueue.hpp new file mode 100644 index 00000000..7104e791 --- /dev/null +++ b/snesfilter/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snesfilter/nall/property.hpp b/snesfilter/nall/property.hpp new file mode 100644 index 00000000..6fd33acd --- /dev/null +++ b/snesfilter/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snesfilter/nall/qt/Makefile b/snesfilter/nall/qt/Makefile new file mode 100644 index 00000000..69e84960 --- /dev/null +++ b/snesfilter/nall/qt/Makefile @@ -0,0 +1,55 @@ +# requires nall/Makefile + +# imports: +# $(qtlibs) -- list of Qt components to link against + +# exports the following symbols: +# $(moc) -- meta-object compiler +# $(rcc) -- resource compiler +# $(qtinc) -- includes for compiling +# $(qtlib) -- libraries for linking + +ifeq ($(moc),) +moc := moc +endif + +ifeq ($(rcc),) +rcc := rcc +endif + +ifeq ($(platform),x) + qtinc := `pkg-config --cflags $(qtlibs)` + qtlib := `pkg-config --libs $(qtlibs)` +else ifeq ($(platform),osx) + qtinc := $(foreach lib,$(qtlibs),-I/Library/Frameworks/$(lib).framework/Versions/4/Headers) + + qtlib := -L/Library/Frameworks + qtlib += $(foreach lib,$(qtlibs),-framework $(lib)) + qtlib += -framework Carbon + qtlib += -framework Cocoa + qtlib += -framework OpenGL + qtlib += -framework AppKit + qtlib += -framework ApplicationServices +else ifeq ($(platform),win) + ifeq ($(qtpath),) + # find Qt install directory from PATH environment variable + qtpath := $(foreach path,$(subst ;, ,$(PATH)),$(if $(wildcard $(path)/$(moc).exe),$(path))) + qtpath := $(strip $(qtpath)) + qtpath := $(subst \,/,$(qtpath)) + qtpath := $(patsubst %/bin,%,$(qtpath)) + endif + + qtinc := -I$(qtpath)/include + qtinc += $(foreach lib,$(qtlibs),-I$(qtpath)/include/$(lib)) + + qtlib := -L$(qtpath)/lib + qtlib += -L$(qtpath)/plugins/imageformats + + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + qtlib += -lmingw32 -lqtmain -lcomdlg32 -loleaut32 -limm32 -lwinmm + qtlib += -lwinspool -lmsimg32 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32 + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + + # optional image-file support: + # qtlib += -lqjpeg -lqmng +endif diff --git a/snesfilter/nall/qt/check-action.moc.hpp b/snesfilter/nall/qt/check-action.moc.hpp new file mode 100644 index 00000000..db378fe9 --- /dev/null +++ b/snesfilter/nall/qt/check-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_CHECKACTION_HPP +#define NALL_QT_CHECKACTION_HPP + +namespace nall { + +class CheckAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + CheckAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool CheckAction::isChecked() const { + return checked; +} + +inline void CheckAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-check-on.png")); + else setIcon(QIcon(":/16x16/item-check-off.png")); +} + +inline void CheckAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline CheckAction::CheckAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/snesfilter/nall/qt/concept.hpp b/snesfilter/nall/qt/concept.hpp new file mode 100644 index 00000000..51cacef4 --- /dev/null +++ b/snesfilter/nall/qt/concept.hpp @@ -0,0 +1,10 @@ +#ifndef NALL_QT_CONCEPT_HPP +#define NALL_QT_CONCEPT_HPP + +#include + +namespace nall { + template struct has_count> { enum { value = true }; }; +} + +#endif diff --git a/snesfilter/nall/qt/file-dialog.moc.hpp b/snesfilter/nall/qt/file-dialog.moc.hpp new file mode 100644 index 00000000..bcccfaf5 --- /dev/null +++ b/snesfilter/nall/qt/file-dialog.moc.hpp @@ -0,0 +1,392 @@ +#ifndef NALL_QT_FILEDIALOG_HPP +#define NALL_QT_FILEDIALOG_HPP + +#include +#include +#include + +namespace nall { + +class FileDialog; + +class NewFolderDialog : public Window { + Q_OBJECT + +public: + void show(); + NewFolderDialog(FileDialog*); + +protected slots: + void createFolderAction(); + +protected: + FileDialog *parent; + QVBoxLayout *layout; + QLineEdit *folderNameEdit; + QHBoxLayout *controlLayout; + QPushButton *okButton; + QPushButton *cancelButton; +}; + +class FileView : public QListView { + Q_OBJECT + +protected: + void keyPressEvent(QKeyEvent*); + +signals: + void changed(const QModelIndex&); + void browseUp(); + +protected slots: + void currentChanged(const QModelIndex&, const QModelIndex&); +}; + +class FileDialog : public Window { + Q_OBJECT + +public: + void showLoad(); + void showSave(); + void showFolder(); + + void setPath(string path); + void setNameFilters(const string &filters); + FileDialog(); + +signals: + void changed(const string&); + void activated(const string&); + void accepted(const string&); + void rejected(); + +protected slots: + void fileViewChange(const QModelIndex&); + void fileViewActivate(const QModelIndex&); + void pathBoxChanged(); + void filterBoxChanged(); + void createNewFolder(); + void browseUp(); + void acceptAction(); + void rejectAction(); + +protected: + NewFolderDialog *newFolderDialog; + QVBoxLayout *layout; + QHBoxLayout *navigationLayout; + QComboBox *pathBox; + QPushButton *newFolderButton; + QPushButton *upFolderButton; + QHBoxLayout *browseLayout; + QFileSystemModel *fileSystemModel; + FileView *fileView; + QGroupBox *previewFrame; + QLineEdit *fileNameEdit; + QHBoxLayout *controlLayout; + QComboBox *filterBox; + QPushButton *optionsButton; + QPushButton *acceptButton; + QPushButton *rejectButton; + bool lock; + void createFolderAction(const string &name); + void closeEvent(QCloseEvent*); + + friend class NewFolderDialog; +}; + +inline void NewFolderDialog::show() { + folderNameEdit->setText(""); + Window::show(); + folderNameEdit->setFocus(); +} + +inline void NewFolderDialog::createFolderAction() { + string name = folderNameEdit->text().toUtf8().constData(); + if(name == "") { + folderNameEdit->setFocus(); + } else { + parent->createFolderAction(name); + close(); + } +} + +inline NewFolderDialog::NewFolderDialog(FileDialog *fileDialog) : parent(fileDialog) { + setMinimumWidth(240); + setWindowTitle("Create New Folder"); + + layout = new QVBoxLayout; + layout->setAlignment(Qt::AlignTop); + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + folderNameEdit = new QLineEdit; + layout->addWidget(folderNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + okButton = new QPushButton("Ok"); + controlLayout->addWidget(okButton); + + cancelButton = new QPushButton("Cancel"); + controlLayout->addWidget(cancelButton); + + connect(folderNameEdit, SIGNAL(returnPressed()), this, SLOT(createFolderAction())); + connect(okButton, SIGNAL(released()), this, SLOT(createFolderAction())); + connect(cancelButton, SIGNAL(released()), this, SLOT(close())); +} + +inline void FileView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QAbstractItemView::currentChanged(current, previous); + emit changed(current); +} + +inline void FileView::keyPressEvent(QKeyEvent *event) { + //enhance consistency: force OS X to act like Windows and Linux; enter = activate item + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + emit activated(currentIndex()); + return; + } + + //simulate popular file manager behavior; backspace = go up one directory + if(event->key() == Qt::Key_Backspace) { + emit browseUp(); + return; + } + + //fallback: unrecognized keypresses get handled by the widget itself + QListView::keyPressEvent(event); +} + +inline void FileDialog::showLoad() { + acceptButton->setText("Load"); + fileNameEdit->hide(); + filterBox->show(); + show(); +} + +inline void FileDialog::showSave() { + acceptButton->setText("Save"); + fileNameEdit->show(); + filterBox->show(); + show(); +} + +inline void FileDialog::showFolder() { + acceptButton->setText("Choose"); + fileNameEdit->hide(); + filterBox->hide(); + setNameFilters("Folders ()"); + show(); +} + +inline void FileDialog::fileViewChange(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(path == fileSystemModel->rootPath().toUtf8().constData()) path = ""; + fileNameEdit->setText(notdir(path)); + emit changed(path); +} + +inline void FileDialog::fileViewActivate(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(fileSystemModel->isDir(index)) { + emit activated(path); + setPath(path); + } else { + emit activated(path); + close(); + } +} + +inline void FileDialog::pathBoxChanged() { + if(lock) return; + setPath(pathBox->currentText().toUtf8().constData()); +} + +inline void FileDialog::filterBoxChanged() { + if(lock) return; + string filters = filterBox->currentText().toUtf8().constData(); + if(filters.length() == 0) { + fileSystemModel->setNameFilters(QStringList() << "*"); + } else { + filters = substr(filters, strpos(filters, "(")); + ltrim(filters, "("); + rtrim(filters, ")"); + lstring part; + part.split(" ", filters); + QStringList list; + for(unsigned i = 0; i < part.size(); i++) list << part[i]; + fileSystemModel->setNameFilters(list); + } +} + +inline void FileDialog::createNewFolder() { + newFolderDialog->show(); +} + +inline void FileDialog::browseUp() { + if(pathBox->count() > 1) pathBox->setCurrentIndex(1); +} + +inline void FileDialog::setPath(string path) { + lock = true; + newFolderDialog->close(); + + if(QDir(path).exists()) { + newFolderButton->setEnabled(true); + } else { + newFolderButton->setEnabled(false); + path = ""; + } + + fileSystemModel->setRootPath(path); + fileView->setRootIndex(fileSystemModel->index(path)); + fileView->setCurrentIndex(fileView->rootIndex()); + fileView->setFocus(); + + pathBox->clear(); + if(path.length() > 0) { + QDir directory(path); + while(true) { + pathBox->addItem(directory.absolutePath()); + if(directory.isRoot()) break; + directory.cdUp(); + } + } + pathBox->addItem(""); + fileNameEdit->setText(""); + + lock = false; +} + +inline void FileDialog::setNameFilters(const string &filters) { + lock = true; + + lstring list; + list.split("\n", filters); + + filterBox->clear(); + for(unsigned i = 0; i < list.size(); i++) { + filterBox->addItem(list[i]); + } + + lock = false; + filterBoxChanged(); +} + +inline void FileDialog::acceptAction() { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(fileNameEdit->text().toUtf8().constData()); + rtrim(path, "/"); + if(QDir(path).exists()) { + emit accepted(path); + setPath(path); + } else { + emit accepted(path); + close(); + } +} + +inline void FileDialog::rejectAction() { + emit rejected(); + close(); +} + +inline void FileDialog::createFolderAction(const string &name) { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(name); + mkdir(path, 0755); +} + +inline void FileDialog::closeEvent(QCloseEvent *event) { + newFolderDialog->close(); + Window::closeEvent(event); +} + +inline FileDialog::FileDialog() { + newFolderDialog = new NewFolderDialog(this); + resize(640, 360); + + layout = new QVBoxLayout; + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + navigationLayout = new QHBoxLayout; + layout->addLayout(navigationLayout); + + pathBox = new QComboBox; + pathBox->setEditable(true); + pathBox->setMinimumContentsLength(16); + pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + pathBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + navigationLayout->addWidget(pathBox); + + newFolderButton = new QPushButton; + newFolderButton->setIconSize(QSize(16, 16)); + newFolderButton->setIcon(QIcon(":/16x16/folder-new.png")); + navigationLayout->addWidget(newFolderButton); + + upFolderButton = new QPushButton; + upFolderButton->setIconSize(QSize(16, 16)); + upFolderButton->setIcon(QIcon(":/16x16/go-up.png")); + navigationLayout->addWidget(upFolderButton); + + browseLayout = new QHBoxLayout; + layout->addLayout(browseLayout); + + fileSystemModel = new QFileSystemModel; + fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + fileSystemModel->setNameFilterDisables(false); + + fileView = new FileView; + fileView->setMinimumWidth(320); + fileView->setModel(fileSystemModel); + fileView->setIconSize(QSize(16, 16)); + browseLayout->addWidget(fileView); + + previewFrame = new QGroupBox; + previewFrame->hide(); + browseLayout->addWidget(previewFrame); + + fileNameEdit = new QLineEdit; + layout->addWidget(fileNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + filterBox = new QComboBox; + filterBox->setMinimumContentsLength(16); + filterBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + filterBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + controlLayout->addWidget(filterBox); + + optionsButton = new QPushButton("Options"); + optionsButton->hide(); + controlLayout->addWidget(optionsButton); + + acceptButton = new QPushButton("Ok"); + controlLayout->addWidget(acceptButton); + + rejectButton = new QPushButton("Cancel"); + controlLayout->addWidget(rejectButton); + + lock = false; + connect(pathBox, SIGNAL(currentIndexChanged(int)), this, SLOT(pathBoxChanged())); + connect(newFolderButton, SIGNAL(released()), this, SLOT(createNewFolder())); + connect(upFolderButton, SIGNAL(released()), this, SLOT(browseUp())); + connect(fileView, SIGNAL(changed(const QModelIndex&)), this, SLOT(fileViewChange(const QModelIndex&))); + connect(fileView, SIGNAL(activated(const QModelIndex&)), this, SLOT(fileViewActivate(const QModelIndex&))); + connect(fileView, SIGNAL(browseUp()), this, SLOT(browseUp())); + connect(fileNameEdit, SIGNAL(returnPressed()), this, SLOT(acceptAction())); + connect(filterBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterBoxChanged())); + connect(acceptButton, SIGNAL(released()), this, SLOT(acceptAction())); + connect(rejectButton, SIGNAL(released()), this, SLOT(rejectAction())); +} + +} + +#endif diff --git a/snesfilter/nall/qt/hex-editor.moc.hpp b/snesfilter/nall/qt/hex-editor.moc.hpp new file mode 100644 index 00000000..d59f4be9 --- /dev/null +++ b/snesfilter/nall/qt/hex-editor.moc.hpp @@ -0,0 +1,173 @@ +#ifndef NALL_QT_HEXEDITOR_HPP +#define NALL_QT_HEXEDITOR_HPP + +#include +#include +#include + +namespace nall { + +class HexEditor : public QTextEdit { + Q_OBJECT + +public: + function reader; + function writer; + + void setColumns(unsigned columns); + void setRows(unsigned rows); + void setOffset(unsigned offset); + void setSize(unsigned size); + unsigned lineWidth() const; + void refresh(); + + HexEditor(); + +protected slots: + void scrolled(); + +protected: + QHBoxLayout *layout; + QScrollBar *scrollBar; + unsigned editorColumns; + unsigned editorRows; + unsigned editorOffset; + unsigned editorSize; + bool lock; + + void keyPressEvent(QKeyEvent*); +}; + +inline void HexEditor::keyPressEvent(QKeyEvent *event) { + QTextCursor cursor = textCursor(); + unsigned x = cursor.position() % lineWidth(); + unsigned y = cursor.position() / lineWidth(); + + int hexCode = -1; + switch(event->key()) { + case Qt::Key_0: hexCode = 0; break; + case Qt::Key_1: hexCode = 1; break; + case Qt::Key_2: hexCode = 2; break; + case Qt::Key_3: hexCode = 3; break; + case Qt::Key_4: hexCode = 4; break; + case Qt::Key_5: hexCode = 5; break; + case Qt::Key_6: hexCode = 6; break; + case Qt::Key_7: hexCode = 7; break; + case Qt::Key_8: hexCode = 8; break; + case Qt::Key_9: hexCode = 9; break; + case Qt::Key_A: hexCode = 10; break; + case Qt::Key_B: hexCode = 11; break; + case Qt::Key_C: hexCode = 12; break; + case Qt::Key_D: hexCode = 13; break; + case Qt::Key_E: hexCode = 14; break; + case Qt::Key_F: hexCode = 15; break; + } + + if(cursor.hasSelection() == false && hexCode != -1) { + bool cursorOffsetValid = (x >= 11 && ((x - 11) % 3) != 2); + if(cursorOffsetValid) { + bool nibble = (x - 11) % 3; //0 = top nibble, 1 = bottom nibble + unsigned cursorOffset = y * editorColumns + ((x - 11) / 3); + unsigned effectiveOffset = editorOffset + cursorOffset; + if(effectiveOffset >= editorSize) effectiveOffset %= editorSize; + + uint8_t data = reader ? reader(effectiveOffset) : 0x00; + data &= (nibble == 0 ? 0x0f : 0xf0); + data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0)); + if(writer) writer(effectiveOffset, data); + refresh(); + + cursor.setPosition(y * lineWidth() + x + 1); //advance cursor + setTextCursor(cursor); + } + } else { + //allow navigation keys to move cursor, but block text input + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + QTextEdit::keyPressEvent(event); + setTextInteractionFlags(Qt::TextEditorInteraction); + } +} + +inline void HexEditor::setColumns(unsigned columns) { + editorColumns = columns; +} + +inline void HexEditor::setRows(unsigned rows) { + editorRows = rows; + scrollBar->setPageStep(editorRows); +} + +inline void HexEditor::setOffset(unsigned offset) { + lock = true; + editorOffset = offset; + scrollBar->setSliderPosition(editorOffset / editorColumns); + lock = false; +} + +inline void HexEditor::setSize(unsigned size) { + editorSize = size; + bool indivisible = (editorSize % editorColumns) != 0; //add one for incomplete row + scrollBar->setRange(0, editorSize / editorColumns + indivisible - editorRows); +} + +inline unsigned HexEditor::lineWidth() const { + return 11 + 3 * editorColumns; +} + +inline void HexEditor::refresh() { + string output; + char temp[256]; + unsigned offset = editorOffset; + + for(unsigned y = 0; y < editorRows; y++) { + if(offset >= editorSize) break; + sprintf(temp, "%.4x:%.4x", (offset >> 16) & 0xffff, (offset >> 0) & 0xffff); + output << "" << temp << "  "; + + for(unsigned x = 0; x < editorColumns; x++) { + if(offset >= editorSize) break; + sprintf(temp, "%.2x", reader ? reader(offset) : 0x00); + offset++; + output << "" << temp << ""; + if(x != (editorColumns - 1)) output << " "; + } + + if(y != (editorRows - 1)) output << "
"; + } + + setHtml(output); +} + +inline void HexEditor::scrolled() { + if(lock) return; + unsigned offset = scrollBar->sliderPosition(); + editorOffset = offset * editorColumns; + refresh(); +} + +inline HexEditor::HexEditor() { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignRight); + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + scrollBar = new QScrollBar(Qt::Vertical); + scrollBar->setSingleStep(1); + layout->addWidget(scrollBar); + + lock = false; + connect(scrollBar, SIGNAL(actionTriggered(int)), this, SLOT(scrolled())); + + setColumns(16); + setRows(16); + setSize(0); + setOffset(0); +} + +} + +#endif diff --git a/snesfilter/nall/qt/radio-action.moc.hpp b/snesfilter/nall/qt/radio-action.moc.hpp new file mode 100644 index 00000000..a2bbca48 --- /dev/null +++ b/snesfilter/nall/qt/radio-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_RADIOACTION_HPP +#define NALL_QT_RADIOACTION_HPP + +namespace nall { + +class RadioAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + RadioAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool RadioAction::isChecked() const { + return checked; +} + +inline void RadioAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-radio-on.png")); + else setIcon(QIcon(":/16x16/item-radio-off.png")); +} + +inline void RadioAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline RadioAction::RadioAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/snesfilter/nall/qt/window.moc.hpp b/snesfilter/nall/qt/window.moc.hpp new file mode 100644 index 00000000..0d3bf390 --- /dev/null +++ b/snesfilter/nall/qt/window.moc.hpp @@ -0,0 +1,105 @@ +#ifndef NALL_QT_WINDOW_HPP +#define NALL_QT_WINDOW_HPP + +#include +#include + +namespace nall { + +class Window : public QWidget { + Q_OBJECT + +public: + void setGeometryString(string *geometryString); + void setCloseOnEscape(bool); + void show(); + void hide(); + void shrink(); + + Window(); + +protected slots: + +protected: + string *geometryString; + bool closeOnEscape; + void keyReleaseEvent(QKeyEvent *event); + void closeEvent(QCloseEvent *event); +}; + +inline void Window::setGeometryString(string *geometryString_) { + geometryString = geometryString_; + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } +} + +inline void Window::setCloseOnEscape(bool value) { + closeOnEscape = value; +} + +inline void Window::show() { + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } + QWidget::show(); + QApplication::processEvents(); + activateWindow(); + raise(); +} + +inline void Window::hide() { + if(geometryString && isVisible() == true) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::hide(); +} + +inline void Window::shrink() { + if(isFullScreen()) return; + + for(unsigned i = 0; i < 2; i++) { + resize(0, 0); + usleep(2000); + QApplication::processEvents(); + } +} + +inline void Window::keyReleaseEvent(QKeyEvent *event) { + if(closeOnEscape && (event->key() == Qt::Key_Escape)) close(); + QWidget::keyReleaseEvent(event); +} + +inline void Window::closeEvent(QCloseEvent *event) { + if(geometryString) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::closeEvent(event); +} + +inline Window::Window() { + geometryString = 0; + closeOnEscape = true; +} + +} + +#endif diff --git a/snesfilter/nall/serial.hpp b/snesfilter/nall/serial.hpp new file mode 100644 index 00000000..6f5cf6d6 --- /dev/null +++ b/snesfilter/nall/serial.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snesfilter/nall/serializer.hpp b/snesfilter/nall/serializer.hpp new file mode 100644 index 00000000..9f816dfe --- /dev/null +++ b/snesfilter/nall/serializer.hpp @@ -0,0 +1,145 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snesfilter/nall/sha256.hpp b/snesfilter/nall/sha256.hpp new file mode 100644 index 00000000..7f41f04e --- /dev/null +++ b/snesfilter/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/snesfilter/nall/sort.hpp b/snesfilter/nall/sort.hpp new file mode 100644 index 00000000..23c317a5 --- /dev/null +++ b/snesfilter/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snesfilter/nall/static.hpp b/snesfilter/nall/static.hpp new file mode 100644 index 00000000..4acb9fd0 --- /dev/null +++ b/snesfilter/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snesfilter/nall/stdint.hpp b/snesfilter/nall/stdint.hpp new file mode 100644 index 00000000..d8b6c788 --- /dev/null +++ b/snesfilter/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snesfilter/nall/string.hpp b/snesfilter/nall/string.hpp new file mode 100644 index 00000000..65a4a4b8 --- /dev/null +++ b/snesfilter/nall/string.hpp @@ -0,0 +1,26 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snesfilter/nall/string/base.hpp b/snesfilter/nall/string/base.hpp new file mode 100644 index 00000000..179a7dd4 --- /dev/null +++ b/snesfilter/nall/string/base.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + inline unsigned length() const; + + inline string& assign(const char*); + inline string& append(const char*); + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string(); + inline string(const char*); + inline string(const string&); + inline string(string&&); + inline string& operator=(const string&); + inline string& operator=(string&&); + inline ~string(); + + inline bool readfile(const char*); + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + protected: + char *data; + unsigned size; + + #if defined(QT_CORE_LIB) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline int find(const char*); + inline void split (const char*, const char*, unsigned = 0); + inline void qsplit(const char*, const char*, unsigned = 0); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *dest, const char *src); + inline int strpos (const char *str, const char *key); + inline int qstrpos(const char *str, const char *key); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //match.hpp + inline bool match(const char *pattern, const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //trim.hpp + inline char* ltrim(char *str, const char *key = " "); + inline char* rtrim(char *str, const char *key = " "); + inline char* trim (char *str, const char *key = " "); + inline char* ltrim_once(char *str, const char *key = " "); + inline char* rtrim_once(char *str, const char *key = " "); + inline char* trim_once (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + inline string& strlower(string &str); + inline string& strupper(string &str); + inline string& strtr(string &dest, const char *before, const char *after); + inline string& ltrim(string &str, const char *key = " "); + inline string& rtrim(string &str, const char *key = " "); + inline string& trim (string &str, const char *key = " "); + inline string& ltrim_once(string &str, const char *key = " "); + inline string& rtrim_once(string &str, const char *key = " "); + inline string& trim_once (string &str, const char *key = " "); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline string sprint(Args... args); + template inline void print(Args... args); +}; + +#endif diff --git a/snesfilter/nall/string/cast.hpp b/snesfilter/nall/string/cast.hpp new file mode 100644 index 00000000..7b48eda0 --- /dev/null +++ b/snesfilter/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QT_CORE_LIB) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snesfilter/nall/string/compare.hpp b/snesfilter/nall/string/compare.hpp new file mode 100644 index 00000000..e1173de4 --- /dev/null +++ b/snesfilter/nall/string/compare.hpp @@ -0,0 +1,104 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +int strpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + return i; + } + } + return -1; +} + +int qstrpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) { + return i; + } else { + i++; + } + } + return -1; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/snesfilter/nall/string/convert.hpp b/snesfilter/nall/string/convert.hpp new file mode 100644 index 00000000..3ff134be --- /dev/null +++ b/snesfilter/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snesfilter/nall/string/core.hpp b/snesfilter/nall/string/core.hpp new file mode 100644 index 00000000..d13bfc38 --- /dev/null +++ b/snesfilter/nall/string/core.hpp @@ -0,0 +1,133 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string::string() { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +string::string(const char *value) { + size = strlen(value); + data = strdup(value); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +string::~string() { + free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +int lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return i; + } + return -1; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snesfilter/nall/string/filename.hpp b/snesfilter/nall/string/filename.hpp new file mode 100644 index 00000000..f3750760 --- /dev/null +++ b/snesfilter/nall/string/filename.hpp @@ -0,0 +1,61 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/", "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snesfilter/nall/string/match.hpp b/snesfilter/nall/string/match.hpp new file mode 100644 index 00000000..d8cf702d --- /dev/null +++ b/snesfilter/nall/string/match.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_STRING_MATCH_HPP +#define NALL_STRING_MATCH_HPP + +namespace nall { + +bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++, *s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +} + +#endif diff --git a/snesfilter/nall/string/math.hpp b/snesfilter/nall/string/math.hpp new file mode 100644 index 00000000..ea8b99c8 --- /dev/null +++ b/snesfilter/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snesfilter/nall/string/replace.hpp b/snesfilter/nall/string/replace.hpp new file mode 100644 index 00000000..db405a9b --- /dev/null +++ b/snesfilter/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snesfilter/nall/string/split.hpp b/snesfilter/nall/string/split.hpp new file mode 100644 index 00000000..bb77dfcd --- /dev/null +++ b/snesfilter/nall/string/split.hpp @@ -0,0 +1,56 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snesfilter/nall/string/strl.hpp b/snesfilter/nall/string/strl.hpp new file mode 100644 index 00000000..84c841fa --- /dev/null +++ b/snesfilter/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snesfilter/nall/string/trim.hpp b/snesfilter/nall/string/trim.hpp new file mode 100644 index 00000000..b13ab9ba --- /dev/null +++ b/snesfilter/nall/string/trim.hpp @@ -0,0 +1,54 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +} + +#endif diff --git a/snesfilter/nall/string/utility.hpp b/snesfilter/nall/string/utility.hpp new file mode 100644 index 00000000..2da2762b --- /dev/null +++ b/snesfilter/nall/string/utility.hpp @@ -0,0 +1,169 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* very simplistic wrappers to return string& instead of char* type */ + +string& strlower(string &str) { strlower(str()); return str; } +string& strupper(string &str) { strupper(str()); return str; } +string& strtr(string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; } +string& ltrim(string &str, const char *key) { ltrim(str(), key); return str; } +string& rtrim(string &str, const char *key) { rtrim(str(), key); return str; } +string& trim (string &str, const char *key) { trim (str(), key); return str; } +string& ltrim_once(string &str, const char *key) { ltrim_once(str(), key); return str; } +string& rtrim_once(string &str, const char *key) { rtrim_once(str(), key); return str; } +string& trim_once (string &str, const char *key) { trim_once (str(), key); return str; } + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snesfilter/nall/string/variadic.hpp b/snesfilter/nall/string/variadic.hpp new file mode 100644 index 00000000..13c477a8 --- /dev/null +++ b/snesfilter/nall/string/variadic.hpp @@ -0,0 +1,27 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +static void isprint(string &output) { +} + +template +static void isprint(string &output, T value, Args... args) { + output << to_string(value); + isprint(output, args...); +} + +template inline string sprint(Args... args) { + string output; + isprint(output, args...); + return output; +} + +template inline void print(Args... args) { + printf("%s", (const char*)sprint(args...)); +} + +} + +#endif diff --git a/snesfilter/nall/string/xml.hpp b/snesfilter/nall/string/xml.hpp new file mode 100644 index 00000000..d423f87f --- /dev/null +++ b/snesfilter/nall/string/xml.hpp @@ -0,0 +1,257 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + source += pos + 3; + continue; + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + string cdata = substr(source, 9, pos - 9); + data << cdata; + offset += strlen(cdata); + + source += offset + 3; + continue; + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ") >= 0) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + rtrim(data); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) trim_once(attr.content, "\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) trim_once(attr.content, "'"); + else throw "..."; + attribute.add(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + signed offset = strpos(data, "-->"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + if(strbegin(data, "![CDATA[")) { + signed offset = strpos(data, "]]>"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + signed offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + string tag = substr(data, 0, offset); + data += offset + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + rtrim_once(tag, "?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + rtrim_once(tag, "/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + tag = substr(data, 0, offset); + data += offset + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ") >= 0) tag.replace(" ", " "); + rtrim(tag); + + if(name != tag) throw "..."; + return true; + } + } else { + element.add(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.add(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snesfilter/nall/ups.hpp b/snesfilter/nall/ups.hpp new file mode 100644 index 00000000..f255ecb3 --- /dev/null +++ b/snesfilter/nall/ups.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new uint8_t[y_size](); + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/snesfilter/nall/utf8.hpp b/snesfilter/nall/utf8.hpp new file mode 100644 index 00000000..c66c341a --- /dev/null +++ b/snesfilter/nall/utf8.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#undef NOMINMAX +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + private: + char *buffer; + }; +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snesfilter/nall/utility.hpp b/snesfilter/nall/utility.hpp new file mode 100644 index 00000000..2a63f515 --- /dev/null +++ b/snesfilter/nall/utility.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template inline T* allocate(size_t size, const T &value) { + T *array = new T[size]; + for(size_t i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snesfilter/nall/varint.hpp b/snesfilter/nall/varint.hpp new file mode 100644 index 00000000..cc3bb17c --- /dev/null +++ b/snesfilter/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert::value> uint_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert::value> int_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snesfilter/nall/vector.hpp b/snesfilter/nall/vector.hpp new file mode 100644 index 00000000..3d69d4d5 --- /dev/null +++ b/snesfilter/nall/vector.hpp @@ -0,0 +1,240 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snesfilter/ntsc/ntsc.cpp b/snesfilter/ntsc/ntsc.cpp new file mode 100644 index 00000000..142ce8f8 --- /dev/null +++ b/snesfilter/ntsc/ntsc.cpp @@ -0,0 +1,396 @@ +#include "snes_ntsc/snes_ntsc.h" +#include "snes_ntsc/snes_ntsc.c" + +#include "ntsc.moc.hpp" +#include "ntsc.moc" + +void NTSCFilter::bind(configuration &config) { + config.attach(hue = 0.0, "snesfilter.ntsc.hue"); + config.attach(saturation = 0.0, "snesfilter.ntsc.saturation"); + config.attach(contrast = 0.0, "snesfilter.ntsc.contrast"); + config.attach(brightness = 0.0, "snesfilter.ntsc.brightness"); + config.attach(sharpness = 0.0, "snesfilter.ntsc.sharpness"); + config.attach(gamma = 0.0, "snesfilter.ntsc.gamma"); + config.attach(resolution = 0.0, "snesfilter.ntsc.resolution"); + config.attach(artifacts = 0.0, "snesfilter.ntsc.artifacts"); + config.attach(fringing = 0.0, "snesfilter.ntsc.fringing"); + config.attach(bleed = 0.0, "snesfilter.ntsc.bleed"); + config.attach(mergeFields = true, "snesfilter.ntsc.mergeFields"); +} + +void NTSCFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = SNES_NTSC_OUT_WIDTH(256); + outheight = height; +} + +void NTSCFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(!ntsc) return; + + width = SNES_NTSC_OUT_WIDTH(256); + burst ^= burst_toggle; + + pitch >>= 1; + outpitch >>= 2; + + unsigned line_burst = burst; + for(unsigned y = 0; y < height;) { + const uint16_t *in = input + y * pitch; + uint32_t *out = output + y * outpitch; + + //render as many lines in one snes_ntsc_blit as possible: + //do this by determining for how many lines the width stays the same + unsigned rheight = 1; + unsigned rwidth = line[y]; + while(y + rheight < height && rwidth == line[y + rheight]) rheight++; + + if(rwidth == 256) { + snes_ntsc_blit (ntsc, in, pitch, line_burst, rwidth, rheight, out, outpitch << 2); + } else { + snes_ntsc_blit_hires(ntsc, in, pitch, line_burst, rwidth, rheight, out, outpitch << 2); + } + + line_burst = (line_burst + rheight) % 3; + y += rheight; + } +} + +QWidget* NTSCFilter::settings() { + if(!widget) { + widget = new QWidget; + widget->setWindowTitle("NTSC Filter Configuration"); + + layout = new QVBoxLayout; + layout->setAlignment(Qt::AlignTop); + widget->setLayout(layout); + + gridLayout = new QGridLayout; + layout->addLayout(gridLayout); + + basicSettings = new QLabel("Basic settings:"); + gridLayout->addWidget(basicSettings, 0, 0, 1, 3); + + hueLabel = new QLabel("Hue:"); + gridLayout->addWidget(hueLabel, 1, 0); + + hueValue = new QLabel; + hueValue->setMinimumWidth(hueValue->fontMetrics().width("-100.0")); + hueValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(hueValue, 1, 1); + + hueSlider = new QSlider(Qt::Horizontal); + hueSlider->setMinimum(-100); + hueSlider->setMaximum(+100); + gridLayout->addWidget(hueSlider, 1, 2); + + saturationLabel = new QLabel("Saturation:"); + gridLayout->addWidget(saturationLabel, 2, 0); + + saturationValue = new QLabel; + saturationValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(saturationValue, 2, 1); + + saturationSlider = new QSlider(Qt::Horizontal); + saturationSlider->setMinimum(-100); + saturationSlider->setMaximum(+100); + gridLayout->addWidget(saturationSlider, 2, 2); + + contrastLabel = new QLabel("Contrast:"); + gridLayout->addWidget(contrastLabel, 3, 0); + + contrastValue = new QLabel; + contrastValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(contrastValue, 3, 1); + + contrastSlider = new QSlider(Qt::Horizontal); + contrastSlider->setMinimum(-100); + contrastSlider->setMaximum(+100); + gridLayout->addWidget(contrastSlider, 3, 2); + + brightnessLabel = new QLabel("Brightness:"); + gridLayout->addWidget(brightnessLabel, 4, 0); + + brightnessValue = new QLabel; + brightnessValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(brightnessValue, 4, 1); + + brightnessSlider = new QSlider(Qt::Horizontal); + brightnessSlider->setMinimum(-100); + brightnessSlider->setMaximum(+100); + gridLayout->addWidget(brightnessSlider, 4, 2); + + sharpnessLabel = new QLabel("Sharpness:"); + gridLayout->addWidget(sharpnessLabel, 5, 0); + + sharpnessValue = new QLabel; + sharpnessValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(sharpnessValue, 5, 1); + + sharpnessSlider = new QSlider(Qt::Horizontal); + sharpnessSlider->setMinimum(-100); + sharpnessSlider->setMaximum(+100); + gridLayout->addWidget(sharpnessSlider, 5, 2); + + advancedSettings = new QLabel("Advanced settings:"); + gridLayout->addWidget(advancedSettings, 6, 0, 1, 3); + + gammaLabel = new QLabel("Gamma:"); + gridLayout->addWidget(gammaLabel, 7, 0); + + gammaValue = new QLabel; + gammaValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(gammaValue, 7, 1); + + gammaSlider = new QSlider(Qt::Horizontal); + gammaSlider->setMinimum(-100); + gammaSlider->setMaximum(+100); + gridLayout->addWidget(gammaSlider, 7, 2); + + resolutionLabel = new QLabel("Resolution:"); + gridLayout->addWidget(resolutionLabel, 8, 0); + + resolutionValue = new QLabel; + resolutionValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(resolutionValue, 8, 1); + + resolutionSlider = new QSlider(Qt::Horizontal); + resolutionSlider->setMinimum(-100); + resolutionSlider->setMaximum(+100); + gridLayout->addWidget(resolutionSlider, 8, 2); + + artifactsLabel = new QLabel("Artifacts:"); + gridLayout->addWidget(artifactsLabel, 9, 0); + + artifactsValue = new QLabel; + artifactsValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(artifactsValue, 9, 1); + + artifactsSlider = new QSlider(Qt::Horizontal); + artifactsSlider->setMinimum(-100); + artifactsSlider->setMaximum(+100); + gridLayout->addWidget(artifactsSlider, 9, 2); + + fringingLabel = new QLabel("Fringing:"); + gridLayout->addWidget(fringingLabel, 10, 0); + + fringingValue = new QLabel; + fringingValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(fringingValue, 10, 1); + + fringingSlider = new QSlider(Qt::Horizontal); + fringingSlider->setMinimum(-100); + fringingSlider->setMaximum(+100); + gridLayout->addWidget(fringingSlider, 10, 2); + + bleedLabel = new QLabel("Color bleed:"); + gridLayout->addWidget(bleedLabel, 11, 0); + + bleedValue = new QLabel; + bleedValue->setAlignment(Qt::AlignHCenter); + gridLayout->addWidget(bleedValue, 11, 1); + + bleedSlider = new QSlider(Qt::Horizontal); + bleedSlider->setMinimum(-100); + bleedSlider->setMaximum(+100); + gridLayout->addWidget(bleedSlider, 11, 2); + + mergeFieldsBox = new QCheckBox("Merge even and odd fields to reduce flicker"); + gridLayout->addWidget(mergeFieldsBox, 12, 0, 1, 3); + + presets = new QLabel("Presets:"); + gridLayout->addWidget(presets, 13, 0, 1, 3); + + controlLayout = new QHBoxLayout; + layout->addLayout(controlLayout); + + rfPreset = new QPushButton("RF"); + controlLayout->addWidget(rfPreset); + + compositePreset = new QPushButton("Composite"); + controlLayout->addWidget(compositePreset); + + svideoPreset = new QPushButton("S-Video"); + controlLayout->addWidget(svideoPreset); + + rgbPreset = new QPushButton("RGB"); + controlLayout->addWidget(rgbPreset); + + monoPreset = new QPushButton("Monochrome"); + controlLayout->addWidget(monoPreset); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + spacer->setMinimumWidth(50); + controlLayout->addWidget(spacer); + + ok = new QPushButton("Ok"); + controlLayout->addWidget(ok); + + blockSignals = true; + loadSettingsFromConfig(); + syncUiToSettings(); + initialize(); + + connect(hueSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(saturationSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(contrastSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(brightnessSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(sharpnessSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(gammaSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(resolutionSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(artifactsSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(fringingSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(bleedSlider, SIGNAL(valueChanged(int)), this, SLOT(syncSettingsToUi())); + connect(mergeFieldsBox, SIGNAL(stateChanged(int)), this, SLOT(syncSettingsToUi())); + connect(rfPreset, SIGNAL(released()), this, SLOT(setRfPreset())); + connect(compositePreset, SIGNAL(released()), this, SLOT(setCompositePreset())); + connect(svideoPreset, SIGNAL(released()), this, SLOT(setSvideoPreset())); + connect(rgbPreset, SIGNAL(released()), this, SLOT(setRgbPreset())); + connect(monoPreset, SIGNAL(released()), this, SLOT(setMonoPreset())); + connect(ok, SIGNAL(released()), widget, SLOT(hide())); + + blockSignals = false; + } + + return widget; +} + +void NTSCFilter::initialize() { + burst = 0; + burst_toggle = (setup.merge_fields ? 0 : 1); //don't toggle burst when fields are merged + snes_ntsc_init(ntsc, &setup); +} + +void NTSCFilter::loadSettingsFromConfig() { + setup.hue = hue; + setup.saturation = saturation; + setup.contrast = contrast; + setup.brightness = brightness; + setup.sharpness = sharpness; + + setup.gamma = gamma; + setup.resolution = resolution; + setup.artifacts = artifacts; + setup.fringing = fringing; + setup.bleed = bleed; + + setup.merge_fields = mergeFields; +} + +void NTSCFilter::syncUiToSettings() { + blockSignals = true; + + hue = setup.hue; + saturation = setup.saturation; + contrast = setup.contrast; + brightness = setup.brightness; + sharpness = setup.sharpness; + + gamma = setup.gamma; + resolution = setup.resolution; + artifacts = setup.artifacts; + fringing = setup.fringing; + bleed = setup.bleed; + + mergeFields = setup.merge_fields; + + hueValue->setText(string() << hue); + hueSlider->setSliderPosition(hue * 100); + + saturationValue->setText(string() << saturation); + saturationSlider->setSliderPosition(saturation * 100); + + contrastValue->setText(string() << contrast); + contrastSlider->setSliderPosition(contrast * 100); + + brightnessValue->setText(string() << brightness); + brightnessSlider->setSliderPosition(brightness * 100); + + sharpnessValue->setText(string() << sharpness); + sharpnessSlider->setSliderPosition(sharpness * 100); + + gammaValue->setText(string() << gamma); + gammaSlider->setSliderPosition(gamma * 100); + + resolutionValue->setText(string() << resolution); + resolutionSlider->setSliderPosition(resolution * 100); + + artifactsValue->setText(string() << artifacts); + artifactsSlider->setSliderPosition(artifacts * 100); + + fringingValue->setText(string() << fringing); + fringingSlider->setSliderPosition(fringing * 100); + + bleedValue->setText(string() << bleed); + bleedSlider->setSliderPosition(bleed * 100); + + mergeFieldsBox->setChecked(mergeFields); + + blockSignals = false; +} + +void NTSCFilter::syncSettingsToUi() { + if(blockSignals) return; + + hue = hueSlider->sliderPosition() / 100.0; + saturation = saturationSlider->sliderPosition() / 100.0; + contrast = contrastSlider->sliderPosition() / 100.0; + brightness = brightnessSlider->sliderPosition() / 100.0; + sharpness = sharpnessSlider->sliderPosition() / 100.0; + + gamma = gammaSlider->sliderPosition() / 100.0; + resolution = resolutionSlider->sliderPosition() / 100.0; + artifacts = artifactsSlider->sliderPosition() / 100.0; + fringing = fringingSlider->sliderPosition() / 100.0; + bleed = bleedSlider->sliderPosition() / 100.0; + + mergeFields = mergeFieldsBox->isChecked(); + + loadSettingsFromConfig(); + syncUiToSettings(); + initialize(); +} + +void NTSCFilter::setRfPreset() { + static snes_ntsc_setup_t defaults; + setup = defaults; + syncUiToSettings(); + initialize(); +} + +void NTSCFilter::setCompositePreset() { + setup = snes_ntsc_composite; + syncUiToSettings(); + initialize(); +} + +void NTSCFilter::setSvideoPreset() { + setup = snes_ntsc_svideo; + syncUiToSettings(); + initialize(); +} + +void NTSCFilter::setRgbPreset() { + setup = snes_ntsc_rgb; + syncUiToSettings(); + initialize(); +} + +void NTSCFilter::setMonoPreset() { + setup = snes_ntsc_monochrome; + syncUiToSettings(); + initialize(); +} + +NTSCFilter::NTSCFilter() : widget(0) { + ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc); + static snes_ntsc_setup_t defaults; + setup = defaults; + initialize(); +} + +NTSCFilter::~NTSCFilter() { + if(ntsc) free(ntsc); +} diff --git a/snesfilter/ntsc/ntsc.moc.hpp b/snesfilter/ntsc/ntsc.moc.hpp new file mode 100644 index 00000000..a99a6bb4 --- /dev/null +++ b/snesfilter/ntsc/ntsc.moc.hpp @@ -0,0 +1,91 @@ +class NTSCFilter : public QObject { + Q_OBJECT + +public: + void bind(configuration&); + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + QWidget* settings(); + + NTSCFilter(); + ~NTSCFilter(); + +private: + void initialize(); + void loadSettingsFromConfig(); + void syncUiToSettings(); + +private slots: + void syncSettingsToUi(); + void setRfPreset(); + void setCompositePreset(); + void setSvideoPreset(); + void setRgbPreset(); + void setMonoPreset(); + +private: + QWidget *widget; + QVBoxLayout *layout; + QGridLayout *gridLayout; + QLabel *basicSettings; + QLabel *hueLabel; + QLabel *hueValue; + QSlider *hueSlider; + QLabel *saturationLabel; + QLabel *saturationValue; + QSlider *saturationSlider; + QLabel *contrastLabel; + QLabel *contrastValue; + QSlider *contrastSlider; + QLabel *brightnessLabel; + QLabel *brightnessValue; + QSlider *brightnessSlider; + QLabel *sharpnessLabel; + QLabel *sharpnessValue; + QSlider *sharpnessSlider; + QLabel *advancedSettings; + QLabel *gammaLabel; + QLabel *gammaValue; + QSlider *gammaSlider; + QLabel *resolutionLabel; + QLabel *resolutionValue; + QSlider *resolutionSlider; + QLabel *artifactsLabel; + QLabel *artifactsValue; + QSlider *artifactsSlider; + QLabel *fringingLabel; + QLabel *fringingValue; + QSlider *fringingSlider; + QLabel *bleedLabel; + QLabel *bleedValue; + QSlider *bleedSlider; + QCheckBox *mergeFieldsBox; + QLabel *presets; + QHBoxLayout *controlLayout; + QPushButton *rfPreset; + QPushButton *compositePreset; + QPushButton *svideoPreset; + QPushButton *rgbPreset; + QPushButton *monoPreset; + QWidget *spacer; + QPushButton *ok; + + bool blockSignals; + + struct snes_ntsc_t *ntsc; + snes_ntsc_setup_t setup; + int burst, burst_toggle; + + //settings + double hue; + double saturation; + double contrast; + double brightness; + double sharpness; + double gamma; + double resolution; + double artifacts; + double fringing; + double bleed; + bool mergeFields; +} filter_ntsc; diff --git a/snesfilter/ntsc/snes_ntsc/snes_ntsc.c b/snesfilter/ntsc/snes_ntsc/snes_ntsc.c new file mode 100644 index 00000000..f622baf8 --- /dev/null +++ b/snesfilter/ntsc/snes_ntsc/snes_ntsc.c @@ -0,0 +1,251 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +#include "snes_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 }; + +#define alignment_count 3 +#define burst_count 3 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 1.0f +#define fringing_mid 1.0f +#define std_decoder_hue 0 + +#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */ +#define gamma_size 32 + +#include "snes_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const snes_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void merge_kernel_fields( snes_ntsc_rgb_t* io ) +{ + int n; + for ( n = burst_size; n; --n ) + { + snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; + snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; + snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; + /* merge colors without losing precision */ + io [burst_size * 0] = + ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 1] = + ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 2] = + ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + ++io; + } +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ) +{ + int n; + for ( n = burst_count; n; --n ) + { + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + snes_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); + } + out += alignment_count * rgb_kernel_size; + } +} + +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) +{ + int merge_fields; + int entry; + init_t impl; + if ( !setup ) + setup = &snes_ntsc_composite; + init( &impl, setup ); + + merge_fields = setup->merge_fields; + if ( setup->artifacts <= -1 && setup->fringing <= -1 ) + merge_fields = 1; + + for ( entry = 0; entry < snes_ntsc_palette_size; entry++ ) + { + /* Reduce number of significant bits of source color. Clearing the + low bits of R and B were least notictable. Modifying green was too + noticeable. */ + int ir = entry >> 8 & 0x1E; + int ig = entry >> 4 & 0x1F; + int ib = entry << 1 & 0x1E; + + #if SNES_NTSC_BSNES_COLORTBL + if ( setup->bsnes_colortbl ) + { + int bgr15 = (ib << 10) | (ig << 5) | ir; + unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; + ir = rgb16 >> 11 & 0x1E; + ig = rgb16 >> 6 & 0x1F; + ib = rgb16 & 0x1E; + } + #endif + + { + float rr = impl.to_float [ir]; + float gg = impl.to_float [ig]; + float bb = impl.to_float [ib]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + snes_ntsc_rgb_t* out = ntsc->table [entry]; + gen_kernel( &impl, y, i, q, out ); + if ( merge_fields ) + merge_kernel_fields( out ); + correct_errors( rgb, out ); + } + } +} + +#ifndef SNES_NTSC_NO_BLITTERS + +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + ++line_in; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 3; + line_out += 7; + } + + /* finish final pixels */ + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_HIRES_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, + SNES_NTSC_ADJ_IN( line_in [0] ), + SNES_NTSC_ADJ_IN( line_in [1] ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + line_in += 2; + + for ( n = chunk_count; n; --n ) + { + /* twice as many input pixels per chunk */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 6; + line_out += 7; + } + + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +#endif diff --git a/snesfilter/ntsc/snes_ntsc/snes_ntsc.h b/snesfilter/ntsc/snes_ntsc/snes_ntsc.h new file mode 100644 index 00000000..fff97ecd --- /dev/null +++ b/snesfilter/ntsc/snes_ntsc/snes_ntsc.h @@ -0,0 +1,228 @@ +/* SNES NTSC video filter */ + +/* snes_ntsc 0.2.2 */ +#ifndef SNES_NTSC_H +#define SNES_NTSC_H + +#include "snes_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct snes_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ +} snes_ntsc_setup_t; + +/* Video format presets */ +extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ +extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ +extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ +extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ + +/* Initializes and adjusts parameters. Can be called multiple times on the same +snes_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct snes_ntsc_t snes_ntsc_t; +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT +and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +/* Number of output pixels written by low-res blitter for given input width. Width +might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded +value. Guaranteed not to round 256 down at all. */ +#define SNES_NTSC_OUT_WIDTH( in_width ) \ + ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) + +/* Number of low-res input pixels that will fit within given output width. Might be +rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define SNES_NTSC_IN_WIDTH( out_width ) \ + (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) + + +/* Interface for user-defined custom blitters */ + +enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ +enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ +enum { snes_ntsc_black = 0 }; /* palette index for black */ +enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ + +/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. +Use snes_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) + +/* Begins input pixel */ +#define SNES_NTSC_COLOR_IN( index, color ) \ + SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) + +/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) +14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ + SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) + +/* Hires equivalents */ +#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ + unsigned const snes_ntsc_pixel3_ = (pixel3);\ + snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ + unsigned const snes_ntsc_pixel4_ = (pixel4);\ + snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ + unsigned const snes_ntsc_pixel5_ = (pixel5);\ + snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ + snes_ntsc_rgb_t const* kernel0 = kernel1;\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel1;\ + snes_ntsc_rgb_t const* kernelx2 = kernel1;\ + snes_ntsc_rgb_t const* kernelx3 = kernel1;\ + snes_ntsc_rgb_t const* kernelx4 = kernel1;\ + snes_ntsc_rgb_t const* kernelx5 = kernel1 + +#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ + kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ + kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ + kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ + SNES_NTSC_CLAMP_( raw_, 0 );\ + SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ +} + + +/* private */ +enum { snes_ntsc_entry_size = 128 }; +enum { snes_ntsc_palette_size = 0x2000 }; +typedef unsigned long snes_ntsc_rgb_t; +struct snes_ntsc_t { + snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; +}; +enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; + +#define SNES_NTSC_RGB16( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +#define SNES_NTSC_BGR15( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +/* common 3->7 ntsc macros */ +#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const snes_ntsc_pixel0_ = (pixel0);\ + snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel0;\ + snes_ntsc_rgb_t const* kernelx2 = kernel0 + +#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + SNES_NTSC_CLAMP_( raw_, shift );\ + SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) +#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) +#define SNES_NTSC_CLAMP_( io, shift ) {\ + snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ + snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +/* original routine */ +/* +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 )\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 24 || bits == 32 )\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + if ( bits == 15 )\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 14 )\ + rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ + if ( bits == 0 )\ + rgb_out = raw_ << x;\ +} +*/ + +/* custom bsnes routine -- hooks into bsnes colortable */ +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 ) {\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\ + rgb_out = colortable[rgb_out];\ + } else if ( bits == 24 || bits == 32 ) {\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\ + rgb_out = colortable[rgb_out];\ + } else if ( bits == 15 ) {\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\ + rgb_out = colortable[rgb_out];\ + } else {\ + rgb_out = raw_ << x;\ + }\ +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesfilter/ntsc/snes_ntsc/snes_ntsc_config.h b/snesfilter/ntsc/snes_ntsc/snes_ntsc_config.h new file mode 100644 index 00000000..7ab94c2c --- /dev/null +++ b/snesfilter/ntsc/snes_ntsc/snes_ntsc_config.h @@ -0,0 +1,26 @@ +/* Configure library by modifying this file */ + +#ifndef SNES_NTSC_CONFIG_H +#define SNES_NTSC_CONFIG_H + +/* Format of source pixels */ +/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */ +#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define SNES_NTSC_OUT_DEPTH 32 + +/* Type of input pixel values */ +#define SNES_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define SNES_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */ + +#endif diff --git a/snesfilter/ntsc/snes_ntsc/snes_ntsc_impl.h b/snesfilter/ntsc/snes_ntsc/snes_ntsc_impl.h new file mode 100644 index 00000000..1d7adc78 --- /dev/null +++ b/snesfilter/ntsc/snes_ntsc/snes_ntsc_impl.h @@ -0,0 +1,439 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = snes_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, snes_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const snes_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = snes_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + snes_ntsc_rgb_t clamped = (rgb);\ + SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict __restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if SNES_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short snes_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int snes_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long snes_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/snesfilter/pixellate2x/pixellate2x.cpp b/snesfilter/pixellate2x/pixellate2x.cpp new file mode 100644 index 00000000..6259efc0 --- /dev/null +++ b/snesfilter/pixellate2x/pixellate2x.cpp @@ -0,0 +1,39 @@ +#include "pixellate2x.hpp" + +void Pixellate2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + outwidth = (width <= 256) ? width * 2 : width; + outheight = (height <= 240) ? height * 2 : height; +} + +void Pixellate2xFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + pitch >>= 1; + outpitch >>= 2; + + uint32_t *out0 = output; + uint32_t *out1 = output + outpitch; + + for(unsigned y = 0; y < height; y++) { + unsigned linewidth = line[y]; + for(unsigned x = 0; x < linewidth; x++) { + uint32_t p = colortable[*input++]; + + *out0++ = p; + if(height <= 240) *out1++ = p; + if(linewidth > 256) continue; + + *out0++ = p; + if(height <= 240) *out1++ = p; + } + + input += pitch - linewidth; + if(height <= 240) { + out0 += outpitch + outpitch - 512; + out1 += outpitch + outpitch - 512; + } else { + out0 += outpitch - 512; + } + } +} diff --git a/snesfilter/pixellate2x/pixellate2x.hpp b/snesfilter/pixellate2x/pixellate2x.hpp new file mode 100644 index 00000000..4e597e4b --- /dev/null +++ b/snesfilter/pixellate2x/pixellate2x.hpp @@ -0,0 +1,5 @@ +class Pixellate2xFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); +} filter_pixellate2x; diff --git a/snesfilter/scale2x/scale2x.cpp b/snesfilter/scale2x/scale2x.cpp new file mode 100644 index 00000000..f1915ab3 --- /dev/null +++ b/snesfilter/scale2x/scale2x.cpp @@ -0,0 +1,61 @@ +#include "scale2x.hpp" + +void Scale2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + if(height > 240) return filter_direct.size(outwidth, outheight, width, height); + outwidth = (width <= 256) ? width * 2 : width; + outheight = (height <= 240) ? height * 2 : height; +} + +void Scale2xFilter::render( + uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + if(height > 240) { + filter_direct.render(output, outpitch, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t *out0 = output; + uint32_t *out1 = output + outpitch; + + for(unsigned y = 0; y < height; y++) { + unsigned linewidth = line[y]; + + if(linewidth == 256) { + int prevline = (y == 0) || (linewidth != line[y - 1]) ? 0 : pitch; + int nextline = (y == height - 1) || (linewidth != line[y + 1]) ? 0 : pitch; + + for(unsigned x = 0; x < 256; x++) { + uint16_t A = *(input - prevline); + uint16_t B = (x > 0) ? *(input - 1) : *input; + uint16_t C = *input; + uint16_t D = (x < 255) ? *(input + 1) : *input; + uint16_t E = *(input++ + nextline); + uint32_t c = colortable[C]; + + if(A != E && B != D) { + *out0++ = (A == B ? colortable[A] : c); + *out0++ = (A == D ? colortable[A] : c); + *out1++ = (E == B ? colortable[E] : c); + *out1++ = (E == D ? colortable[E] : c); + } else { + *out0++ = c; + *out0++ = c; + *out1++ = c; + *out1++ = c; + } + } + } else { + for(unsigned x = 0; x < 512; x++) { + *out0++ = *out1++ = colortable[*input++]; + } + } + + input += pitch - linewidth; + out0 += outpitch + outpitch - 512; + out1 += outpitch + outpitch - 512; + } +} diff --git a/snesfilter/scale2x/scale2x.hpp b/snesfilter/scale2x/scale2x.hpp new file mode 100644 index 00000000..fad44e6a --- /dev/null +++ b/snesfilter/scale2x/scale2x.hpp @@ -0,0 +1,5 @@ +class Scale2xFilter { +public: + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); +} filter_scale2x; diff --git a/snesfilter/snesfilter.cpp b/snesfilter/snesfilter.cpp new file mode 100644 index 00000000..b73b9072 --- /dev/null +++ b/snesfilter/snesfilter.cpp @@ -0,0 +1,84 @@ +#include "snesfilter.hpp" + +#if defined(_WIN32) + #define dllexport __declspec(dllexport) +#else + #define dllexport +#endif + +#include +#include +#include + +#define QT_CORE_LIB +#include + +#include +#include +#include +using namespace nall; + +const uint32_t *colortable; +configuration *config; + +#include "direct/direct.cpp" +#include "pixellate2x/pixellate2x.cpp" +#include "scale2x/scale2x.cpp" +#include "2xsai/2xsai.cpp" +#include "lq2x/lq2x.cpp" +#include "hq2x/hq2x.cpp" +#include "ntsc/ntsc.cpp" + +dllexport const char* snesfilter_supported() { + return "Pixellate2x;Scale2x;2xSaI;Super 2xSaI;Super Eagle;LQ2x;HQ2x;NTSC"; +} + +dllexport void snesfilter_configuration(configuration &config_) { + config = &config_; + if(config) { + filter_ntsc.bind(*config); + } +} + +dllexport void snesfilter_colortable(const uint32_t *colortable_) { + colortable = colortable_; +} + +dllexport void snesfilter_size(unsigned filter, unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + switch(filter) { + default: return filter_direct.size(outwidth, outheight, width, height); + case 1: return filter_pixellate2x.size(outwidth, outheight, width, height); + case 2: return filter_scale2x.size(outwidth, outheight, width, height); + case 3: return filter_2xsai.size(outwidth, outheight, width, height); + case 4: return filter_super2xsai.size(outwidth, outheight, width, height); + case 5: return filter_supereagle.size(outwidth, outheight, width, height); + case 6: return filter_lq2x.size(outwidth, outheight, width, height); + case 7: return filter_hq2x.size(outwidth, outheight, width, height); + case 8: return filter_ntsc.size(outwidth, outheight, width, height); + } +} + +dllexport void snesfilter_render( + unsigned filter, uint32_t *output, unsigned outpitch, + const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + switch(filter) { + default: return filter_direct.render(output, outpitch, input, pitch, line, width, height); + case 1: return filter_pixellate2x.render(output, outpitch, input, pitch, line, width, height); + case 2: return filter_scale2x.render(output, outpitch, input, pitch, line, width, height); + case 3: return filter_2xsai.render(output, outpitch, input, pitch, line, width, height); + case 4: return filter_super2xsai.render(output, outpitch, input, pitch, line, width, height); + case 5: return filter_supereagle.render(output, outpitch, input, pitch, line, width, height); + case 6: return filter_lq2x.render(output, outpitch, input, pitch, line, width, height); + case 7: return filter_hq2x.render(output, outpitch, input, pitch, line, width, height); + case 8: return filter_ntsc.render(output, outpitch, input, pitch, line, width, height); + } +} + +dllexport QWidget* snesfilter_settings(unsigned filter) { + switch(filter) { + default: return 0; + case 8: return filter_ntsc.settings(); + } +} diff --git a/snesfilter/snesfilter.hpp b/snesfilter/snesfilter.hpp new file mode 100644 index 00000000..15a5ba4a --- /dev/null +++ b/snesfilter/snesfilter.hpp @@ -0,0 +1,16 @@ +#include +class QWidget; +namespace nall { class configuration; } + +extern "C" { + const char* snesfilter_supported(); + void snesfilter_configuration(nall::configuration&); + void snesfilter_colortable(const uint32_t*); + void snesfilter_size(unsigned, unsigned&, unsigned&, unsigned, unsigned); + void snesfilter_render( + unsigned, uint32_t*, unsigned, + const uint16_t*, unsigned, + const unsigned*, unsigned, unsigned + ); + QWidget* snesfilter_settings(unsigned); +} diff --git a/snesfilter/sync.sh b/snesfilter/sync.sh new file mode 100644 index 00000000..4bbaf34f --- /dev/null +++ b/snesfilter/sync.sh @@ -0,0 +1,2 @@ +rm -r nall +cp -r ../nall ./nall diff --git a/snesreader/7z_C/7zAlloc.c b/snesreader/7z_C/7zAlloc.c new file mode 100644 index 00000000..4bfaf42a --- /dev/null +++ b/snesreader/7z_C/7zAlloc.c @@ -0,0 +1,77 @@ +/* 7zAlloc.c -- Allocation functions +2008-10-04 : Igor Pavlov : Public domain */ + +#include +#include "7zAlloc.h" + +/* #define _SZ_ALLOC_DEBUG */ +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ + +#ifdef _SZ_ALLOC_DEBUG + +#ifdef _WIN32 +#include +#endif + +#include +int g_allocCount = 0; +int g_allocCountTemp = 0; + +#endif + +void *SzAlloc(void *p, size_t size) +{ + p = p; + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount); + g_allocCount++; + #endif + return malloc(size); +} + +void SzFree(void *p, void *address) +{ + p = p; + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCount--; + fprintf(stderr, "\nFree; count = %10d", g_allocCount); + } + #endif + free(address); +} + +void *SzAllocTemp(void *p, size_t size) +{ + p = p; + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_temp %10d bytes; count = %10d", size, g_allocCountTemp); + g_allocCountTemp++; + #ifdef _WIN32 + return HeapAlloc(GetProcessHeap(), 0, size); + #endif + #endif + return malloc(size); +} + +void SzFreeTemp(void *p, void *address) +{ + p = p; + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + { + g_allocCountTemp--; + fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp); + } + #ifdef _WIN32 + HeapFree(GetProcessHeap(), 0, address); + return; + #endif + #endif + free(address); +} diff --git a/snesreader/7z_C/7zAlloc.h b/snesreader/7z_C/7zAlloc.h new file mode 100644 index 00000000..f84ca5ae --- /dev/null +++ b/snesreader/7z_C/7zAlloc.h @@ -0,0 +1,23 @@ +/* 7zAlloc.h -- Allocation functions +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ALLOC_H +#define __7Z_ALLOC_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +void *SzAlloc(void *p, size_t size); +void SzFree(void *p, void *address); + +void *SzAllocTemp(void *p, size_t size); +void SzFreeTemp(void *p, void *address); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/7z_C/7zBuf.c b/snesreader/7z_C/7zBuf.c new file mode 100644 index 00000000..14e7f4e2 --- /dev/null +++ b/snesreader/7z_C/7zBuf.c @@ -0,0 +1,36 @@ +/* 7zBuf.c -- Byte Buffer +2008-03-28 +Igor Pavlov +Public domain */ + +#include "7zBuf.h" + +void Buf_Init(CBuf *p) +{ + p->data = 0; + p->size = 0; +} + +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) +{ + p->size = 0; + if (size == 0) + { + p->data = 0; + return 1; + } + p->data = (Byte *)alloc->Alloc(alloc, size); + if (p->data != 0) + { + p->size = size; + return 1; + } + return 0; +} + +void Buf_Free(CBuf *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->data); + p->data = 0; + p->size = 0; +} diff --git a/snesreader/7z_C/7zBuf.h b/snesreader/7z_C/7zBuf.h new file mode 100644 index 00000000..c5bd7187 --- /dev/null +++ b/snesreader/7z_C/7zBuf.h @@ -0,0 +1,31 @@ +/* 7zBuf.h -- Byte Buffer +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_BUF_H +#define __7Z_BUF_H + +#include "Types.h" + +typedef struct +{ + Byte *data; + size_t size; +} CBuf; + +void Buf_Init(CBuf *p); +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); +void Buf_Free(CBuf *p, ISzAlloc *alloc); + +typedef struct +{ + Byte *data; + size_t size; + size_t pos; +} CDynBuf; + +void DynBuf_Construct(CDynBuf *p); +void DynBuf_SeekToBeg(CDynBuf *p); +int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); +void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); + +#endif diff --git a/snesreader/7z_C/7zC.txt b/snesreader/7z_C/7zC.txt new file mode 100644 index 00000000..4ff63250 --- /dev/null +++ b/snesreader/7z_C/7zC.txt @@ -0,0 +1,194 @@ +7z ANSI-C Decoder 4.62 +---------------------- + +7z ANSI-C provides 7z/LZMA decoding. +7z ANSI-C version is simplified version ported from C++ code. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + + +LICENSE +------- + +7z ANSI-C Decoder is part of the LZMA SDK. +LZMA SDK is written and placed in the public domain by Igor Pavlov. + +Files +--------------------- + +7zDecode.* - Low level 7z decoding +7zExtract.* - High level 7z decoding +7zHeader.* - .7z format constants +7zIn.* - .7z archive opening +7zItem.* - .7z structures +7zMain.c - Test application + + +How To Use +---------- + +You must download 7-Zip program from www.7-zip.org. + +You can create .7z archive with 7z.exe or 7za.exe: + + 7za.exe a archive.7z *.htm -r -mx -m0fb=255 + +If you have big number of files in archive, and you need fast extracting, +you can use partly-solid archives: + + 7za.exe a archive.7z *.htm -ms=512K -r -mx -m0fb=255 -m0d=512K + +In that example 7-Zip will use 512KB solid blocks. So it needs to decompress only +512KB for extracting one file from such archive. + + +Limitations of current version of 7z ANSI-C Decoder +--------------------------------------------------- + + - It reads only "FileName", "Size", "LastWriteTime" and "CRC" information for each file in archive. + - It supports only LZMA and Copy (no compression) methods with BCJ or BCJ2 filters. + - It converts original UTF-16 Unicode file names to UTF-8 Unicode file names. + +These limitations will be fixed in future versions. + + +Using 7z ANSI-C Decoder Test application: +----------------------------------------- + +Usage: 7zDec + +: + e: Extract files from archive + l: List contents of archive + t: Test integrity of archive + +Example: + + 7zDec l archive.7z + +lists contents of archive.7z + + 7zDec e archive.7z + +extracts files from archive.7z to current folder. + + +How to use .7z Decoder +---------------------- + +Memory allocation +~~~~~~~~~~~~~~~~~ + +7z Decoder uses two memory pools: +1) Temporary pool +2) Main pool +Such scheme can allow you to avoid fragmentation of allocated blocks. + + +Steps for using 7z decoder +-------------------------- + +Use code at 7zMain.c as example. + +1) Declare variables: + inStream /* implements ILookInStream interface */ + CSzArEx db; /* 7z archive database structure */ + ISzAlloc allocImp; /* memory functions for main pool */ + ISzAlloc allocTempImp; /* memory functions for temporary pool */ + +2) call CrcGenerateTable(); function to initialize CRC structures. + +3) call SzArEx_Init(&db); function to initialize db structures. + +4) call SzArEx_Open(&db, inStream, &allocMain, &allocTemp) to open archive + +This function opens archive "inStream" and reads headers to "db". +All items in "db" will be allocated with "allocMain" functions. +SzArEx_Open function allocates and frees temporary structures by "allocTemp" functions. + +5) List items or Extract items + + Listing code: + ~~~~~~~~~~~~~ + { + UInt32 i; + for (i = 0; i < db.db.NumFiles; i++) + { + CFileItem *f = db.db.Files + i; + printf("%10d %s\n", (int)f->Size, f->Name); + } + } + + Extracting code: + ~~~~~~~~~~~~~~~~ + + SZ_RESULT SzAr_Extract( + CArchiveDatabaseEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + If you need to decompress more than one file, you can send these values from previous call: + blockIndex, + outBuffer, + outBufferSize, + You can consider "outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + After decompressing you must free "outBuffer": + allocImp.Free(outBuffer); + +6) call SzArEx_Free(&db, allocImp.Free) to free allocated items in "db". + + + + +Memory requirements for .7z decoding +------------------------------------ + +Memory usage for Archive opening: + - Temporary pool: + - Memory for uncompressed .7z headers + - some other temporary blocks + - Main pool: + - Memory for database: + Estimated size of one file structures in solid archive: + - Size (4 or 8 Bytes) + - CRC32 (4 bytes) + - LastWriteTime (8 bytes) + - Some file information (4 bytes) + - File Name (variable length) + pointer + allocation structures + +Memory usage for archive Decompressing: + - Temporary pool: + - Memory for LZMA decompressing structures + - Main pool: + - Memory for decompressed solid block + - Memory for temprorary buffers, if BCJ2 fileter is used. Usually these + temprorary buffers can be about 15% of solid block size. + + +7z Decoder doesn't allocate memory for compressed blocks. +Instead of this, you must allocate buffer with desired +size before calling 7z Decoder. Use 7zMain.c as example. + + +Defines +------- + +_SZ_ALLOC_DEBUG - define it if you want to debug alloc/free operations to stderr. + + +--- + +http://www.7-zip.org +http://www.7-zip.org/sdk.html +http://www.7-zip.org/support.html diff --git a/snesreader/7z_C/7zCrc.c b/snesreader/7z_C/7zCrc.c new file mode 100644 index 00000000..71962b2c --- /dev/null +++ b/snesreader/7z_C/7zCrc.c @@ -0,0 +1,35 @@ +/* 7zCrc.c -- CRC32 calculation +2008-08-05 +Igor Pavlov +Public domain */ + +#include "7zCrc.h" + +#define kCrcPoly 0xEDB88320 +UInt32 g_CrcTable[256]; + +void MY_FAST_CALL CrcGenerateTable(void) +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } +} + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 ; size--, p++) + v = CRC_UPDATE_BYTE(v, *p); + return v; +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return CrcUpdate(CRC_INIT_VAL, data, size) ^ 0xFFFFFFFF; +} diff --git a/snesreader/7z_C/7zCrc.h b/snesreader/7z_C/7zCrc.h new file mode 100644 index 00000000..ab8cf8c4 --- /dev/null +++ b/snesreader/7z_C/7zCrc.h @@ -0,0 +1,32 @@ +/* 7zCrc.h -- CRC32 calculation +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include + +#include "Types.h" + +#ifdef __cplusplus + extern "C" { +#endif + +extern UInt32 g_CrcTable[]; + +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ 0xFFFFFFFF) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/7z_C/7zDecode.c b/snesreader/7z_C/7zDecode.c new file mode 100644 index 00000000..c643da5f --- /dev/null +++ b/snesreader/7z_C/7zDecode.c @@ -0,0 +1,257 @@ +/* 7zDecode.c -- Decoding from 7z folder +2008-11-23 : Igor Pavlov : Public domain */ + +#include + +#include "Bcj2.h" +#include "Bra.h" +#include "LzmaDec.h" +#include "7zDecode.h" + +#define k_Copy 0 +#define k_LZMA 0x30101 +#define k_BCJ 0x03030103 +#define k_BCJ2 0x0303011B + +static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzmaDec state; + SRes res = SZ_OK; + + LzmaDec_Construct(&state); + RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); + state.dic = outBuffer; + state.dicBufSize = outSize; + LzmaDec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; + ELzmaStatus status; + res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) + { + if (state.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK && + status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + LzmaDec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + void *inBuf; + size_t curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)inSize; + RINOK(inStream->Look((void *)inStream, (void **)&inBuf, &curSize)); + if (curSize == 0) + return SZ_ERROR_INPUT_EOF; + memcpy(outBuffer, inBuf, curSize); + outBuffer += curSize; + inSize -= curSize; + RINOK(inStream->Skip((void *)inStream, curSize)); + } + return SZ_OK; +} + +#define IS_UNSUPPORTED_METHOD(m) ((m) != k_Copy && (m) != k_LZMA) +#define IS_UNSUPPORTED_CODER(c) (IS_UNSUPPORTED_METHOD(c.MethodID) || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ(c) (c.MethodID != k_BCJ || c.NumInStreams != 1 || c.NumOutStreams != 1) +#define IS_NO_BCJ2(c) (c.MethodID != k_BCJ2 || c.NumInStreams != 4 || c.NumOutStreams != 1) + +static +SRes CheckSupportedFolder(const CSzFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZ_ERROR_UNSUPPORTED; + if (IS_UNSUPPORTED_CODER(f->Coders[0])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 2) + { + if (IS_NO_BCJ(f->Coders[1]) || + f->NumPackStreams != 1 || f->PackStreams[0] != 0 || + f->NumBindPairs != 1 || + f->BindPairs[0].InIndex != 1 || f->BindPairs[0].OutIndex != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 4) + { + if (IS_UNSUPPORTED_CODER(f->Coders[1]) || + IS_UNSUPPORTED_CODER(f->Coders[2]) || + IS_NO_BCJ2(f->Coders[3])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumPackStreams != 4 || + f->PackStreams[0] != 2 || + f->PackStreams[1] != 6 || + f->PackStreams[2] != 1 || + f->PackStreams[3] != 0 || + f->NumBindPairs != 3 || + f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || + f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || + f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + return SZ_ERROR_UNSUPPORTED; +} + +static +UInt64 GetSum(const UInt64 *values, UInt32 index) +{ + UInt64 sum = 0; + UInt32 i; + for (i = 0; i < index; i++) + sum += values[i]; + return sum; +} + +static +SRes SzDecode2(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + SizeT tempSizes[3] = { 0, 0, 0}; + SizeT tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + CSzCoderInfo *coder = &folder->Coders[ci]; + + if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA) + { + UInt32 si = 0; + UInt64 offset; + UInt64 inSize; + Byte *outBufCur = outBuffer; + SizeT outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + UInt64 unpackSize = folder->UnpackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (SizeT)unpackSize; + if (outSizeCur != unpackSize) + return SZ_ERROR_MEM; + temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); + if (temp == 0 && outSizeCur != 0) + return SZ_ERROR_MEM; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) /* check it */ + return SZ_ERROR_PARAM; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (SizeT)unpackSize; + } + else + return SZ_ERROR_UNSUPPORTED; + } + offset = GetSum(packSizes, si); + inSize = packSizes[si]; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) /* check it */ + return SZ_ERROR_DATA; + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + } + else + { + RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + } + else if (coder->MethodID == k_BCJ) + { + UInt32 state; + if (ci != 1) + return SZ_ERROR_UNSUPPORTED; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + } + else if (coder->MethodID == k_BCJ2) + { + UInt64 offset = GetSum(packSizes, 1); + UInt64 s3Size = packSizes[1]; + SRes res; + if (ci != 3) + return SZ_ERROR_UNSUPPORTED; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + tempSizes[2] = (SizeT)s3Size; + if (tempSizes[2] != s3Size) + return SZ_ERROR_MEM; + tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); + if (tempBuf[2] == 0 && tempSizes[2] != 0) + return SZ_ERROR_MEM; + res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); + RINOK(res) + + res = Bcj2_Decode( + tempBuf3, tempSize3, + tempBuf[0], tempSizes[0], + tempBuf[1], tempSizes[1], + tempBuf[2], tempSizes[2], + outBuffer, outSize); + RINOK(res) + } + else + return SZ_ERROR_UNSUPPORTED; + } + return SZ_OK; +} + +SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + Byte *tempBuf[3] = { 0, 0, 0}; + int i; + SRes res = SzDecode2(packSizes, folder, inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + return res; +} diff --git a/snesreader/7z_C/7zDecode.h b/snesreader/7z_C/7zDecode.h new file mode 100644 index 00000000..e19fe387 --- /dev/null +++ b/snesreader/7z_C/7zDecode.h @@ -0,0 +1,13 @@ +/* 7zDecode.h -- Decoding from 7z folder +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "7zItem.h" + +SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +#endif diff --git a/snesreader/7z_C/7zExtract.c b/snesreader/7z_C/7zExtract.c new file mode 100644 index 00000000..99ef3654 --- /dev/null +++ b/snesreader/7z_C/7zExtract.c @@ -0,0 +1,93 @@ +/* 7zExtract.c -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#include "7zCrc.h" +#include "7zDecode.h" +#include "7zExtract.h" + +SRes SzAr_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; + SRes res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); + size_t unpackSize = (size_t)unpackSizeSpec; + UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *outBuffer); + *outBuffer = 0; + + RINOK(LookInStream_SeekTo(inStream, startOffset)); + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*outBuffer == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + { + res = SzDecode(p->db.PackSizes + + p->FolderStartPackStreamIndex[folderIndex], folder, + inStream, startOffset, + *outBuffer, unpackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnpackCRCDefined) + { + if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) + res = SZ_ERROR_CRC; + } + } + } + } + } + if (res == SZ_OK) + { + UInt32 i; + CSzFileItem *fileItem = p->db.Files + fileIndex; + *offset = 0; + for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)p->db.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + { + if (fileItem->FileCRCDefined) + { + if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC) + res = SZ_ERROR_CRC; + } + } + } + return res; +} diff --git a/snesreader/7z_C/7zExtract.h b/snesreader/7z_C/7zExtract.h new file mode 100644 index 00000000..1ca110c6 --- /dev/null +++ b/snesreader/7z_C/7zExtract.h @@ -0,0 +1,49 @@ +/* 7zExtract.h -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_EXTRACT_H +#define __7Z_EXTRACT_H + +#include "7zIn.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +SRes SzAr_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/7z_C/7zHeader.c b/snesreader/7z_C/7zHeader.c new file mode 100644 index 00000000..e48faa48 --- /dev/null +++ b/snesreader/7z_C/7zHeader.c @@ -0,0 +1,6 @@ +/* 7zHeader.c -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zHeader.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; diff --git a/snesreader/7z_C/7zHeader.h b/snesreader/7z_C/7zHeader.h new file mode 100644 index 00000000..ad095df4 --- /dev/null +++ b/snesreader/7z_C/7zHeader.h @@ -0,0 +1,57 @@ +/* 7zHeader.h -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "Types.h" + +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; + +#define k7zMajorVersion 0 + +#define k7zStartHeaderSize 0x20 + +enum EIdEnum +{ + k7zIdEnd, + + k7zIdHeader, + + k7zIdArchiveProperties, + + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + + k7zIdSize, + k7zIdCRC, + + k7zIdFolder, + + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttributes, + k7zIdComment, + + k7zIdEncodedHeader, + + k7zIdStartPos, + k7zIdDummy +}; + +#endif diff --git a/snesreader/7z_C/7zIn.c b/snesreader/7z_C/7zIn.c new file mode 100644 index 00000000..e594b7de --- /dev/null +++ b/snesreader/7z_C/7zIn.c @@ -0,0 +1,1204 @@ +/* 7zIn.c -- 7z Input functions +2008-12-31 : Igor Pavlov : Public domain */ + +#include "7zCrc.h" +#include "CpuArch.h" + +#include "7zDecode.h" +#include "7zIn.h" + +#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } + +#define NUM_FOLDER_CODERS_MAX 32 +#define NUM_CODER_STREAMS_MAX 32 + +void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + p->FolderStartPackStreamIndex = 0; + p->PackStreamStartPositions = 0; + p->FolderStartFileIndex = 0; + p->FileIndexToFolderIndexMap = 0; +} + +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->FolderStartPackStreamIndex); + IAlloc_Free(alloc, p->PackStreamStartPositions); + IAlloc_Free(alloc, p->FolderStartFileIndex); + IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + +/* +UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const +{ + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; +} + +UInt64 GetFilePackSize(int fileIndex) const +{ + int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex >= 0) + { + const CSzFolder &folderInfo = Folders[folderIndex]; + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + } + return 0; +} +*/ + +#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } + +static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) +{ + UInt32 startPos = 0; + UInt64 startPosSize = 0; + UInt32 i; + UInt32 folderIndex = 0; + UInt32 indexInFolder = 0; + MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); + for (i = 0; i < p->db.NumFolders; i++) + { + p->FolderStartPackStreamIndex[i] = startPos; + startPos += p->db.Folders[i].NumPackStreams; + } + + MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); + + for (i = 0; i < p->db.NumPackStreams; i++) + { + p->PackStreamStartPositions[i] = startPosSize; + startPosSize += p->db.PackSizes[i]; + } + + MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); + MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); + + for (i = 0; i < p->db.NumFiles; i++) + { + CSzFileItem *file = p->db.Files + i; + int emptyStream = !file->HasStream; + if (emptyStream && indexInFolder == 0) + { + p->FileIndexToFolderIndexMap[i] = (UInt32)-1; + continue; + } + if (indexInFolder == 0) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: Loop for skipping empty folders + */ + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderStartFileIndex[folderIndex] = i; + if (p->db.Folders[folderIndex].NumUnpackStreams != 0) + break; + folderIndex++; + } + } + p->FileIndexToFolderIndexMap[i] = folderIndex; + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) + { + folderIndex++; + indexInFolder = 0; + } + } + return SZ_OK; +} + + +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) +{ + return p->dataPos + + p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; +} + +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) +{ + UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 size = 0; + UInt32 i; + for (i = 0; i < folder->NumPackStreams; i++) + { + UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; + if (t < size) /* check it */ + return SZ_ERROR_FAIL; + size = t; + } + *resSize = size; + return SZ_OK; +} + + +/* +SRes SzReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt64 type) +{ + CBoolVector boolVector; + RINOK(ReadBoolVector2(files.Size(), boolVector)) + + CStreamSwitch streamSwitch; + RINOK(streamSwitch.Set(this, &dataVector)); + + for (int i = 0; i < files.Size(); i++) + { + CSzFileItem &file = files[i]; + CArchiveFileTime fileTime; + bool defined = boolVector[i]; + if (defined) + { + UInt32 low, high; + RINOK(SzReadUInt32(low)); + RINOK(SzReadUInt32(high)); + fileTime.dwLowDateTime = low; + fileTime.dwHighDateTime = high; + } + switch(type) + { + case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; + case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; + case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; + } + } + return SZ_OK; +} +*/ + +static int TestSignatureCandidate(Byte *testBytes) +{ + size_t i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +typedef struct _CSzState +{ + Byte *Data; + size_t Size; +}CSzData; + +static SRes SzReadByte(CSzData *sd, Byte *b) +{ + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + sd->Size--; + *b = *sd->Data++; + return SZ_OK; +} + +static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) + { + RINOK(SzReadByte(sd, data + i)); + } + return SZ_OK; +} + +static SRes SzReadUInt32(CSzData *sd, UInt32 *value) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt32)(b) << (8 * i)); + } + return SZ_OK; +} + +static SRes SzReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte; + Byte mask = 0x80; + int i; + RINOK(SzReadByte(sd, &firstByte)); + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + *value += (highPart << (8 * i)); + return SZ_OK; + } + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + if (value64 >= 0x80000000) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +static SRes SzReadID(CSzData *sd, UInt64 *value) +{ + return SzReadNumber(sd, value); +} + +static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) +{ + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + sd->Size -= (size_t)size; + sd->Data += (size_t)size; + return SZ_OK; +} + +static SRes SzSkeepData(CSzData *sd) +{ + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + return SzSkeepDataSize(sd, size); +} + +static SRes SzReadArchiveProperties(CSzData *sd) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + SzSkeepData(sd); + } + return SZ_OK; +} + +static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == attribute) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte b = 0; + Byte mask = 0; + size_t i; + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + { + if (mask == 0) + { + RINOK(SzReadByte(sd, &b)); + mask = 0x80; + } + (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + size_t i; + RINOK(SzReadByte(sd, &allAreDefined)); + if (allAreDefined == 0) + return SzReadBoolVector(sd, numItems, v, alloc); + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + (*v)[i] = 1; + return SZ_OK; +} + +static SRes SzReadHashDigests( + CSzData *sd, + size_t numItems, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *alloc) +{ + size_t i; + RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); + MY_ALLOC(UInt32, *digests, numItems, alloc); + for (i = 0; i < numItems; i++) + if ((*digestsDefined)[i]) + { + RINOK(SzReadUInt32(sd, (*digests) + i)); + } + return SZ_OK; +} + +static SRes SzReadPackInfo( + CSzData *sd, + UInt64 *dataOffset, + UInt32 *numPackStreams, + UInt64 **packSizes, + Byte **packCRCsDefined, + UInt32 **packCRCs, + ISzAlloc *alloc) +{ + UInt32 i; + RINOK(SzReadNumber(sd, dataOffset)); + RINOK(SzReadNumber32(sd, numPackStreams)); + + RINOK(SzWaitAttribute(sd, k7zIdSize)); + + MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); + + for (i = 0; i < *numPackStreams; i++) + { + RINOK(SzReadNumber(sd, (*packSizes) + i)); + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + if (type == k7zIdCRC) + { + RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); + continue; + } + RINOK(SzSkeepData(sd)); + } + if (*packCRCsDefined == 0) + { + MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); + MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); + for (i = 0; i < *numPackStreams; i++) + { + (*packCRCsDefined)[i] = 0; + (*packCRCs)[i] = 0; + } + } + return SZ_OK; +} + +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} + +static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) +{ + UInt32 numCoders, numBindPairs, numPackStreams, i; + UInt32 numInStreams = 0, numOutStreams = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders > NUM_FOLDER_CODERS_MAX) + return SZ_ERROR_UNSUPPORTED; + folder->NumCoders = numCoders; + + MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); + + for (i = 0; i < numCoders; i++) + SzCoderInfo_Init(folder->Coders + i); + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = folder->Coders + i; + { + unsigned idSize, j; + Byte longID[15]; + RINOK(SzReadByte(sd, &mainByte)); + idSize = (unsigned)(mainByte & 0xF); + RINOK(SzReadBytes(sd, longID, idSize)); + if (idSize > sizeof(coder->MethodID)) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = 0; + for (j = 0; j < idSize; j++) + coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); + + if ((mainByte & 0x10) != 0) + { + RINOK(SzReadNumber32(sd, &coder->NumInStreams)); + RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); + if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || + coder->NumOutStreams > NUM_CODER_STREAMS_MAX) + return SZ_ERROR_UNSUPPORTED; + } + else + { + coder->NumInStreams = 1; + coder->NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) + return SZ_ERROR_MEM; + RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); + } + } + while ((mainByte & 0x80) != 0) + { + RINOK(SzReadByte(sd, &mainByte)); + RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); + if ((mainByte & 0x10) != 0) + { + UInt32 n; + RINOK(SzReadNumber32(sd, &n)); + RINOK(SzReadNumber32(sd, &n)); + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + RINOK(SzSkeepDataSize(sd, propertiesSize)); + } + } + numInStreams += coder->NumInStreams; + numOutStreams += coder->NumOutStreams; + } + + if (numOutStreams == 0) + return SZ_ERROR_UNSUPPORTED; + + folder->NumBindPairs = numBindPairs = numOutStreams - 1; + MY_ALLOC(CBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); + + for (i = 0; i < numBindPairs; i++) + { + CBindPair *bp = folder->BindPairs + i; + RINOK(SzReadNumber32(sd, &bp->InIndex)); + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + } + + if (numInStreams < numBindPairs) + return SZ_ERROR_UNSUPPORTED; + + folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; + MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams ; i++) + if (SzFolder_FindBindPairForInStream(folder, i) < 0) + break; + if (i == numInStreams) + return SZ_ERROR_UNSUPPORTED; + folder->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + RINOK(SzReadNumber32(sd, folder->PackStreams + i)); + } + return SZ_OK; +} + +static SRes SzReadUnpackInfo( + CSzData *sd, + UInt32 *numFolders, + CSzFolder **folders, /* for alloc */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + UInt32 i; + RINOK(SzWaitAttribute(sd, k7zIdFolder)); + RINOK(SzReadNumber32(sd, numFolders)); + { + RINOK(SzReadSwitch(sd)); + + MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); + + for (i = 0; i < *numFolders; i++) + SzFolder_Init((*folders) + i); + + for (i = 0; i < *numFolders; i++) + { + RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); + } + } + + RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); + + for (i = 0; i < *numFolders; i++) + { + UInt32 j; + CSzFolder *folder = (*folders) + i; + UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); + + MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); + + for (j = 0; j < numOutStreams; j++) + { + RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); + } + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + SRes res; + Byte *crcsDefined = 0; + UInt32 *crcs = 0; + res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < *numFolders; i++) + { + CSzFolder *folder = (*folders) + i; + folder->UnpackCRCDefined = crcsDefined[i]; + folder->UnpackCRC = crcs[i]; + } + } + IAlloc_Free(allocTemp, crcs); + IAlloc_Free(allocTemp, crcsDefined); + RINOK(res); + continue; + } + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadSubStreamsInfo( + CSzData *sd, + UInt32 numFolders, + CSzFolder *folders, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + UInt64 type = 0; + UInt32 i; + UInt32 si = 0; + UInt32 numDigests = 0; + + for (i = 0; i < numFolders; i++) + folders[i].NumUnpackStreams = 1; + *numUnpackStreams = numFolders; + + for (;;) + { + RINOK(SzReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + *numUnpackStreams = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + folders[i].NumUnpackStreams = numStreams; + *numUnpackStreams += numStreams; + } + continue; + } + if (type == k7zIdCRC || type == k7zIdSize) + break; + if (type == k7zIdEnd) + break; + RINOK(SzSkeepData(sd)); + } + + if (*numUnpackStreams == 0) + { + *unpackSizes = 0; + *digestsDefined = 0; + *digests = 0; + } + else + { + *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); + RINOM(*unpackSizes); + *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); + RINOM(*digestsDefined); + *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); + RINOM(*digests); + } + + for (i = 0; i < numFolders; i++) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: we check that folder is empty + */ + UInt64 sum = 0; + UInt32 j; + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams == 0) + continue; + if (type == k7zIdSize) + for (j = 1; j < numSubstreams; j++) + { + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + (*unpackSizes)[si++] = size; + sum += size; + } + (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; + } + if (type == k7zIdSize) + { + RINOK(SzReadID(sd, &type)); + } + + for (i = 0; i < *numUnpackStreams; i++) + { + (*digestsDefined)[i] = 0; + (*digests)[i] = 0; + } + + + for (i = 0; i < numFolders; i++) + { + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + numDigests += numSubstreams; + } + + + si = 0; + for (;;) + { + if (type == k7zIdCRC) + { + int digestIndex = 0; + Byte *digestsDefined2 = 0; + UInt32 *digests2 = 0; + SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < numFolders; i++) + { + CSzFolder *folder = folders + i; + UInt32 numSubstreams = folder->NumUnpackStreams; + if (numSubstreams == 1 && folder->UnpackCRCDefined) + { + (*digestsDefined)[si] = 1; + (*digests)[si] = folder->UnpackCRC; + si++; + } + else + { + UInt32 j; + for (j = 0; j < numSubstreams; j++, digestIndex++) + { + (*digestsDefined)[si] = digestsDefined2[digestIndex]; + (*digests)[si] = digests2[digestIndex]; + si++; + } + } + } + } + IAlloc_Free(allocTemp, digestsDefined2); + IAlloc_Free(allocTemp, digests2); + RINOK(res); + } + else if (type == k7zIdEnd) + return SZ_OK; + else + { + RINOK(SzSkeepData(sd)); + } + RINOK(SzReadID(sd, &type)); + } +} + + +static SRes SzReadStreamsInfo( + CSzData *sd, + UInt64 *dataOffset, + CSzAr *p, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if ((UInt64)(int)type != type) + return SZ_ERROR_UNSUPPORTED; + switch((int)type) + { + case k7zIdEnd: + return SZ_OK; + case k7zIdPackInfo: + { + RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, + &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); + break; + } + case k7zIdUnpackInfo: + { + RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); + break; + } + case k7zIdSubStreamsInfo: + { + RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, + numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); + break; + } + default: + return SZ_ERROR_UNSUPPORTED; + } + } +} + +Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +static SRes SzReadFileNames(CSzData *sd, UInt32 numFiles, CSzFileItem *files, ISzAlloc *alloc) +{ + UInt32 i; + for (i = 0; i < numFiles; i++) + { + UInt32 len = 0; + UInt32 pos = 0; + CSzFileItem *file = files + i; + while (pos + 2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + len++; + if (value == 0) + break; + if (value < 0x80) + continue; + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2; + if (value >= 0xDC00) + return SZ_ERROR_ARCHIVE; + if (pos + 2 > sd->Size) + return SZ_ERROR_ARCHIVE; + c2 = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8)); + pos += 2; + if (c2 < 0xDC00 || c2 >= 0xE000) + return SZ_ERROR_ARCHIVE; + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + len += numAdds; + } + + MY_ALLOC(char, file->Name, (size_t)len, alloc); + + len = 0; + while (2 <= sd->Size) + { + int numAdds; + UInt32 value = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + if (value < 0x80) + { + file->Name[len++] = (char)value; + if (value == 0) + break; + continue; + } + if (value >= 0xD800 && value < 0xE000) + { + UInt32 c2 = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8)); + SzSkeepDataSize(sd, 2); + value = ((value - 0xD800) << 10) | (c2 - 0xDC00); + } + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((UInt32)1) << (numAdds * 5 + 6))) + break; + file->Name[len++] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); + do + { + numAdds--; + file->Name[len++] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); + } + while (numAdds > 0); + + len += numAdds; + } + } + return SZ_OK; +} + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + Byte **emptyStreamVector, /* allocTemp */ + Byte **emptyFileVector, /* allocTemp */ + Byte **lwtVector, /* allocTemp */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 htype; + UInt32 numUnpackStreams = 0; + UInt32 numFiles = 0; + CSzFileItem *files = 0; + UInt32 numEmptyStreams = 0; + UInt32 i; + + RINOK(SzReadID(sd, &htype)); + + if (htype == k7zIdArchiveProperties) + { + RINOK(SzReadArchiveProperties(sd)); + RINOK(SzReadID(sd, &htype)); + } + + + if (htype == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(sd, + &p->dataPos, + &p->db, + &numUnpackStreams, + unpackSizes, + digestsDefined, + digests, allocMain, allocTemp)); + p->dataPos += p->startPosAfterHeader; + RINOK(SzReadID(sd, &htype)); + } + + if (htype == k7zIdEnd) + return SZ_OK; + if (htype != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->db.NumFiles = numFiles; + + MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); + + p->db.Files = files; + for (i = 0; i < numFiles; i++) + SzFile_Init(files + i); + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SzReadNumber(sd, &size)); + + if ((UInt64)(int)type != type) + { + RINOK(SzSkeepDataSize(sd, size)); + } + else + switch((int)type) + { + case k7zIdName: + { + RINOK(SzReadSwitch(sd)); + RINOK(SzReadFileNames(sd, numFiles, files, allocMain)) + break; + } + case k7zIdEmptyStream: + { + RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); + numEmptyStreams = 0; + for (i = 0; i < numFiles; i++) + if ((*emptyStreamVector)[i]) + numEmptyStreams++; + break; + } + case k7zIdEmptyFile: + { + RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); + break; + } + case k7zIdMTime: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->MTimeDefined = defined; + f->MTime.Low = f->MTime.High = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->MTime.Low)); + RINOK(SzReadUInt32(sd, &f->MTime.High)); + } + } + break; + } + default: + { + RINOK(SzSkeepDataSize(sd, size)); + } + } + } + + { + UInt32 emptyFileIndex = 0; + UInt32 sizeIndex = 0; + for (i = 0; i < numFiles; i++) + { + CSzFileItem *file = files + i; + file->IsAnti = 0; + if (*emptyStreamVector == 0) + file->HasStream = 1; + else + file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); + if (file->HasStream) + { + file->IsDir = 0; + file->Size = (*unpackSizes)[sizeIndex]; + file->FileCRC = (*digests)[sizeIndex]; + file->FileCRCDefined = (Byte)(*digestsDefined)[sizeIndex]; + sizeIndex++; + } + else + { + if (*emptyFileVector == 0) + file->IsDir = 1; + else + file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); + emptyFileIndex++; + file->Size = 0; + file->FileCRCDefined = 0; + } + } + } + return SzArEx_Fill(p, allocMain); +} + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + Byte *emptyStreamVector = 0; + Byte *emptyFileVector = 0; + Byte *lwtVector = 0; + SRes res = SzReadHeader2(p, sd, + &unpackSizes, &digestsDefined, &digests, + &emptyStreamVector, &emptyFileVector, &lwtVector, + allocMain, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + IAlloc_Free(allocTemp, emptyStreamVector); + IAlloc_Free(allocTemp, emptyFileVector); + IAlloc_Free(allocTemp, lwtVector); + return res; +} + +static SRes SzReadAndDecodePackedStreams2( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + CSzAr *p, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + + UInt32 numUnpackStreams = 0; + UInt64 dataStartPos; + CSzFolder *folder; + UInt64 unpackSize; + SRes res; + + RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, + &numUnpackStreams, unpackSizes, digestsDefined, digests, + allocTemp, allocTemp)); + + dataStartPos += baseOffset; + if (p->NumFolders != 1) + return SZ_ERROR_ARCHIVE; + + folder = p->Folders; + unpackSize = SzFolder_GetUnpackSize(folder); + + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + + if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + + res = SzDecode(p->PackSizes, folder, + inStream, dataStartPos, + outBuffer->data, (size_t)unpackSize, allocTemp); + RINOK(res); + if (folder->UnpackCRCDefined) + if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) + return SZ_ERROR_CRC; + return SZ_OK; +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + ISzAlloc *allocTemp) +{ + CSzAr p; + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + SRes res; + SzAr_Init(&p); + res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, + &p, &unpackSizes, &digestsDefined, &digests, + allocTemp); + SzAr_Free(&p, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buffer; + SRes res; + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < nextHeaderOffset || + (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + if (res == SZ_OK) + { + if (type == k7zIdEncodedHeader) + { + CBuf outBuffer; + Buf_Init(&outBuffer); + res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); + if (res != SZ_OK) + Buf_Free(&outBuffer, allocTemp); + else + { + Buf_Free(&buffer, allocTemp); + buffer.data = outBuffer.data; + buffer.size = outBuffer.size; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + } + } + } + if (res == SZ_OK) + { + if (type == k7zIdHeader) + res = SzReadHeader(p, &sd, allocMain, allocTemp); + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + Buf_Free(&buffer, allocTemp); + return res; +} + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} diff --git a/snesreader/7z_C/7zIn.h b/snesreader/7z_C/7zIn.h new file mode 100644 index 00000000..89e0fb85 --- /dev/null +++ b/snesreader/7z_C/7zIn.h @@ -0,0 +1,49 @@ +/* 7zIn.h -- 7z Input functions +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "7zHeader.h" +#include "7zItem.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 *FolderStartPackStreamIndex; + UInt64 *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; +} CSzArEx; + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/7z_C/7zItem.c b/snesreader/7z_C/7zItem.c new file mode 100644 index 00000000..4a092614 --- /dev/null +++ b/snesreader/7z_C/7zItem.c @@ -0,0 +1,129 @@ +/* 7zItem.c -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zItem.h" + +void SzCoderInfo_Init(CSzCoderInfo *p) +{ + Buf_Init(&p->Props); +} + +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) +{ + Buf_Free(&p->Props, alloc); + SzCoderInfo_Init(p); +} + +void SzFolder_Init(CSzFolder *p) +{ + p->Coders = 0; + p->BindPairs = 0; + p->PackStreams = 0; + p->UnpackSizes = 0; + p->NumCoders = 0; + p->NumBindPairs = 0; + p->NumPackStreams = 0; + p->UnpackCRCDefined = 0; + p->UnpackCRC = 0; + p->NumUnpackStreams = 0; +} + +static +void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Coders) + for (i = 0; i < p->NumCoders; i++) + SzCoderInfo_Free(&p->Coders[i], alloc); + IAlloc_Free(alloc, p->Coders); + IAlloc_Free(alloc, p->BindPairs); + IAlloc_Free(alloc, p->PackStreams); + IAlloc_Free(alloc, p->UnpackSizes); + SzFolder_Init(p); +} + +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < p->NumCoders; i++) + result += p->Coders[i].NumOutStreams; + return result; +} + +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +static +int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +UInt64 SzFolder_GetUnpackSize(CSzFolder *p) +{ + int i = (int)SzFolder_GetNumOutStreams(p); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolder_FindBindPairForOutStream(p, i) < 0) + return p->UnpackSizes[i]; + /* throw 1; */ + return 0; +} + +void SzFile_Init(CSzFileItem *p) +{ + p->HasStream = 1; + p->IsDir = 0; + p->IsAnti = 0; + p->FileCRCDefined = 0; + p->MTimeDefined = 0; + p->Name = 0; +} + +static void SzFile_Free(CSzFileItem *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Name); + SzFile_Init(p); +} + +void SzAr_Init(CSzAr *p) +{ + p->PackSizes = 0; + p->PackCRCsDefined = 0; + p->PackCRCs = 0; + p->Folders = 0; + p->Files = 0; + p->NumPackStreams = 0; + p->NumFolders = 0; + p->NumFiles = 0; +} + +void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Folders) + for (i = 0; i < p->NumFolders; i++) + SzFolder_Free(&p->Folders[i], alloc); + if (p->Files) + for (i = 0; i < p->NumFiles; i++) + SzFile_Free(&p->Files[i], alloc); + IAlloc_Free(alloc, p->PackSizes); + IAlloc_Free(alloc, p->PackCRCsDefined); + IAlloc_Free(alloc, p->PackCRCs); + IAlloc_Free(alloc, p->Folders); + IAlloc_Free(alloc, p->Files); + SzAr_Init(p); +} diff --git a/snesreader/7z_C/7zItem.h b/snesreader/7z_C/7zItem.h new file mode 100644 index 00000000..7ef24731 --- /dev/null +++ b/snesreader/7z_C/7zItem.h @@ -0,0 +1,83 @@ +/* 7zItem.h -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "7zBuf.h" + +typedef struct +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + UInt64 MethodID; + CBuf Props; +} CSzCoderInfo; + +void SzCoderInfo_Init(CSzCoderInfo *p); +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CBindPair; + +typedef struct +{ + CSzCoderInfo *Coders; + CBindPair *BindPairs; + UInt32 *PackStreams; + UInt64 *UnpackSizes; + UInt32 NumCoders; + UInt32 NumBindPairs; + UInt32 NumPackStreams; + int UnpackCRCDefined; + UInt32 UnpackCRC; + + UInt32 NumUnpackStreams; +} CSzFolder; + +void SzFolder_Init(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); + +typedef struct +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct +{ + CNtfsFileTime MTime; + UInt64 Size; + char *Name; + UInt32 FileCRC; + + Byte HasStream; + Byte IsDir; + Byte IsAnti; + Byte FileCRCDefined; + Byte MTimeDefined; +} CSzFileItem; + +void SzFile_Init(CSzFileItem *p); + +typedef struct +{ + UInt64 *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + CSzFolder *Folders; + CSzFileItem *Files; + UInt32 NumPackStreams; + UInt32 NumFolders; + UInt32 NumFiles; +} CSzAr; + +void SzAr_Init(CSzAr *p); +void SzAr_Free(CSzAr *p, ISzAlloc *alloc); + +#endif diff --git a/snesreader/7z_C/7zStream.c b/snesreader/7z_C/7zStream.c new file mode 100644 index 00000000..6dc333ef --- /dev/null +++ b/snesreader/7z_C/7zStream.c @@ -0,0 +1,184 @@ +/* 7zStream.c -- 7z Stream functions +2008-11-23 : Igor Pavlov : Public domain */ + +#include + +#include "Types.h" + +#if NEVER_CALLED +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) +{ + return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) +{ + size_t processed = 1; + RINOK(stream->Read(stream, buf, &processed)); + return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; +} +#endif + +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) +{ + Int64 t = offset; + return stream->Seek(stream, &t, SZ_SEEK_SET); +} + +#if NEVER_CALLED +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) +{ + void *lookBuf; + if (*size == 0) + return SZ_OK; + RINOK(stream->Look(stream, &lookBuf, size)); + memcpy(buf, lookBuf, *size); + return stream->Skip(stream, *size); +} +#endif + +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) +{ + return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +static SRes LookToRead_Look_Lookahead(void *pp, void **buf, size_t *size) +{ + SRes res = SZ_OK; +#if !NEVER_CALLED + (void)pp; + (void)buf; + (void)size; +#else + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + size2 = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, &size2); + p->size = size2; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; +#endif + return res; +} + +static SRes LookToRead_Look_Exact(void *pp, void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + if (*size > LookToRead_BUF_SIZE) + *size = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, size); + size2 = p->size = *size; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Skip(void *pp, size_t offset) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos += offset; + return SZ_OK; +} + +static SRes LookToRead_Read(void *pp, void *buf, size_t *size) +{ + CLookToRead *p = (CLookToRead *)pp; + size_t rem = p->size - p->pos; + if (rem == 0) + return p->realStream->Read(p->realStream, buf, size); + if (rem > *size) + rem = *size; + memcpy(buf, p->buf + p->pos, rem); + p->pos += rem; + *size = rem; + return SZ_OK; +} + +static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos = p->size = 0; + return p->realStream->Seek(p->realStream, pos, origin); +} + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead) +{ +#if !NEVER_CALLED + lookahead = 0; +#endif + p->s.Look = lookahead ? + LookToRead_Look_Lookahead : + LookToRead_Look_Exact; + p->s.Skip = LookToRead_Skip; + p->s.Read = LookToRead_Read; + p->s.Seek = LookToRead_Seek; +} + +void LookToRead_Init(CLookToRead *p) +{ + p->pos = p->size = 0; +} + +#if NEVER_CALLED +static SRes SecToLook_Read(void *pp, void *buf, size_t *size) +{ + CSecToLook *p = (CSecToLook *)pp; + return LookInStream_LookRead(p->realStream, buf, size); +} + +void SecToLook_CreateVTable(CSecToLook *p) +{ + p->s.Read = SecToLook_Read; +} + +static SRes SecToRead_Read(void *pp, void *buf, size_t *size) +{ + CSecToRead *p = (CSecToRead *)pp; + return p->realStream->Read(p->realStream, buf, size); +} + +void SecToRead_CreateVTable(CSecToRead *p) +{ + p->s.Read = SecToRead_Read; +} +#endif diff --git a/snesreader/7z_C/Bcj2.c b/snesreader/7z_C/Bcj2.c new file mode 100644 index 00000000..bc3dae92 --- /dev/null +++ b/snesreader/7z_C/Bcj2.c @@ -0,0 +1,132 @@ +/* Bcj2.c -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bcj2.h" + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*buffer++) +#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } +#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ + { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} + +#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } + +#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize) +{ + CProb p[256 + 2]; + SizeT inPos = 0, outPos = 0; + + const Byte *buffer, *bufferLim; + UInt32 range, code; + Byte prevByte = 0; + { + unsigned int i; + for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) + p[i] = kBitModelTotal >> 1; + } + buffer = buf3; + bufferLim = buffer + size3; + RC_INIT2 + + if (outSize == 0) + return SZ_OK; + + for (;;) + { + Byte b; + CProb *prob; + UInt32 bound; + UInt32 ttt; + + SizeT limit = size0 - inPos; + if (outSize - outPos < limit) + limit = outSize - outPos; + while (limit != 0) + { + Byte bb = buf0[inPos]; + outBuf[outPos++] = bb; + if (IsJ(prevByte, bb)) + break; + inPos++; + prevByte = bb; + limit--; + } + + if (limit == 0 || outPos == outSize) + break; + + b = buf0[inPos++]; + + if (b == 0xE8) + prob = p + prevByte; + else if (b == 0xE9) + prob = p + 256; + else + prob = p + 257; + + IF_BIT_0(prob) + { + UPDATE_0(prob) + prevByte = b; + } + else + { + UInt32 dest; + const Byte *v; + UPDATE_1(prob) + if (b == 0xE8) + { + v = buf1; + if (size1 < 4) + return SZ_ERROR_DATA; + buf1 += 4; + size1 -= 4; + } + else + { + v = buf2; + if (size2 < 4) + return SZ_ERROR_DATA; + buf2 += 4; + size2 -= 4; + } + dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | + ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); + outBuf[outPos++] = (Byte)dest; + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 8); + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 16); + if (outPos == outSize) + break; + outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + } + return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; +} diff --git a/snesreader/7z_C/Bcj2.h b/snesreader/7z_C/Bcj2.h new file mode 100644 index 00000000..32d450b3 --- /dev/null +++ b/snesreader/7z_C/Bcj2.h @@ -0,0 +1,30 @@ +/* Bcj2.h -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __BCJ2_H +#define __BCJ2_H + +#include "Types.h" + +/* +Conditions: + outSize <= FullOutputSize, + where FullOutputSize is full size of output stream of x86_2 filter. + +If buf0 overlaps outBuf, there are two required conditions: + 1) (buf0 >= outBuf) + 2) (buf0 + size0 >= outBuf + FullOutputSize). + +Returns: + SZ_OK + SZ_ERROR_DATA - Data error +*/ + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize); + +#endif diff --git a/snesreader/7z_C/Bra.h b/snesreader/7z_C/Bra.h new file mode 100644 index 00000000..45e231e8 --- /dev/null +++ b/snesreader/7z_C/Bra.h @@ -0,0 +1,60 @@ +/* Bra.h -- Branch converters for executables +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +#include "Types.h" + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +#endif diff --git a/snesreader/7z_C/Bra86.c b/snesreader/7z_C/Bra86.c new file mode 100644 index 00000000..1ee0e709 --- /dev/null +++ b/snesreader/7z_C/Bra86.c @@ -0,0 +1,85 @@ +/* Bra86.c -- Converter for x86 code (BCJ) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) + +const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; +const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; + +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT bufferPos = 0, prevPosT; + UInt32 prevMask = *state & 0x7; + if (size < 5) + return 0; + ip += 5; + prevPosT = (SizeT)0 - 1; + + for (;;) + { + Byte *p = data + bufferPos; + Byte *limit = data + size - 4; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + bufferPos = (SizeT)(p - data); + if (p >= limit) + break; + prevPosT = bufferPos - prevPosT; + if (prevPosT > 3) + prevMask = 0; + else + { + prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; + if (prevMask != 0) + { + Byte b = p[4 - kMaskToBitNumber[prevMask]]; + if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + { + prevPosT = bufferPos; + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + continue; + } + } + } + prevPosT = bufferPos; + + if (Test86MSByte(p[4])) + { + UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 dest; + for (;;) + { + Byte b; + int index; + if (encoding) + dest = (ip + (UInt32)bufferPos) + src; + else + dest = src - (ip + (UInt32)bufferPos); + if (prevMask == 0) + break; + index = kMaskToBitNumber[prevMask] * 8; + b = (Byte)(dest >> (24 - index)); + if (!Test86MSByte(b)) + break; + src = dest ^ ((1 << (32 - index)) - 1); + } + p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); + p[3] = (Byte)(dest >> 16); + p[2] = (Byte)(dest >> 8); + p[1] = (Byte)dest; + bufferPos += 5; + } + else + { + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + } + } + prevPosT = bufferPos - prevPosT; + *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); + return bufferPos; +} diff --git a/snesreader/7z_C/CpuArch.h b/snesreader/7z_C/CpuArch.h new file mode 100644 index 00000000..7384b0c3 --- /dev/null +++ b/snesreader/7z_C/CpuArch.h @@ -0,0 +1,69 @@ +/* CpuArch.h +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __CPUARCH_H +#define __CPUARCH_H + +/* +LITTLE_ENDIAN_UNALIGN means: + 1) CPU is LITTLE_ENDIAN + 2) it's allowed to make unaligned memory accesses +if LITTLE_ENDIAN_UNALIGN is not defined, it means that we don't know +about these properties of platform. +*/ + +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__) +#define LITTLE_ENDIAN_UNALIGN +#endif + +#ifdef LITTLE_ENDIAN_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(p)) +#define GetUi32(p) (*(const UInt32 *)(p)) +#define GetUi64(p) (*(const UInt64 *)(p)) +#define SetUi32(p, d) *(UInt32 *)(p) = (d); + +#else + +#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi32(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ + ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ + ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } + +#endif + +#if defined(LITTLE_ENDIAN_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#endif + +#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) + +#endif diff --git a/snesreader/7z_C/LzmaDec.c b/snesreader/7z_C/LzmaDec.c new file mode 100644 index 00000000..fb08e786 --- /dev/null +++ b/snesreader/7z_C/LzmaDec.c @@ -0,0 +1,1010 @@ +/* LzmaDec.c -- LZMA Decoder +2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +static const Byte kLiteralNextStates[kNumStates * 2] = +{ + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, + 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 +}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit2, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit2 = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit2 = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit2 = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit2, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +static +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +#if NEVER_CALLED +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} +#endif + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/snesreader/7z_C/LzmaDec.h b/snesreader/7z_C/LzmaDec.h new file mode 100644 index 00000000..98cdbe94 --- /dev/null +++ b/snesreader/7z_C/LzmaDec.h @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "Types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff --git a/snesreader/7z_C/Types.h b/snesreader/7z_C/Types.h new file mode 100644 index 00000000..286ce83b --- /dev/null +++ b/snesreader/7z_C/Types.h @@ -0,0 +1,206 @@ +/* Types.h -- Basic types +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/7z_C/lzma.txt b/snesreader/7z_C/lzma.txt new file mode 100644 index 00000000..d4f4af92 --- /dev/null +++ b/snesreader/7z_C/lzma.txt @@ -0,0 +1,594 @@ +LZMA SDK 4.65 +------------- + +LZMA SDK provides the documentation, samples, header files, libraries, +and tools you need to develop applications that use LZMA compression. + +LZMA is default and general compression method of 7z format +in 7-Zip compression program (www.7-zip.org). LZMA provides high +compression ratio and very fast decompression. + +LZMA is an improved version of famous LZ77 compression algorithm. +It was improved in way of maximum increasing of compression ratio, +keeping high decompression speed and low memory requirements for +decompressing. + + + +LICENSE +------- + +LZMA SDK is written and placed in the public domain by Igor Pavlov. + + +LZMA SDK Contents +----------------- + +LZMA SDK includes: + + - ANSI-C/C++/C#/Java source code for LZMA compressing and decompressing + - Compiled file->file LZMA compressing/decompressing program for Windows system + + +UNIX/Linux version +------------------ +To compile C++ version of file->file LZMA encoding, go to directory +C++/7zip/Compress/LZMA_Alone +and call make to recompile it: + make -f makefile.gcc clean all + +In some UNIX/Linux versions you must compile LZMA with static libraries. +To compile with static libraries, you can use +LIB = -lm -static + + +Files +--------------------- +lzma.txt - LZMA SDK description (this file) +7zFormat.txt - 7z Format description +7zC.txt - 7z ANSI-C Decoder description +methods.txt - Compression method IDs for .7z +lzma.exe - Compiled file->file LZMA encoder/decoder for Windows +history.txt - history of the LZMA SDK + + +Source code structure +--------------------- + +C/ - C files + 7zCrc*.* - CRC code + Alloc.* - Memory allocation functions + Bra*.* - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code + LzFind.* - Match finder for LZ (LZMA) encoders + LzFindMt.* - Match finder for LZ (LZMA) encoders for multithreading encoding + LzHash.h - Additional file for LZ match finder + LzmaDec.* - LZMA decoding + LzmaEnc.* - LZMA encoding + LzmaLib.* - LZMA Library for DLL calling + Types.h - Basic types for another .c files + Threads.* - The code for multithreading. + + LzmaLib - LZMA Library (.DLL for Windows) + + LzmaUtil - LZMA Utility (file->file LZMA encoder/decoder). + + Archive - files related to archiving + 7z - 7z ANSI-C Decoder + +CPP/ -- CPP files + + Common - common files for C++ projects + Windows - common files for Windows related code + + 7zip - files related to 7-Zip Project + + Common - common files for 7-Zip + + Compress - files related to compression/decompression + + Copy - Copy coder + RangeCoder - Range Coder (special code of compression/decompression) + LZMA - LZMA compression/decompression on C++ + LZMA_Alone - file->file LZMA compression/decompression + Branch - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code + + Archive - files related to archiving + + Common - common files for archive handling + 7z - 7z C++ Encoder/Decoder + + Bundles - Modules that are bundles of other modules + + Alone7z - 7zr.exe: Standalone version of 7z.exe that supports only 7z/LZMA/BCJ/BCJ2 + Format7zR - 7zr.dll: Reduced version of 7za.dll: extracting/compressing to 7z/LZMA/BCJ/BCJ2 + Format7zExtractR - 7zxr.dll: Reduced version of 7zxa.dll: extracting from 7z/LZMA/BCJ/BCJ2. + + UI - User Interface files + + Client7z - Test application for 7za.dll, 7zr.dll, 7zxr.dll + Common - Common UI files + Console - Code for console archiver + + + +CS/ - C# files + 7zip + Common - some common files for 7-Zip + Compress - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + LzmaAlone - file->file LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + +Java/ - Java files + SevenZip + Compression - files related to compression/decompression + LZ - files related to LZ (Lempel-Ziv) compression algorithm + LZMA - LZMA compression/decompression + RangeCoder - Range Coder (special code of compression/decompression) + + +C/C++ source code of LZMA SDK is part of 7-Zip project. +7-Zip source code can be downloaded from 7-Zip's SourceForge page: + + http://sourceforge.net/projects/sevenzip/ + + + +LZMA features +------------- + - Variable dictionary size (up to 1 GB) + - Estimated compressing speed: about 2 MB/s on 2 GHz CPU + - Estimated decompressing speed: + - 20-30 MB/s on 2 GHz Core 2 or AMD Athlon 64 + - 1-2 MB/s on 200 MHz ARM, MIPS, PowerPC or other simple RISC + - Small memory requirements for decompressing (16 KB + DictionarySize) + - Small code size for decompressing: 5-8 KB + +LZMA decoder uses only integer operations and can be +implemented in any modern 32-bit CPU (or on 16-bit CPU with some conditions). + +Some critical operations that affect the speed of LZMA decompression: + 1) 32*16 bit integer multiply + 2) Misspredicted branches (penalty mostly depends from pipeline length) + 3) 32-bit shift and arithmetic operations + +The speed of LZMA decompressing mostly depends from CPU speed. +Memory speed has no big meaning. But if your CPU has small data cache, +overall weight of memory speed will slightly increase. + + +How To Use +---------- + +Using LZMA encoder/decoder executable +-------------------------------------- + +Usage: LZMA inputFile outputFile [...] + + e: encode file + + d: decode file + + b: Benchmark. There are two tests: compressing and decompressing + with LZMA method. Benchmark shows rating in MIPS (million + instructions per second). Rating value is calculated from + measured speed and it is normalized with Intel's Core 2 results. + Also Benchmark checks possible hardware errors (RAM + errors in most cases). Benchmark uses these settings: + (-a1, -d21, -fb32, -mfbt4). You can change only -d parameter. + Also you can change the number of iterations. Example for 30 iterations: + LZMA b 30 + Default number of iterations is 10. + + + + + -a{N}: set compression mode 0 = fast, 1 = normal + default: 1 (normal) + + d{N}: Sets Dictionary size - [0, 30], default: 23 (8MB) + The maximum value for dictionary size is 1 GB = 2^30 bytes. + Dictionary size is calculated as DictionarySize = 2^N bytes. + For decompressing file compressed by LZMA method with dictionary + size D = 2^N you need about D bytes of memory (RAM). + + -fb{N}: set number of fast bytes - [5, 273], default: 128 + Usually big number gives a little bit better compression ratio + and slower compression process. + + -lc{N}: set number of literal context bits - [0, 8], default: 3 + Sometimes lc=4 gives gain for big files. + + -lp{N}: set number of literal pos bits - [0, 4], default: 0 + lp switch is intended for periodical data when period is + equal 2^N. For example, for 32-bit (4 bytes) + periodical data you can use lp=2. Often it's better to set lc0, + if you change lp switch. + + -pb{N}: set number of pos bits - [0, 4], default: 2 + pb switch is intended for periodical data + when period is equal 2^N. + + -mf{MF_ID}: set Match Finder. Default: bt4. + Algorithms from hc* group doesn't provide good compression + ratio, but they often works pretty fast in combination with + fast mode (-a0). + + Memory requirements depend from dictionary size + (parameter "d" in table below). + + MF_ID Memory Description + + bt2 d * 9.5 + 4MB Binary Tree with 2 bytes hashing. + bt3 d * 11.5 + 4MB Binary Tree with 3 bytes hashing. + bt4 d * 11.5 + 4MB Binary Tree with 4 bytes hashing. + hc4 d * 7.5 + 4MB Hash Chain with 4 bytes hashing. + + -eos: write End Of Stream marker. By default LZMA doesn't write + eos marker, since LZMA decoder knows uncompressed size + stored in .lzma file header. + + -si: Read data from stdin (it will write End Of Stream marker). + -so: Write data to stdout + + +Examples: + +1) LZMA e file.bin file.lzma -d16 -lc0 + +compresses file.bin to file.lzma with 64 KB dictionary (2^16=64K) +and 0 literal context bits. -lc0 allows to reduce memory requirements +for decompression. + + +2) LZMA e file.bin file.lzma -lc0 -lp2 + +compresses file.bin to file.lzma with settings suitable +for 32-bit periodical data (for example, ARM or MIPS code). + +3) LZMA d file.lzma file.bin + +decompresses file.lzma to file.bin. + + +Compression ratio hints +----------------------- + +Recommendations +--------------- + +To increase the compression ratio for LZMA compressing it's desirable +to have aligned data (if it's possible) and also it's desirable to locate +data in such order, where code is grouped in one place and data is +grouped in other place (it's better than such mixing: code, data, code, +data, ...). + + +Filters +------- +You can increase the compression ratio for some data types, using +special filters before compressing. For example, it's possible to +increase the compression ratio on 5-10% for code for those CPU ISAs: +x86, IA-64, ARM, ARM-Thumb, PowerPC, SPARC. + +You can find C source code of such filters in C/Bra*.* files + +You can check the compression ratio gain of these filters with such +7-Zip commands (example for ARM code): +No filter: + 7z a a1.7z a.bin -m0=lzma + +With filter for little-endian ARM code: + 7z a a2.7z a.bin -m0=arm -m1=lzma + +It works in such manner: +Compressing = Filter_encoding + LZMA_encoding +Decompressing = LZMA_decoding + Filter_decoding + +Compressing and decompressing speed of such filters is very high, +so it will not increase decompressing time too much. +Moreover, it reduces decompression time for LZMA_decoding, +since compression ratio with filtering is higher. + +These filters convert CALL (calling procedure) instructions +from relative offsets to absolute addresses, so such data becomes more +compressible. + +For some ISAs (for example, for MIPS) it's impossible to get gain from such filter. + + +LZMA compressed file format +--------------------------- +Offset Size Description + 0 1 Special LZMA properties (lc,lp, pb in encoded form) + 1 4 Dictionary size (little endian) + 5 8 Uncompressed size (little endian). -1 means unknown size + 13 Compressed data + + +ANSI-C LZMA Decoder +~~~~~~~~~~~~~~~~~~~ + +Please note that interfaces for ANSI-C code were changed in LZMA SDK 4.58. +If you want to use old interfaces you can download previous version of LZMA SDK +from sourceforge.net site. + +To use ANSI-C LZMA Decoder you need the following files: +1) LzmaDec.h + LzmaDec.c + Types.h +LzmaUtil/LzmaUtil.c is example application that uses these files. + + +Memory requirements for LZMA decoding +------------------------------------- + +Stack usage of LZMA decoding function for local variables is not +larger than 200-400 bytes. + +LZMA Decoder uses dictionary buffer and internal state structure. +Internal state structure consumes + state_size = (4 + (1.5 << (lc + lp))) KB +by default (lc=3, lp=0), state_size = 16 KB. + + +How To decompress data +---------------------- + +LZMA Decoder (ANSI-C version) now supports 2 interfaces: +1) Single-call Decompressing +2) Multi-call State Decompressing (zlib-like interface) + +You must use external allocator: +Example: +void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } +void SzFree(void *p, void *address) { p = p; free(address); } +ISzAlloc alloc = { SzAlloc, SzFree }; + +You can use p = p; operator to disable compiler warnings. + + +Single-call Decompressing +------------------------- +When to use: RAM->RAM decompressing +Compile files: LzmaDec.h + LzmaDec.c + Types.h +Compile defines: no defines +Memory Requirements: + - Input buffer: compressed size + - Output buffer: uncompressed size + - LZMA Internal Structures: state_size (16 KB for default settings) + +Interface: + int LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size + propData - LZMA properties (5 bytes) + propSize - size of propData buffer (5 bytes) + finishMode - It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + You can use LZMA_FINISH_END, when you know that + current output buffer covers last bytes of stream. + alloc - Memory allocator. + + Out: + destLen - processed output size + srcLen - processed input size + + Output: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). + + If LZMA decoder sees end_marker before reaching output limit, it returns OK result, + and output value of destLen will be less than output buffer size limit. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + + +Multi-call State Decompressing (zlib-like interface) +---------------------------------------------------- + +When to use: file->file decompressing +Compile files: LzmaDec.h + LzmaDec.c + Types.h + +Memory Requirements: + - Buffer for input stream: any size (for example, 16 KB) + - Buffer for output stream: any size (for example, 16 KB) + - LZMA Internal Structures: state_size (16 KB for default settings) + - LZMA dictionary (dictionary size is encoded in LZMA properties header) + +1) read LZMA properties (5 bytes) and uncompressed size (8 bytes, little-endian) to header: + unsigned char header[LZMA_PROPS_SIZE + 8]; + ReadFile(inFile, header, sizeof(header) + +2) Allocate CLzmaDec structures (state + dictionary) using LZMA properties + + CLzmaDec state; + LzmaDec_Constr(&state); + res = LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc); + if (res != SZ_OK) + return res; + +3) Init LzmaDec structure before any new LZMA stream. And call LzmaDec_DecodeToBuf in loop + + LzmaDec_Init(&state); + for (;;) + { + ... + int res = LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode); + ... + } + + +4) Free all allocated structures + LzmaDec_Free(&state, &g_Alloc); + +For full code example, look at C/LzmaUtil/LzmaUtil.c code. + + +How To compress data +-------------------- + +Compile files: LzmaEnc.h + LzmaEnc.c + Types.h + +LzFind.c + LzFind.h + LzFindMt.c + LzFindMt.h + LzHash.h + +Memory Requirements: + - (dictSize * 11.5 + 6 MB) + state_size + +Lzma Encoder can use two memory allocators: +1) alloc - for small arrays. +2) allocBig - for big arrays. + +For example, you can use Large RAM Pages (2 MB) in allocBig allocator for +better compression speed. Note that Windows has bad implementation for +Large RAM Pages. +It's OK to use same allocator for alloc and allocBig. + + +Single-call Compression with callbacks +-------------------------------------- + +Check C/LzmaUtil/LzmaUtil.c as example, + +When to use: file->file decompressing + +1) you must implement callback structures for interfaces: +ISeqInStream +ISeqOutStream +ICompressProgress +ISzAlloc + +static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } +static void SzFree(void *p, void *address) { p = p; MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + + CFileSeqInStream inStream; + CFileSeqOutStream outStream; + + inStream.funcTable.Read = MyRead; + inStream.file = inFile; + outStream.funcTable.Write = MyWrite; + outStream.file = outFile; + + +2) Create CLzmaEncHandle object; + + CLzmaEncHandle enc; + + enc = LzmaEnc_Create(&g_Alloc); + if (enc == 0) + return SZ_ERROR_MEM; + + +3) initialize CLzmaEncProps properties; + + LzmaEncProps_Init(&props); + + Then you can change some properties in that structure. + +4) Send LZMA properties to LZMA Encoder + + res = LzmaEnc_SetProps(enc, &props); + +5) Write encoded properties to header + + Byte header[LZMA_PROPS_SIZE + 8]; + size_t headerSize = LZMA_PROPS_SIZE; + UInt64 fileSize; + int i; + + res = LzmaEnc_WriteProperties(enc, header, &headerSize); + fileSize = MyGetFileLength(inFile); + for (i = 0; i < 8; i++) + header[headerSize++] = (Byte)(fileSize >> (8 * i)); + MyWriteFileAndCheck(outFile, header, headerSize) + +6) Call encoding function: + res = LzmaEnc_Encode(enc, &outStream.funcTable, &inStream.funcTable, + NULL, &g_Alloc, &g_Alloc); + +7) Destroy LZMA Encoder Object + LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc); + + +If callback function return some error code, LzmaEnc_Encode also returns that code. + + +Single-call RAM->RAM Compression +-------------------------------- + +Single-call RAM->RAM Compression is similar to Compression with callbacks, +but you provide pointers to buffers instead of pointers to stream callbacks: + +HRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + + + +LZMA Defines +------------ + +_LZMA_SIZE_OPT - Enable some optimizations in LZMA Decoder to get smaller executable code. + +_LZMA_PROB32 - It can increase the speed on some 32-bit CPUs, but memory usage for + some structures will be doubled in that case. + +_LZMA_UINT32_IS_ULONG - Define it if int is 16-bit on your compiler and long is 32-bit. + +_LZMA_NO_SYSTEM_SIZE_T - Define it if you don't want to use size_t type. + + +C++ LZMA Encoder/Decoder +~~~~~~~~~~~~~~~~~~~~~~~~ +C++ LZMA code use COM-like interfaces. So if you want to use it, +you can study basics of COM/OLE. +C++ LZMA code is just wrapper over ANSI-C code. + + +C++ Notes +~~~~~~~~~~~~~~~~~~~~~~~~ +If you use some C++ code folders in 7-Zip (for example, C++ code for .7z handling), +you must check that you correctly work with "new" operator. +7-Zip can be compiled with MSVC 6.0 that doesn't throw "exception" from "new" operator. +So 7-Zip uses "CPP\Common\NewHandler.cpp" that redefines "new" operator: +operator new(size_t size) +{ + void *p = ::malloc(size); + if (p == 0) + throw CNewException(); + return p; +} +If you use MSCV that throws exception for "new" operator, you can compile without +"NewHandler.cpp". So standard exception will be used. Actually some code of +7-Zip catches any exception in internal code and converts it to HRESULT code. +So you don't need to catch CNewException, if you call COM interfaces of 7-Zip. + +--- + +http://www.7-zip.org +http://www.7-zip.org/sdk.html +http://www.7-zip.org/support.html diff --git a/snesreader/7z_C/readme.txt b/snesreader/7z_C/readme.txt new file mode 100644 index 00000000..a07f1fcc --- /dev/null +++ b/snesreader/7z_C/readme.txt @@ -0,0 +1,19 @@ +Modified LZMA 4.65 +------------------ +This is just the ANSI C 7-zip extraction code from the LZMA 4.65 source +code release, with unnecessary files removed. I've made minor changes to +allow the code to compile with almost all warnings enabled in GCC. + +* Made relevant functions extern "C" so that they can be called from +C++. + +* Put all files in same directory and removed "../../" from #includes. + +* Made private (unprototyped) functions static. + +* #if'd out code that is never called. + +* Removed a couple of Windows references. + +-- +Shay Green diff --git a/snesreader/Makefile b/snesreader/Makefile new file mode 100644 index 00000000..761317b0 --- /dev/null +++ b/snesreader/Makefile @@ -0,0 +1,187 @@ +include nall/Makefile + +qtlibs := QtCore QtGui +include nall/qt/Makefile + +c := $(compiler) -std=gnu99 +cpp := $(subst cc,++,$(compiler)) -std=gnu++0x +flags := -O3 -I. -Iobj -fomit-frame-pointer +link := + +ifeq ($(platform),x) + flags := -fPIC $(flags) + link += -s +else ifeq ($(platform),osx) + flags := -fPIC $(flags) +endif + +objects := snesreader + +# fex +objects += Binary_Extractor blargg_common blargg_errors Data_Reader fex File_Extractor Gzip_Extractor Gzip_Reader Rar_Extractor Zip7_Extractor Zip_Extractor Zlib_Inflater +# zlib +objects += adler32 crc32 inffast inflate inftrees zutil +# 7-zip +objects += 7zAlloc 7zBuf 7zCrc 7zDecode 7zExtract 7zHeader 7zIn 7zItem 7zStream Bcj2 Bra86 LzmaDec +# unrar +objects += archive arcread coder crc encname extract getbits model rarvm rarvmtbl rawread suballoc unicode unpack unpack15 unpack20 unrar unrar_misc unrar_open +# micro-bunzip +objects += micro-bunzip +# jma +objects += jma jcrc32 lzmadecode 7zlzma iiostrm inbyte lzma winout + +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 -c $< -o $@ \ + ) \ + ) \ + ) + +%.o: $<; $(call compile) + +all: build; + +objects := $(patsubst %,obj/%.o,$(objects)) +moc_headers := $(call rwildcard,./,%.moc.hpp) +moc_objects := $(foreach f,$(moc_headers),obj/$(notdir $(patsubst %.moc.hpp,%.moc,$f))) + +# automatically run moc on all .moc.hpp (MOC header) files +%.moc: $<; $(moc) -i $< -o $@ + +# automatically generate %.moc build rules +__list = $(moc_headers) +$(foreach f,$(moc_objects), \ + $(eval __file = $(word 1,$(__list))) \ + $(eval __list = $(wordlist 2,$(words $(__list)),$(__list))) \ + $(eval $f: $(__file)) \ +) + +################## +### snesreader ### +################## + +obj/snesreader.o: snesreader.cpp * + $(call compile,$(qtinc)) + +########### +### fex ### +########### + +obj/Binary_Extractor.o: fex/Binary_Extractor.cpp fex/* +obj/blargg_common.o : fex/blargg_common.cpp fex/* +obj/blargg_errors.o : fex/blargg_errors.cpp fex/* +obj/Data_Reader.o : fex/Data_Reader.cpp fex/* +obj/fex.o : fex/fex.cpp fex/* +obj/File_Extractor.o : fex/File_Extractor.cpp fex/* +obj/Gzip_Extractor.o : fex/Gzip_Extractor.cpp fex/* +obj/Gzip_Reader.o : fex/Gzip_Reader.cpp fex/* +obj/Rar_Extractor.o : fex/Rar_Extractor.cpp fex/* +obj/Zip7_Extractor.o : fex/Zip7_Extractor.cpp fex/* +obj/Zip_Extractor.o : fex/Zip_Extractor.cpp fex/* +obj/Zlib_Inflater.o : fex/Zlib_Inflater.cpp fex/* + +############ +### zlib ### +############ + +obj/adler32.o : zlib/adler32.c zlib/* +obj/crc32.o : zlib/crc32.c zlib/* +obj/inffast.o : zlib/inffast.c zlib/* +obj/inflate.o : zlib/inflate.c zlib/* +obj/inftrees.o: zlib/inftrees.c zlib/* +obj/zutil.o : zlib/zutil.c zlib/* + +############# +### 7-zip ### +############# + +obj/7zAlloc.o : 7z_C/7zAlloc.c 7z_C/* +obj/7zBuf.o : 7z_C/7zBuf.c 7z_C/* +obj/7zCrc.o : 7z_C/7zCrc.c 7z_C/* +obj/7zDecode.o : 7z_C/7zDecode.c 7z_C/* +obj/7zExtract.o: 7z_C/7zExtract.c 7z_C/* +obj/7zHeader.o : 7z_C/7zHeader.c 7z_C/* +obj/7zIn.o : 7z_C/7zIn.c 7z_C/* +obj/7zItem.o : 7z_C/7zItem.c 7z_C/* +obj/7zStream.o : 7z_C/7zStream.c 7z_C/* +obj/Bcj2.o : 7z_C/Bcj2.c 7z_C/* +obj/Bra86.o : 7z_C/Bra86.c 7z_C/* +obj/LzmaDec.o : 7z_C/LzmaDec.c 7z_C/* + +#################### +### micro-bunzip ### +#################### + +obj/micro-bunzip.o: micro-bunzip/micro-bunzip.c micro-bunzip/* + +############# +### unrar ### +############# + +obj/archive.o : unrar/archive.cpp unrar/* +obj/arcread.o : unrar/arcread.cpp unrar/* +obj/coder.o : unrar/coder.cpp unrar/* +obj/crc.o : unrar/crc.cpp unrar/* +obj/encname.o : unrar/encname.cpp unrar/* +obj/extract.o : unrar/extract.cpp unrar/* +obj/getbits.o : unrar/getbits.cpp unrar/* +obj/model.o : unrar/model.cpp unrar/* +obj/rarvm.o : unrar/rarvm.cpp unrar/* +obj/rarvmtbl.o : unrar/rarvmtbl.cpp unrar/* +obj/rawread.o : unrar/rawread.cpp unrar/* +obj/suballoc.o : unrar/suballoc.cpp unrar/* +obj/unicode.o : unrar/unicode.cpp unrar/* +obj/unpack.o : unrar/unpack.cpp unrar/* +obj/unpack15.o : unrar/unpack15.cpp unrar/* +obj/unpack20.o : unrar/unpack20.cpp unrar/* +obj/unrar.o : unrar/unrar.cpp unrar/* +obj/unrar_misc.o: unrar/unrar_misc.cpp unrar/* +obj/unrar_open.o: unrar/unrar_open.cpp unrar/* + +############## +### libjma ### +############## + +obj/jma.o : libjma/jma.cpp libjma/* +obj/jcrc32.o : libjma/jcrc32.cpp libjma/* +obj/lzmadecode.o: libjma/lzmadecode.cpp libjma/* +obj/7zlzma.o : libjma/7zlzma.cpp libjma/* +obj/iiostrm.o : libjma/iiostrm.cpp libjma/* +obj/inbyte.o : libjma/inbyte.cpp libjma/* +obj/lzma.o : libjma/lzma.cpp libjma/* +obj/winout.o : libjma/winout.cpp libjma/* + +############### +### targets ### +############### + +build: $(moc_objects) $(objects) +ifeq ($(platform),x) + ar rcs libsnesreader.a $(objects) + $(cpp) $(link) -o libsnesreader.so -shared -Wl,-soname,libsnesreader.so.1 $(objects) $(qtlib) +else ifeq ($(platform),osx) + ar rcs libsnesreader.a $(objects) + $(cpp) $(link) -o libsnesreader.dylib -shared -dynamiclib $(objects) $(qtlib) +else ifeq ($(platform),win) + $(cpp) $(link) -o snesreader.dll -shared -Wl,--out-implib,libsnesreader.a $(objects) $(qtlib) +endif + +install: +ifeq ($(platform),x) + install -D -m 755 libsnesreader.a $(DESTDIR)$(prefix)/lib + install -D -m 755 libsnesreader.so $(DESTDIR)$(prefix)/lib + ldconfig -n $(DESTDIR)$(prefix)/lib +else ifeq ($(platform),osx) + cp libsnesreader.dylib /usr/local/lib/libsnesreader.dylib +endif + +clean: + -@$(call delete,obj/*.o) + -@$(call delete,obj/*.moc) + -@$(call delete,libsnesreader.a) + -@$(call delete,libsnesreader.so) + -@$(call delete,libsnesreader.dylib) + -@$(call delete,snesreader.dll) diff --git a/snesreader/cc.bat b/snesreader/cc.bat new file mode 100644 index 00000000..8359a530 --- /dev/null +++ b/snesreader/cc.bat @@ -0,0 +1,2 @@ +@mingw32-make +@pause \ No newline at end of file diff --git a/snesreader/clean.bat b/snesreader/clean.bat new file mode 100644 index 00000000..d8bb7e0b --- /dev/null +++ b/snesreader/clean.bat @@ -0,0 +1 @@ +@mingw32-make clean diff --git a/snesreader/fex/Binary_Extractor.cpp b/snesreader/fex/Binary_Extractor.cpp new file mode 100644 index 00000000..8c85b992 --- /dev/null +++ b/snesreader/fex/Binary_Extractor.cpp @@ -0,0 +1,77 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Binary_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static File_Extractor* new_binary() +{ + return BLARGG_NEW Binary_Extractor; +} + +fex_type_t_ const fex_bin_type [1] = {{ + "", + &new_binary, + "file", + NULL +}}; + +Binary_Extractor::Binary_Extractor() : + File_Extractor( fex_bin_type ) +{ } + +Binary_Extractor::~Binary_Extractor() +{ + close(); +} + +blargg_err_t Binary_Extractor::open_path_v() +{ + set_name( arc_path() ); + return blargg_ok; +} + +blargg_err_t Binary_Extractor::open_v() +{ + set_name( arc_path() ); + set_info( arc().remain(), 0, 0 ); + return blargg_ok; +} + +void Binary_Extractor::close_v() +{ } + +blargg_err_t Binary_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t Binary_Extractor::rewind_v() +{ + return open_path_v(); +} + +blargg_err_t Binary_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file() ); + RETURN_ERR( arc().seek( 0 ) ); + return open_v(); +} + +blargg_err_t Binary_Extractor::extract_v( void* p, int n ) +{ + return arc().read( p, n ); +} diff --git a/snesreader/fex/Binary_Extractor.h b/snesreader/fex/Binary_Extractor.h new file mode 100644 index 00000000..339a0873 --- /dev/null +++ b/snesreader/fex/Binary_Extractor.h @@ -0,0 +1,26 @@ +// Presents a single file as an "archive" of just that file. + +// File_Extractor 1.0.0 +#ifndef BINARY_EXTRACTOR_H +#define BINARY_EXTRACTOR_H + +#include "File_Extractor.h" + +class Binary_Extractor : public File_Extractor { +public: + Binary_Extractor(); + virtual ~Binary_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); +}; + +#endif diff --git a/snesreader/fex/Data_Reader.cpp b/snesreader/fex/Data_Reader.cpp new file mode 100644 index 00000000..cbea9f47 --- /dev/null +++ b/snesreader/fex/Data_Reader.cpp @@ -0,0 +1,551 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Data_Reader.h" + +#include "blargg_endian.h" +#include +#include + +#if BLARGG_UTF8_PATHS + #include +#endif + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Data_Reader + +blargg_err_t Data_Reader::read( void* p, int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + remain_ -= n; + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, int* n_ ) +{ + assert( *n_ >= 0 ); + + int n = min( *n_, remain() ); + *n_ = 0; + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + { + remain_ -= n; + *n_ = n; + } + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, long* n ) +{ + int i = STATIC_CAST(int, *n); + blargg_err_t err = read_avail( p, &i ); + *n = i; + return err; +} + +blargg_err_t Data_Reader::skip_v( int count ) +{ + char buf [512]; + while ( count ) + { + int n = min( count, (int) sizeof buf ); + count -= n; + RETURN_ERR( read_v( buf, n ) ); + } + return blargg_ok; +} + +blargg_err_t Data_Reader::skip( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = skip_v( n ); + if ( !err ) + remain_ -= n; + + return err; +} + + +// File_Reader + +blargg_err_t File_Reader::seek( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n == tell() ) + return blargg_ok; + + if ( n > size() ) + return blargg_err_file_eof; + + blargg_err_t err = seek_v( n ); + if ( !err ) + set_tell( n ); + + return err; +} + +blargg_err_t File_Reader::skip_v( int n ) +{ + return seek_v( tell() + n ); +} + + +// Subset_Reader + +Subset_Reader::Subset_Reader( Data_Reader* dr, int size ) : + in( dr ) +{ + set_remain( min( size, dr->remain() ) ); +} + +blargg_err_t Subset_Reader::read_v( void* p, int s ) +{ + return in->read( p, s ); +} + + +// Remaining_Reader + +Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) : + in( r ) +{ + header = h; + header_remain = size; + + set_remain( size + r->remain() ); +} + +blargg_err_t Remaining_Reader::read_v( void* out, int count ) +{ + int first = min( count, header_remain ); + if ( first ) + { + memcpy( out, header, first ); + header = STATIC_CAST(char const*, header) + first; + header_remain -= first; + } + + return in->read( STATIC_CAST(char*, out) + first, count - first ); +} + + +// Mem_File_Reader + +Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : + begin( STATIC_CAST(const char*, p) ) +{ + set_size( s ); +} + +blargg_err_t Mem_File_Reader::read_v( void* p, int s ) +{ + memcpy( p, begin + tell(), s ); + return blargg_ok; +} + +blargg_err_t Mem_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + + +// Callback_Reader + +Callback_Reader::Callback_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_remain( s ); +} + +blargg_err_t Callback_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count ); +} + + +// Callback_File_Reader + +Callback_File_Reader::Callback_File_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_size( s ); +} + +blargg_err_t Callback_File_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count, tell() ); +} + +blargg_err_t Callback_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + + +// BLARGG_UTF8_PATHS + +#if BLARGG_UTF8_PATHS + +// Thanks to byuu for the idea for BLARGG_UTF8_PATHS and the implementations + +// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. +char* blargg_to_utf8( const wchar_t* wpath ) +{ + if ( wpath == NULL ) + return NULL; + + int needed = WideCharToMultiByte( CP_UTF8, 0, wpath, -1, NULL, 0, NULL, NULL ); + if ( needed <= 0 ) + return NULL; + + char* path = (char*) malloc( needed ); + if ( path == NULL ) + return NULL; + + int actual = WideCharToMultiByte( CP_UTF8, 0, wpath, -1, path, needed, NULL, NULL ); + if ( actual == 0 ) + { + free( path ); + return NULL; + } + + assert( actual == needed ); + return path; +} + +// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. +wchar_t* blargg_to_wide( const char* path ) +{ + if ( path == NULL ) + return NULL; + + int needed = MultiByteToWideChar( CP_UTF8, 0, path, -1, NULL, 0 ); + if ( needed <= 0 ) + return NULL; + + wchar_t* wpath = (wchar_t*) malloc( needed * sizeof *wpath ); + if ( wpath == NULL ) + return NULL; + + int actual = MultiByteToWideChar( CP_UTF8, 0, path, -1, wpath, needed ); + if ( actual == 0 ) + { + free( wpath ); + return NULL; + } + + assert( actual == needed ); + return wpath; +} + +static FILE* blargg_fopen( const char path [], const char mode [] ) +{ + FILE* file = NULL; + wchar_t* wmode = NULL; + wchar_t* wpath = NULL; + + wpath = blargg_to_wide( path ); + if ( wpath ) + { + wmode = blargg_to_wide( mode ); + if ( wmode ) + file = _wfopen( wpath, wmode ); + } + + // Save and restore errno in case free() clears it + int saved_errno = errno; + free( wmode ); + free( wpath ); + errno = saved_errno; + + return file; +} + +#else + +static inline FILE* blargg_fopen( const char path [], const char mode [] ) +{ + return fopen( path, mode ); +} + +#endif + + +// Std_File_Reader + +Std_File_Reader::Std_File_Reader() +{ + file_ = NULL; +} + +Std_File_Reader::~Std_File_Reader() +{ + close(); +} + +static blargg_err_t blargg_fopen( FILE** out, const char path [] ) +{ + errno = 0; + *out = blargg_fopen( path, "rb" ); + if ( !*out ) + { + #ifdef ENOENT + if ( errno == ENOENT ) + return blargg_err_file_missing; + #endif + #ifdef ENOMEM + if ( errno == ENOMEM ) + return blargg_err_memory; + #endif + return blargg_err_file_read; + } + + return blargg_ok; +} + +static blargg_err_t blargg_fsize( FILE* f, long* out ) +{ + if ( fseek( f, 0, SEEK_END ) ) + return blargg_err_file_io; + + *out = ftell( f ); + if ( *out < 0 ) + return blargg_err_file_io; + + if ( fseek( f, 0, SEEK_SET ) ) + return blargg_err_file_io; + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::open( const char path [] ) +{ + close(); + + FILE* f; + RETURN_ERR( blargg_fopen( &f, path ) ); + + long s; + blargg_err_t err = blargg_fsize( f, &s ); + if ( err ) + { + fclose( f ); + return err; + } + + file_ = f; + set_size( s ); + + return blargg_ok; +} + +void Std_File_Reader::make_unbuffered() +{ + if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) ) + check( false ); // shouldn't fail, but OK if it does +} + +blargg_err_t Std_File_Reader::read_v( void* p, int s ) +{ + if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::seek_v( int n ) +{ + if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { + fclose( STATIC_CAST(FILE*, file_) ); + file_ = NULL; + } +} + + +// Gzip_File_Reader + +#ifdef HAVE_ZLIB_H + +#include "zlib.h" + +static const char* get_gzip_eof( const char path [], long* eof ) +{ + FILE* file; + RETURN_ERR( blargg_fopen( &file, path ) ); + + int const h_size = 4; + unsigned char h [h_size]; + + // read four bytes to ensure that we can seek to -4 later + if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B ) + { + // Not gzipped + if ( ferror( file ) ) + return blargg_err_file_io; + + if ( fseek( file, 0, SEEK_END ) ) + return blargg_err_file_io; + + *eof = ftell( file ); + if ( *eof < 0 ) + return blargg_err_file_io; + } + else + { + // Gzipped; get uncompressed size from end + if ( fseek( file, -h_size, SEEK_END ) ) + return blargg_err_file_io; + + if ( fread( h, 1, h_size, file ) != (size_t) h_size ) + return blargg_err_file_io; + + *eof = get_le32( h ); + } + + if ( fclose( file ) ) + check( false ); + + return blargg_ok; +} + +Gzip_File_Reader::Gzip_File_Reader() +{ + file_ = NULL; +} + +Gzip_File_Reader::~Gzip_File_Reader() +{ + close(); +} + +blargg_err_t Gzip_File_Reader::open( const char path [] ) +{ + close(); + + long s; + RETURN_ERR( get_gzip_eof( path, &s ) ); + + file_ = gzopen( path, "rb" ); + if ( !file_ ) + return blargg_err_file_read; + + set_size( s ); + return blargg_ok; +} + +static blargg_err_t convert_gz_error( gzFile file ) +{ + int err; + gzerror( file, &err ); + + switch ( err ) + { + case Z_STREAM_ERROR: break; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + case Z_MEM_ERROR: return blargg_err_memory; + case Z_BUF_ERROR: break; + } + return blargg_err_internal; +} + +blargg_err_t Gzip_File_Reader::read_v( void* p, int s ) +{ + int result = gzread( file_, p, s ); + if ( result != s ) + { + if ( result < 0 ) + return convert_gz_error( file_ ); + + return blargg_err_file_corrupt; + } + + return blargg_ok; +} + +blargg_err_t Gzip_File_Reader::seek_v( int n ) +{ + if ( gzseek( file_, n, SEEK_SET ) < 0 ) + return convert_gz_error( file_ ); + + return blargg_ok; +} + +void Gzip_File_Reader::close() +{ + if ( file_ ) + { + if ( gzclose( file_ ) ) + check( false ); + file_ = NULL; + } +} + +#endif diff --git a/snesreader/fex/Data_Reader.h b/snesreader/fex/Data_Reader.h new file mode 100644 index 00000000..be206f7b --- /dev/null +++ b/snesreader/fex/Data_Reader.h @@ -0,0 +1,264 @@ +// Lightweight interface for reading data from byte stream + +// File_Extractor 1.0.0 +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "blargg_common.h" + +/* Some functions accept a long instead of int for convenience where caller has +a long due to some other interface, and would otherwise have to get a warning, +or cast it (and verify that it wasn't outside the range of an int). + +To really support huge (>2GB) files, long isn't a solution, since there's no +guarantee it's more than 32 bits. We'd need to use long long (if available), or +something compiler-specific, and change all places file sizes or offsets are +used. */ + +// Supports reading and finding out how many bytes are remaining +class Data_Reader { +public: + + // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more + // tham remain() bytes doesn't result in error, just *n being set to remain(). + blargg_err_t read_avail( void* p, int* n ); + blargg_err_t read_avail( void* p, long* n ); + + // Reads exactly n bytes, or returns error if they couldn't ALL be read. + // Reading past end of file results in blargg_err_file_eof. + blargg_err_t read( void* p, int n ); + + // Number of bytes remaining until end of file + int remain() const { return remain_; } + + // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof. + blargg_err_t skip( int n ); + + virtual ~Data_Reader() { } + +private: + // noncopyable + Data_Reader( const Data_Reader& ); + Data_Reader& operator = ( const Data_Reader& ); + +// Derived interface +protected: + Data_Reader() : remain_( 0 ) { } + + // Sets remain + void set_remain( int n ) { assert( n >= 0 ); remain_ = n; } + + // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated + // AFTER this call succeeds, not before. set_remain() should NOT be called from this. + virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + + // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data + // and discards it. Value of remain() is updated AFTER this call succeeds, not + // before. set_remain() should NOT be called from this. + virtual blargg_err_t skip_v( int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + int remain_; +}; + + +// Supports seeking in addition to Data_Reader operations +class File_Reader : public Data_Reader { +public: + + // Size of file + int size() const { return size_; } + + // Current position in file + int tell() const { return size_ - remain(); } + + // Goes to new position + blargg_err_t seek( int ); + +// Derived interface +protected: + // Sets size and resets position + void set_size( int n ) { size_ = n; Data_Reader::set_remain( n ); } + void set_size( long n ) { set_size( STATIC_CAST(int, n) ); } + + // Sets reported position + void set_tell( int i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); } + + // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated + // AFTER this call succeeds, not before. set_* functions should NOT be called from this. + virtual blargg_err_t seek_v( int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + +// Implementation +protected: + File_Reader() : size_( 0 ) { } + + virtual blargg_err_t skip_v( int ); + +private: + int size_; + + void set_remain(); // avoid accidental use of set_remain +}; + + +// Reads from file on disk +class Std_File_Reader : public File_Reader { +public: + + // Opens file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + + // Switches to unbuffered mode. Useful if buffering is already being + // done at a higher level. + void make_unbuffered(); + +// Implementation +public: + Std_File_Reader(); + virtual ~Std_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + void* file_; +}; + + +// Treats range of memory as a file +class Mem_File_Reader : public File_Reader { +public: + + Mem_File_Reader( const void* begin, long size ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + const char* const begin; +}; + + +// Allows only count bytes to be read from reader passed +class Subset_Reader : public Data_Reader { +public: + + Subset_Reader( Data_Reader*, int count ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; +}; + + +// Joins already-read header and remaining data into original file. +// Meant for cases where you've already read header and don't want +// to seek and re-read data (for efficiency). +class Remaining_Reader : public Data_Reader { +public: + + Remaining_Reader( void const* header, int header_size, Data_Reader* ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; + void const* header; + int header_remain; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count // Number of bytes to read + ); +} +class Callback_Reader : public Data_Reader { +public: + typedef callback_reader_func_t callback_t; + Callback_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_file_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count, // Number of bytes to read + int pos // Position in file to read from + ); +} +class Callback_File_Reader : public File_Reader { +public: + typedef callback_file_reader_func_t callback_t; + Callback_File_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +#ifdef HAVE_ZLIB_H + +// Reads file compressed with gzip (or uncompressed) +class Gzip_File_Reader : public File_Reader { +public: + + // Opens possibly gzipped file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + +// Implementation +public: + Gzip_File_Reader(); + ~Gzip_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + // void* so "zlib.h" doesn't have to be included here + void* file_; +}; +#endif + +char* blargg_to_utf8( const wchar_t* ); +wchar_t* blargg_to_wide( const char* ); + +#endif diff --git a/snesreader/fex/File_Extractor.cpp b/snesreader/fex/File_Extractor.cpp new file mode 100644 index 00000000..e060e095 --- /dev/null +++ b/snesreader/fex/File_Extractor.cpp @@ -0,0 +1,341 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "File_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +File_Extractor::fex_t( fex_type_t t ) : + type_( t ) +{ + own_file_ = NULL; + + close_(); +} + +// Open + +blargg_err_t File_Extractor::set_path( const char* path ) +{ + if ( !path ) + path = ""; + + RETURN_ERR( path_.resize( strlen( path ) + 1 ) ); + memcpy( path_.begin(), path, path_.size() ); + return blargg_ok; +} + +blargg_err_t File_Extractor::open( const char path [] ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + blargg_err_t err = open_path_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +blargg_err_t File_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file() ); + + return open_v(); +} + +inline +static void make_unbuffered( Std_File_Reader* r ) +{ + r->make_unbuffered(); +} + +inline +static void make_unbuffered( void* ) +{ } + +blargg_err_t File_Extractor::open_arc_file( bool unbuffered ) +{ + if ( reader_ ) + return blargg_ok; + + FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER; + CHECK_ALLOC( in ); + + blargg_err_t err = in->open( arc_path() ); + if ( err ) + { + delete in; + } + else + { + reader_ = in; + own_file(); + if ( unbuffered ) + make_unbuffered( in ); + } + + return err; +} + +blargg_err_t File_Extractor::open( File_Reader* input, const char* path ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + RETURN_ERR( input->seek( 0 ) ); + + reader_ = input; + blargg_err_t err = open_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +// Close + +void File_Extractor::close() +{ + close_v(); + close_(); +} + +void File_Extractor::close_() +{ + delete own_file_; + own_file_ = NULL; + + tell_ = 0; + reader_ = NULL; + opened_ = false; + + path_.clear(); + clear_file(); +} + +File_Extractor::~fex_t() +{ + check( !opened() ); // fails if derived destructor didn't call close() + + delete own_file_; +} + +// Scanning + +void File_Extractor::clear_file() +{ + name_ = NULL; + wname_ = NULL; + done_ = true; + stat_called = false; + data_ptr_ = NULL; + + set_info( 0 ); + own_data_.clear(); + clear_file_v(); +} + +void File_Extractor::set_name( const char new_name [], const wchar_t* new_wname ) +{ + name_ = new_name; + wname_ = new_wname; + done_ = false; +} + +void File_Extractor::set_info( int new_size, unsigned date, unsigned crc ) +{ + size_ = new_size; + date_ = (date != 0xFFFFFFFF ? date : 0); + crc32_ = crc; + set_remain( new_size ); +} + +blargg_err_t File_Extractor::next_() +{ + tell_++; + clear_file(); + + blargg_err_t err = next_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::next() +{ + assert( !done() ); + return next_(); +} + +blargg_err_t File_Extractor::rewind() +{ + assert( opened() ); + + tell_ = 0; + clear_file(); + + blargg_err_t err = rewind_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::stat() +{ + assert( !done() ); + + if ( !stat_called ) + { + RETURN_ERR( stat_v() ); + stat_called = true; + } + return blargg_ok; +} + +// Tell/seek + +int const pos_offset = 1; + +fex_pos_t File_Extractor::tell_arc() const +{ + assert( opened() ); + + fex_pos_t pos = tell_arc_v(); + assert( pos >= 0 ); + + return pos + pos_offset; +} + +blargg_err_t File_Extractor::seek_arc( fex_pos_t pos ) +{ + assert( opened() ); + assert( pos != 0 ); + + clear_file(); + + blargg_err_t err = seek_arc_v( pos - pos_offset ); + if ( err ) + clear_file(); + + return err; +} + +fex_pos_t File_Extractor::tell_arc_v() const +{ + return tell_; +} + +blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos ) +{ + // >= because seeking to current file should always reset read pointer etc. + if ( tell_ >= pos ) + RETURN_ERR( rewind() ); + + while ( tell_ < pos ) + { + RETURN_ERR( next_() ); + + if ( done() ) + { + assert( false ); + return blargg_err_caller; + } + } + + assert( tell_ == pos ); + + return blargg_ok; +} + +// Extraction + +blargg_err_t File_Extractor::rewind_file() +{ + RETURN_ERR( stat() ); + + if ( tell() > 0 ) + { + if ( data_ptr_ ) + { + set_remain( size() ); + } + else + { + RETURN_ERR( seek_arc( tell_arc() ) ); + RETURN_ERR( stat() ); + } + } + + return blargg_ok; +} + +blargg_err_t File_Extractor::data( const void** data_out ) +{ + assert( !done() ); + + *data_out = NULL; + if ( !data_ptr_ ) + { + int old_tell = tell(); + + RETURN_ERR( rewind_file() ); + + void const* ptr; + RETURN_ERR( data_v( &ptr ) ); + data_ptr_ = ptr; + + // Now that data is in memory, we can seek by simply setting remain + set_remain( size() - old_tell ); + } + + *data_out = data_ptr_; + return blargg_ok; +} + +blargg_err_t File_Extractor::data_v( void const** out ) +{ + RETURN_ERR( own_data_.resize( size() ) ); + *out = own_data_.begin(); + + blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() ); + if ( err ) + own_data_.clear(); + + return err; +} + +blargg_err_t File_Extractor::extract_v( void* out, int count ) +{ + void const* p; + RETURN_ERR( data( &p ) ); + memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count ); + + return blargg_ok; +} + +blargg_err_t File_Extractor::read_v( void* out, int count ) +{ + if ( data_ptr_ ) + return File_Extractor::extract_v( out, count ); + + return extract_v( out, count ); +} diff --git a/snesreader/fex/File_Extractor.h b/snesreader/fex/File_Extractor.h new file mode 100644 index 00000000..ad25d5f8 --- /dev/null +++ b/snesreader/fex/File_Extractor.h @@ -0,0 +1,191 @@ +// Compressed file archive interface + +// File_Extractor 1.0.0 +#ifndef FILE_EXTRACTOR_H +#define FILE_EXTRACTOR_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "fex.h" + +struct fex_t : private Data_Reader { +public: + virtual ~fex_t(); + +// Open/close + + // Opens archive from custom data source. Keeps pointer until close(). + blargg_err_t open( File_Reader* input, const char* path = NULL ); + + // Takes ownership of File_Reader* passed to open(), so that close() + // will delete it. + void own_file() { own_file_ = reader_; } + + // See fex.h + blargg_err_t open( const char path [] ); + fex_type_t type() const { return type_; } + void close(); + +// Scanning + + // See fex.h + bool done() const { return done_; } + blargg_err_t next(); + blargg_err_t rewind(); + fex_pos_t tell_arc() const; + blargg_err_t seek_arc( fex_pos_t ); + +// Info + + // See fex.h + const char* name() const { return name_; } + const wchar_t* wname() const { return wname_; } + blargg_err_t stat(); + int size() const { assert( stat_called ); return size_; } + unsigned int dos_date() const { return date_; } + unsigned int crc32() const { return crc32_; } + +// Extraction + + // Data_Reader to current file's data, so standard Data_Reader interface can + // be used, rather than having to treat archives specially. stat() must have + // been called. + Data_Reader& reader() { assert( stat_called ); return *this; } + + // See fex.h + blargg_err_t data( const void** data_out ); + int tell() const { return size_ - remain(); } + +// Derived interface +protected: + + // Sets type of object + fex_t( fex_type_t ); + + // Path to archive file, or "" if none supplied + const char* arc_path() const { return path_.begin(); } + + // Opens archive file if it's not already. If unbuffered is true, opens file + // without any buffering. + blargg_err_t open_arc_file( bool unbuffered = false ); + + // Archive file + File_Reader& arc() const { return *reader_; } + + // Sets current file name + void set_name( const char name [], const wchar_t* wname = NULL ); + + // Sets current file information + void set_info( int size, unsigned date = 0, unsigned crc = 0 ); + +// User overrides + + // Overrides must do indicated task. Non-pure functions have reasonable default + // implementation. Overrides should avoid calling public functions like + // next() and rewind(). + + // Open archive using file_path(). OK to delay actual file opening until later. + // Default just calls open_arc_file(), then open_v(). + virtual blargg_err_t open_path_v(); + + // Open archive using file() for source data. If unsupported, return error. + virtual blargg_err_t open_v() BLARGG_PURE( ; ) + + // Go to next file in archive and call set_name() and optionally set_info() + virtual blargg_err_t next_v() BLARGG_PURE( ; ) + + // Go back to first file in archive + virtual blargg_err_t rewind_v() BLARGG_PURE( ; ) + + // Close archive. Called even if open_path_v() or open_v() return unsuccessfully. + virtual void close_v() BLARGG_PURE( ; ) + + // Clear any fields related to current file + virtual void clear_file_v() { } + + // Call set_info() if not already called by next_v() + virtual blargg_err_t stat_v() { return blargg_ok; } + + // Return value that allows later return to this file. Result must be >= 0. + virtual fex_pos_t tell_arc_v() const; + + // Return to previously saved position + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + // One or both of the following must be overridden + + // Provide pointer to data for current file in archive + virtual blargg_err_t data_v( const void** out ); + + // Extract next n bytes + virtual blargg_err_t extract_v( void* out, int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + fex_type_t const type_; + + // Archive file + blargg_vector path_; + File_Reader* reader_; + File_Reader* own_file_; + bool opened_; + + // Position in archive + fex_pos_t tell_; // only used by default implementation of tell/seek + bool done_; + + // Info for current file in archive + const char* name_; + const wchar_t* wname_; + unsigned date_; + unsigned crc32_; + int size_; + bool stat_called; + + // Current file contents + void const* data_ptr_; // NULL if not read into memory + blargg_vector own_data_; + + bool opened() const { return opened_; } + void clear_file(); + void close_(); + blargg_err_t set_path( const char* path ); + blargg_err_t rewind_file(); + blargg_err_t next_(); + + // Data_Reader overrides + // TODO: override skip_v? + virtual blargg_err_t read_v( void* out, int n ); +}; + +struct fex_type_t_ +{ + const char* extension; + File_Extractor* (*new_fex)(); + const char* name; + blargg_err_t (*init)(); // Called by fex_init(). Can be NULL. +}; + +extern const fex_type_t_ + fex_7z_type [1], + fex_gz_type [1], + fex_rar_type [1], + fex_zip_type [1], + fex_bin_type [1]; + +inline blargg_err_t File_Extractor::open_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::next_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::rewind_v() { return blargg_ok; } +inline void File_Extractor::close_v() { } + +// Default to Std_File_Reader for archive access +#ifndef FEX_FILE_READER + #define FEX_FILE_READER Std_File_Reader +#elif defined (FEX_FILE_READER_INCLUDE) + #include FEX_FILE_READER_INCLUDE +#endif + +#endif diff --git a/snesreader/fex/Gzip_Extractor.cpp b/snesreader/fex/Gzip_Extractor.cpp new file mode 100644 index 00000000..f169fed9 --- /dev/null +++ b/snesreader/fex/Gzip_Extractor.cpp @@ -0,0 +1,98 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static blargg_err_t init_gzip_file() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_gzip() +{ + return BLARGG_NEW Gzip_Extractor; +} + +fex_type_t_ const fex_gz_type [1] = {{ + ".gz", + &new_gzip, + "gzipped file", + &init_gzip_file +}}; + +Gzip_Extractor::Gzip_Extractor() : + File_Extractor( fex_gz_type ) +{ } + +Gzip_Extractor::~Gzip_Extractor() +{ + close(); +} + +blargg_err_t Gzip_Extractor::open_path_v() +{ + // skip opening file + return open_v(); +} + +blargg_err_t Gzip_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file( true ) ); + if ( !gr.opened() || gr.tell() != 0 ) + RETURN_ERR( gr.open( &arc() ) ); + + set_info( gr.remain(), 0, gr.crc32() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::open_v() +{ + // Remove .gz suffix + size_t len = strlen( arc_path() ); + if ( fex_has_extension( arc_path(), ".gz" ) ) + len -= 3; + + RETURN_ERR( name.resize( len + 1 ) ); + memcpy( name.begin(), arc_path(), name.size() ); + name [name.size() - 1] = '\0'; + + set_name( name.begin() ); + return blargg_ok; +} + +void Gzip_Extractor::close_v() +{ + name.clear(); + gr.close(); +} + +blargg_err_t Gzip_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::rewind_v() +{ + set_name( name.begin() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::extract_v( void* p, int n ) +{ + return gr.read( p, n ); +} diff --git a/snesreader/fex/Gzip_Extractor.h b/snesreader/fex/Gzip_Extractor.h new file mode 100644 index 00000000..814dc9b3 --- /dev/null +++ b/snesreader/fex/Gzip_Extractor.h @@ -0,0 +1,34 @@ +// Presents a gzipped file as an "archive" of just that file. +// Also handles non-gzipped files. + +// File_Extractor 1.0.0 +#ifndef GZIP_EXTRACTOR_H +#define GZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Gzip_Reader.h" + +class Gzip_Extractor : public File_Extractor { +public: + Gzip_Extractor(); + virtual ~Gzip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); + +private: + Gzip_Reader gr; + blargg_vector name; + + void set_info_(); +}; + +#endif diff --git a/snesreader/fex/Gzip_Reader.cpp b/snesreader/fex/Gzip_Reader.cpp new file mode 100644 index 00000000..2aad302c --- /dev/null +++ b/snesreader/fex/Gzip_Reader.cpp @@ -0,0 +1,85 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Reader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Gzip_Reader::Gzip_Reader() +{ + close(); +} + +Gzip_Reader::~Gzip_Reader() +{ } + +static blargg_err_t gzip_reader_read( void* file, void* out, int* count ) +{ + return STATIC_CAST(File_Reader*,file)->read_avail( out, count ); +} + +blargg_err_t Gzip_Reader::calc_size() +{ + size_ = in->size(); + crc32_ = 0; + if ( inflater.deflated() ) + { + byte trailer [8]; + int old_pos = in->tell(); + RETURN_ERR( in->seek( size_ - sizeof trailer ) ); + RETURN_ERR( in->read( trailer, sizeof trailer ) ); + RETURN_ERR( in->seek( old_pos ) ); + crc32_ = get_le32( trailer + 0 ); + + unsigned n = get_le32( trailer + 4 ); + if ( n > INT_MAX ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "gzip larger than 2GB" ); + + size_ = n; + } + return blargg_ok; +} + +blargg_err_t Gzip_Reader::open( File_Reader* new_in ) +{ + close(); + + in = new_in; + RETURN_ERR( in->seek( 0 ) ); + RETURN_ERR( inflater.begin( gzip_reader_read, new_in ) ); + RETURN_ERR( inflater.set_mode( inflater.mode_auto ) ); + RETURN_ERR( calc_size() ); + set_remain( size_ ); + + return blargg_ok; +} + +void Gzip_Reader::close() +{ + in = NULL; + inflater.end(); +} + +blargg_err_t Gzip_Reader::read_v( void* out, int count ) +{ + assert( in ); + int actual = count; + RETURN_ERR( inflater.read( out, &actual ) ); + + if ( actual != count ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/snesreader/fex/Gzip_Reader.h b/snesreader/fex/Gzip_Reader.h new file mode 100644 index 00000000..a9b2d6a9 --- /dev/null +++ b/snesreader/fex/Gzip_Reader.h @@ -0,0 +1,46 @@ +// Transparently decompresses gzip files, as well as uncompressed + +// File_Extractor 1.0.0 +#ifndef GZIP_READER_H +#define GZIP_READER_H + +#include "Data_Reader.h" +#include "Zlib_Inflater.h" + +class Gzip_Reader : public Data_Reader { +public: + // Keeps pointer to reader until close(). If + blargg_err_t open( File_Reader* ); + + // True if file is open + bool opened() const { return in != NULL; } + + // Frees memory + void close(); + + // True if file is compressed + bool deflated() const { return inflater.deflated(); } + + // CRC-32 of data, of 0 if unavailable + unsigned int crc32() const { return crc32_; } + + // Number of bytes read since opening + int tell() const { return size_ - remain(); } + +public: + Gzip_Reader(); + virtual ~Gzip_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + File_Reader* in; + unsigned crc32_; + int size_; + Zlib_Inflater inflater; + + blargg_err_t calc_size(); +}; + +#endif diff --git a/snesreader/fex/Rar_Extractor.cpp b/snesreader/fex/Rar_Extractor.cpp new file mode 100644 index 00000000..afade7fa --- /dev/null +++ b/snesreader/fex/Rar_Extractor.cpp @@ -0,0 +1,197 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +#if FEX_ENABLE_RAR + +#include "Rar_Extractor.h" + +/* Copyright (C) 2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static blargg_err_t init_rar() +{ + unrar_init(); + return blargg_ok; +} + +static File_Extractor* new_rar() +{ + return BLARGG_NEW Rar_Extractor; +} + +fex_type_t_ const fex_rar_type [1] = {{ + ".rar", + &new_rar, + "RAR archive", + &init_rar +}}; + +blargg_err_t Rar_Extractor::convert_err( unrar_err_t err ) +{ + blargg_err_t reader_err = reader.err; + reader.err = blargg_ok; + if ( reader_err ) + check( err == unrar_next_err ); + + switch ( err ) + { + case unrar_ok: return blargg_ok; + case unrar_err_memory: return blargg_err_memory; + case unrar_err_open: return blargg_err_file_read; + case unrar_err_not_arc: return blargg_err_file_type; + case unrar_err_corrupt: return blargg_err_file_corrupt; + case unrar_err_io: return blargg_err_file_io; + case unrar_err_arc_eof: return blargg_err_internal; + case unrar_err_encrypted: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR encryption not supported" ); + case unrar_err_segmented: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR segmentation not supported" ); + case unrar_err_huge: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Huge RAR files not supported" ); + case unrar_err_old_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Old RAR compression not supported" ); + case unrar_err_new_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR uses unknown newer compression" ); + case unrar_next_err: break; + default: + check( false ); // unhandled RAR error + } + + if ( reader_err ) + return reader_err; + + check( false ); + return BLARGG_ERR( BLARGG_ERR_INTERNAL, "RAR archive" ); +} + +static inline unrar_err_t handle_err( Rar_Extractor::read_callback_t* h, blargg_err_t err ) +{ + if ( !err ) + return unrar_ok; + + h->err = err; + return unrar_next_err; +} + +extern "C" +{ + static unrar_err_t my_unrar_read( void* data, void* out, int* count, unrar_pos_t pos ) + { + // TODO: 64-bit file support + + Rar_Extractor::read_callback_t* h = STATIC_CAST(Rar_Extractor::read_callback_t*,data); + if ( h->pos != pos ) + { + blargg_err_t err = h->in->seek( pos ); + if ( err ) + return handle_err( h, err ); + + h->pos = pos; + } + + blargg_err_t err = h->in->read_avail( out, count ); + if ( err ) + return handle_err( h, err ); + + h->pos += *count; + + return unrar_ok; + } +} + +Rar_Extractor::Rar_Extractor() : + File_Extractor( fex_rar_type ) +{ + unrar = NULL; +} + +Rar_Extractor::~Rar_Extractor() +{ + close(); +} + +blargg_err_t Rar_Extractor::open_v() +{ + reader.pos = 0; + reader.in = &arc(); + reader.err = blargg_ok; + + RETURN_ERR( arc().seek( 0 ) ); + RETURN_ERR( convert_err( unrar_open_custom( &unrar, &my_unrar_read, &reader ) ) ); + return skip_unextractables(); +} + +void Rar_Extractor::close_v() +{ + unrar_close( unrar ); + + unrar = NULL; + reader.in = NULL; +} + +blargg_err_t Rar_Extractor::skip_unextractables() +{ + while ( !unrar_done( unrar ) && unrar_try_extract( unrar ) ) + RETURN_ERR( next_raw() ); + + if ( !unrar_done( unrar ) ) + { + unrar_info_t const* info = unrar_info( unrar ); + + set_name( info->name, (info->name_w && *info->name_w) ? info->name_w : NULL ); + set_info( info->size, info->dos_date, (info->is_crc32 ? info->crc : 0) ); + } + + return blargg_ok; +} + +blargg_err_t Rar_Extractor::next_raw() +{ + return convert_err( unrar_next( unrar ) ); +} + +blargg_err_t Rar_Extractor::next_v() +{ + RETURN_ERR( next_raw() ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::rewind_v() +{ + RETURN_ERR( convert_err( unrar_rewind( unrar ) ) ); + return skip_unextractables(); +} + +fex_pos_t Rar_Extractor::tell_arc_v() const +{ + return unrar_tell( unrar ); +} + +blargg_err_t Rar_Extractor::seek_arc_v( fex_pos_t pos ) +{ + RETURN_ERR( convert_err( unrar_seek( unrar, pos ) ) ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::data_v( void const** out ) +{ + return convert_err( unrar_extract_mem( unrar, out ) ); +} + +blargg_err_t Rar_Extractor::extract_v( void* out, int count ) +{ + // We can read entire file directly into user buffer + if ( count == size() ) + return convert_err( unrar_extract( unrar, out, count ) ); + + // This will call data_v() and copy from that buffer for us + return File_Extractor::extract_v( out, count ); +} + +#endif diff --git a/snesreader/fex/Rar_Extractor.h b/snesreader/fex/Rar_Extractor.h new file mode 100644 index 00000000..9a74dea3 --- /dev/null +++ b/snesreader/fex/Rar_Extractor.h @@ -0,0 +1,43 @@ +// RAR archive extractor + +// File_Extractor 1.0.0 +#ifndef RAR_EXTRACTOR_H +#define RAR_EXTRACTOR_H + +#include "File_Extractor.h" +#include "unrar/unrar.h" + +class Rar_Extractor : public File_Extractor { +public: + Rar_Extractor(); + virtual ~Rar_Extractor(); + + struct read_callback_t + { + const char* err; + int pos; + File_Reader* in; + }; + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** ); + virtual blargg_err_t extract_v( void*, int ); + +private: + unrar_t* unrar; + read_callback_t reader; + + blargg_err_t convert_err( unrar_err_t ); + blargg_err_t skip_unextractables(); + blargg_err_t next_raw(); +}; + +#endif diff --git a/snesreader/fex/Zip7_Extractor.cpp b/snesreader/fex/Zip7_Extractor.cpp new file mode 100644 index 00000000..1803a71c --- /dev/null +++ b/snesreader/fex/Zip7_Extractor.cpp @@ -0,0 +1,252 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip7_Extractor.h" + +#include "7z_C/7zExtract.h" +#include "7z_C/7zAlloc.h" +#include "7z_C/7zCrc.h" + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static ISzAlloc zip7_alloc = { SzAlloc, SzFree }; +static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp }; + +struct Zip7_Extractor_Impl : + ISeekInStream +{ + CLookToRead look; + CSzArEx db; + + // SzExtract state + UInt32 block_index; + Byte* buf; + size_t buf_size; + + File_Reader* in; + const char* in_err; +}; + +extern "C" +{ + // 7-zip callbacks pass an ISeekInStream* for data, so we must cast it + // back to ISeekInStream* FIRST, then cast to our Impl structure + + static SRes zip7_read_( void* vstream, void* out, size_t* size ) + { + assert( out && size ); + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + long lsize = *size; + blargg_err_t err = impl->in->read_avail( out, &lsize ); + if ( err ) + { + *size = 0; + impl->in_err = err; + return SZ_ERROR_READ; + } + + *size = lsize; + return SZ_OK; + } + + static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode ) + { + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + assert( mode != SZ_SEEK_CUR ); // never used + + if ( mode == SZ_SEEK_END ) + { + assert( *pos == 0 ); // only used to find file length + *pos = impl->in->size(); + return SZ_OK; + } + + assert( mode == SZ_SEEK_SET ); + blargg_err_t err = impl->in->seek( *pos ); + if ( err ) + { + // don't set in_err in this case, since it might be benign + if ( err == blargg_err_file_eof ) + return SZ_ERROR_INPUT_EOF; + + impl->in_err = err; + return SZ_ERROR_READ; + } + + return SZ_OK; + } +} + +blargg_err_t Zip7_Extractor::zip7_err( int err ) +{ + // TODO: ignore in_err in some cases? unsure about which error to use + blargg_err_t in_err = impl->in_err; + impl->in_err = NULL; + if ( in_err ) + { + check( err != SZ_OK ); + return in_err; + } + + switch ( err ) + { + case SZ_OK: return blargg_ok; + case SZ_ERROR_MEM: return blargg_err_memory; + case SZ_ERROR_READ: return blargg_err_file_io; + case SZ_ERROR_CRC: + case SZ_ERROR_DATA: + case SZ_ERROR_INPUT_EOF: + case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt; + case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature; + case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type; + } + + return blargg_err_generic; +} + +static blargg_err_t init_7z() +{ + static bool inited; + if ( !inited ) + { + inited = true; + CrcGenerateTable(); + } + return blargg_ok; +} + +static File_Extractor* new_7z() +{ + return BLARGG_NEW Zip7_Extractor; +} + +fex_type_t_ const fex_7z_type [1] = {{ + ".7z", + &new_7z, + "7-zip archive", + &init_7z +}}; + +Zip7_Extractor::Zip7_Extractor() : + File_Extractor( fex_7z_type ) +{ + impl = NULL; +} + +Zip7_Extractor::~Zip7_Extractor() +{ + close(); +} + +blargg_err_t Zip7_Extractor::open_v() +{ + RETURN_ERR( init_7z() ); + + if ( !impl ) + { + impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl ); + CHECK_ALLOC( impl ); + } + + impl->in = &arc(); + impl->block_index = (UInt32) -1; + impl->buf = NULL; + impl->buf_size = 0; + + LookToRead_CreateVTable( &impl->look, false ); + impl->ISeekInStream::Read = zip7_read_; + impl->ISeekInStream::Seek = zip7_seek_; + impl->look.realStream = impl; + LookToRead_Init( &impl->look ); + + SzArEx_Init( &impl->db ); + + impl->in_err = NULL; + RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s, + &zip7_alloc, &zip7_alloc_temp ) ) ); + + return seek_arc_v( 0 ); +} + +void Zip7_Extractor::close_v() +{ + if ( impl ) + { + if ( impl->in ) + { + impl->in = NULL; + SzArEx_Free( &impl->db, &zip7_alloc ); + } + IAlloc_Free( &zip7_alloc, impl->buf ); + free( impl ); + impl = NULL; + } +} + +blargg_err_t Zip7_Extractor::next_v() +{ + while ( ++index < (int) impl->db.db.NumFiles ) + { + CSzFileItem const& item = impl->db.db.Files [index]; + if ( !item.IsDir ) + { + // TODO: Support date. + // NTFS representation, stored as 64-bit value. + // Divide by 10000000 (ten million) to get seconds + //item.MTime.Low + (.High << 32) + // How to convert to DOS style? + + set_name( item.Name ); + set_info( item.Size, 0, (item.FileCRCDefined ? item.FileCRC : 0) ); + break; + } + } + + return blargg_ok; +} + +blargg_err_t Zip7_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip7_Extractor::tell_arc_v() const +{ + return index; +} + +blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles ); + + index = pos - 1; + return next_v(); +} + +blargg_err_t Zip7_Extractor::data_v( void const** out ) +{ + impl->in_err = NULL; + size_t offset = 0; + size_t count = 0; + RETURN_ERR( zip7_err( SzAr_Extract( &impl->db, &impl->look.s, index, + &impl->block_index, &impl->buf, &impl->buf_size, + &offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) ); + assert( count == (size_t) size() ); + + *out = impl->buf + offset; + return blargg_ok; +} diff --git a/snesreader/fex/Zip7_Extractor.h b/snesreader/fex/Zip7_Extractor.h new file mode 100644 index 00000000..f658ba04 --- /dev/null +++ b/snesreader/fex/Zip7_Extractor.h @@ -0,0 +1,34 @@ +// 7-zip archive extractor + +// File_Extractor 1.0.0 +#ifndef ZIP7_EXTRACTOR_H +#define ZIP7_EXTRACTOR_H + +#include "File_Extractor.h" + +struct Zip7_Extractor_Impl; + +class Zip7_Extractor : public File_Extractor { +public: + Zip7_Extractor(); + virtual ~Zip7_Extractor(); + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** out ); + +private: + Zip7_Extractor_Impl* impl; + int index; + + blargg_err_t zip7_err( int err ); +}; + +#endif diff --git a/snesreader/fex/Zip_Extractor.cpp b/snesreader/fex/Zip_Extractor.cpp new file mode 100644 index 00000000..8bcc61c3 --- /dev/null +++ b/snesreader/fex/Zip_Extractor.cpp @@ -0,0 +1,390 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip_Extractor.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* To avoid copying filename string from catalog, I terminate it by modifying +catalog data. This potentially requires moving the first byte of the type +of the next entry elsewhere; I move it to the first byte of made_by. Kind +of hacky, but I'd rather not have to allocate memory for a copy of it. */ + +#include "blargg_source.h" + +/* Reads this much from end of file when first opening. Only this much is +searched for the end catalog entry. If whole catalog is within this data, +nothing more needs to be read on open. */ +int const end_read_size = 8 * 1024; + +/* Reads are are made using file offset that's a multiple of this, +increasing performance. */ +int const disk_block_size = 4 * 1024; + +// Read buffer used for extracting file data +int const read_buf_size = 16 * 1024; + +struct header_t +{ + char type [4]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + char filename [2]; // [filename_len] + //char extra [extra_len]; +}; +int const header_size = 30; + +struct entry_t +{ + char type [4]; + byte made_by [2]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + byte comment_len [2]; + byte disk [2]; + byte int_attrib [2]; + byte ext_attrib [4]; + byte file_offset [4]; + char filename [2]; // [filename_len] + //char extra [extra_len]; + //char comment [comment_len]; +}; +int const entry_size = 46; + +struct end_entry_t +{ + char type [4]; + byte disk [2]; + byte first_disk [2]; + byte disk_entry_count [2]; + byte entry_count [2]; + byte dir_size [4]; + byte dir_offset [4]; + byte comment_len [2]; + char comment [2]; // [comment_len] +}; +int const end_entry_size = 22; + +static blargg_err_t init_zip() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_zip() +{ + return BLARGG_NEW Zip_Extractor; +} + +fex_type_t_ const fex_zip_type [1] = {{ + ".zip", + &new_zip, + "ZIP archive", + &init_zip +}}; + +Zip_Extractor::Zip_Extractor() : + File_Extractor( fex_zip_type ) +{ + Zip_Extractor::clear_file_v(); + + // If these fail, structures had extra padding inserted by compiler + assert( offsetof (header_t,filename) == header_size ); + assert( offsetof (entry_t,filename) == entry_size ); + assert( offsetof (end_entry_t,comment) == end_entry_size ); +} + +Zip_Extractor::~Zip_Extractor() +{ + close(); +} + +blargg_err_t Zip_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file( true ) ); + return File_Extractor::open_path_v(); +} + +inline +void Zip_Extractor::reorder_entry_header( int offset ) +{ + catalog [offset + 0] = 0; + catalog [offset + 4] = 'P'; +} + +blargg_err_t Zip_Extractor::open_v() +{ + if ( arc().size() < end_entry_size ) + return blargg_err_file_type; + + // Read final end_read_size bytes of file + int file_pos = max( 0, arc().size() - end_read_size ); + file_pos -= file_pos % disk_block_size; + RETURN_ERR( catalog.resize( arc().size() - file_pos ) ); + RETURN_ERR( arc().seek( file_pos ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + + // Find end-of-catalog entry + int end_pos = catalog.size() - end_entry_size; + while ( end_pos >= 0 && memcmp( &catalog [end_pos], "PK\5\6", 4 ) ) + end_pos--; + if ( end_pos < 0 ) + return blargg_err_file_type; + end_entry_t const& end_entry = (end_entry_t&) catalog [end_pos]; + end_pos += file_pos; + + // some idiotic zip compressors add data to end of zip without setting comment len +// check( arc().size() == end_pos + end_entry_size + get_le16( end_entry.comment_len ) ); + + // Find file offset of beginning of catalog + catalog_begin = get_le32( end_entry.dir_offset ); + int catalog_size = end_pos - catalog_begin; + if ( catalog_size < 0 ) + return blargg_err_file_corrupt; + catalog_size += end_entry_size; + + // See if catalog is entirely contained in bytes already read + int begin_offset = catalog_begin - file_pos; + if ( begin_offset >= 0 ) + memmove( catalog.begin(), &catalog [begin_offset], catalog_size ); + + RETURN_ERR( catalog.resize( catalog_size ) ); + if ( begin_offset < 0 ) + { + // Catalog begins before bytes read, so it needs to be read + RETURN_ERR( arc().seek( catalog_begin ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + } + + // First entry in catalog should be a file or end of archive + if ( memcmp( catalog.begin(), "PK\1\2", 4 ) && memcmp( catalog.begin(), "PK\5\6", 4 ) ) + return blargg_err_file_type; + + reorder_entry_header( 0 ); + return rewind_v(); +} + +void Zip_Extractor::close_v() +{ + catalog.clear(); +} + +// Scanning + +inline +static bool is_normal_file( entry_t const& e, unsigned len ) +{ + int last_char = (len ? e.filename [len - 1] : '/'); + bool is_dir = (last_char == '/' || last_char == '\\'); + if ( is_dir && get_le32( e.size ) == 0 ) + return false; + check( !is_dir ); + + // Mac OS X puts meta-information in separate files with normal extensions, + // so they must be filtered out or caller will mistake them for normal files. + if ( e.made_by[1] == 3 ) + { + const char* dir = strrchr( e.filename, '/' ); + if ( dir ) + dir++; + else + dir = e.filename; + + if ( *dir == '.' ) + return false; + + if ( !strcmp( dir, "Icon\x0D" ) ) + return false; + } + + return true; +} + +blargg_err_t Zip_Extractor::update_info( bool advance_first ) +{ + while ( 1 ) + { + entry_t& e = (entry_t&) catalog [catalog_pos]; + + if ( memcmp( e.type, "\0K\1\2P", 5 ) && memcmp( e.type, "PK\1\2", 4 ) ) + { + check( !memcmp( e.type, "\0K\5\6P", 5 ) ); + break; + } + + unsigned len = get_le16( e.filename_len ); + int next_offset = catalog_pos + entry_size + len + get_le16( e.extra_len ) + + get_le16( e.comment_len ); + if ( (unsigned) next_offset > catalog.size() - end_entry_size ) + return blargg_err_file_corrupt; + + if ( catalog [next_offset] == 'P' ) + reorder_entry_header( next_offset ); + + if ( !advance_first ) + { + e.filename [len] = 0; // terminate name + + if ( is_normal_file( e, len ) ) + { + set_name( e.filename ); + set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) ); + break; + } + } + + catalog_pos = next_offset; + advance_first = false; + } + + return blargg_ok; +} + +blargg_err_t Zip_Extractor::next_v() +{ + return update_info( true ); +} + +blargg_err_t Zip_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip_Extractor::tell_arc_v() const +{ + return catalog_pos; +} + +blargg_err_t Zip_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && (size_t) pos <= catalog.size() - end_entry_size ); + + catalog_pos = pos; + return update_info( false ); +} + +// Reading + +void Zip_Extractor::clear_file_v() +{ + buf.end(); +} + +blargg_err_t Zip_Extractor::inflater_read( void* data, void* out, int* count ) +{ + Zip_Extractor& self = *STATIC_CAST(Zip_Extractor*,data); + + if ( *count > self.raw_remain ) + *count = self.raw_remain; + + self.raw_remain -= *count; + + return self.arc().read( out, *count ); +} + +blargg_err_t Zip_Extractor::fill_buf( int offset, int buf_size, int initial_read ) +{ + raw_remain = arc().size() - offset; + RETURN_ERR( arc().seek( offset ) ); + return buf.begin( inflater_read, this, buf_size, initial_read ); +} + +blargg_err_t Zip_Extractor::first_read( int count ) +{ + entry_t const& e = (entry_t&) catalog [catalog_pos]; + + // Determine compression + { + int method = get_le16( e.method ); + if ( (method && method != Z_DEFLATED) || get_le16( e.vers ) > 20 ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "compression method" ); + file_deflated = (method != 0); + } + + int raw_size = get_le32( e.raw_size ); + + int file_offset = get_le32( e.file_offset ); + int align = file_offset % disk_block_size; + { + // read header + int buf_size = 3 * disk_block_size - 1 + raw_size; // space for all raw data + buf_size -= buf_size % disk_block_size; + int initial_read = buf_size; + if ( !file_deflated || count < size() ) + { + buf_size = read_buf_size; + initial_read = disk_block_size * 2; + } + // TODO: avoid re-reading if buffer already has data we want? + RETURN_ERR( fill_buf( file_offset - align, buf_size, initial_read ) ); + } + header_t const& h = (header_t&) buf.data() [align]; + if ( buf.filled() < align + header_size || memcmp( h.type, "PK\3\4", 4 ) ) + return blargg_err_file_corrupt; + + // CRCs of header and file data + correct_crc = get_le32( h.crc ); + if ( !correct_crc ) + correct_crc = get_le32( e.crc ); + check( correct_crc == get_le32( e.crc ) ); // catalog CRC should match + crc = ::crc32( 0, NULL, 0 ); + + // Data offset + int data_offset = file_offset + header_size + + get_le16( h.filename_len ) + get_le16( h.extra_len ); + if ( data_offset + raw_size > catalog_begin ) + return blargg_err_file_corrupt; + + // Refill buffer if there's lots of extra data after header + int buf_offset = data_offset - file_offset + align; + if ( buf_offset > buf.filled() ) + { + // TODO: this will almost never occur, making it a good place for bugs + buf_offset = data_offset % disk_block_size; + RETURN_ERR( fill_buf( data_offset - buf_offset, read_buf_size, disk_block_size ) ); + } + + raw_remain = raw_size - (buf.filled() - buf_offset); + return buf.set_mode( (file_deflated ? buf.mode_raw_deflate : buf.mode_copy), buf_offset ); +} + +blargg_err_t Zip_Extractor::extract_v( void* out, int count ) +{ + if ( tell() == 0 ) + RETURN_ERR( first_read( count ) ); + + int actual = count; + RETURN_ERR( buf.read( out, &actual ) ); + if ( actual < count ) + return blargg_err_file_corrupt; + + crc = ::crc32( crc, (byte const*) out, count ); + if ( count == reader().remain() && crc != correct_crc ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/snesreader/fex/Zip_Extractor.h b/snesreader/fex/Zip_Extractor.h new file mode 100644 index 00000000..9742df99 --- /dev/null +++ b/snesreader/fex/Zip_Extractor.h @@ -0,0 +1,45 @@ +// ZIP archive extractor. Only supports deflation and store (no compression). + +// File_Extractor 1.0.0 +#ifndef ZIP_EXTRACTOR_H +#define ZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Zlib_Inflater.h" + +class Zip_Extractor : public File_Extractor { +public: + Zip_Extractor(); + virtual ~Zip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual void clear_file_v(); + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t extract_v( void*, int ); + +private: + blargg_vector catalog; + int catalog_begin; // offset of first catalog entry in file (to detect corruption) + int catalog_pos; // position of current entry in catalog + int raw_remain; // bytes remaining to be read from zip file for current file + unsigned crc; // ongoing CRC of extracted bytes + unsigned correct_crc; + bool file_deflated; + Zlib_Inflater buf; + + blargg_err_t fill_buf( int offset, int buf_size, int initial_read ); + blargg_err_t update_info( bool advance_first ); + blargg_err_t first_read( int count ); + void reorder_entry_header( int offset ); + static blargg_err_t inflater_read( void* data, void* out, int* count ); +}; + +#endif diff --git a/snesreader/fex/Zlib_Inflater.cpp b/snesreader/fex/Zlib_Inflater.cpp new file mode 100644 index 00000000..8d31b514 --- /dev/null +++ b/snesreader/fex/Zlib_Inflater.cpp @@ -0,0 +1,257 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zlib_Inflater.h" + +/* Copyright (C) 2006-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const block_size = 4096; + +static const char* get_zlib_err( int code ) +{ + assert( code != Z_OK ); + switch ( code ) + { + case Z_MEM_ERROR: return blargg_err_memory; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + // TODO: handle more error codes + } + + const char* str = zError( code ); + if ( !str ) + str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" ); + + return str; +} + +void Zlib_Inflater::end() +{ + if ( deflated_ ) + { + deflated_ = false; + if ( inflateEnd( &zbuf ) ) + check( false ); + } + buf.clear(); + + static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + memcpy( &zbuf, &empty, sizeof zbuf ); +} + +Zlib_Inflater::Zlib_Inflater() +{ + deflated_ = false; + end(); // initialize things +} + +Zlib_Inflater::~Zlib_Inflater() +{ + end(); +} + +blargg_err_t Zlib_Inflater::fill_buf( int count ) +{ + byte* out = buf.end() - count; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.avail_in = count; + zbuf.next_in = out; + return blargg_ok; +} + +blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data, + int new_buf_size, int initial_read ) +{ + callback = new_callback; + user_data = new_user_data; + + end(); + + // TODO: decide whether using different size on alloc failure is a good idea + //RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) ); + if ( new_buf_size && buf.resize( new_buf_size ) ) + { + ACK_FAILURE(); + new_buf_size = 0; + } + + if ( !new_buf_size ) + { + RETURN_ERR( buf.resize( 4 * block_size ) ); + initial_read = 0; + } + + // Fill buffer with some data, less than normal buffer size since caller might + // just be examining beginning of file. + return fill_buf( initial_read ? initial_read : block_size ); +} + +blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset ) +{ + zbuf.next_in += data_offset; + zbuf.avail_in -= data_offset; + + if ( mode == mode_auto ) + { + // examine buffer for gzip header + mode = mode_copy; + unsigned const min_gzip_size = 2 + 8 + 8; + if ( zbuf.avail_in >= min_gzip_size && + zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B ) + mode = mode_ungz; + } + + if ( mode != mode_copy ) + { + int wb = MAX_WBITS + 16; // have zlib handle gzip header + if ( mode == mode_raw_deflate ) + wb = -MAX_WBITS; + + int zerr = inflateInit2( &zbuf, wb ); + if ( zerr ) + { + zbuf.next_in = NULL; + return get_zlib_err( zerr ); + } + + deflated_ = true; + } + return blargg_ok; +} + +/* +// Reads/inflates entire stream. All input must be in buffer, and count must be total +// of all output. +blargg_err_t read_all( void* out, int count ); + + +// zlib automatically applies this optimization (uses inflateFast) +// TODO: remove +blargg_err_t Zlib_Inflater::read_all( void* out, int count ) +{ + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = count; + + int err = inflate( &zbuf, Z_FINISH ); + + if ( zbuf.avail_out || err != Z_STREAM_END ) + return blargg_err_file_corrupt; + } + else + { + if ( zbuf.avail_in < count ) + return blargg_err_file_corrupt; + + memcpy( out, zbuf.next_in, count ); + + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + return blargg_ok; +} +*/ + +blargg_err_t Zlib_Inflater::read( void* out, int* count_io ) +{ + int remain = *count_io; + if ( remain && zbuf.next_in ) + { + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = remain; + + while ( 1 ) + { + uInt old_avail_in = zbuf.avail_in; + int err = inflate( &zbuf, Z_NO_FLUSH ); + if ( err == Z_STREAM_END ) + { + remain = zbuf.avail_out; + end(); + break; // no more data to inflate + } + + if ( err && (err != Z_BUF_ERROR || old_avail_in) ) + return get_zlib_err( err ); + + if ( !zbuf.avail_out ) + { + remain = 0; + break; // requested number of bytes inflated + } + + if ( zbuf.avail_in ) + { + // inflate() should never leave input if there's still space for output + check( false ); + return blargg_err_file_corrupt; + } + + RETURN_ERR( fill_buf( buf.size() ) ); + if ( !zbuf.avail_in ) + return blargg_err_file_corrupt; // stream didn't end but there's no more data + } + } + else + { + while ( 1 ) + { + // copy buffered data + if ( zbuf.avail_in ) + { + long count = zbuf.avail_in; + if ( count > remain ) + count = remain; + memcpy( out, zbuf.next_in, count ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + if ( !zbuf.avail_in && zbuf.next_in < buf.end() ) + { + end(); + break; + } + + // read large request directly + if ( remain + zbuf.total_out % block_size >= buf.size() ) + { + int count = remain; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + + if ( remain ) + { + end(); + break; + } + } + + if ( !remain ) + break; + + RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) ); + } + } + } + *count_io -= remain; + return blargg_ok; +} diff --git a/snesreader/fex/Zlib_Inflater.h b/snesreader/fex/Zlib_Inflater.h new file mode 100644 index 00000000..8a49ff52 --- /dev/null +++ b/snesreader/fex/Zlib_Inflater.h @@ -0,0 +1,70 @@ +// Simplifies use of zlib for inflating data + +// File_Extractor 1.0.0 +#ifndef ZLIB_INFLATER_H +#define ZLIB_INFLATER_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "zlib/zlib.h" + +class Zlib_Inflater { +public: + + // Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count + // to that number, or returns error if that many can't be read. + typedef blargg_err_t (*callback_t)( void* user_data, void* out, int* count ); + + // Begins by setting callback and filling buffer. Default buffer is 16K and + // filled to 4K, or specify buf_size and initial_read for custom buffer size + // and how much to read initially. + blargg_err_t begin( callback_t, void* user_data, + int buf_size = 0, int initial_read = 0 ); + + // Data read into buffer by begin() + const unsigned char* data() const { return zbuf.next_in; } + int filled() const { return zbuf.avail_in; } + + // Begins inflation using specified mode. Using mode_auto selects between + // mode_copy and mode_ungz by examining first two bytes of buffer. Use + // buf_offset to specify where data begins in buffer, in case there is + // header data that should be skipped. + enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto }; + blargg_err_t set_mode( mode_t, int buf_offset = 0 ); + + // True if set_mode() has been called with mode_ungz or mode_raw_deflate + bool deflated() const { return deflated_; } + + // Reads/inflates at most *count_io bytes into *out and sets *count_io to actual + // number of bytes read (less than requested if end of data was reached). + // Buffers source data internally, even in copy mode, so input file can be + // unbuffered without sacrificing performance. + blargg_err_t read( void* out, int* count_io ); + + // Total number of bytes read since begin() + int tell() const { return zbuf.total_out; } + + // Ends inflation and frees memory + void end(); + +private: + // noncopyable + Zlib_Inflater( const Zlib_Inflater& ); + Zlib_Inflater& operator = ( const Zlib_Inflater& ); + +// Implementation +public: + Zlib_Inflater(); + ~Zlib_Inflater(); + +private: + z_stream_s zbuf; + blargg_vector buf; + bool deflated_; + callback_t callback; + void* user_data; + + blargg_err_t fill_buf( int count ); +}; + +#endif diff --git a/snesreader/fex/blargg_common.cpp b/snesreader/fex/blargg_common.cpp new file mode 100644 index 00000000..9f3e9ebd --- /dev/null +++ b/snesreader/fex/blargg_common.cpp @@ -0,0 +1,51 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +/* Copyright (C) 2008-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void blargg_vector_::init() +{ + begin_ = NULL; + size_ = 0; +} + +void blargg_vector_::clear() +{ + void* p = begin_; + begin_ = NULL; + size_ = 0; + free( p ); +} + +blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) +{ + if ( n != size_ ) + { + if ( n == 0 ) + { + // Simpler to handle explicitly. Realloc will handle a size of 0, + // but then we have to avoid raising an error for a NULL return. + clear(); + } + else + { + void* p = realloc( begin_, n * elem_size ); + CHECK_ALLOC( p ); + begin_ = p; + size_ = n; + } + } + return blargg_ok; +} diff --git a/snesreader/fex/blargg_common.h b/snesreader/fex/blargg_common.h new file mode 100644 index 00000000..a11579fd --- /dev/null +++ b/snesreader/fex/blargg_common.h @@ -0,0 +1,206 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// File_Extractor 1.0.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include + +typedef const char* blargg_err_t; // 0 on success, otherwise error string + +// Success; no error +int const blargg_ok = 0; + +// BLARGG_RESTRICT: equivalent to C99's restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +#if __cplusplus >= 199711 + #define BLARGG_MUTABLE mutable +#else + #define BLARGG_MUTABLE +#endif + +/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). +I don't just use 'abcd' because that's implementation-dependent. */ +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +Can be used at file, function, or class scope. */ +#ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) +#else + // Others fail when declaring same function multiple times in class, + // so differentiate them by line + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) +#endif + +/* Pure virtual functions cause a vtable entry to a "called pure virtual" +error handler, requiring linkage to the C++ runtime library. This macro is +used in place of the "= 0", and simply expands to its argument. During +development, it expands to "= 0", allowing detection of missing overrides. */ +#define BLARGG_PURE( def ) def + +/* My code depends on ASCII anywhere a character or string constant is +compared with data read from a file, and anywhere file data is read and +treated as a string. */ +#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 + #error "ASCII character set required" +#endif + +/* My code depends on int being at least 32 bits. Almost everything these days +uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints +to test with. The issue can't be gotten around by using a suitable blargg_int +everywhere either, because int is often converted to implicitly when doing +arithmetic on smaller types. */ +#if UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" +#endif + +// In case compiler doesn't support these properly. Used rarely. +#define STATIC_CAST(T,expr) static_cast (expr) +#define CONST_CAST( T,expr) const_cast (expr) + +// User configuration can override the above macros if necessary +#include "blargg_config.h" + +/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a +future version. In GCC, we can let the compiler warn. In other compilers, +we strip it out unless BLARGG_LEGACY is true. */ +#if BLARGG_LEGACY + // Allow old client code to work without warnings + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) text +#elif __GNUC__ >= 4 + // In GCC, we can mark declarations and let the compiler warn + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text +#else + // By default, deprecated items are removed, to avoid use in new code + #define BLARGG_DEPRECATED_TEXT( text ) + #define BLARGG_DEPRECATED( text ) +#endif + +/* BOOST::int8_t, BOOST::int32_t, etc. +I used BOOST since I originally was going to allow use of the boost library +for prividing the definitions. If I'm defining them, they must be scoped or +else they could conflict with the standard ones at global scope. Even if +HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at +global scope already. */ +#if defined (HAVE_STDINT_H) || \ + UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF + #include + #define BOOST +#else + struct BOOST + { + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + }; +#endif + +/* My code is not written with exceptions in mind, so either uses new (nothrow) +OR overrides operator new in my classes. The former is best since clients +creating objects will get standard exceptions on failure, but that causes it +to require the standard C++ library. So, when the client is using the C +interface, I override operator new to use malloc. */ + +// BLARGG_DISABLE_NOTHROW is put inside classes +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if NULL can be returned + #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 + #define BLARGG_THROWS_NOTHING throw () + #else + #define BLARGG_THROWS_NOTHING + #endif + + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ + void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } + + #define BLARGG_NEW new +#else + // BLARGG_NEW is used in place of new in library code + #include + #define BLARGG_NEW new (std::nothrow) +#endif + + class blargg_vector_ { + protected: + void* begin_; + size_t size_; + void init(); + blargg_err_t resize_( size_t n, size_t elem_size ); + public: + size_t size() const { return size_; } + void clear(); + }; + +// Very lightweight vector for POD types (no constructor/destructor) +template +class blargg_vector : public blargg_vector_ { + union T_must_be_pod { T t; }; // fails if T is not POD +public: + blargg_vector() { init(); } + ~blargg_vector() { clear(); } + + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } + + T* begin() { return static_cast (begin_); } + const T* begin() const { return static_cast (begin_); } + + T* end() { return static_cast (begin_) + size_; } + const T* end() const { return static_cast (begin_) + size_; } + + T& operator [] ( size_t n ) + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } + + const T& operator [] ( size_t n ) const + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } +}; + +// Callback function with user data. +// blargg_callback set_callback; // for user, this acts like... +// void set_callback( T func, void* user_data = NULL ); // ...this +// To call function, do set_callback.f( .. set_callback.data ... ); +template +struct blargg_callback +{ + T f; + void* data; + blargg_callback() { f = NULL; } + void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } +}; + +#ifndef _WIN32 + // Not supported on any other platforms + #undef BLARGG_UTF8_PATHS +#endif + +BLARGG_DEPRECATED( typedef signed int blargg_long; ) +BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) +#if BLARGG_LEGACY + #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT +#endif + +#endif diff --git a/snesreader/fex/blargg_config.h b/snesreader/fex/blargg_config.h new file mode 100644 index 00000000..eb862609 --- /dev/null +++ b/snesreader/fex/blargg_config.h @@ -0,0 +1,34 @@ +// Library configuration. Modify this file as necessary. + +// File_Extractor 1.0.0 +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment a #define line below to have effect described. + +// Enable RAR archive support. Doing so adds extra licensing restrictions +// to this library (see unrar/readme.txt for more information). +#define FEX_ENABLE_RAR 1 + +// Accept file paths encoded as UTF-8. Currently only affects Windows, +// as Unix/Linux/Mac OS X already use UTF-8 paths. +#define BLARGG_UTF8_PATHS 1 + +// Enable support for as building DLL on Windows. +//#define BLARGG_BUILD_DLL 1 + +// Support only the listed archive types. Remove any you don't need. +/* +#define FEX_TYPE_LIST \ + fex_7z_type,\ + fex_gz_type,\ + fex_rar_type,\ + fex_zip_type, +*/ + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/snesreader/fex/blargg_endian.h b/snesreader/fex/blargg_endian.h new file mode 100644 index 00000000..c32c12f5 --- /dev/null +++ b/snesreader/fex/blargg_endian.h @@ -0,0 +1,185 @@ +// CPU Byte Order Utilities + +// File_Extractor 1.0.0 +#ifndef BLARGG_ENDIAN_H +#define BLARGG_ENDIAN_H + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ + defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline unsigned get_le32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [3] << 24 | + (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 24 | + (unsigned) ((unsigned char const*) p) [1] << 16 | + (unsigned) ((unsigned char const*) p) [2] << 8 | + (unsigned) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, unsigned n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } +inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } +inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } + +#endif diff --git a/snesreader/fex/blargg_errors.cpp b/snesreader/fex/blargg_errors.cpp new file mode 100644 index 00000000..14076cdb --- /dev/null +++ b/snesreader/fex/blargg_errors.cpp @@ -0,0 +1,113 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_errors.h" + +/* Copyright (C) 2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC; +blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY; +blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER; +blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL; +blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION; + +blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING; +blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ; +blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE; +blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO; +blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL; +blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF; + +blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE; +blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE; +blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT; + +const char* blargg_err_str( blargg_err_t err ) +{ + if ( !err ) + return ""; + + if ( *err == BLARGG_ERR_TYPE("")[0] ) + return err + 1; + + return err; +} + +bool blargg_is_err_type( blargg_err_t err, const char type [] ) +{ + if ( err ) + { + // True if first strlen(type) characters of err match type + char const* p = err; + while ( *type && *type == *p ) + { + type++; + p++; + } + + if ( !*type ) + return true; + } + + return false; +} + +const char* blargg_err_details( blargg_err_t err ) +{ + const char* p = err; + if ( !p ) + { + p = ""; + } + else if ( *p == BLARGG_ERR_TYPE("")[0] ) + { + while ( *p && *p != ';' ) + p++; + + // Skip ; and space after it + if ( *p ) + { + p++; + + check( *p == ' ' ); + if ( *p ) + p++; + } + } + return p; +} + +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] ) +{ + if ( !err ) + return 0; + + while ( codes->str && !blargg_is_err_type( err, codes->str ) ) + codes++; + + return codes->code; +} + +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] ) +{ + if ( !code ) + return blargg_ok; + + while ( codes->str && codes->code != code ) + codes++; + + if ( !codes->str ) + return blargg_err_generic; + + return codes->str; +} diff --git a/snesreader/fex/blargg_errors.h b/snesreader/fex/blargg_errors.h new file mode 100644 index 00000000..9c5206d5 --- /dev/null +++ b/snesreader/fex/blargg_errors.h @@ -0,0 +1,80 @@ +// Error strings and conversion functions + +// File_Extractor 1.0.0 +#ifndef BLARGG_ERRORS_H +#define BLARGG_ERRORS_H + +#ifndef BLARGG_COMMON_H + #include "blargg_common.h" +#endif + +typedef const char blargg_err_def_t []; + +// Basic errors +extern blargg_err_def_t blargg_err_generic; +extern blargg_err_def_t blargg_err_memory; +extern blargg_err_def_t blargg_err_caller; +extern blargg_err_def_t blargg_err_internal; +extern blargg_err_def_t blargg_err_limitation; + +// File low-level +extern blargg_err_def_t blargg_err_file_missing; // not found +extern blargg_err_def_t blargg_err_file_read; +extern blargg_err_def_t blargg_err_file_write; +extern blargg_err_def_t blargg_err_file_io; +extern blargg_err_def_t blargg_err_file_full; +extern blargg_err_def_t blargg_err_file_eof; + +// File high-level +extern blargg_err_def_t blargg_err_file_type; // wrong file type +extern blargg_err_def_t blargg_err_file_feature; +extern blargg_err_def_t blargg_err_file_corrupt; + +// C string describing error, or "" if err == NULL +const char* blargg_err_str( blargg_err_t err ); + +// True iff error is of given type, or false if err == NULL +bool blargg_is_err_type( blargg_err_t, const char type [] ); + +// Details of error without describing main cause, or "" if err == NULL +const char* blargg_err_details( blargg_err_t err ); + +// Converts error string to integer code using mapping table. Calls blargg_is_err_type() +// for each str and returns code on first match. Returns 0 if err == NULL. +struct blargg_err_to_code_t { + const char* str; + int code; +}; +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] ); + +// Converts error code back to string. If code == 0, returns NULL. If not in table, +// returns blargg_err_generic. +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] ); + +// Generates error string literal with details of cause +#define BLARGG_ERR( type, str ) (type "; " str) + +// Extra space to make it clear when blargg_err_str() isn't called to get +// printable version of error. At some point, I might prefix error strings +// with a code, to speed conversion to a code. +#define BLARGG_ERR_TYPE( str ) " " str + +// Error types to pass to BLARGG_ERR macro +#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" ) +#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" ) +#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" ) +#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" ) +#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" ) + +#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" ) +#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" ) +#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" ) +#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" ) +#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" ) +#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" ) + +#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" ) +#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" ) +#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" ) + +#endif diff --git a/snesreader/fex/blargg_source.h b/snesreader/fex/blargg_source.h new file mode 100644 index 00000000..659f34c5 --- /dev/null +++ b/snesreader/fex/blargg_source.h @@ -0,0 +1,125 @@ +/* Included at the beginning of library source files, AFTER all other #include +lines. Sets up helpful macros and services used in my source code. Since this +is only "active" in my source code, I don't have to worry about polluting the +global namespace with unprefixed names. */ + +// File_Extractor 1.0.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +#ifndef BLARGG_COMMON_H // optimization only + #include "blargg_common.h" +#endif +#include "blargg_errors.h" + +#include /* memcpy(), memset(), memmove() */ +#include /* offsetof() */ + +/* The following four macros are for debugging only. Some or all might be +defined to do nothing, depending on the circumstances. Described is what +happens when a particular macro is defined to do something. When defined to +do nothing, the macros do NOT evaluate their argument(s). */ + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking internal state and consistency. A failed assertion indicates a bug +in MY code. + +void assert( bool expr ); */ +#include + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking caller-supplied parameters and operations that are outside the +control of the module. A failed requirement probably indicates a bug in YOUR +code. + +void require( bool expr ); */ +#undef require +#define require( expr ) assert( expr ) + +/* Like printf() except output goes to debugging console/file. + +void dprintf( const char format [], ... ); */ +static inline void blargg_dprintf_( const char [], ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +/* If expr is false, prints file and line number to debug console/log, then +continues execution normally. Meant for flagging potential problems or things +that should be looked into, but that aren't serious problems. + +void check( bool expr ); */ +#undef check +#define check( expr ) ((void) 0) + +/* If expr yields non-NULL error string, returns it from current function, +otherwise continues normally. */ +#undef RETURN_ERR +#define RETURN_ERR( expr ) \ + do {\ + blargg_err_t blargg_return_err_ = (expr);\ + if ( blargg_return_err_ )\ + return blargg_return_err_;\ + } while ( 0 ) + +/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */ +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) \ + do {\ + if ( !(ptr) )\ + return blargg_err_memory;\ + } while ( 0 ) + +/* The usual min/max functions for built-in types. + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } */ +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && BLARGG_BUILD_DLL + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (__GNUC__) + // can always set visibility, even when not building DLL + #define BLARGG_EXPORT __attribute__ ((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + +#if BLARGG_LEGACY + #define BLARGG_CHECK_ALLOC CHECK_ALLOC + #define BLARGG_RETURN_ERR RETURN_ERR +#endif + +// Called after failed operation when overall operation may still complete OK. +// Only used by unit testing framework. +#undef ACK_FAILURE +#define ACK_FAILURE() ((void)0) + +/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc. +and check */ +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/snesreader/fex/fex.cpp b/snesreader/fex/fex.cpp new file mode 100644 index 00000000..d0946dd9 --- /dev/null +++ b/snesreader/fex/fex.cpp @@ -0,0 +1,323 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "fex.h" + +#include "File_Extractor.h" +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2005-2009 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + + +//// Types + +BLARGG_EXPORT const fex_type_t* fex_type_list( void ) +{ + static fex_type_t const fex_type_list_ [] = + { + #ifdef FEX_TYPE_LIST + FEX_TYPE_LIST + #else + // Modify blargg_config.h to change type list, NOT this file + fex_7z_type, + fex_gz_type, + #if FEX_ENABLE_RAR + fex_rar_type, + #endif + fex_zip_type, + #endif + fex_bin_type, + NULL + }; + + return fex_type_list_; +} + +BLARGG_EXPORT fex_err_t fex_init( void ) +{ + static bool inited; + if ( !inited ) + { + for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t ) + { + if ( (*t)->init ) + RETURN_ERR( (*t)->init() ); + } + inited = true; + } + return blargg_ok; +} + +BLARGG_EXPORT const char* fex_identify_header( void const* header ) +{ + unsigned four = get_be32( header ); + switch ( four ) + { + case 0x52457E5E: + case 0x52617221: return ".rar"; + + case 0x377ABCAF: return ".7z"; + + case 0x504B0304: + case 0x504B0506: return ".zip"; + + case 0x53495421: return ".sit"; + case 0x41724301: return ".arc"; + case 0x4D534346: return ".cab"; + case 0x5A4F4F20: return ".zoo"; + } + + unsigned three = four >> 8; + switch ( three ) + { + case 0x425A68: return ".bz2"; + } + + unsigned two = four >> 16; + switch ( two ) + { + case 0x1F8B: return ".gz"; + case 0x60EA: return ".arj"; + } + + unsigned skip_first_two = four & 0xFFFF; + if ( skip_first_two == 0x2D6C ) + return ".lha"; + + return ""; +} + +static int fex_has_extension_( const char str [], const char suffix [], size_t str_len ) +{ + size_t suffix_len = strlen( suffix ); + if ( str_len >= suffix_len ) + { + str += str_len - suffix_len; + while ( *str && tolower( (unsigned char) *str ) == *suffix ) + { + str++; + suffix++; + } + } + return *suffix == 0; +} + +BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] ) +{ + return fex_has_extension_( str, suffix, strlen( str ) ); +} + +static int is_archive_extension( const char str [] ) +{ + static const char exts [] [6] = { + ".7z", + ".arc", + ".arj", + ".bz2", + ".cab", + ".dmg", + ".gz", + ".lha", + ".lz", + ".lzh", + ".lzma", + ".lzo", + ".lzx", + ".pea", + ".rar", + ".sit", + ".sitx", + ".tgz", + ".tlz", + ".z", + ".zip", + ".zoo", + "" + }; + + size_t str_len = strlen( str ); + const char (*ext) [6] = exts; + for ( ; **ext; ext++ ) + { + if ( fex_has_extension_( str, *ext, str_len ) ) + return 1; + } + return 0; +} + +BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] ) +{ + size_t str_len = strlen( str ); + for ( fex_type_t const* types = fex_type_list(); *types; types++ ) + { + if ( fex_has_extension_( str, (*types)->extension, str_len ) ) + { + // Avoid treating known archive type as binary + if ( *(*types)->extension || !is_archive_extension( str ) ) + return *types; + } + } + return NULL; +} + +BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] ) +{ + *type_out = NULL; + + fex_type_t type = fex_identify_extension( path ); + + // Unsupported extension? + if ( !type ) + return blargg_ok; // reject + + // Unknown/no extension? + if ( !*(type->extension) ) + { + // Examine header + FEX_FILE_READER in; + RETURN_ERR( in.open( path ) ); + if ( in.remain() >= fex_identify_header_size ) + { + char h [fex_identify_header_size]; + RETURN_ERR( in.read( h, sizeof h ) ); + + type = fex_identify_extension( fex_identify_header( h ) ); + } + } + + *type_out = type; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type ) +{ + *fe_out = NULL; + + if ( !type ) + return blargg_err_file_type; + + fex_t* fe = type->new_fex(); + CHECK_ALLOC( fe ); + + fex_err_t err = fe->open( path ); + if ( err ) + { + delete fe; + return err; + } + + *fe_out = fe; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] ) +{ + *fe_out = NULL; + + fex_type_t type; + RETURN_ERR( fex_identify_file( &type, path ) ); + + return fex_open_type( fe_out, path, type ); +} + + +//// Wide paths + +#if BLARGG_UTF8_PATHS +char* fex_wide_to_path( const wchar_t* wide ) +{ + return blargg_to_utf8( wide ); +} + +void fex_free_path( char* path ) +{ + free( path ); +} +#endif + + +//// Errors + +#define ENTRY( name ) { blargg_err_##name, fex_err_##name } +static blargg_err_to_code_t const fex_codes [] = +{ + ENTRY( generic ), + ENTRY( memory ), + ENTRY( caller ), + ENTRY( internal ), + ENTRY( limitation ), + + ENTRY( file_missing ), + ENTRY( file_read ), + ENTRY( file_io ), + ENTRY( file_eof ), + + ENTRY( file_type ), + ENTRY( file_feature ), + ENTRY( file_corrupt ), + + { 0, -1 } +}; +#undef ENTRY + +static int err_code( fex_err_t err ) +{ + return blargg_err_to_code( err, fex_codes ); +} + +BLARGG_EXPORT int fex_err_code( fex_err_t err ) +{ + int code = err_code( err ); + return (code >= 0 ? code : fex_err_generic); +} + +BLARGG_EXPORT fex_err_t fex_code_to_err( int code ) +{ + return blargg_code_to_err( code, fex_codes ); +} + +BLARGG_EXPORT const char* fex_err_details( fex_err_t err ) +{ + // If we don't have error code assigned, return entire string + return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); +} + + +//// Wrappers + +BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count ) +{ + RETURN_ERR( fe->stat() ); + return fe->reader().read( out, count ); +} + +BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; } +BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); } +BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); } +BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); } +BLARGG_EXPORT const wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); } +BLARGG_EXPORT int fex_size ( const fex_t* fe ) { return fe->size(); } +BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); } +BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); } +BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); } +BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); } +BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); } +BLARGG_EXPORT int fex_tell ( const fex_t* fe ) { return fe->tell(); } +BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); } +BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); } +BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; } +BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; } +BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); } +BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); } diff --git a/snesreader/fex/fex.h b/snesreader/fex/fex.h new file mode 100644 index 00000000..f9452771 --- /dev/null +++ b/snesreader/fex/fex.h @@ -0,0 +1,206 @@ +/** Uniform access to zip, gzip, 7-zip, and RAR compressed archives \file */ + +/* File_Extractor 1.0.0 */ +#ifndef FEX_H +#define FEX_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + + +/** First parameter of most functions is fex_t*, or const fex_t* if nothing is +changed. Once one of these functions returns an error, the archive should not +be used any further, other than to close it. One exception is +fex_error_file_eof; the archive may still be used after this. */ +typedef struct fex_t fex_t; + +/** Pointer to error, or NULL if function was successful. See error functions +below. */ +#ifndef fex_err_t /* (#ifndef allows better testing of library) */ + typedef const char* fex_err_t; +#endif + + +/**** File types ****/ + +/** Archive file type identifier. Can also hold NULL. */ +typedef const struct fex_type_t_* fex_type_t; + +/** Array of supported types, with NULL at end */ +const fex_type_t* fex_type_list( void ); + +/** Name of this archive type, e.g. "ZIP archive", "file" */ +const char* fex_type_name( fex_type_t ); + +/** Usual file extension for type, e.g. ".zip", ".7z". For binary file type, +returns "", since it can open any file. */ +const char* fex_type_extension( fex_type_t ); + + +/**** Wide-character file paths (Windows only) ****/ + +/** Converts wide-character path to form suitable for use with fex functions. +Only supported when BLARGG_UTF8_PATHS is defined and building on Windows. */ +char* fex_wide_to_path( const wchar_t* wide ); + +/** Frees converted path. OK to pass NULL. Only supported when BLARGG_UTF8_PATHS +is defined and building on Windows */ +void fex_free_path( char* ); + + +/**** Identification ****/ + +/** True if str ends in extension. If extension is "", always returns true. +Converts str to lowercase before comparison, so extension should ALREADY be +lowercase (i.e. pass ".zip", NOT ".ZIP"). */ +int fex_has_extension( const char str [], const char extension [] ); + +/** Determines type based on first fex_identify_header_size bytes of file. +Returns usual file extension this should have (e.g. ".zip", ".gz", etc.). +Returns "" if file header is not recognized. */ +const char* fex_identify_header( const void* header ); +enum { fex_identify_header_size = 16 }; + +/** Determines type based on extension of a file path, or just a lone extension +(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is +for an unsupported type (e.g. ".lzh"). */ +fex_type_t fex_identify_extension( const char path_or_extension [] ); + +/** Determines type based on filename extension and/or file header. Sets *out +to determined type, or NULL if type is not supported. */ +fex_err_t fex_identify_file( fex_type_t* out, const char path [] ); + +/** Type of an already-opened archive */ +fex_type_t fex_type( const fex_t* ); + + +/**** Open/close ****/ + +/** Initializes static tables used by library. Automatically called by +fex_open(). OK to call more than once. */ +fex_err_t fex_init( void ); + +/** Opens archive and points *out at it. If error, sets *out to NULL. */ +fex_err_t fex_open( fex_t** out, const char path [] ); + +/** Opens archive of specified type and sets *out. Returns error if file is not +of that archive type. If error, sets *out to NULL. */ +fex_err_t fex_open_type( fex_t** out, const char path [], fex_type_t ); + +/** Closes archive and frees memory. OK to pass NULL. */ +void fex_close( fex_t* ); + + +/**** Scanning ****/ + +/** True if at end of archive. Must be called after fex_open() or fex_rewind(), +as an archive might contain no files. */ +int fex_done( const fex_t* ); + +/** Goes to next file in archive. If there are no more files, fex_done() will +now return true. */ +fex_err_t fex_next( fex_t* ); + +/** Goes back to first file in archive, as if it were just opened with +fex_open() */ +fex_err_t fex_rewind( fex_t* ); + +/** Saved position in archive. Can also store zero. */ +typedef int fex_pos_t; + +/** Position of current file in archive. Never returns zero. */ +fex_pos_t fex_tell_arc( const fex_t* ); + +/** Returns to file at previously-saved position */ +fex_err_t fex_seek_arc( fex_t*, fex_pos_t ); + + +/**** Info ****/ + +/** Name of current file */ +const char* fex_name( const fex_t* ); + +/** Wide-character name of current file, or NULL if unavailable */ +const wchar_t* fex_wname( const fex_t* ); + +/** Makes further information available for file */ +fex_err_t fex_stat( fex_t* ); + +/** Size of current file. fex_stat() or fex_data() must have been called. */ +int fex_size( const fex_t* ); + +/** Modification date of current file (MS-DOS format), or 0 if unavailable. +fex_stat() must have been called. */ +unsigned int fex_dos_date( const fex_t* ); + +/** CRC-32 checksum of current file's contents, or 0 if unavailable. Doesn't +require calculation; simply gets it from file's header. fex_stat() must have +been called. */ +unsigned int fex_crc32( const fex_t* ); + + +/**** Extraction ****/ + +/** Reads n bytes from current file. Reading past end of file results in +fex_err_file_eof. */ +fex_err_t fex_read( fex_t*, void* out, int n ); + +/** Number of bytes read from current file */ +int fex_tell( const fex_t* ); + +/** Points *out at current file's data in memory. Pointer is valid until +fex_next(), fex_rewind(), fex_seek_arc(), or fex_close() is called. Pointer +must NOT be freed(); library frees it automatically. If error, sets *out to +NULL. */ +fex_err_t fex_data( fex_t*, const void** out ); + + +/**** Errors ****/ + +/** Error string associated with err. Returns "" if err is NULL. Returns err +unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_str( fex_err_t err ); + +/** Details of error beyond main cause, or "" if none or err is NULL. Returns +err unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_details( fex_err_t err ); + +/** Numeric code corresponding to err. Returns fex_ok if err is NULL. Returns +fex_err_generic if err isn't a fex_err_t returned by library. */ +int fex_err_code( fex_err_t err ); + +enum { + fex_ok = 0,/**< Successful call. Guaranteed to be zero. */ + fex_err_generic = 0x01,/**< Error of unspecified type */ + fex_err_memory = 0x02,/**< Out of memory */ + fex_err_caller = 0x03,/**< Caller called function with bad args */ + fex_err_internal = 0x04,/**< Internal problem, bug, etc. */ + fex_err_limitation = 0x05,/**< Exceeded program limit */ + + fex_err_file_missing = 0x20,/**< File not found at specified path */ + fex_err_file_read = 0x21,/**< Couldn't open file for reading */ + fex_err_file_io = 0x23,/**< Read/write error */ + fex_err_file_eof = 0x25,/**< Tried to read past end of file */ + + fex_err_file_type = 0x30,/**< File is of wrong type */ + fex_err_file_feature = 0x32,/**< File requires unsupported feature */ + fex_err_file_corrupt = 0x33 /**< File is corrupt */ +}; + +/** fex_err_t corresponding to numeric code. Note that this might not recover +the original fex_err_t before it was converted to a numeric code; in +particular, fex_err_details(fex_code_to_err(code)) will be "" in most cases. */ +fex_err_t fex_code_to_err( int code ); + + +/* Deprecated */ +typedef fex_t File_Extractor; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/filechooser.cpp b/snesreader/filechooser.cpp new file mode 100644 index 00000000..e1f9d8b0 --- /dev/null +++ b/snesreader/filechooser.cpp @@ -0,0 +1,57 @@ +#include "filechooser.moc.hpp" +#include "filechooser.moc" + +//FileChooser is implemented as a modal QWidget instead of a QDialog +//due to a bug in Qt 4.6.0 (QTBUG-7188); which causes the FileChooser +//to not refresh when a QTimer is active from the main application. +string FileChooser::exec() { + if(list.size() == 0) return ""; + if(list.size() == 1) return list[0]; + + listWidget->clear(); + for(unsigned i = 0; i < list.size(); i++) { + listWidget->addItem(list[i]); + } + listWidget->sortItems(Qt::AscendingOrder); + listWidget->setCurrentRow(0); + listWidget->setFocus(); + + name = ""; + setWindowModality(Qt::ApplicationModal); + show(); + while(isVisible()) QApplication::processEvents(); + setWindowModality(Qt::NonModal); + return name; +} + +void FileChooser::load() { + QListWidgetItem *item = listWidget->currentItem(); + if(item) name = item->text().toUtf8().constData(); + close(); +} + +FileChooser::FileChooser() { + setWindowTitle("Select Cartridge To Load"); + setMinimumWidth(480); + setMinimumHeight(320); + + layout = new QVBoxLayout; + setLayout(layout); + + listWidget = new QListWidget; + layout->addWidget(listWidget); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + okButton = new QPushButton("Ok"); + controlLayout->addWidget(okButton); + + cancelButton = new QPushButton("Cancel"); + controlLayout->addWidget(cancelButton); + + connect(listWidget, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(load())); + connect(okButton, SIGNAL(released()), this, SLOT(load())); + connect(cancelButton, SIGNAL(released()), this, SLOT(close())); +} diff --git a/snesreader/filechooser.moc.hpp b/snesreader/filechooser.moc.hpp new file mode 100644 index 00000000..e024cab0 --- /dev/null +++ b/snesreader/filechooser.moc.hpp @@ -0,0 +1,20 @@ +class FileChooser : public QWidget { + Q_OBJECT + +public: + lstring list; + string name; + string exec(); + + FileChooser(); + +private slots: + void load(); + +private: + QVBoxLayout *layout; + QListWidget *listWidget; + QHBoxLayout *controlLayout; + QPushButton *okButton; + QPushButton *cancelButton; +} *fileChooser; diff --git a/snesreader/libjma/7z.h b/snesreader/libjma/7z.h new file mode 100644 index 00000000..50e1f242 --- /dev/null +++ b/snesreader/libjma/7z.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/7zlzma.cpp b/snesreader/libjma/7zlzma.cpp new file mode 100644 index 00000000..b849d8df --- /dev/null +++ b/snesreader/libjma/7zlzma.cpp @@ -0,0 +1,50 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/aribitcd.h b/snesreader/libjma/aribitcd.h new file mode 100644 index 00000000..1fb421ba --- /dev/null +++ b/snesreader/libjma/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/snesreader/libjma/ariconst.h b/snesreader/libjma/ariconst.h new file mode 100644 index 00000000..751b2b7c --- /dev/null +++ b/snesreader/libjma/ariconst.h @@ -0,0 +1,29 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/ariprice.h b/snesreader/libjma/ariprice.h new file mode 100644 index 00000000..ccc398e1 --- /dev/null +++ b/snesreader/libjma/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/snesreader/libjma/btreecd.h b/snesreader/libjma/btreecd.h new file mode 100644 index 00000000..acce3664 --- /dev/null +++ b/snesreader/libjma/btreecd.h @@ -0,0 +1,126 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/crc32.h b/snesreader/libjma/crc32.h new file mode 100644 index 00000000..876a7d3d --- /dev/null +++ b/snesreader/libjma/crc32.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2004-2007 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 +version 2 as published by the Free Software Foundation. + +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); +} + +#endif diff --git a/snesreader/libjma/iiostrm.cpp b/snesreader/libjma/iiostrm.cpp new file mode 100644 index 00000000..f2719969 --- /dev/null +++ b/snesreader/libjma/iiostrm.cpp @@ -0,0 +1,132 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/iiostrm.h b/snesreader/libjma/iiostrm.h new file mode 100644 index 00000000..a5b2ab20 --- /dev/null +++ b/snesreader/libjma/iiostrm.h @@ -0,0 +1,210 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/inbyte.cpp b/snesreader/libjma/inbyte.cpp new file mode 100644 index 00000000..c727a4b2 --- /dev/null +++ b/snesreader/libjma/inbyte.cpp @@ -0,0 +1,60 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/inbyte.h b/snesreader/libjma/inbyte.h new file mode 100644 index 00000000..53afa171 --- /dev/null +++ b/snesreader/libjma/inbyte.h @@ -0,0 +1,76 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/jcrc32.cpp b/snesreader/libjma/jcrc32.cpp new file mode 100644 index 00000000..e3377d58 --- /dev/null +++ b/snesreader/libjma/jcrc32.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2004-2007 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 +version 2 as published by the Free Software Foundation. + +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/snesreader/libjma/jma.cpp b/snesreader/libjma/jma.cpp new file mode 100644 index 00000000..87e03228 --- /dev/null +++ b/snesreader/libjma/jma.cpp @@ -0,0 +1,550 @@ +/* +Copyright (C) 2005-2007 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 +version 2 as published by the Free Software Foundation. + +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/snesreader/libjma/jma.h b/snesreader/libjma/jma.h new file mode 100644 index 00000000..2aaa5ca1 --- /dev/null +++ b/snesreader/libjma/jma.h @@ -0,0 +1,88 @@ +/* +Copyright (C) 2005-2007 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 +version 2 as published by the Free Software Foundation. + +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/snesreader/libjma/lencoder.h b/snesreader/libjma/lencoder.h new file mode 100644 index 00000000..6f30e478 --- /dev/null +++ b/snesreader/libjma/lencoder.h @@ -0,0 +1,93 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/litcoder.h b/snesreader/libjma/litcoder.h new file mode 100644 index 00000000..639d6c55 --- /dev/null +++ b/snesreader/libjma/litcoder.h @@ -0,0 +1,122 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/lzma.cpp b/snesreader/libjma/lzma.cpp new file mode 100644 index 00000000..d020ed27 --- /dev/null +++ b/snesreader/libjma/lzma.cpp @@ -0,0 +1,41 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/lzma.h b/snesreader/libjma/lzma.h new file mode 100644 index 00000000..949b70b3 --- /dev/null +++ b/snesreader/libjma/lzma.h @@ -0,0 +1,124 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/lzmadec.h b/snesreader/libjma/lzmadec.h new file mode 100644 index 00000000..bb91912e --- /dev/null +++ b/snesreader/libjma/lzmadec.h @@ -0,0 +1,82 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/lzmadecode.cpp b/snesreader/libjma/lzmadecode.cpp new file mode 100644 index 00000000..ad6b5709 --- /dev/null +++ b/snesreader/libjma/lzmadecode.cpp @@ -0,0 +1,298 @@ +/* +Copyright (C) 2005-2007 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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/portable.h b/snesreader/libjma/portable.h new file mode 100644 index 00000000..12416c7f --- /dev/null +++ b/snesreader/libjma/portable.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2004-2007 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 +version 2 as published by the Free Software Foundation. + +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/snesreader/libjma/rcdefs.h b/snesreader/libjma/rcdefs.h new file mode 100644 index 00000000..6106b57a --- /dev/null +++ b/snesreader/libjma/rcdefs.h @@ -0,0 +1,60 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/rngcoder.h b/snesreader/libjma/rngcoder.h new file mode 100644 index 00000000..711c2de8 --- /dev/null +++ b/snesreader/libjma/rngcoder.h @@ -0,0 +1,143 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/winout.cpp b/snesreader/libjma/winout.cpp new file mode 100644 index 00000000..1f33885c --- /dev/null +++ b/snesreader/libjma/winout.cpp @@ -0,0 +1,89 @@ +/* +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 version 2.1 as published by the Free Software Foundation. + +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/snesreader/libjma/winout.h b/snesreader/libjma/winout.h new file mode 100644 index 00000000..1f6d7e33 --- /dev/null +++ b/snesreader/libjma/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 version 2.1 as published by the Free Software Foundation. + +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++; + BYTE *p2 = p - aDistance; + for(UINT32 i = 0; i < aLen; i++) + p[i] = p2[i]; + 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/snesreader/micro-bunzip/micro-bunzip.c b/snesreader/micro-bunzip/micro-bunzip.c new file mode 100644 index 00000000..e7f6f7dc --- /dev/null +++ b/snesreader/micro-bunzip/micro-bunzip.c @@ -0,0 +1,515 @@ +/* vi: set sw=4 ts=4: */ +/* micro-bunzip, a small, simple bzip2 decompression implementation. + Copyright 2003 by Rob Landley (rob@landley.net). + + Based on bzip2 decompression code by Julian R Seward (jseward@acm.org), + which also acknowledges contributions by Mike Burrows, David Wheeler, + Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten, + Robert Sedgewick, and Jon L. Bentley. + + I hereby release this code under the GNU Library General Public License + (LGPL) version 2, available at http://www.gnu.org/copyleft/lgpl.html +*/ + +#include +#include +#include +#include +#include + +/* Constants for huffman coding */ +#define MAX_GROUPS 6 +#define GROUP_SIZE 50 /* 64 would have been more efficient */ +#define MAX_HUFCODE_BITS 20 /* Longest huffman code allowed */ +#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */ +#define SYMBOL_RUNA 0 +#define SYMBOL_RUNB 1 + +/* Status return values */ +#define RETVAL_OK 0 +#define RETVAL_LAST_BLOCK (-1) +#define RETVAL_NOT_BZIP_DATA (-2) +#define RETVAL_UNEXPECTED_INPUT_EOF (-3) +#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4) +#define RETVAL_DATA_ERROR (-5) +#define RETVAL_OUT_OF_MEMORY (-6) +#define RETVAL_OBSOLETE_INPUT (-7) + +/* Other housekeeping constants */ +#define IOBUF_SIZE 4096 + +char *bunzip_errors[]={NULL,"Bad file checksum","Not bzip data", + "Unexpected input EOF","Unexpected output EOF","Data error", + "Out of memory","Obsolete (pre 0.9.5) bzip format not supported."}; + +/* This is what we know about each huffman coding group */ +struct group_data { + int limit[MAX_HUFCODE_BITS],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS]; + char minLen, maxLen; +}; + +/* Structure holding all the housekeeping data, including IO buffers and + memory that persists between calls to bunzip */ +typedef struct { + /* For I/O error handling */ + jmp_buf jmpbuf; + /* Input stream, input buffer, input bit buffer */ + int in_fd,inbufCount,inbufPos; + unsigned char *inbuf; + unsigned int inbufBitCount, inbufBits; + /* Output buffer */ + char outbuf[IOBUF_SIZE]; + int outbufPos; + /* The CRC values stored in the block header and calculated from the data */ + unsigned int crc32Table[256],headerCRC, dataCRC, totalCRC; + /* Intermediate buffer and its size (in bytes) */ + unsigned int *dbuf, dbufSize; + /* State for interrupting output loop */ + int writePos,writeRun,writeCount,writeCurrent; + + /* These things are a bit too big to go on the stack */ + unsigned char selectors[32768]; /* nSelectors=15 bits */ + struct group_data groups[MAX_GROUPS]; /* huffman coding tables */ +} bunzip_data; + +/* Return the next nnn bits of input. All reads from the compressed input + are done through this function. All reads are big endian */ +static unsigned int get_bits(bunzip_data *bd, char bits_wanted) +{ + unsigned int bits=0; + + /* If we need to get more data from the byte buffer, do so. (Loop getting + one byte at a time to enforce endianness and avoid unaligned access.) */ + while (bd->inbufBitCountinbufPos==bd->inbufCount) { + if(!(bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE))) + longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_INPUT_EOF); + bd->inbufPos=0; + } + /* Avoid 32-bit overflow (dump bit buffer to top of output) */ + if(bd->inbufBitCount>=24) { + bits=bd->inbufBits&((1<inbufBitCount)-1); + bits_wanted-=bd->inbufBitCount; + bits<<=bits_wanted; + bd->inbufBitCount=0; + } + /* Grab next 8 bits of input from buffer. */ + bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount+=8; + } + /* Calculate result */ + bd->inbufBitCount-=bits_wanted; + bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<headerCRC=get_bits(bd,32); + /* Is this the last block (with CRC for file)? */ + if(!strcmp(mtfSymbol,"\x17\x72\x45\x38\x50\x90")) + return RETVAL_LAST_BLOCK; + /* If it's not a valid data block, barf. */ + if(strcmp(mtfSymbol,"\x31\x41\x59\x26\x53\x59")) + return RETVAL_NOT_BZIP_DATA; + + dbuf=bd->dbuf; + dbufSize=bd->dbufSize; + selectors=bd->selectors; + /* We can add support for blockRandomised if anybody complains. There was + some code for this in busybox 1.0.0-pre3, but nobody ever noticed that + it didn't actually work. */ + if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT; + if((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR; + /* mapping table: if some byte values are never used (encoding things + like ascii text), the compression code removes the gaps to have fewer + symbols to deal with, and writes a sparse bitfield indicating which + values were present. We make a translation table to convert the symbols + back to the corresponding bytes. */ + t=get_bits(bd, 16); + memset(symToByte,0,256); + symTotal=0; + for (i=0;i<16;i++) { + if(t&(1<<(15-i))) { + k=get_bits(bd,16); + for(j=0;j<16;j++) + if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j; + } + } + /* How many different huffman coding groups does this block use? */ + groupCount=get_bits(bd,3); + if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR; + /* nSelectors: Every GROUP_SIZE many symbols we select a new huffman coding + group. Read in the group selector list, which is stored as MTF encoded + bit runs. */ + if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR; + for(i=0; i=groupCount) return RETVAL_DATA_ERROR; + /* Decode MTF to get the next selector */ + uc = mtfSymbol[j]; + memmove(mtfSymbol+1,mtfSymbol,j); + mtfSymbol[0]=selectors[i]=uc; + } + /* Read the huffman coding tables for each group, which code for symTotal + literal symbols, plus two run symbols (RUNA, RUNB) */ + symCount=symTotal+2; + for (j=0; j MAX_HUFCODE_BITS) return RETVAL_DATA_ERROR; + if(!get_bits(bd, 1)) break; + if(!get_bits(bd, 1)) t++; + else t--; + } + length[i] = t; + } + /* Find largest and smallest lengths in this group */ + minLen=maxLen=length[0]; + for(i = 1; i < symCount; i++) { + if(length[i] > maxLen) maxLen = length[i]; + else if(length[i] < minLen) minLen = length[i]; + } + /* Calculate permute[], base[], and limit[] tables from length[]. + * + * permute[] is the lookup table for converting huffman coded symbols + * into decoded symbols. base[] is the amount to subtract from the + * value of a huffman symbol of a given length when using permute[]. + * + * limit[] indicates the largest numerical value a symbol with a given + * number of bits can have. It lets us know when to stop reading. + * + * To use these, keep reading bits until value<=limit[bitcount] or + * you've read over 20 bits (error). Then the decoded symbol + * equals permute[hufcode_value-base[hufcode_bitcount]]. + */ + hufGroup=bd->groups+j; + hufGroup->minLen = minLen; + hufGroup->maxLen = maxLen; + /* Note that minLen can't be smaller than 1, so we adjust the base + and limit array pointers so we're not always wasting the first + entry. We do this again when using them (during symbol decoding).*/ + base=hufGroup->base-1; + limit=hufGroup->limit-1; + /* Calculate permute[] */ + pp = 0; + for(i=minLen;i<=maxLen;i++) + for(t=0;tpermute[pp++] = t; + /* Count cumulative symbols coded for at each bit length */ + for (i=minLen;i<=maxLen;i++) temp[i]=limit[i]=0; + for (i=0;i=nSelectors) return RETVAL_DATA_ERROR; + hufGroup=bd->groups+selectors[selector++]; + base=hufGroup->base-1; + limit=hufGroup->limit-1; + } + /* Read next huffman-coded symbol */ + i = hufGroup->minLen; + j=get_bits(bd, i); + for(;;) { + if (i > hufGroup->maxLen) return RETVAL_DATA_ERROR; + if (j <= limit[i]) break; + i++; + + j = (j << 1) | get_bits(bd,1); + } + /* Huffman decode nextSym (with bounds checking) */ + j-=base[i]; + if (j < 0 || j >= MAX_SYMBOLS) return RETVAL_DATA_ERROR; + nextSym = hufGroup->permute[j]; + /* If this is a repeated run, loop collecting data */ + if (nextSym == SYMBOL_RUNA || nextSym == SYMBOL_RUNB) { + /* If this is the start of a new run, zero out counter */ + if(!runPos) { + runPos = 1; + t = 0; + } + /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at + each bit position, add 1 or 2 instead. For example, + 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2. + You can make any bit pattern that way using 1 less symbol than + the basic or 0/1 method (except all bits 0, which would use no + symbols, but a run of length 0 doesn't mean anything in this + context). Thus space is saved. */ + if (nextSym == SYMBOL_RUNA) t += runPos; + else t += 2*runPos; + runPos <<= 1; + continue; + } + /* When we hit the first non-run symbol after a run, we now know + how many times to repeat the last literal, so append that many + copies to our buffer of decoded symbols (dbuf) now. (The last + literal used is the one at the head of the mtfSymbol array.) */ + if(runPos) { + runPos=0; + if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR; + + uc = symToByte[mtfSymbol[0]]; + byteCount[uc] += t; + while(t--) dbuf[dbufCount++]=uc; + } + /* Is this the terminating symbol? */ + if(nextSym>symTotal) break; + /* At this point, the symbol we just decoded indicates a new literal + character. Subtract one to get the position in the MTF array + at which this literal is currently to be found. (Note that the + result can't be -1 or 0, because 0 and 1 are RUNA and RUNB. + Another instance of the first symbol in the mtf array, position 0, + would have been handled as part of a run.) */ + if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR; + i = nextSym - 1; + uc = mtfSymbol[i]; + memmove(mtfSymbol+1,mtfSymbol,i); + mtfSymbol[0] = uc; + uc=symToByte[uc]; + /* We have our literal byte. Save it into dbuf. */ + byteCount[uc]++; + dbuf[dbufCount++] = (unsigned int)uc; + } + /* At this point, we've finished reading huffman-coded symbols and + compressed runs from the input stream. There are dbufCount many of + them in dbuf[]. Now undo the Burrows-Wheeler transform on dbuf. + See http://dogma.net/markn/articles/bwt/bwt.htm + */ + + /* Now we know what dbufCount is, do a better sanity check on origPtr. */ + if (origPtr<0 || origPtr>=dbufCount) return RETVAL_DATA_ERROR; + /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */ + j=0; + for(i=0;i<256;i++) { + k=j+byteCount[i]; + byteCount[i] = j; + j=k; + } + /* Figure out what order dbuf would be in if we sorted it. */ + for (i=0;idataCRC = 0xffffffffL; + /* Decode first byte by hand to initialize "previous" byte. Note that it + doesn't get output, and if the first three characters are identical + it doesn't qualify as a run (hence uc=255, which will either wrap + to 1 or get reset). */ + if(dbufCount) { + bd->writePos=dbuf[origPtr]; + bd->writeCurrent=(unsigned char)(bd->writePos&0xff); + bd->writePos>>=8; + bd->writeRun=-1; + } + bd->writeCount=dbufCount; + + return RETVAL_OK; +} + +/* Flush output buffer to disk */ +extern void flush_bunzip_outbuf(bunzip_data *bd, int out_fd) +{ + if(bd->outbufPos) { + if(write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos) + longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_OUTPUT_EOF); + bd->outbufPos=0; + } +} + + +/* Undo burrows-wheeler transform on intermediate buffer to produce output. + If !len, write up to len bytes of data to buf. Otherwise write to out_fd. + Returns len ? bytes written : RETVAL_OK. Notice all errors negative #'s. */ +extern int write_bunzip_data(bunzip_data *bd, int out_fd, char *outbuf, int len) +{ + unsigned int *dbuf=bd->dbuf; + int count,pos,current, run,copies,outbyte,previous,gotcount=0; + + for(;;) { + /* If last read was short due to end of file, return last block now */ + if(bd->writeCount<0) return bd->writeCount; + /* If we need to refill dbuf, do it. */ + if(!bd->writeCount) { + int i=read_bunzip_data(bd); + if(i) { + if(i==RETVAL_LAST_BLOCK) { + bd->writeCount=i; + return gotcount; + } else return i; + } + } + /* Loop generating output */ + count=bd->writeCount; + pos=bd->writePos; + current=bd->writeCurrent; + run=bd->writeRun; + while(count) { + /* If somebody (like busybox tar) wants a certain number of bytes of + data from memory instead of written to a file, humor them */ + if(len && bd->outbufPos>=len) goto dataus_interruptus; + count--; + /* Follow sequence vector to undo Burrows-Wheeler transform */ + previous=current; + pos=dbuf[pos]; + current=pos&0xff; + pos>>=8; + /* Whenever we see 3 consecutive copies of the same byte, + the 4th is a repeat count */ + if(run++==3) { + copies=current; + outbyte=previous; + current=-1; + } else { + copies=1; + outbyte=current; + } + /* Output bytes to buffer, flushing to file if necessary */ + while(copies--) { + if(bd->outbufPos == IOBUF_SIZE) flush_bunzip_outbuf(bd,out_fd); + bd->outbuf[bd->outbufPos++] = outbyte; + bd->dataCRC = (bd->dataCRC << 8) + ^ bd->crc32Table[(bd->dataCRC >> 24) ^ outbyte]; + } + if(current!=previous) run=0; + } + /* Decompression of this block completed successfully */ + bd->dataCRC=~(bd->dataCRC); + bd->totalCRC=((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bd->dataCRC; + /* If this block had a CRC error, force file level CRC error. */ + if(bd->dataCRC!=bd->headerCRC) { + bd->totalCRC=bd->headerCRC+1; + return RETVAL_LAST_BLOCK; + } +dataus_interruptus: + bd->writeCount=count; + if(len) { + gotcount+=bd->outbufPos; + memcpy(outbuf,bd->outbuf,len); + /* If we got enough data, checkpoint loop state and return */ + if((len-=bd->outbufPos)<1) { + bd->outbufPos-=len; + if(bd->outbufPos) + memmove(bd->outbuf,bd->outbuf+len,bd->outbufPos); + bd->writePos=pos; + bd->writeCurrent=current; + bd->writeRun=run; + return gotcount; + } + } + } +} + +/* Allocate the structure, read file header. If !len, src_fd contains + filehandle to read from. Else inbuf contains data. */ +extern int start_bunzip(bunzip_data **bdp, int src_fd, char *inbuf, int len) +{ + bunzip_data *bd; + unsigned int i,j,c; + + /* Figure out how much data to allocate */ + i=sizeof(bunzip_data); + if(!len) i+=IOBUF_SIZE; + /* Allocate bunzip_data. Most fields initialize to zero. */ + if(!(bd=*bdp=malloc(i))) return RETVAL_OUT_OF_MEMORY; + memset(bd,0,sizeof(bunzip_data)); + if(len) { + bd->inbuf=inbuf; + bd->inbufCount=len; + bd->in_fd=-1; + } else { + bd->inbuf=(char *)(bd+1); + bd->in_fd=src_fd; + } + /* Init the CRC32 table (big endian) */ + for(i=0;i<256;i++) { + c=i<<24; + for(j=8;j;j--) + c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1); + bd->crc32Table[i]=c; + } + /* Setup for I/O error handling via longjmp */ + i=setjmp(bd->jmpbuf); + if(i) return i; + /* Ensure that file starts with "BZh" */ + for(i=0;i<3;i++) if(get_bits(bd,8)!="BZh"[i]) return RETVAL_NOT_BZIP_DATA; + /* Next byte ascii '1'-'9', indicates block size in units of 100k of + uncompressed data. Allocate intermediate buffer for block. */ + i=get_bits(bd,8); + if (i<'1' || i>'9') return RETVAL_NOT_BZIP_DATA; + bd->dbufSize=100000*(i-'0'); + if(!(bd->dbuf=malloc(bd->dbufSize * sizeof(int)))) + return RETVAL_OUT_OF_MEMORY; + return RETVAL_OK; +} + +/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data, + not end of file.) */ +extern char *uncompressStream(int src_fd, int dst_fd) +{ + bunzip_data *bd; + int i; + + if(!(i=start_bunzip(&bd,src_fd,0,0))) { + i=write_bunzip_data(bd,dst_fd,0,0); + if(i==RETVAL_LAST_BLOCK && bd->headerCRC==bd->totalCRC) i=RETVAL_OK; + } + flush_bunzip_outbuf(bd,dst_fd); + if(bd->dbuf) free(bd->dbuf); + free(bd); + return bunzip_errors[-i]; +} + +/* Dumb little test thing, decompress stdin to stdout */ +/*int main(int argc, char *argv[]) +{ + char *c=uncompressStream(0,1); + fprintf(stderr,"\n%s\n", c ? c : "Completed OK"); +}*/ diff --git a/snesreader/nall/Makefile b/snesreader/nall/Makefile new file mode 100644 index 00000000..8149bf15 --- /dev/null +++ b/snesreader/nall/Makefile @@ -0,0 +1,107 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),osx) + compiler := gcc-4.2 + else + compiler := gcc + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/snesreader/nall/algorithm.hpp b/snesreader/nall/algorithm.hpp new file mode 100644 index 00000000..cdc48dcf --- /dev/null +++ b/snesreader/nall/algorithm.hpp @@ -0,0 +1,23 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } +} + +#endif diff --git a/snesreader/nall/any.hpp b/snesreader/nall/any.hpp new file mode 100644 index 00000000..b31cff3c --- /dev/null +++ b/snesreader/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/snesreader/nall/array.hpp b/snesreader/nall/array.hpp new file mode 100644 index 00000000..c1d33fd1 --- /dev/null +++ b/snesreader/nall/array.hpp @@ -0,0 +1,120 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void add(const T data) { + operator[](buffersize) = data; + } + + signed find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i; + return -1; //not found + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snesreader/nall/base64.hpp b/snesreader/nall/base64.hpp new file mode 100644 index 00000000..e41c87b7 --- /dev/null +++ b/snesreader/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/snesreader/nall/bit.hpp b/snesreader/nall/bit.hpp new file mode 100644 index 00000000..169fc144 --- /dev/null +++ b/snesreader/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/snesreader/nall/concept.hpp b/snesreader/nall/concept.hpp new file mode 100644 index 00000000..2949cd5e --- /dev/null +++ b/snesreader/nall/concept.hpp @@ -0,0 +1,15 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; +} + +#endif diff --git a/snesreader/nall/config.hpp b/snesreader/nall/config.hpp new file mode 100644 index 00000000..31ae4e00 --- /dev/null +++ b/snesreader/nall/config.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + part.qsplit(" = ", line[i]); + trim(part[0]); + trim(part[1]); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/snesreader/nall/crc32.hpp b/snesreader/nall/crc32.hpp new file mode 100644 index 00000000..ad36fbf6 --- /dev/null +++ b/snesreader/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[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 + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/snesreader/nall/detect.hpp b/snesreader/nall/detect.hpp new file mode 100644 index 00000000..b4991aaf --- /dev/null +++ b/snesreader/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/snesreader/nall/dictionary.hpp b/snesreader/nall/dictionary.hpp new file mode 100644 index 00000000..f14e2095 --- /dev/null +++ b/snesreader/nall/dictionary.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + trim(part[0]); + trim(part[1]); + + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/snesreader/nall/dl.hpp b/snesreader/nall/dl.hpp new file mode 100644 index 00000000..22acf51f --- /dev/null +++ b/snesreader/nall/dl.hpp @@ -0,0 +1,119 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 8]; + strcpy(t, name); + strcat(t, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(t)); + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/snesreader/nall/endian.hpp b/snesreader/nall/endian.hpp new file mode 100644 index 00000000..40d15633 --- /dev/null +++ b/snesreader/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/snesreader/nall/file.hpp b/snesreader/nall/file.hpp new file mode 100644 index 00000000..4c8ca8ee --- /dev/null +++ b/snesreader/nall/file.hpp @@ -0,0 +1,259 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/snesreader/nall/filemap.hpp b/snesreader/nall/filemap.hpp new file mode 100644 index 00000000..a05f0eb7 --- /dev/null +++ b/snesreader/nall/filemap.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode) { + default: return false; + case mode_read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode_write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode mode) { + int open_flags, mmap_flags; + + switch(mode) { + default: return false; + case mode_read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode_write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode_readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode_writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/snesreader/nall/foreach.hpp b/snesreader/nall/foreach.hpp new file mode 100644 index 00000000..ea975b84 --- /dev/null +++ b/snesreader/nall/foreach.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = foreach_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#include +#include +#include + +namespace nall { + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/snesreader/nall/function.hpp b/snesreader/nall/function.hpp new file mode 100644 index 00000000..3f0f704e --- /dev/null +++ b/snesreader/nall/function.hpp @@ -0,0 +1,102 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include +#include + +namespace nall { + template class function; + + template + class function { + private: + struct base1 { virtual void func1(P...) {} }; + struct base2 { virtual void func2(P...) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*callback)(const data_t&, P...); + union { + R (*callback_global)(P...); + struct { + R (derived::*callback_member)(P...); + void *object; + }; + }; + } data; + + static R callback_global(const data_t &data, P... p) { + return data.callback_global(p...); + } + + template + static R callback_member(const data_t &data, P... p) { + return (((C*)data.object)->*((R (C::*&)(P...))data.callback_member))(p...); + } + + public: + R operator()(P... p) const { return data.callback(data, p...); } + operator bool() const { return data.callback; } + void reset() { data.callback = 0; } + + function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { operator=(source); } + + //no pointer + function() { + data.callback = 0; + } + + //symbolic link pointer (nall/dl.hpp::sym, etc) + function(void *callback) { + data.callback = callback ? &callback_global : 0; + data.callback_global = (R (*)(P...))callback; + } + + //global function pointer + function(R (*callback)(P...)) { + data.callback = &callback_global; + data.callback_global = callback; + } + + //member function pointer + template + function(R (C::*callback)(P...), C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = callback; + data.object = object; + } + + //const member function pointer + template + function(R (C::*callback)(P...) const, C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = (R (C::*&)(P...))callback; + data.object = object; + } + + //lambda function pointer + template + function(T callback) { + static_assert(std::is_same::type>::value, "lambda mismatch"); + data.callback = &callback_global; + data.callback_global = (R (*)(P...))callback; + } + }; + + //bind functions to ease construction and assignment of function() with more than one argument + + template + function bind(R (C::*callback)(P...), C *object) { + return function(callback, object); + } + + template + function bind(R (C::*callback)(P...) const, C *object) { + return function(callback, object); + } +} + +#endif diff --git a/snesreader/nall/input.hpp b/snesreader/nall/input.hpp new file mode 100644 index 00000000..b3ce9ebf --- /dev/null +++ b/snesreader/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + ltrim(s, "KB"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + ltrim(s, "MS"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + ltrim(s, "JP"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/snesreader/nall/lzss.hpp b/snesreader/nall/lzss.hpp new file mode 100644 index 00000000..202bc814 --- /dev/null +++ b/snesreader/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/snesreader/nall/moduloarray.hpp b/snesreader/nall/moduloarray.hpp new file mode 100644 index 00000000..be549ae9 --- /dev/null +++ b/snesreader/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/snesreader/nall/platform.hpp b/snesreader/nall/platform.hpp new file mode 100644 index 00000000..68ed37ce --- /dev/null +++ b/snesreader/nall/platform.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface +#else + #include + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +#endif + diff --git a/snesreader/nall/priorityqueue.hpp b/snesreader/nall/priorityqueue.hpp new file mode 100644 index 00000000..7104e791 --- /dev/null +++ b/snesreader/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/snesreader/nall/property.hpp b/snesreader/nall/property.hpp new file mode 100644 index 00000000..6fd33acd --- /dev/null +++ b/snesreader/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/snesreader/nall/qt/Makefile b/snesreader/nall/qt/Makefile new file mode 100644 index 00000000..69e84960 --- /dev/null +++ b/snesreader/nall/qt/Makefile @@ -0,0 +1,55 @@ +# requires nall/Makefile + +# imports: +# $(qtlibs) -- list of Qt components to link against + +# exports the following symbols: +# $(moc) -- meta-object compiler +# $(rcc) -- resource compiler +# $(qtinc) -- includes for compiling +# $(qtlib) -- libraries for linking + +ifeq ($(moc),) +moc := moc +endif + +ifeq ($(rcc),) +rcc := rcc +endif + +ifeq ($(platform),x) + qtinc := `pkg-config --cflags $(qtlibs)` + qtlib := `pkg-config --libs $(qtlibs)` +else ifeq ($(platform),osx) + qtinc := $(foreach lib,$(qtlibs),-I/Library/Frameworks/$(lib).framework/Versions/4/Headers) + + qtlib := -L/Library/Frameworks + qtlib += $(foreach lib,$(qtlibs),-framework $(lib)) + qtlib += -framework Carbon + qtlib += -framework Cocoa + qtlib += -framework OpenGL + qtlib += -framework AppKit + qtlib += -framework ApplicationServices +else ifeq ($(platform),win) + ifeq ($(qtpath),) + # find Qt install directory from PATH environment variable + qtpath := $(foreach path,$(subst ;, ,$(PATH)),$(if $(wildcard $(path)/$(moc).exe),$(path))) + qtpath := $(strip $(qtpath)) + qtpath := $(subst \,/,$(qtpath)) + qtpath := $(patsubst %/bin,%,$(qtpath)) + endif + + qtinc := -I$(qtpath)/include + qtinc += $(foreach lib,$(qtlibs),-I$(qtpath)/include/$(lib)) + + qtlib := -L$(qtpath)/lib + qtlib += -L$(qtpath)/plugins/imageformats + + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + qtlib += -lmingw32 -lqtmain -lcomdlg32 -loleaut32 -limm32 -lwinmm + qtlib += -lwinspool -lmsimg32 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32 + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + + # optional image-file support: + # qtlib += -lqjpeg -lqmng +endif diff --git a/snesreader/nall/qt/check-action.moc.hpp b/snesreader/nall/qt/check-action.moc.hpp new file mode 100644 index 00000000..db378fe9 --- /dev/null +++ b/snesreader/nall/qt/check-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_CHECKACTION_HPP +#define NALL_QT_CHECKACTION_HPP + +namespace nall { + +class CheckAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + CheckAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool CheckAction::isChecked() const { + return checked; +} + +inline void CheckAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-check-on.png")); + else setIcon(QIcon(":/16x16/item-check-off.png")); +} + +inline void CheckAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline CheckAction::CheckAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/snesreader/nall/qt/concept.hpp b/snesreader/nall/qt/concept.hpp new file mode 100644 index 00000000..51cacef4 --- /dev/null +++ b/snesreader/nall/qt/concept.hpp @@ -0,0 +1,10 @@ +#ifndef NALL_QT_CONCEPT_HPP +#define NALL_QT_CONCEPT_HPP + +#include + +namespace nall { + template struct has_count> { enum { value = true }; }; +} + +#endif diff --git a/snesreader/nall/qt/file-dialog.moc.hpp b/snesreader/nall/qt/file-dialog.moc.hpp new file mode 100644 index 00000000..bcccfaf5 --- /dev/null +++ b/snesreader/nall/qt/file-dialog.moc.hpp @@ -0,0 +1,392 @@ +#ifndef NALL_QT_FILEDIALOG_HPP +#define NALL_QT_FILEDIALOG_HPP + +#include +#include +#include + +namespace nall { + +class FileDialog; + +class NewFolderDialog : public Window { + Q_OBJECT + +public: + void show(); + NewFolderDialog(FileDialog*); + +protected slots: + void createFolderAction(); + +protected: + FileDialog *parent; + QVBoxLayout *layout; + QLineEdit *folderNameEdit; + QHBoxLayout *controlLayout; + QPushButton *okButton; + QPushButton *cancelButton; +}; + +class FileView : public QListView { + Q_OBJECT + +protected: + void keyPressEvent(QKeyEvent*); + +signals: + void changed(const QModelIndex&); + void browseUp(); + +protected slots: + void currentChanged(const QModelIndex&, const QModelIndex&); +}; + +class FileDialog : public Window { + Q_OBJECT + +public: + void showLoad(); + void showSave(); + void showFolder(); + + void setPath(string path); + void setNameFilters(const string &filters); + FileDialog(); + +signals: + void changed(const string&); + void activated(const string&); + void accepted(const string&); + void rejected(); + +protected slots: + void fileViewChange(const QModelIndex&); + void fileViewActivate(const QModelIndex&); + void pathBoxChanged(); + void filterBoxChanged(); + void createNewFolder(); + void browseUp(); + void acceptAction(); + void rejectAction(); + +protected: + NewFolderDialog *newFolderDialog; + QVBoxLayout *layout; + QHBoxLayout *navigationLayout; + QComboBox *pathBox; + QPushButton *newFolderButton; + QPushButton *upFolderButton; + QHBoxLayout *browseLayout; + QFileSystemModel *fileSystemModel; + FileView *fileView; + QGroupBox *previewFrame; + QLineEdit *fileNameEdit; + QHBoxLayout *controlLayout; + QComboBox *filterBox; + QPushButton *optionsButton; + QPushButton *acceptButton; + QPushButton *rejectButton; + bool lock; + void createFolderAction(const string &name); + void closeEvent(QCloseEvent*); + + friend class NewFolderDialog; +}; + +inline void NewFolderDialog::show() { + folderNameEdit->setText(""); + Window::show(); + folderNameEdit->setFocus(); +} + +inline void NewFolderDialog::createFolderAction() { + string name = folderNameEdit->text().toUtf8().constData(); + if(name == "") { + folderNameEdit->setFocus(); + } else { + parent->createFolderAction(name); + close(); + } +} + +inline NewFolderDialog::NewFolderDialog(FileDialog *fileDialog) : parent(fileDialog) { + setMinimumWidth(240); + setWindowTitle("Create New Folder"); + + layout = new QVBoxLayout; + layout->setAlignment(Qt::AlignTop); + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + folderNameEdit = new QLineEdit; + layout->addWidget(folderNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + okButton = new QPushButton("Ok"); + controlLayout->addWidget(okButton); + + cancelButton = new QPushButton("Cancel"); + controlLayout->addWidget(cancelButton); + + connect(folderNameEdit, SIGNAL(returnPressed()), this, SLOT(createFolderAction())); + connect(okButton, SIGNAL(released()), this, SLOT(createFolderAction())); + connect(cancelButton, SIGNAL(released()), this, SLOT(close())); +} + +inline void FileView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QAbstractItemView::currentChanged(current, previous); + emit changed(current); +} + +inline void FileView::keyPressEvent(QKeyEvent *event) { + //enhance consistency: force OS X to act like Windows and Linux; enter = activate item + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + emit activated(currentIndex()); + return; + } + + //simulate popular file manager behavior; backspace = go up one directory + if(event->key() == Qt::Key_Backspace) { + emit browseUp(); + return; + } + + //fallback: unrecognized keypresses get handled by the widget itself + QListView::keyPressEvent(event); +} + +inline void FileDialog::showLoad() { + acceptButton->setText("Load"); + fileNameEdit->hide(); + filterBox->show(); + show(); +} + +inline void FileDialog::showSave() { + acceptButton->setText("Save"); + fileNameEdit->show(); + filterBox->show(); + show(); +} + +inline void FileDialog::showFolder() { + acceptButton->setText("Choose"); + fileNameEdit->hide(); + filterBox->hide(); + setNameFilters("Folders ()"); + show(); +} + +inline void FileDialog::fileViewChange(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(path == fileSystemModel->rootPath().toUtf8().constData()) path = ""; + fileNameEdit->setText(notdir(path)); + emit changed(path); +} + +inline void FileDialog::fileViewActivate(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(fileSystemModel->isDir(index)) { + emit activated(path); + setPath(path); + } else { + emit activated(path); + close(); + } +} + +inline void FileDialog::pathBoxChanged() { + if(lock) return; + setPath(pathBox->currentText().toUtf8().constData()); +} + +inline void FileDialog::filterBoxChanged() { + if(lock) return; + string filters = filterBox->currentText().toUtf8().constData(); + if(filters.length() == 0) { + fileSystemModel->setNameFilters(QStringList() << "*"); + } else { + filters = substr(filters, strpos(filters, "(")); + ltrim(filters, "("); + rtrim(filters, ")"); + lstring part; + part.split(" ", filters); + QStringList list; + for(unsigned i = 0; i < part.size(); i++) list << part[i]; + fileSystemModel->setNameFilters(list); + } +} + +inline void FileDialog::createNewFolder() { + newFolderDialog->show(); +} + +inline void FileDialog::browseUp() { + if(pathBox->count() > 1) pathBox->setCurrentIndex(1); +} + +inline void FileDialog::setPath(string path) { + lock = true; + newFolderDialog->close(); + + if(QDir(path).exists()) { + newFolderButton->setEnabled(true); + } else { + newFolderButton->setEnabled(false); + path = ""; + } + + fileSystemModel->setRootPath(path); + fileView->setRootIndex(fileSystemModel->index(path)); + fileView->setCurrentIndex(fileView->rootIndex()); + fileView->setFocus(); + + pathBox->clear(); + if(path.length() > 0) { + QDir directory(path); + while(true) { + pathBox->addItem(directory.absolutePath()); + if(directory.isRoot()) break; + directory.cdUp(); + } + } + pathBox->addItem(""); + fileNameEdit->setText(""); + + lock = false; +} + +inline void FileDialog::setNameFilters(const string &filters) { + lock = true; + + lstring list; + list.split("\n", filters); + + filterBox->clear(); + for(unsigned i = 0; i < list.size(); i++) { + filterBox->addItem(list[i]); + } + + lock = false; + filterBoxChanged(); +} + +inline void FileDialog::acceptAction() { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(fileNameEdit->text().toUtf8().constData()); + rtrim(path, "/"); + if(QDir(path).exists()) { + emit accepted(path); + setPath(path); + } else { + emit accepted(path); + close(); + } +} + +inline void FileDialog::rejectAction() { + emit rejected(); + close(); +} + +inline void FileDialog::createFolderAction(const string &name) { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(name); + mkdir(path, 0755); +} + +inline void FileDialog::closeEvent(QCloseEvent *event) { + newFolderDialog->close(); + Window::closeEvent(event); +} + +inline FileDialog::FileDialog() { + newFolderDialog = new NewFolderDialog(this); + resize(640, 360); + + layout = new QVBoxLayout; + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + navigationLayout = new QHBoxLayout; + layout->addLayout(navigationLayout); + + pathBox = new QComboBox; + pathBox->setEditable(true); + pathBox->setMinimumContentsLength(16); + pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + pathBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + navigationLayout->addWidget(pathBox); + + newFolderButton = new QPushButton; + newFolderButton->setIconSize(QSize(16, 16)); + newFolderButton->setIcon(QIcon(":/16x16/folder-new.png")); + navigationLayout->addWidget(newFolderButton); + + upFolderButton = new QPushButton; + upFolderButton->setIconSize(QSize(16, 16)); + upFolderButton->setIcon(QIcon(":/16x16/go-up.png")); + navigationLayout->addWidget(upFolderButton); + + browseLayout = new QHBoxLayout; + layout->addLayout(browseLayout); + + fileSystemModel = new QFileSystemModel; + fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + fileSystemModel->setNameFilterDisables(false); + + fileView = new FileView; + fileView->setMinimumWidth(320); + fileView->setModel(fileSystemModel); + fileView->setIconSize(QSize(16, 16)); + browseLayout->addWidget(fileView); + + previewFrame = new QGroupBox; + previewFrame->hide(); + browseLayout->addWidget(previewFrame); + + fileNameEdit = new QLineEdit; + layout->addWidget(fileNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + filterBox = new QComboBox; + filterBox->setMinimumContentsLength(16); + filterBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + filterBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + controlLayout->addWidget(filterBox); + + optionsButton = new QPushButton("Options"); + optionsButton->hide(); + controlLayout->addWidget(optionsButton); + + acceptButton = new QPushButton("Ok"); + controlLayout->addWidget(acceptButton); + + rejectButton = new QPushButton("Cancel"); + controlLayout->addWidget(rejectButton); + + lock = false; + connect(pathBox, SIGNAL(currentIndexChanged(int)), this, SLOT(pathBoxChanged())); + connect(newFolderButton, SIGNAL(released()), this, SLOT(createNewFolder())); + connect(upFolderButton, SIGNAL(released()), this, SLOT(browseUp())); + connect(fileView, SIGNAL(changed(const QModelIndex&)), this, SLOT(fileViewChange(const QModelIndex&))); + connect(fileView, SIGNAL(activated(const QModelIndex&)), this, SLOT(fileViewActivate(const QModelIndex&))); + connect(fileView, SIGNAL(browseUp()), this, SLOT(browseUp())); + connect(fileNameEdit, SIGNAL(returnPressed()), this, SLOT(acceptAction())); + connect(filterBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterBoxChanged())); + connect(acceptButton, SIGNAL(released()), this, SLOT(acceptAction())); + connect(rejectButton, SIGNAL(released()), this, SLOT(rejectAction())); +} + +} + +#endif diff --git a/snesreader/nall/qt/hex-editor.moc.hpp b/snesreader/nall/qt/hex-editor.moc.hpp new file mode 100644 index 00000000..d59f4be9 --- /dev/null +++ b/snesreader/nall/qt/hex-editor.moc.hpp @@ -0,0 +1,173 @@ +#ifndef NALL_QT_HEXEDITOR_HPP +#define NALL_QT_HEXEDITOR_HPP + +#include +#include +#include + +namespace nall { + +class HexEditor : public QTextEdit { + Q_OBJECT + +public: + function reader; + function writer; + + void setColumns(unsigned columns); + void setRows(unsigned rows); + void setOffset(unsigned offset); + void setSize(unsigned size); + unsigned lineWidth() const; + void refresh(); + + HexEditor(); + +protected slots: + void scrolled(); + +protected: + QHBoxLayout *layout; + QScrollBar *scrollBar; + unsigned editorColumns; + unsigned editorRows; + unsigned editorOffset; + unsigned editorSize; + bool lock; + + void keyPressEvent(QKeyEvent*); +}; + +inline void HexEditor::keyPressEvent(QKeyEvent *event) { + QTextCursor cursor = textCursor(); + unsigned x = cursor.position() % lineWidth(); + unsigned y = cursor.position() / lineWidth(); + + int hexCode = -1; + switch(event->key()) { + case Qt::Key_0: hexCode = 0; break; + case Qt::Key_1: hexCode = 1; break; + case Qt::Key_2: hexCode = 2; break; + case Qt::Key_3: hexCode = 3; break; + case Qt::Key_4: hexCode = 4; break; + case Qt::Key_5: hexCode = 5; break; + case Qt::Key_6: hexCode = 6; break; + case Qt::Key_7: hexCode = 7; break; + case Qt::Key_8: hexCode = 8; break; + case Qt::Key_9: hexCode = 9; break; + case Qt::Key_A: hexCode = 10; break; + case Qt::Key_B: hexCode = 11; break; + case Qt::Key_C: hexCode = 12; break; + case Qt::Key_D: hexCode = 13; break; + case Qt::Key_E: hexCode = 14; break; + case Qt::Key_F: hexCode = 15; break; + } + + if(cursor.hasSelection() == false && hexCode != -1) { + bool cursorOffsetValid = (x >= 11 && ((x - 11) % 3) != 2); + if(cursorOffsetValid) { + bool nibble = (x - 11) % 3; //0 = top nibble, 1 = bottom nibble + unsigned cursorOffset = y * editorColumns + ((x - 11) / 3); + unsigned effectiveOffset = editorOffset + cursorOffset; + if(effectiveOffset >= editorSize) effectiveOffset %= editorSize; + + uint8_t data = reader ? reader(effectiveOffset) : 0x00; + data &= (nibble == 0 ? 0x0f : 0xf0); + data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0)); + if(writer) writer(effectiveOffset, data); + refresh(); + + cursor.setPosition(y * lineWidth() + x + 1); //advance cursor + setTextCursor(cursor); + } + } else { + //allow navigation keys to move cursor, but block text input + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + QTextEdit::keyPressEvent(event); + setTextInteractionFlags(Qt::TextEditorInteraction); + } +} + +inline void HexEditor::setColumns(unsigned columns) { + editorColumns = columns; +} + +inline void HexEditor::setRows(unsigned rows) { + editorRows = rows; + scrollBar->setPageStep(editorRows); +} + +inline void HexEditor::setOffset(unsigned offset) { + lock = true; + editorOffset = offset; + scrollBar->setSliderPosition(editorOffset / editorColumns); + lock = false; +} + +inline void HexEditor::setSize(unsigned size) { + editorSize = size; + bool indivisible = (editorSize % editorColumns) != 0; //add one for incomplete row + scrollBar->setRange(0, editorSize / editorColumns + indivisible - editorRows); +} + +inline unsigned HexEditor::lineWidth() const { + return 11 + 3 * editorColumns; +} + +inline void HexEditor::refresh() { + string output; + char temp[256]; + unsigned offset = editorOffset; + + for(unsigned y = 0; y < editorRows; y++) { + if(offset >= editorSize) break; + sprintf(temp, "%.4x:%.4x", (offset >> 16) & 0xffff, (offset >> 0) & 0xffff); + output << "" << temp << "  "; + + for(unsigned x = 0; x < editorColumns; x++) { + if(offset >= editorSize) break; + sprintf(temp, "%.2x", reader ? reader(offset) : 0x00); + offset++; + output << "" << temp << ""; + if(x != (editorColumns - 1)) output << " "; + } + + if(y != (editorRows - 1)) output << "
"; + } + + setHtml(output); +} + +inline void HexEditor::scrolled() { + if(lock) return; + unsigned offset = scrollBar->sliderPosition(); + editorOffset = offset * editorColumns; + refresh(); +} + +inline HexEditor::HexEditor() { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignRight); + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + scrollBar = new QScrollBar(Qt::Vertical); + scrollBar->setSingleStep(1); + layout->addWidget(scrollBar); + + lock = false; + connect(scrollBar, SIGNAL(actionTriggered(int)), this, SLOT(scrolled())); + + setColumns(16); + setRows(16); + setSize(0); + setOffset(0); +} + +} + +#endif diff --git a/snesreader/nall/qt/radio-action.moc.hpp b/snesreader/nall/qt/radio-action.moc.hpp new file mode 100644 index 00000000..a2bbca48 --- /dev/null +++ b/snesreader/nall/qt/radio-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_RADIOACTION_HPP +#define NALL_QT_RADIOACTION_HPP + +namespace nall { + +class RadioAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + RadioAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool RadioAction::isChecked() const { + return checked; +} + +inline void RadioAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-radio-on.png")); + else setIcon(QIcon(":/16x16/item-radio-off.png")); +} + +inline void RadioAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline RadioAction::RadioAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/snesreader/nall/qt/window.moc.hpp b/snesreader/nall/qt/window.moc.hpp new file mode 100644 index 00000000..0d3bf390 --- /dev/null +++ b/snesreader/nall/qt/window.moc.hpp @@ -0,0 +1,105 @@ +#ifndef NALL_QT_WINDOW_HPP +#define NALL_QT_WINDOW_HPP + +#include +#include + +namespace nall { + +class Window : public QWidget { + Q_OBJECT + +public: + void setGeometryString(string *geometryString); + void setCloseOnEscape(bool); + void show(); + void hide(); + void shrink(); + + Window(); + +protected slots: + +protected: + string *geometryString; + bool closeOnEscape; + void keyReleaseEvent(QKeyEvent *event); + void closeEvent(QCloseEvent *event); +}; + +inline void Window::setGeometryString(string *geometryString_) { + geometryString = geometryString_; + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } +} + +inline void Window::setCloseOnEscape(bool value) { + closeOnEscape = value; +} + +inline void Window::show() { + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } + QWidget::show(); + QApplication::processEvents(); + activateWindow(); + raise(); +} + +inline void Window::hide() { + if(geometryString && isVisible() == true) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::hide(); +} + +inline void Window::shrink() { + if(isFullScreen()) return; + + for(unsigned i = 0; i < 2; i++) { + resize(0, 0); + usleep(2000); + QApplication::processEvents(); + } +} + +inline void Window::keyReleaseEvent(QKeyEvent *event) { + if(closeOnEscape && (event->key() == Qt::Key_Escape)) close(); + QWidget::keyReleaseEvent(event); +} + +inline void Window::closeEvent(QCloseEvent *event) { + if(geometryString) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::closeEvent(event); +} + +inline Window::Window() { + geometryString = 0; + closeOnEscape = true; +} + +} + +#endif diff --git a/snesreader/nall/serial.hpp b/snesreader/nall/serial.hpp new file mode 100644 index 00000000..6f5cf6d6 --- /dev/null +++ b/snesreader/nall/serial.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/snesreader/nall/serializer.hpp b/snesreader/nall/serializer.hpp new file mode 100644 index 00000000..9f816dfe --- /dev/null +++ b/snesreader/nall/serializer.hpp @@ -0,0 +1,145 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/snesreader/nall/sha256.hpp b/snesreader/nall/sha256.hpp new file mode 100644 index 00000000..7f41f04e --- /dev/null +++ b/snesreader/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/snesreader/nall/sort.hpp b/snesreader/nall/sort.hpp new file mode 100644 index 00000000..23c317a5 --- /dev/null +++ b/snesreader/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/snesreader/nall/static.hpp b/snesreader/nall/static.hpp new file mode 100644 index 00000000..4acb9fd0 --- /dev/null +++ b/snesreader/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/snesreader/nall/stdint.hpp b/snesreader/nall/stdint.hpp new file mode 100644 index 00000000..d8b6c788 --- /dev/null +++ b/snesreader/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/snesreader/nall/string.hpp b/snesreader/nall/string.hpp new file mode 100644 index 00000000..65a4a4b8 --- /dev/null +++ b/snesreader/nall/string.hpp @@ -0,0 +1,26 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/snesreader/nall/string/base.hpp b/snesreader/nall/string/base.hpp new file mode 100644 index 00000000..179a7dd4 --- /dev/null +++ b/snesreader/nall/string/base.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + inline unsigned length() const; + + inline string& assign(const char*); + inline string& append(const char*); + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string(); + inline string(const char*); + inline string(const string&); + inline string(string&&); + inline string& operator=(const string&); + inline string& operator=(string&&); + inline ~string(); + + inline bool readfile(const char*); + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + protected: + char *data; + unsigned size; + + #if defined(QT_CORE_LIB) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline int find(const char*); + inline void split (const char*, const char*, unsigned = 0); + inline void qsplit(const char*, const char*, unsigned = 0); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *dest, const char *src); + inline int strpos (const char *str, const char *key); + inline int qstrpos(const char *str, const char *key); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //match.hpp + inline bool match(const char *pattern, const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //trim.hpp + inline char* ltrim(char *str, const char *key = " "); + inline char* rtrim(char *str, const char *key = " "); + inline char* trim (char *str, const char *key = " "); + inline char* ltrim_once(char *str, const char *key = " "); + inline char* rtrim_once(char *str, const char *key = " "); + inline char* trim_once (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + inline string& strlower(string &str); + inline string& strupper(string &str); + inline string& strtr(string &dest, const char *before, const char *after); + inline string& ltrim(string &str, const char *key = " "); + inline string& rtrim(string &str, const char *key = " "); + inline string& trim (string &str, const char *key = " "); + inline string& ltrim_once(string &str, const char *key = " "); + inline string& rtrim_once(string &str, const char *key = " "); + inline string& trim_once (string &str, const char *key = " "); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline string sprint(Args... args); + template inline void print(Args... args); +}; + +#endif diff --git a/snesreader/nall/string/cast.hpp b/snesreader/nall/string/cast.hpp new file mode 100644 index 00000000..7b48eda0 --- /dev/null +++ b/snesreader/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QT_CORE_LIB) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/snesreader/nall/string/compare.hpp b/snesreader/nall/string/compare.hpp new file mode 100644 index 00000000..e1173de4 --- /dev/null +++ b/snesreader/nall/string/compare.hpp @@ -0,0 +1,104 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +int strpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + return i; + } + } + return -1; +} + +int qstrpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) { + return i; + } else { + i++; + } + } + return -1; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/snesreader/nall/string/convert.hpp b/snesreader/nall/string/convert.hpp new file mode 100644 index 00000000..3ff134be --- /dev/null +++ b/snesreader/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/snesreader/nall/string/core.hpp b/snesreader/nall/string/core.hpp new file mode 100644 index 00000000..d13bfc38 --- /dev/null +++ b/snesreader/nall/string/core.hpp @@ -0,0 +1,133 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string::string() { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +string::string(const char *value) { + size = strlen(value); + data = strdup(value); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +string::~string() { + free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +int lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return i; + } + return -1; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/snesreader/nall/string/filename.hpp b/snesreader/nall/string/filename.hpp new file mode 100644 index 00000000..f3750760 --- /dev/null +++ b/snesreader/nall/string/filename.hpp @@ -0,0 +1,61 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/", "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/snesreader/nall/string/match.hpp b/snesreader/nall/string/match.hpp new file mode 100644 index 00000000..d8cf702d --- /dev/null +++ b/snesreader/nall/string/match.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_STRING_MATCH_HPP +#define NALL_STRING_MATCH_HPP + +namespace nall { + +bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++, *s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +} + +#endif diff --git a/snesreader/nall/string/math.hpp b/snesreader/nall/string/math.hpp new file mode 100644 index 00000000..ea8b99c8 --- /dev/null +++ b/snesreader/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/snesreader/nall/string/replace.hpp b/snesreader/nall/string/replace.hpp new file mode 100644 index 00000000..db405a9b --- /dev/null +++ b/snesreader/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/snesreader/nall/string/split.hpp b/snesreader/nall/string/split.hpp new file mode 100644 index 00000000..bb77dfcd --- /dev/null +++ b/snesreader/nall/string/split.hpp @@ -0,0 +1,56 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/snesreader/nall/string/strl.hpp b/snesreader/nall/string/strl.hpp new file mode 100644 index 00000000..84c841fa --- /dev/null +++ b/snesreader/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/snesreader/nall/string/trim.hpp b/snesreader/nall/string/trim.hpp new file mode 100644 index 00000000..b13ab9ba --- /dev/null +++ b/snesreader/nall/string/trim.hpp @@ -0,0 +1,54 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +} + +#endif diff --git a/snesreader/nall/string/utility.hpp b/snesreader/nall/string/utility.hpp new file mode 100644 index 00000000..2da2762b --- /dev/null +++ b/snesreader/nall/string/utility.hpp @@ -0,0 +1,169 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* very simplistic wrappers to return string& instead of char* type */ + +string& strlower(string &str) { strlower(str()); return str; } +string& strupper(string &str) { strupper(str()); return str; } +string& strtr(string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; } +string& ltrim(string &str, const char *key) { ltrim(str(), key); return str; } +string& rtrim(string &str, const char *key) { rtrim(str(), key); return str; } +string& trim (string &str, const char *key) { trim (str(), key); return str; } +string& ltrim_once(string &str, const char *key) { ltrim_once(str(), key); return str; } +string& rtrim_once(string &str, const char *key) { rtrim_once(str(), key); return str; } +string& trim_once (string &str, const char *key) { trim_once (str(), key); return str; } + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/snesreader/nall/string/variadic.hpp b/snesreader/nall/string/variadic.hpp new file mode 100644 index 00000000..13c477a8 --- /dev/null +++ b/snesreader/nall/string/variadic.hpp @@ -0,0 +1,27 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +static void isprint(string &output) { +} + +template +static void isprint(string &output, T value, Args... args) { + output << to_string(value); + isprint(output, args...); +} + +template inline string sprint(Args... args) { + string output; + isprint(output, args...); + return output; +} + +template inline void print(Args... args) { + printf("%s", (const char*)sprint(args...)); +} + +} + +#endif diff --git a/snesreader/nall/string/xml.hpp b/snesreader/nall/string/xml.hpp new file mode 100644 index 00000000..d423f87f --- /dev/null +++ b/snesreader/nall/string/xml.hpp @@ -0,0 +1,257 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + source += pos + 3; + continue; + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + string cdata = substr(source, 9, pos - 9); + data << cdata; + offset += strlen(cdata); + + source += offset + 3; + continue; + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ") >= 0) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + rtrim(data); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) trim_once(attr.content, "\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) trim_once(attr.content, "'"); + else throw "..."; + attribute.add(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + signed offset = strpos(data, "-->"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + if(strbegin(data, "![CDATA[")) { + signed offset = strpos(data, "]]>"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + signed offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + string tag = substr(data, 0, offset); + data += offset + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + rtrim_once(tag, "?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + rtrim_once(tag, "/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + tag = substr(data, 0, offset); + data += offset + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ") >= 0) tag.replace(" ", " "); + rtrim(tag); + + if(name != tag) throw "..."; + return true; + } + } else { + element.add(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.add(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/snesreader/nall/ups.hpp b/snesreader/nall/ups.hpp new file mode 100644 index 00000000..f255ecb3 --- /dev/null +++ b/snesreader/nall/ups.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new uint8_t[y_size](); + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/snesreader/nall/utf8.hpp b/snesreader/nall/utf8.hpp new file mode 100644 index 00000000..c66c341a --- /dev/null +++ b/snesreader/nall/utf8.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#undef NOMINMAX +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + private: + char *buffer; + }; +} + +#endif //if defined(_WIN32) + +#endif diff --git a/snesreader/nall/utility.hpp b/snesreader/nall/utility.hpp new file mode 100644 index 00000000..2a63f515 --- /dev/null +++ b/snesreader/nall/utility.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template inline T* allocate(size_t size, const T &value) { + T *array = new T[size]; + for(size_t i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/snesreader/nall/varint.hpp b/snesreader/nall/varint.hpp new file mode 100644 index 00000000..cc3bb17c --- /dev/null +++ b/snesreader/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert::value> uint_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert::value> int_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/snesreader/nall/vector.hpp b/snesreader/nall/vector.hpp new file mode 100644 index 00000000..3d69d4d5 --- /dev/null +++ b/snesreader/nall/vector.hpp @@ -0,0 +1,240 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/snesreader/snesreader.cpp b/snesreader/snesreader.cpp new file mode 100644 index 00000000..055efd48 --- /dev/null +++ b/snesreader/snesreader.cpp @@ -0,0 +1,235 @@ +#include "snesreader.hpp" + +#if defined(_WIN32) + #define dllexport __declspec(dllexport) +#else + #define dllexport +#endif + +#include "fex/fex.h" +#include "libjma/jma.h" +extern "C" char* uncompressStream(int, int); //micro-bunzip + +#define QT_CORE_LIB +#include + +#include +#include +using namespace nall; + +#include "xml.cpp" + +dllexport const char* snesreader_supported() { + return "*.zip *.z *.7z *.rar *.gz *.bz2 *.jma"; +} + +void snesreader_apply_ips(const char *filename, uint8_t *&data, unsigned &size) { + file fp; + if(fp.open(filename, file::mode_read) == false) return; + + unsigned psize = fp.size(); + uint8_t *pdata = new uint8_t[psize]; + fp.read(pdata, psize); + fp.close(); + + if(psize < 8 || pdata[0] != 'P' || pdata[1] != 'A' || pdata[2] != 'T' || pdata[3] != 'C' || pdata[4] != 'H') { delete[] pdata; return; } + + unsigned outsize = 0; + uint8_t *outdata = new uint8_t[16 * 1024 * 1024]; + memset(outdata, 0, 16 * 1024 * 1024); + memcpy(outdata, data, size); + + unsigned offset = 5; + while(offset < psize - 3) { + unsigned addr; + addr = pdata[offset++] << 16; + addr |= pdata[offset++] << 8; + addr |= pdata[offset++] << 0; + + unsigned size; + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + if(size == 0) { + //RLE + size = pdata[offset++] << 8; + size |= pdata[offset++] << 0; + + for(unsigned n = addr; n < addr + size;) { + outdata[n++] = pdata[offset]; + if(n > outsize) outsize = n; + } + offset++; + } else { + //uncompressed + for(unsigned n = addr; n < addr + size;) { + outdata[n++] = pdata[offset++]; + if(n > outsize) outsize = n; + } + } + } + + delete[] pdata; + delete[] data; + data = outdata; + size = max(size, outsize); +} + +bool snesreader_load_normal(const char *filename, uint8_t *&data, unsigned &size) { + file fp; + if(fp.open(filename, file::mode_read) == false) return false; + size = fp.size(); + data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + return true; +} + +#include "filechooser.cpp" + +bool snesreader_load_fex(string &filename, uint8_t *&data, unsigned &size) { + fex_t *fex; + fex_open(&fex, filename); + if(fex_done(fex)) { fex_close(fex); return false; } + + if(!fileChooser) fileChooser = new FileChooser; + fileChooser->list.reset(); + + while(fex_done(fex) == false) { + fex_stat(fex); + const char *name = fex_name(fex); + //only add valid ROM extensions to list (ignore text files, save RAM files, etc) + if(striend(name, ".sfc") || striend(name, ".smc") + || striend(name, ".swc") || striend(name, ".fig") + || striend(name, ".bs") || striend(name, ".st") + || striend(name, ".gb") || striend(name, ".sgb") || striend(name, ".gbc") + || striend(filename, ".gz") //GZip files only contain a single file + ) { + fileChooser->list[fileChooser->list.size()] = name; + } + fex_next(fex); + } + + string name = fileChooser->exec(); + if(name == "") { fex_close(fex); return false; } + + fex_rewind(fex); + while(fex_done(fex) == false) { + fex_stat(fex); + if(name == fex_name(fex)) { + size = fex_size(fex); + data = new uint8_t[size]; + fex_read(fex, data, size); + fex_close(fex); + + if(fileChooser->list.size() > 1) { + strtr(name, "\\", "/"); + strtr(filename, "\\", "/"); + + //retain only path from filename, "/foo/bar.7z" -> "/foo/" + for(signed i = filename.length() - 1; i >= 0; i--) { + if(filename[i] == '/') { + filename[i + 1] = 0; + break; + } + } + + //append only filename from archive, "foo/bar.sfc" -> "bar.sfc" + lstring part; + part.split("/", name); + filename = string() << filename << part[part.size() - 1]; + } + + return true; + } + fex_next(fex); + } + + fex_close(fex); + return false; +} + +bool snesreader_load_bz2(const char *filename, uint8_t *&data, unsigned &size) { + //TODO: need a way to get the size of a bzip2 file, so we can pre-allocate + //a buffer to decompress into memory. for now, use a temporary file. + + string name = "/tmp/.bz2_temporary_decompression_object"; + FILE *wr; + wr = fopen_utf8(name, "wb"); + if(!wr) { + //try the local directory + name = ".bz2_temporary_decompression_object"; + wr = fopen_utf8(name, "wb"); + //can't get write access, so give up + if(!wr) return false; + } + + FILE *fp = fopen_utf8(filename, "rb"); + uncompressStream(fileno(fp), fileno(wr)); + fclose(fp); + fclose(wr); + + bool success = snesreader_load_normal(name, data, size); + unlink(name); + return success; +} + +bool snesreader_load_jma(const char *filename, uint8_t *&data, unsigned &size) { + try { + JMA::jma_open JMAFile(filename); + std::string name; + + std::vector file_info = JMAFile.get_files_info(); + for(std::vector::iterator i = file_info.begin(); i != file_info.end(); i++) { + name = i->name; + size = i->size; + break; + } + + data = new uint8_t[size]; + JMAFile.extract_file(name, data); + return true; + } catch(JMA::jma_errors) { + return false; + } +} + +dllexport bool snesreader_load(string &filename, uint8_t *&data, unsigned &size) { + if(file::exists(filename) == false) return false; + + bool success = false; + if(striend(filename, ".zip") + || striend(filename, ".z") + || striend(filename, ".7z") + || striend(filename, ".rar") + || striend(filename, ".gz")) { + success = snesreader_load_fex(filename, data, size); + } else if(striend(filename, ".bz2")) { + success = snesreader_load_bz2(filename, data, size); + } else if(striend(filename, ".jma")) { + success = snesreader_load_jma(filename, data, size); + } else { + success = snesreader_load_normal(filename, data, size); + } + + if(success == false) return false; + + //apply IPS patch, if it exists + string patchname = filename; + for(int i = patchname.length() - 1; i >= 0; i--) { + if(patchname[i] == '.') { patchname[i] = 0; break; } + } + patchname << ".ips"; + if(file::exists(patchname)) snesreader_apply_ips(patchname, data, size); + + //remove copier header, if it exists + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + + return true; +} + +dllexport bool snesreader_map(string &xmldata, const uint8_t *data, unsigned size) { + xmldata = ""; + xml.generate(xmldata, data, size); + return true; +} diff --git a/snesreader/snesreader.hpp b/snesreader/snesreader.hpp new file mode 100644 index 00000000..ad5d63ce --- /dev/null +++ b/snesreader/snesreader.hpp @@ -0,0 +1,8 @@ +#include +namespace nall { class string; } + +extern "C" { + const char* snesreader_supported(); + bool snesreader_load(nall::string &filename, uint8_t *&data, unsigned &size); + bool snesreader_map(nall::string &xml, const uint8_t *data, unsigned size); +} diff --git a/snesreader/sync.sh b/snesreader/sync.sh new file mode 100644 index 00000000..4bbaf34f --- /dev/null +++ b/snesreader/sync.sh @@ -0,0 +1,2 @@ +rm -r nall +cp -r ../nall ./nall diff --git a/snesreader/unrar/archive.cpp b/snesreader/unrar/archive.cpp new file mode 100644 index 00000000..338a0eb7 --- /dev/null +++ b/snesreader/unrar/archive.cpp @@ -0,0 +1,97 @@ +#include +#include "rar.hpp" + +#include "unrar.h" + +Archive::Archive() : Raw( this ) +{ + OldFormat=false; + Solid=false; + + CurBlockPos=0; + NextBlockPos=0; + + memset(&NewMhd,0,sizeof(NewMhd)); + NewMhd.HeadType=MAIN_HEAD; + NewMhd.HeadSize=SIZEOF_NEWMHD; + HeaderCRC=0; +} + +bool Archive::IsSignature(byte *D) +{ + bool Valid=false; + if (D[0]==0x52) +#ifndef SFX_MODULE + if (D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + { + OldFormat=true; + Valid=true; + } + else +#endif + if (D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07 && D[6]==0x00) + { + OldFormat=false; + Valid=true; + } + return(Valid); +} + + +unrar_err_t Archive::IsArchive() +{ + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD) + return unrar_err_not_arc; + + if (IsSignature(MarkHead.Mark)) + { + if (OldFormat) + Seek(0,SEEK_SET); + } + else + { + if (SFXSize==0) + return unrar_err_not_arc; + } + + unrar_err_t error = + ReadHeader(); + // (no need to seek to next) + if ( error != unrar_ok ) + return error; + +#ifndef SFX_MODULE + if (OldFormat) + { + NewMhd.Flags=OldMhd.Flags & 0x3f; + NewMhd.HeadSize=OldMhd.HeadSize; + } + else +#endif + { + if (HeaderCRC!=NewMhd.HeadCRC) + { + return unrar_err_corrupt; + } + } + bool + Volume=(NewMhd.Flags & MHD_VOLUME); + Solid=(NewMhd.Flags & MHD_SOLID)!=0; + bool + Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0; + + // (removed decryption and volume handling) + + if ( Encrypted ) + return unrar_err_encrypted; + + if ( Volume ) + return unrar_err_segmented; + + return unrar_ok; +} + +void Archive::SeekToNext() +{ + Seek(NextBlockPos,SEEK_SET); +} diff --git a/snesreader/unrar/archive.hpp b/snesreader/unrar/archive.hpp new file mode 100644 index 00000000..0106e6fd --- /dev/null +++ b/snesreader/unrar/archive.hpp @@ -0,0 +1,45 @@ +#ifndef _RAR_ARCHIVE_ +#define _RAR_ARCHIVE_ + +typedef ComprDataIO File; +#include "rawread.hpp" + +class Archive:public File +{ +private: + bool IsSignature(byte *D); + void ConvertUnknownHeader(); + int ReadOldHeader(); + + RawRead Raw; + + MarkHeader MarkHead; + OldMainHeader OldMhd; + + int CurHeaderType; + +public: + Archive(); + unrar_err_t IsArchive(); + unrar_err_t ReadHeader(); + void SeekToNext(); + bool IsArcDir(); + bool IsArcLabel(); + int GetHeaderType() {return(CurHeaderType);}; + + BaseBlock ShortBlock; + MainHeader NewMhd; + FileHeader NewLhd; + SubBlockHeader SubBlockHead; + FileHeader SubHead; + ProtectHeader ProtectHead; + + Int64 CurBlockPos; + Int64 NextBlockPos; + + bool Solid; + enum { SFXSize = 0 }; // self-extracting not supported + ushort HeaderCRC; +}; + +#endif diff --git a/snesreader/unrar/arcread.cpp b/snesreader/unrar/arcread.cpp new file mode 100644 index 00000000..3a9f711c --- /dev/null +++ b/snesreader/unrar/arcread.cpp @@ -0,0 +1,314 @@ +#include "rar.hpp" + +#include "unrar.h" +#include "unicode.hpp" +#include "encname.hpp" + +// arcread.cpp +unrar_err_t Archive::ReadHeader() +{ + CurBlockPos=Tell(); + +#ifndef SFX_MODULE + if (OldFormat) + { + ReadOldHeader(); + + if ( Raw.Size() == 0 ) + return unrar_err_arc_eof; // right at end of file + + if ( Raw.PaddedSize() > 0 ) // added check + return unrar_err_corrupt; // missing data + + return unrar_ok; + } +#endif + + Raw.Reset(); + + // (removed decryption) + + Raw.Read(SIZEOF_SHORTBLOCKHEAD); + if (Raw.Size()==0) + { + return unrar_err_arc_eof; // right at end of file + } + + Raw.Get(ShortBlock.HeadCRC); + byte HeadType; + Raw.Get(HeadType); + ShortBlock.HeadType=(HEADER_TYPE)HeadType; + Raw.Get(ShortBlock.Flags); + Raw.Get(ShortBlock.HeadSize); + if (ShortBlock.HeadSize 0 ) // fewer than requested bytes read above? + return unrar_err_corrupt; // missing data + + NextBlockPos=CurBlockPos+ShortBlock.HeadSize; + + switch(ShortBlock.HeadType) + { + case MAIN_HEAD: + *(BaseBlock *)&NewMhd=ShortBlock; + Raw.Get(NewMhd.HighPosAV); + Raw.Get(NewMhd.PosAV); + check( Raw.ReadPos == Raw.DataSize ); // we should have read all fields + break; + case FILE_HEAD: + case NEWSUB_HEAD: + { + FileHeader *hd=ShortBlock.HeadType==FILE_HEAD ? &NewLhd:&SubHead; + *(BaseBlock *)hd=ShortBlock; + Raw.Get(hd->PackSize); + Raw.Get(hd->UnpSize); + Raw.Get(hd->HostOS); + Raw.Get(hd->FileCRC); + Raw.Get(hd->FileTime); + Raw.Get(hd->UnpVer); + Raw.Get(hd->Method); + Raw.Get(hd->NameSize); + Raw.Get(hd->FileAttr); + if (hd->Flags & LHD_LARGE) + { + Raw.Get(hd->HighPackSize); + Raw.Get(hd->HighUnpSize); + } + else + { + hd->HighPackSize=hd->HighUnpSize=0; + if (hd->UnpSize==0xffffffff) + { + // TODO: what the heck is this for anyway? + hd->UnpSize=0; + hd->HighUnpSize=0x7fffffff; + } + } + hd->FullPackSize=int32to64(hd->HighPackSize,hd->PackSize); + hd->FullUnpSize=int32to64(hd->HighUnpSize,hd->UnpSize); + + if ( int32to64( 1, 0 ) == 0 && (hd->HighPackSize || hd->HighUnpSize) ) + return unrar_err_huge; + + char (&FileName) [sizeof hd->FileName] = hd->FileName; // eliminated local buffer + int NameSize=Min(hd->NameSize,sizeof(FileName)-1); + Raw.Get((byte *)FileName,NameSize); + FileName[NameSize]=0; + + if (hd->HeadType==NEWSUB_HEAD) + { + // have to adjust this, even through we're ignoring this block + NextBlockPos+=hd->FullPackSize; + break; + } + else + if (hd->HeadType==FILE_HEAD) + { + if (hd->Flags & LHD_UNICODE) + { + EncodeFileName NameCoder; + int Length=strlen(FileName); + if (Length==hd->NameSize) + { + UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1); + WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1); + ExtToInt(hd->FileName,hd->FileName); + } + else + { + Length++; + NameCoder.Decode(FileName,(byte *)FileName+Length, + hd->NameSize-Length,hd->FileNameW, + sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])); + } + if (*hd->FileNameW==0) + hd->Flags &= ~LHD_UNICODE; + } + else + *hd->FileNameW=0; + + ConvertUnknownHeader(); + } + if (hd->Flags & LHD_SALT) + Raw.Get(hd->Salt,SALT_SIZE); + hd->mtime.SetDos(hd->FileTime); + if (hd->Flags & LHD_EXTTIME) + { + ushort Flags; + Raw.Get(Flags); + // Ignore additional time information + for (int I=0;I<4;I++) + { + uint rmode=Flags>>(3-I)*4; + if ((rmode & 8)==0) + continue; + if (I!=0) + { + uint DosTime; + Raw.Get(DosTime); + } + + // skip time info + int count=rmode&3; + for (int J=0;JFullPackSize; + bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0; + HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff; + if (hd->HeadCRC!=HeaderCRC) + return unrar_err_corrupt; + check( CRCProcessedOnly == false ); // I need to test on archives where this doesn't hold + check( Raw.ReadPos == Raw.DataSize ); // we should have read all fields + } + break; +#ifndef SFX_MODULE + // Handle these block types just so we can adjust NextBlockPos properly + case PROTECT_HEAD: + Raw.Get(ProtectHead.DataSize); + NextBlockPos+=ProtectHead.DataSize; + break; + case SUB_HEAD: + Raw.Get(SubBlockHead.DataSize); + NextBlockPos+=SubBlockHead.DataSize; + break; +#endif + default: + if (ShortBlock.Flags & LONG_BLOCK) + { + uint DataSize; + Raw.Get(DataSize); + NextBlockPos+=DataSize; + } + break; + } + HeaderCRC=~Raw.GetCRC(false)&0xffff; + CurHeaderType=ShortBlock.HeadType; + // (removed decryption) + + if (NextBlockPosCurBlockPos ? Raw.Size():0); +} +#endif + +// (removed name case and attribute conversion) + +bool Archive::IsArcDir() +{ + return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY); +} + + +bool Archive::IsArcLabel() +{ + return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8)); +} + +// TODO: use '\\' on Windows? +char const CPATHDIVIDER = '/'; +#define charnext(s) ((s)+1) + +void Archive::ConvertUnknownHeader() +{ + if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10)) + NewLhd.Flags|=LHD_DIRECTORY; + if (NewLhd.HostOS>=HOST_MAX) + { + if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) + NewLhd.FileAttr=0x10; + else + NewLhd.FileAttr=0x20; + } + { + for (char *s=NewLhd.FileName;*s!=0;s=charnext(s)) + { + if (*s=='/' || *s=='\\') + *s=CPATHDIVIDER; + } + } + // (removed Apple Unicode handling) + for (wchar *s=NewLhd.FileNameW;*s!=0;s++) + { + if (*s=='/' || *s=='\\') + *s=CPATHDIVIDER; + } +} diff --git a/snesreader/unrar/array.hpp b/snesreader/unrar/array.hpp new file mode 100644 index 00000000..1f2d4e8c --- /dev/null +++ b/snesreader/unrar/array.hpp @@ -0,0 +1,135 @@ +#ifndef _RAR_ARRAY_ +#define _RAR_ARRAY_ + +template class Array +{ +private: + T *Buffer; + int BufSize; + int AllocSize; +public: + Rar_Error_Handler& ErrHandler; + Array(Rar_Error_Handler*); + Array(int Size,Rar_Error_Handler*); + ~Array(); + inline void CleanData(); + inline T& operator [](int Item); + inline int Size(); + void Add(int Items); + void Alloc(int Items); + void Reset(); + void operator = (Array &Src); + void Push(T Item); + T* Addr() {return(Buffer);} +}; + +template void Array::CleanData() +{ + Buffer=NULL; + BufSize=0; + AllocSize=0; +} + + +template Array::Array(Rar_Error_Handler* eh) : ErrHandler( *eh ) +{ + CleanData(); +} + + +template Array::Array(int Size, Rar_Error_Handler* eh) : ErrHandler( *eh ) +{ + Buffer=(T *)rarmalloc(sizeof(T)*Size); + if (Buffer==NULL && Size!=0) + ErrHandler.MemoryError(); + + AllocSize=BufSize=Size; +} + + +template Array::~Array() +{ + if (Buffer!=NULL) + rarfree(Buffer); +} + + +template inline T& Array::operator [](int Item) +{ + return(Buffer[Item]); +} + + +template inline int Array::Size() +{ + return(BufSize); +} + + +template void Array::Add(int Items) +{ + int BufSize = this->BufSize; // don't change actual vars until alloc succeeds + T* Buffer = this->Buffer; + + BufSize+=Items; + if (BufSize>AllocSize) + { + int Suggested=AllocSize+AllocSize/4+32; + int NewSize=Max(BufSize,Suggested); + + Buffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T)); + if (Buffer==NULL) + ErrHandler.MemoryError(); + AllocSize=NewSize; + } + + this->Buffer = Buffer; + this->BufSize = BufSize; +} + + +template void Array::Alloc(int Items) +{ + if (Items>AllocSize) + Add(Items-BufSize); + else + BufSize=Items; +} + + +template void Array::Reset() +{ + // Keep memory allocated if it's small + // Eliminates constant reallocation when scanning archive + if ( AllocSize < 1024/sizeof(T) ) + { + BufSize = 0; + return; + } + + if (Buffer!=NULL) + { + rarfree(Buffer); + Buffer=NULL; + } + BufSize=0; + AllocSize=0; +} + + +template void Array::operator =(Array &Src) +{ + Reset(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template void Array::Push(T Item) +{ + Add(1); + (*this)[Size()-1]=Item; +} + +#endif diff --git a/snesreader/unrar/changes.txt b/snesreader/unrar/changes.txt new file mode 100644 index 00000000..35345fd5 --- /dev/null +++ b/snesreader/unrar/changes.txt @@ -0,0 +1,141 @@ +unrar_core source code changes +------------------------------ +Unrar_core is based on UnRAR (unrarsrc-3.8.5.tar.gz) by Alexander L. +Roshal. The original sources have been HEAVILY modified, trimmed down, +and purged of all OS-specific calls for file access and other +unnecessary operations. Support for encryption, recovery records, and +segmentation has been REMOVED. See license.txt for licensing. In +particular, this code cannot be used to re-create the RAR compression +algorithm, which is proprietary. + +If you obtained this code as a part of my File_Extractor library and +want to use it on its own, get my unrar_core library, which includes +examples and documentation. + +The source is as close as possible to the original, to make it simple to +update when a new version of UnRAR comes out. In many places the +original names and object nesting are kept, even though it's a bit +harder to follow. See rar.hpp for the main "glue". + +Website: http://www.slack.net/~ant/ +E-mail : Shay Green + + +Contents +-------- +* Diff-friendly changes +* Removal of features +* Error reporting changes +* Minor tweaks +* Unrar findings + + +Diff-friendly changes +--------------------- +To make my source code changes more easily visible with a line-based +file diff, I've tried to make changes by inserting or deleting lines, +rather than modifying them. So if the original declared a static array + + static int array [4] = { 1, 2, 3, 4 }; + +and I want to make it const, I add the const on a line before + + const // added + static int array [4] = { 1, 2, 3, 4 }; + +rather than on the same line + + static const int array [4] = { 1, 2, 3, 4 }; + +This way a diff will simply show an added line, making it clear what was +added. If I simply inserted const on the same line, it wouldn't be as +clear what all I had changed. + +I've also made use of several macros rather than changing the source +text. For example, since a class name like Unpack might easily conflict, +I've renamed it to Rar_Unpack by using #define Unpack Rar_Unpack rather +than changing the source text. These macros are only defined when +compiling the library sources; the user-visible unrar.h is very clean. + + +Removal of features +------------------- +This library is meant for simple access to common archives without +having to extract them first. Encryption, segmentation, huge files, and +self-extracting archives aren't common for things that need to be +accessed in this manner, so I've removed support for them. Also, +encryption adds complexity to the code that must be maintained. +Segmentation would require a way to specify the other segments. + + +Error reporting changes +----------------------- +The original used C++ exceptions to report errors. I've eliminated use +of these through a combination of error codes and longjmp. This allows +use of the library from C or some other language which doesn't easily +support exceptions. + +I tried to make as few changes as possible in the conversion. Due to the +number of places file reads occur, propagating an error code via return +statements would have required too many code changes. Instead, I perform +the read, save the error code, and return 0 bytes read in case of an +error. I also ensure that the calling code interprets this zero in an +acceptable way. I then check this saved error code after the operation +completes, and have it take priority over the error the RAR code +returned. I do a similar thing for write errors. + + +Minor tweaks +------------ +- Eliminated as many GCC warnings as reasonably possible. + +- Non-class array allocations now use malloc(), allowing the code to be +linked without the standard C++ library (particularly, operator new). +Class object allocations use a class-specific allocator that just calls +malloc(), also avoiding calls to operator new. + +- Made all unchanging static data const. Several pieces of static data +in the original code weren't marked const where they could be. + +- Initialization of some static tables was done on an as-needed basis, +creating a problem when extracting from archives in multiple threads. +This initialization can now be done by the user before any archives are +opened. + +- Tweaked CopyString, the major bottleneck during compression. I inlined +it, cached some variables in locals in case the compiler couldn't easily +see that the memory accesses don't modify them, and made them use +memcpy() where possible. This improved performance by at least 20% on +x86. Perhaps it won't work as well on files with lots of smaller string +matches. + +- Some .cpp files are #included by others. I've added guards to these so +that you can simply compile all .cpp files and not get any redefinition +errors. + +- The current solid extraction position is kept track of, allowing the +user to randomly extract files without regard to proper extraction +order. The library optimizes solid extraction and only restarts it if +the user is extracting a file earlier in the archive than the last +solid-extracted one. + +- Most of the time a solid file's data is already contiguously in the +internal Unpack::Window, which unrar_extract_mem() takes advantage of. +This avoids extra allocation in many cases. + +- Allocation of Unpack is delayed until the first extraction, rather +than being allocated immediately on opening the archive. This allows +scanning with minimal memory usage. + + +Unrar findings +-------------- +- Apparently the LHD_SOLID flag indicates that file depends on previous +files, rather than that later files depend on the current file's +contents. Thus this flag can't be used to intelligently decide which +files need to be internally extracted when skipping them, making it +necessary to internally extract every file before the one to be +extracted, if the archive is solid. + +-- +Shay Green diff --git a/snesreader/unrar/coder.cpp b/snesreader/unrar/coder.cpp new file mode 100644 index 00000000..c3f3aac6 --- /dev/null +++ b/snesreader/unrar/coder.cpp @@ -0,0 +1,49 @@ +// #included by unpack.cpp +#ifdef RAR_COMMON_HPP + +inline unsigned int RangeCoder::GetChar() +{ + return(UnpackRead->GetChar()); +} + + +void RangeCoder::InitDecoder(Unpack *UnpackRead) +{ + RangeCoder::UnpackRead=UnpackRead; + + low=code=0; + range=uint(-1); + for (int i=0;i < 4;i++) + code=(code << 8) | GetChar(); +} + + +#define ARI_DEC_NORMALIZE(code,low,range,read) \ +{ \ + while ((low^(low+range))GetChar(); \ + range <<= 8; \ + low <<= 8; \ + } \ +} + + +inline int RangeCoder::GetCurrentCount() +{ + return (code-low)/(range /= SubRange.scale); +} + + +inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT) +{ + return (code-low)/(range >>= SHIFT); +} + + +inline void RangeCoder::Decode() +{ + low += range*SubRange.LowCount; + range *= SubRange.HighCount-SubRange.LowCount; +} +#endif diff --git a/snesreader/unrar/coder.hpp b/snesreader/unrar/coder.hpp new file mode 100644 index 00000000..8384cdc6 --- /dev/null +++ b/snesreader/unrar/coder.hpp @@ -0,0 +1,24 @@ +/**************************************************************************** + * Contents: 'Carryless rangecoder' by Dmitry Subbotin * + ****************************************************************************/ + +const uint TOP=1 << 24, BOT=1 << 15; + +class RangeCoder +{ +public: + void InitDecoder(Unpack *UnpackRead); + inline int GetCurrentCount(); + inline uint GetCurrentShiftCount(uint SHIFT); + inline void Decode(); + inline void PutChar(unsigned int c); + inline unsigned int GetChar(); + + uint low, code, range; + struct SUBRANGE + { + uint LowCount, HighCount, scale; + } SubRange; + + Unpack *UnpackRead; +}; diff --git a/snesreader/unrar/compress.hpp b/snesreader/unrar/compress.hpp new file mode 100644 index 00000000..3181e45d --- /dev/null +++ b/snesreader/unrar/compress.hpp @@ -0,0 +1,36 @@ +#ifndef _RAR_COMPRESS_ +#define _RAR_COMPRESS_ + +class ComprDataIO; +class PackingFileTable; + +#define CODEBUFSIZE 0x4000 +#define MAXWINSIZE 0x400000 +#define MAXWINMASK (MAXWINSIZE-1) + +#define LOW_DIST_REP_COUNT 16 + +#define NC 299 /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define DC 60 +#define LDC 17 +#define RC 28 +#define HUFF_TABLE_SIZE (NC+DC+RC+LDC) +#define BC 20 + +#define NC20 298 /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define DC20 48 +#define RC20 28 +#define BC20 19 +#define MC20 257 + +enum {CODE_HUFFMAN,CODE_LZ,CODE_LZ2,CODE_REPEATLZ,CODE_CACHELZ, + CODE_STARTFILE,CODE_ENDFILE,CODE_VM,CODE_VMDATA}; + + +enum FilterType { + FILTER_NONE, FILTER_PPM /*dummy*/, FILTER_E8, FILTER_E8E9, + FILTER_UPCASETOLOW, FILTER_AUDIO, FILTER_RGB, FILTER_DELTA, + FILTER_ITANIUM, FILTER_E8E9V2 +}; + +#endif diff --git a/snesreader/unrar/crc.cpp b/snesreader/unrar/crc.cpp new file mode 100644 index 00000000..bc23b5a9 --- /dev/null +++ b/snesreader/unrar/crc.cpp @@ -0,0 +1,69 @@ +#include "rar.hpp" + +uint CRCTab[256]; + +void InitCRC() +{ + for (int I=0;I<256;I++) + { + uint C=I; + for (int J=0;J<8;J++) + C=(C & 1) ? (C>>1)^0xEDB88320L : (C>>1); + CRCTab[I]=C; + } +} + + +uint CRC(uint StartCRC,const void *Addr,size_t Size) +{ + // Always initialized ahead of time, and this func call makes it a non-leaf func. + if (false) + if (CRCTab[1]==0) + InitCRC(); + byte *Data=(byte *)Addr; +#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) + +#ifdef _MSC_VER + // avoid a warning about 'Data' pointer truncation in 64 bit mode + #pragma warning( disable : 4311 ) +#endif + + while (Size>0 && ((long)Data & 7)) + { + StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); + Size--; + Data++; + } + while (Size>=8) + { + StartCRC^=*(uint32 *)Data; + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC^=*(uint32 *)(Data+4); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); + Data+=8; + Size-=8; + } +#endif + for (size_t I=0;I>8); + return(StartCRC); +} + +#ifndef SFX_MODULE +ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + for (size_t I=0;I>15))&0xffff; + } + return(StartCRC); +} +#endif diff --git a/snesreader/unrar/encname.cpp b/snesreader/unrar/encname.cpp new file mode 100644 index 00000000..6f57cd91 --- /dev/null +++ b/snesreader/unrar/encname.cpp @@ -0,0 +1,57 @@ +#include "rar.hpp" + +EncodeFileName::EncodeFileName() +{ + Flags=0; + FlagBits=0; + FlagsPos=0; + DestSize=0; +} + + + + +void EncodeFileName::Decode(char *Name,byte *EncName,int EncSize,wchar *NameW, + int MaxDecSize) +{ + int EncPos=0,DecPos=0; + byte HighByte=EncName[EncPos++]; + while (EncPos>6) + { + case 0: + NameW[DecPos++]=EncName[EncPos++]; + break; + case 1: + NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); + break; + case 2: + NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); + EncPos+=2; + break; + case 3: + { + int Length=EncName[EncPos++]; + if (Length & 0x80) + { + byte Correction=EncName[EncPos++]; + for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos +#include "rar.hpp" + +#include "unrar.h" + +#define DataIO Arc + +unrar_err_t CmdExtract::ExtractCurrentFile( bool SkipSolid, bool check_compatibility_only ) +{ + check( Arc.GetHeaderType() == FILE_HEAD ); + + if ( Arc.NewLhd.Flags & (LHD_SPLIT_AFTER | LHD_SPLIT_BEFORE) ) + return unrar_err_segmented; + + if ( Arc.NewLhd.Flags & LHD_PASSWORD ) + return unrar_err_encrypted; + + if ( !check_compatibility_only ) + { + check( Arc.NextBlockPos-Arc.NewLhd.FullPackSize == Arc.Tell() ); + Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + } + + // (removed lots of command-line handling) + +#ifdef SFX_MODULE + if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) && + Arc.NewLhd.Method!=0x30) +#else + if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER) +#endif + { + if (Arc.NewLhd.UnpVer>UNP_VER) + return unrar_err_new_algo; + return unrar_err_old_algo; + } + + if ( check_compatibility_only ) + return unrar_ok; + + // (removed lots of command-line/encryption/volume handling) + + update_first_file_pos(); + FileCount++; + DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff; + // (removed decryption) + DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize); + // (removed command-line handling) + DataIO.SetSkipUnpCRC(SkipSolid); + + if (Arc.NewLhd.Method==0x30) + UnstoreFile(Arc.NewLhd.FullUnpSize); + else + { + // Defer creation of Unpack until first extraction + if ( !Unp ) + { + Unp = new Unpack( &Arc ); + if ( !Unp ) + return unrar_err_memory; + + Unp->Init( NULL ); + } + + Unp->SetDestSize(Arc.NewLhd.FullUnpSize); +#ifndef SFX_MODULE + if (Arc.NewLhd.UnpVer<=15) + Unp->DoUnpack(15,FileCount>1 && Arc.Solid); + else +#endif + Unp->DoUnpack(Arc.NewLhd.UnpVer,Arc.NewLhd.Flags & LHD_SOLID); + } + + // (no need to seek to next file) + + if (!SkipSolid) + { + if (Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC) || + !Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC^0xffffffff)) + { + // CRC is correct + } + else + { + return unrar_err_corrupt; + } + } + + // (removed broken file handling) + // (removed command-line handling) + + return unrar_ok; +} + + +void CmdExtract::UnstoreFile(Int64 DestUnpSize) +{ + Buffer.Alloc(Min(DestUnpSize,0x10000)); + while (1) + { + unsigned int Code=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (Code==0 || (int)Code==-1) + break; + Code=Code=0) + DestUnpSize-=Code; + } + Buffer.Reset(); +} diff --git a/snesreader/unrar/getbits.cpp b/snesreader/unrar/getbits.cpp new file mode 100644 index 00000000..559bdd03 --- /dev/null +++ b/snesreader/unrar/getbits.cpp @@ -0,0 +1,34 @@ +#include "rar.hpp" + +BitInput::BitInput() +{ + InBuf = (byte*) rarmalloc( MAX_SIZE ); + + // Otherwise getbits() reads uninitialized memory + // TODO: instead of clearing entire block, just clear last two + // bytes after reading from file + if ( InBuf ) + memset( InBuf, 0, MAX_SIZE ); +} + +BitInput::~BitInput() +{ + rarfree( InBuf ); +} + +void BitInput::handle_mem_error( Rar_Error_Handler& ErrHandler ) +{ + if ( !InBuf ) + ErrHandler.MemoryError(); +} + +void BitInput::faddbits(int Bits) +{ + addbits(Bits); +} + + +unsigned int BitInput::fgetbits() +{ + return(getbits()); +} diff --git a/snesreader/unrar/getbits.hpp b/snesreader/unrar/getbits.hpp new file mode 100644 index 00000000..5a4cb4a3 --- /dev/null +++ b/snesreader/unrar/getbits.hpp @@ -0,0 +1,40 @@ +#ifndef _RAR_GETBITS_ +#define _RAR_GETBITS_ + +class BitInput + : public Rar_Allocator +{ +public: + enum BufferSize {MAX_SIZE=0x8000}; +protected: + int InAddr,InBit; +public: + BitInput(); + ~BitInput(); + void handle_mem_error( Rar_Error_Handler& ); + + byte *InBuf; + + void InitBitInput() + { + InAddr=InBit=0; + } + void addbits(int Bits) + { + Bits+=InBit; + InAddr+=Bits>>3; + InBit=Bits&7; + } + unsigned int getbits() + { + unsigned int BitField=(uint)InBuf[InAddr] << 16; + BitField|=(uint)InBuf[InAddr+1] << 8; + BitField|=(uint)InBuf[InAddr+2]; + BitField >>= (8-InBit); + return(BitField & 0xffff); + } + void faddbits(int Bits); + unsigned int fgetbits(); + bool Overflow(int IncPtr) {return(InAddr+IncPtr>=MAX_SIZE);} +}; +#endif diff --git a/snesreader/unrar/headers.hpp b/snesreader/unrar/headers.hpp new file mode 100644 index 00000000..abe1c66a --- /dev/null +++ b/snesreader/unrar/headers.hpp @@ -0,0 +1,145 @@ +#ifndef _RAR_HEADERS_ +#define _RAR_HEADERS_ + +#define SIZEOF_MARKHEAD 7 +#define SIZEOF_OLDMHD 7 +#define SIZEOF_NEWMHD 13 +#define SIZEOF_OLDLHD 21 +#define SIZEOF_NEWLHD 32 +#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_SUBBLOCKHEAD 14 +#define SIZEOF_COMMHEAD 13 + +#define UNP_VER 36 + +#define MHD_VOLUME 0x0001 +#define MHD_COMMENT 0x0002 +#define MHD_SOLID 0x0008 +#define MHD_PASSWORD 0x0080 + +#define LHD_SPLIT_BEFORE 0x0001 +#define LHD_SPLIT_AFTER 0x0002 +#define LHD_PASSWORD 0x0004 +#define LHD_COMMENT 0x0008 +#define LHD_SOLID 0x0010 + +#define LHD_WINDOWMASK 0x00e0 +#define LHD_DIRECTORY 0x00e0 + +#define LHD_LARGE 0x0100 +#define LHD_UNICODE 0x0200 +#define LHD_SALT 0x0400 +#define LHD_EXTTIME 0x1000 + +#define LONG_BLOCK 0x8000 + +enum HEADER_TYPE { + MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76, + SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a, + ENDARC_HEAD=0x7b +}; + +enum HOST_SYSTEM { + HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, + HOST_BEOS=5,HOST_MAX +}; + +struct OldMainHeader +{ + byte Mark[4]; + ushort HeadSize; + byte Flags; +}; + + +struct OldFileHeader +{ + uint PackSize; + uint UnpSize; + ushort FileCRC; + ushort HeadSize; + uint FileTime; + byte FileAttr; + byte Flags; + byte UnpVer; + byte NameSize; + byte Method; +}; + + +struct MarkHeader +{ + byte Mark[7]; +}; + + +struct BaseBlock +{ + ushort HeadCRC; + HEADER_TYPE HeadType;//byte + ushort Flags; + ushort HeadSize; +}; + +struct BlockHeader:BaseBlock +{ + union { + uint DataSize; + uint PackSize; + }; +}; + + +struct MainHeader:BaseBlock +{ + ushort HighPosAV; + uint PosAV; +}; + +#define SALT_SIZE 8 + +struct FileHeader:BlockHeader +{ + uint UnpSize; + byte HostOS; + uint FileCRC; + uint FileTime; + byte UnpVer; + byte Method; + ushort NameSize; + union { + uint FileAttr; + uint SubFlags; + }; +/* optional */ + uint HighPackSize; + uint HighUnpSize; +/* names */ + char FileName[NM*4]; // *4 to avoid using lots of stack in arcread + wchar FileNameW[NM]; +/* optional */ + byte Salt[SALT_SIZE]; + + RarTime mtime; +/* dummy */ + Int64 FullPackSize; + Int64 FullUnpSize; +}; + +// SubBlockHeader and its successors were used in RAR 2.x format. +// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks. +struct SubBlockHeader:BlockHeader +{ + ushort SubType; + byte Level; +}; + +struct ProtectHeader:BlockHeader +{ + byte Version; + ushort RecSectors; + uint TotalBlocks; + byte Mark[8]; +}; + +#endif diff --git a/snesreader/unrar/license.txt b/snesreader/unrar/license.txt new file mode 100644 index 00000000..2aa475c7 --- /dev/null +++ b/snesreader/unrar/license.txt @@ -0,0 +1,40 @@ + ****** ***** ****** UnRAR - free utility for RAR archives + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ****** ******* ****** License for use and distribution of + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ** ** ** ** ** ** FREE portable version + ~~~~~~~~~~~~~~~~~~~~~ + + The source code of UnRAR utility is freeware. This means: + + 1. All copyrights to RAR and the utility UnRAR are exclusively + owned by the author - Alexander Roshal. + + 2. The UnRAR sources may be used in any software to handle RAR + archives without limitations free of charge, but cannot be used + to re-create the RAR compression algorithm, which is proprietary. + Distribution of modified UnRAR sources in separate form or as a + part of other software is permitted, provided that it is clearly + stated in the documentation and source comments that the code may + not be used to develop a RAR (WinRAR) compatible archiver. + + 3. The UnRAR utility may be freely distributed. It is allowed + to distribute UnRAR inside of other software packages. + + 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". + NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT + YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, + DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING + OR MISUSING THIS SOFTWARE. + + 5. Installing and using the UnRAR utility signifies acceptance of + these terms and conditions of the license. + + 6. If you don't agree with terms of the license you must remove + UnRAR files from your storage devices and cease to use the + utility. + + Thank you for your interest in RAR and UnRAR. + + + Alexander L. Roshal \ No newline at end of file diff --git a/snesreader/unrar/model.cpp b/snesreader/unrar/model.cpp new file mode 100644 index 00000000..68e1a4f1 --- /dev/null +++ b/snesreader/unrar/model.cpp @@ -0,0 +1,612 @@ +// #included by unpack.cpp +#ifdef RAR_COMMON_HPP +/**************************************************************************** + * This file is part of PPMd project * + * Written and distributed to public domain by Dmitry Shkarin 1997, * + * 1999-2000 * + * Contents: model description and encoding/decoding routines * + ****************************************************************************/ + +inline PPM_CONTEXT* PPM_CONTEXT::createChild(ModelPPM *Model,STATE* pStats, + STATE& FirstState) +{ + PPM_CONTEXT* pc = (PPM_CONTEXT*) Model->SubAlloc.AllocContext(); + if ( pc ) + { + pc->NumStats=1; + pc->OneState=FirstState; + pc->Suffix=this; + pStats->Successor=pc; + } + return pc; +} + + +ModelPPM::ModelPPM() +{ + MinContext=NULL; + MaxContext=NULL; + MedContext=NULL; +} + + +void ModelPPM::RestartModelRare() +{ + int i, k, m; + memset(CharMask,0,sizeof(CharMask)); + SubAlloc.InitSubAllocator(); + InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1; + MinContext = MaxContext = (PPM_CONTEXT*) SubAlloc.AllocContext(); + MinContext->Suffix=NULL; + OrderFall=MaxOrder; + MinContext->U.SummFreq=(MinContext->NumStats=256)+1; + FoundState=MinContext->U.Stats=(STATE*)SubAlloc.AllocUnits(256/2); + for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++) + { + MinContext->U.Stats[i].Symbol=i; + MinContext->U.Stats[i].Freq=1; + MinContext->U.Stats[i].Successor=NULL; + } + + static const ushort InitBinEsc[]={ + 0x3CDD,0x1F3F,0x59BF,0x48F3,0x64A1,0x5ABC,0x6632,0x6051 + }; + + for (i=0;i < 128;i++) + for (k=0;k < 8;k++) + for (m=0;m < 64;m += 8) + BinSumm[i][k+m]=BIN_SCALE-InitBinEsc[k]/(i+2); + for (i=0;i < 25;i++) + for (k=0;k < 16;k++) + SEE2Cont[i][k].init(5*i+10); +} + + +void ModelPPM::StartModelRare(int MaxOrder) +{ + int i, k, m ,Step; + EscCount=1; +/* + if (MaxOrder < 2) + { + memset(CharMask,0,sizeof(CharMask)); + OrderFall=ModelPPM::MaxOrder; + MinContext=MaxContext; + while (MinContext->Suffix != NULL) + { + MinContext=MinContext->Suffix; + OrderFall--; + } + FoundState=MinContext->U.Stats; + MinContext=MaxContext; + } + else +*/ + { + ModelPPM::MaxOrder=MaxOrder; + RestartModelRare(); + NS2BSIndx[0]=2*0; + NS2BSIndx[1]=2*1; + memset(NS2BSIndx+2,2*2,9); + memset(NS2BSIndx+11,2*3,256-11); + for (i=0;i < 3;i++) + NS2Indx[i]=i; + for (m=i, k=Step=1;i < 256;i++) + { + NS2Indx[i]=m; + if ( !--k ) + { + k = ++Step; + m++; + } + } + memset(HB2Flag,0,0x40); + memset(HB2Flag+0x40,0x08,0x100-0x40); + DummySEE2Cont.Shift=PERIOD_BITS; + } +} + + +void PPM_CONTEXT::rescale(ModelPPM *Model) +{ + int OldNS=NumStats, i=NumStats-1, Adder, EscFreq; + STATE* p1, * p; + for (p=Model->FoundState;p != U.Stats;p--) + _PPMD_SWAP(p[0],p[-1]); + U.Stats->Freq += 4; + U.SummFreq += 4; + EscFreq=U.SummFreq-p->Freq; + Adder=(Model->OrderFall != 0); + U.SummFreq = (p->Freq=(p->Freq+Adder) >> 1); + do + { + EscFreq -= (++p)->Freq; + U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1); + if (p[0].Freq > p[-1].Freq) + { + STATE tmp=*(p1=p); + do + { + p1[0]=p1[-1]; + } while (--p1 != U.Stats && tmp.Freq > p1[-1].Freq); + *p1=tmp; + } + } while ( --i ); + if (p->Freq == 0) + { + do + { + i++; + } while ((--p)->Freq == 0); + EscFreq += i; + if ((NumStats -= i) == 1) + { + STATE tmp=*U.Stats; + do + { + tmp.Freq-=(tmp.Freq >> 1); + EscFreq>>=1; + } while (EscFreq > 1); + Model->SubAlloc.FreeUnits(U.Stats,(OldNS+1) >> 1); + *(Model->FoundState=&OneState)=tmp; return; + } + } + U.SummFreq += (EscFreq -= (EscFreq >> 1)); + int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1; + if (n0 != n1) + U.Stats = (STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); + Model->FoundState=U.Stats; +} + + +inline PPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,STATE* p1) +{ + // (removed conditional static) + STATE UpState; + PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; + STATE * p, * ps[MAX_O], ** pps=ps; + if ( !Skip ) + { + *pps++ = FoundState; + if ( !pc->Suffix ) + goto NO_LOOP; + } + if ( p1 ) + { + p=p1; + pc=pc->Suffix; + goto LOOP_ENTRY; + } + do + { + pc=pc->Suffix; + if (pc->NumStats != 1) + { + if ((p=pc->U.Stats)->Symbol != FoundState->Symbol) + do + { + p++; + } while (p->Symbol != FoundState->Symbol); + } + else + p=&(pc->OneState); +LOOP_ENTRY: + if (p->Successor != UpBranch) + { + pc=p->Successor; + break; + } + *pps++ = p; + } while ( pc->Suffix ); +NO_LOOP: + if (pps == ps) + return pc; + UpState.Symbol=*(byte*) UpBranch; + UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1); + if (pc->NumStats != 1) + { + if ((byte*) pc <= SubAlloc.pText) + return(NULL); + if ((p=pc->U.Stats)->Symbol != UpState.Symbol) + do + { + p++; + } while (p->Symbol != UpState.Symbol); + uint cf=p->Freq-1; + uint s0=pc->U.SummFreq-pc->NumStats-cf; + UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0))); + } + else + UpState.Freq=pc->OneState.Freq; + do + { + pc = pc->createChild(this,*--pps,UpState); + if ( !pc ) + return NULL; + } while (pps != ps); + return pc; +} + + +inline void ModelPPM::UpdateModel() +{ + STATE fs = *FoundState, *p = NULL; + PPM_CONTEXT *pc, *Successor; + uint ns1, ns, cf, sf, s0; + if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL) + { + if (pc->NumStats != 1) + { + if ((p=pc->U.Stats)->Symbol != fs.Symbol) + { + do + { + p++; + } while (p->Symbol != fs.Symbol); + if (p[0].Freq >= p[-1].Freq) + { + _PPMD_SWAP(p[0],p[-1]); + p--; + } + } + if (p->Freq < MAX_FREQ-9) + { + p->Freq += 2; + pc->U.SummFreq += 2; + } + } + else + { + p=&(pc->OneState); + p->Freq += (p->Freq < 32); + } + } + if ( !OrderFall ) + { + MinContext=MaxContext=FoundState->Successor=CreateSuccessors(true,p); + if ( !MinContext ) + goto RESTART_MODEL; + return; + } + *SubAlloc.pText++ = fs.Symbol; + Successor = (PPM_CONTEXT*) SubAlloc.pText; + if (SubAlloc.pText >= SubAlloc.FakeUnitsStart) + goto RESTART_MODEL; + if ( fs.Successor ) + { + if ((byte*) fs.Successor <= SubAlloc.pText && + (fs.Successor=CreateSuccessors(false,p)) == NULL) + goto RESTART_MODEL; + if ( !--OrderFall ) + { + Successor=fs.Successor; + SubAlloc.pText -= (MaxContext != MinContext); + } + } + else + { + FoundState->Successor=Successor; + fs.Successor=MinContext; + } + s0=MinContext->U.SummFreq-(ns=MinContext->NumStats)-(fs.Freq-1); + for (pc=MaxContext;pc != MinContext;pc=pc->Suffix) + { + if ((ns1=pc->NumStats) != 1) + { + if ((ns1 & 1) == 0) + { + pc->U.Stats=(STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); + if ( !pc->U.Stats ) + goto RESTART_MODEL; + } + pc->U.SummFreq += (2*ns1 < ns)+2*((4*ns1 <= ns) & (pc->U.SummFreq <= 8*ns1)); + } + else + { + p=(STATE*) SubAlloc.AllocUnits(1); + if ( !p ) + goto RESTART_MODEL; + *p=pc->OneState; + pc->U.Stats=p; + if (p->Freq < MAX_FREQ/4-1) + p->Freq += p->Freq; + else + p->Freq = MAX_FREQ-4; + pc->U.SummFreq=p->Freq+InitEsc+(ns > 3); + } + cf=2*fs.Freq*(pc->U.SummFreq+6); + sf=s0+pc->U.SummFreq; + if (cf < 6*sf) + { + cf=1+(cf > sf)+(cf >= 4*sf); + pc->U.SummFreq += 3; + } + else + { + cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf); + pc->U.SummFreq += cf; + } + p=pc->U.Stats+ns1; + p->Successor=Successor; + p->Symbol = fs.Symbol; + p->Freq = cf; + pc->NumStats=++ns1; + } + MaxContext=MinContext=fs.Successor; + return; +RESTART_MODEL: + RestartModelRare(); + EscCount=0; +} + + +// Tabulated escapes for exponential symbol distribution +static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +#define GET_MEAN(SUMM,SHIFT,ROUND) ((SUMM+(1 << (SHIFT-ROUND))) >> (SHIFT)) + + + +inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) +{ + STATE& rs=OneState; + Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; + ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+ + Model->NS2BSIndx[Suffix->NumStats-1]+ + Model->HiBitsFlag+2*Model->HB2Flag[rs.Symbol]+ + ((Model->RunLength >> 26) & 0x20)]; + if (Model->Coder.GetCurrentShiftCount(TOT_BITS) < bs) + { + Model->FoundState=&rs; + rs.Freq += (rs.Freq < 128); + Model->Coder.SubRange.LowCount=0; + Model->Coder.SubRange.HighCount=bs; + bs = SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); + Model->PrevSuccess=1; + Model->RunLength++; + } + else + { + Model->Coder.SubRange.LowCount=bs; + bs = SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); + Model->Coder.SubRange.HighCount=BIN_SCALE; + Model->InitEsc=ExpEscape[bs >> 10]; + Model->NumMasked=1; + Model->CharMask[rs.Symbol]=Model->EscCount; + Model->PrevSuccess=0; + Model->FoundState=NULL; + } +} + + +inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p) +{ + (Model->FoundState=p)->Freq += 4; + U.SummFreq += 4; + if (p[0].Freq > p[-1].Freq) + { + _PPMD_SWAP(p[0],p[-1]); + Model->FoundState=--p; + if (p->Freq > MAX_FREQ) + rescale(Model); + } +} + + + + +inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model) +{ + Model->Coder.SubRange.scale=U.SummFreq; + STATE* p=U.Stats; + int i, HiCnt; + int count=Model->Coder.GetCurrentCount(); + if (count>=Model->Coder.SubRange.scale) + return(false); + if (count < (HiCnt=p->Freq)) + { + Model->PrevSuccess=(2*(Model->Coder.SubRange.HighCount=HiCnt) > Model->Coder.SubRange.scale); + Model->RunLength += Model->PrevSuccess; + (Model->FoundState=p)->Freq=(HiCnt += 4); + U.SummFreq += 4; + if (HiCnt > MAX_FREQ) + rescale(Model); + Model->Coder.SubRange.LowCount=0; + return(true); + } + else + if (Model->FoundState==NULL) + return(false); + Model->PrevSuccess=0; + i=NumStats-1; + while ((HiCnt += (++p)->Freq) <= count) + if (--i == 0) + { + Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; + Model->Coder.SubRange.LowCount=HiCnt; + Model->CharMask[p->Symbol]=Model->EscCount; + i=(Model->NumMasked=NumStats)-1; + Model->FoundState=NULL; + do + { + Model->CharMask[(--p)->Symbol]=Model->EscCount; + } while ( --i ); + Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; + return(true); + } + Model->Coder.SubRange.LowCount=(Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; + update1(Model,p); + return(true); +} + + +inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p) +{ + (Model->FoundState=p)->Freq += 4; + U.SummFreq += 4; + if (p->Freq > MAX_FREQ) + rescale(Model); + Model->EscCount++; + Model->RunLength=Model->InitRL; +} + + +inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) +{ + SEE2_CONTEXT* psee2c; + if (NumStats != 256) + { + psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+ + (Diff < Suffix->NumStats-NumStats)+ + 2*(U.SummFreq < 11*NumStats)+4*(Model->NumMasked > Diff)+ + Model->HiBitsFlag; + Model->Coder.SubRange.scale=psee2c->getMean(); + } + else + { + psee2c=&Model->DummySEE2Cont; + Model->Coder.SubRange.scale=1; + } + return psee2c; +} + + + + +inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) +{ + int count, HiCnt, i=NumStats-Model->NumMasked; + SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); + STATE* ps[256], ** pps=ps, * p=U.Stats-1; + HiCnt=0; + do + { + do + { + p++; + } while (Model->CharMask[p->Symbol] == Model->EscCount); + HiCnt += p->Freq; + *pps++ = p; + } while ( --i ); + Model->Coder.SubRange.scale += HiCnt; + count=Model->Coder.GetCurrentCount(); + if (count>=Model->Coder.SubRange.scale) + return(false); + p=*(pps=ps); + if (count < HiCnt) + { + HiCnt=0; + while ((HiCnt += p->Freq) <= count) + p=*++pps; + Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; + psee2c->update(); + update2(Model,p); + } + else + { + Model->Coder.SubRange.LowCount=HiCnt; + Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; + i=NumStats-Model->NumMasked; + pps--; + do + { + Model->CharMask[(*++pps)->Symbol]=Model->EscCount; + } while ( --i ); + psee2c->Summ += Model->Coder.SubRange.scale; + Model->NumMasked = NumStats; + } + return(true); +} + + +inline void ModelPPM::ClearMask() +{ + EscCount=1; + memset(CharMask,0,sizeof(CharMask)); +} + + + + +// reset PPM variables after data error allowing safe resuming +// of further data processing +void ModelPPM::CleanUp() +{ + SubAlloc.StopSubAllocator(); + SubAlloc.StartSubAllocator(1); + StartModelRare(2); +} + + +bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar) +{ + int MaxOrder=UnpackRead->GetChar(); + bool Reset=MaxOrder & 0x20; + + int MaxMB; + MaxMB = 0; // avoids warning of being uninitialized + if (Reset) + MaxMB=UnpackRead->GetChar(); + else + if (SubAlloc.GetAllocatedMemory()==0) + return(false); + if (MaxOrder & 0x40) + EscChar=UnpackRead->GetChar(); + Coder.InitDecoder(UnpackRead); + if (Reset) + { + MaxOrder=(MaxOrder & 0x1f)+1; + if (MaxOrder>16) + MaxOrder=16+(MaxOrder-16)*3; + if (MaxOrder==1) + { + SubAlloc.StopSubAllocator(); + return(false); + } + SubAlloc.StartSubAllocator(MaxMB+1); + StartModelRare(MaxOrder); + } + return(MinContext!=NULL); +} + + +int ModelPPM::DecodeChar() +{ + if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd) + return(-1); + if (MinContext->NumStats != 1) + { + if ((byte*)MinContext->U.Stats <= SubAlloc.pText || (byte*)MinContext->U.Stats>SubAlloc.HeapEnd) + return(-1); + if (!MinContext->decodeSymbol1(this)) + return(-1); + } + else + MinContext->decodeBinSymbol(this); + Coder.Decode(); + while ( !FoundState ) + { + ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); + do + { + OrderFall++; + MinContext=MinContext->Suffix; + if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd) + return(-1); + } while (MinContext->NumStats == NumMasked); + if (!MinContext->decodeSymbol2(this)) + return(-1); + Coder.Decode(); + } + int Symbol=FoundState->Symbol; + if (!OrderFall && (byte*) FoundState->Successor > SubAlloc.pText) + MinContext=MaxContext=FoundState->Successor; + else + { + UpdateModel(); + if (EscCount == 0) + ClearMask(); + } + ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); + return(Symbol); +} +#endif diff --git a/snesreader/unrar/model.hpp b/snesreader/unrar/model.hpp new file mode 100644 index 00000000..1ccf2f1d --- /dev/null +++ b/snesreader/unrar/model.hpp @@ -0,0 +1,133 @@ +#ifndef _RAR_PPMMODEL_ +#define _RAR_PPMMODEL_ + +#include "coder.hpp" +#include "suballoc.hpp" + +const int MAX_O=64; /* maximum allowed model order */ + +const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, + INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; + +#ifndef STRICT_ALIGNMENT_REQUIRED +#pragma pack(1) +#endif + +struct SEE2_CONTEXT +{ // SEE-contexts for PPM-contexts with masked symbols + ushort Summ; + byte Shift, Count; + void init(int InitVal) + { + Summ=InitVal << (Shift=PERIOD_BITS-4); + Count=4; + } + uint getMean() + { + uint RetVal=SHORT16(Summ) >> Shift; + Summ -= RetVal; + return RetVal+(RetVal == 0); + } + void update() + { + if (Shift < PERIOD_BITS && --Count == 0) + { + Summ += Summ; + Count=3 << Shift++; + } + } +}; + + +class ModelPPM; +struct PPM_CONTEXT; + +struct STATE +{ + byte Symbol; + byte Freq; + PPM_CONTEXT* Successor; +}; + +struct FreqData +{ + ushort SummFreq; + STATE _PACK_ATTR * Stats; +}; + +struct PPM_CONTEXT +{ + ushort NumStats; + union + { + FreqData U; + STATE OneState; + }; + + PPM_CONTEXT* Suffix; + inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder: + inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context + inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix + inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor + inline bool decodeSymbol1(ModelPPM *Model); // other orders: + inline bool decodeSymbol2(ModelPPM *Model); // BCD context + inline void update1(ModelPPM *Model,STATE* p); // CD suffix + inline void update2(ModelPPM *Model,STATE* p); // BCDE successor + void rescale(ModelPPM *Model); + inline PPM_CONTEXT* createChild(ModelPPM *Model,STATE* pStats,STATE& FirstState); + inline SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); +}; + +#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef _AIX +#pragma pack(pop) +#else +#pragma pack() +#endif +#endif + +const uint UNIT_SIZE=Max(sizeof(PPM_CONTEXT),sizeof(RAR_MEM_BLK)); +const uint FIXED_UNIT_SIZE=12; + +/* +inline PPM_CONTEXT::PPM_CONTEXT(STATE* pStats,PPM_CONTEXT* ShorterContext): + NumStats(1), Suffix(ShorterContext) { pStats->Successor=this; } +inline PPM_CONTEXT::PPM_CONTEXT(): NumStats(0) {} +*/ + +template +inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } + + +class ModelPPM +{ + private: + friend struct PPM_CONTEXT; + + /*_PACK_ATTR*/ SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; + + struct PPM_CONTEXT *MinContext, *MedContext, *MaxContext; + STATE* FoundState; // found next state transition + int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL; + byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; + byte EscCount, PrevSuccess, HiBitsFlag; + ushort BinSumm[128][64]; // binary SEE-contexts + + RangeCoder Coder; + SubAllocator SubAlloc; + + void RestartModelRare(); + void StartModelRare(int MaxOrder); + inline PPM_CONTEXT* CreateSuccessors(bool Skip,STATE* p1); + + inline void UpdateModel(); + inline void ClearMask(); + friend class Unpack; + public: + ModelPPM(); + void CleanUp(); // reset PPM variables after data error + bool DecodeInit(Unpack *UnpackRead,int &EscChar); + int DecodeChar(); +}; + +#endif diff --git a/snesreader/unrar/rar.hpp b/snesreader/unrar/rar.hpp new file mode 100644 index 00000000..3302b2b2 --- /dev/null +++ b/snesreader/unrar/rar.hpp @@ -0,0 +1,209 @@ +// This source code is a heavily modified version based on the unrar package. +// It may NOT be used to develop a RAR (WinRAR) compatible archiver. +// See license.txt for copyright and licensing. + +// unrar_core 3.8.5 +#ifndef RAR_COMMON_HPP +#define RAR_COMMON_HPP + +#include "unrar.h" + +#include +#include +#include +#include + +//// Glue + +// One goal is to keep source code as close to original as possible, so +// that changes to the original can be found and merged more easily. + +// These names are too generic and might clash (or have already, hmpf) +#define Array Rar_Array +#define uint32 rar_uint32 +#define sint32 rar_sint32 +#define Unpack Rar_Unpack +#define Archive Rar_Archive +#define RawRead Rar_RawRead +#define BitInput Rar_BitInput +#define ModelPPM Rar_ModelPPM +#define RangeCoder Rar_RangeCoder +#define SubAllocator Rar_SubAllocator +#define UnpackFilter Rar_UnpackFilter +#define VM_PreparedProgram Rar_VM_PreparedProgram +#define CRCTab Rar_CRCTab + +// original source used rar* names for these as well +#define rarmalloc malloc +#define rarrealloc realloc +#define rarfree free + +// Internal flags, possibly set later +#undef SFX_MODULE +#undef VM_OPTIMIZE +#undef VM_STANDARDFILTERS +#undef NORARVM + +// During debugging if expr is false, prints message then continues execution +#ifndef check + #define check( expr ) ((void) 0) +#endif + +struct Rar_Error_Handler +{ + jmp_buf jmp_env; + + void MemoryError(); + void ReportError( unrar_err_t ); +}; + +// throw spec is mandatory in ISO C++ if operator new can return NULL +#if __cplusplus >= 199711 || __GNUC__ >= 3 + #define UNRAR_NOTHROW throw () +#else + #define UNRAR_NOTHROW +#endif + +struct Rar_Allocator +{ + // provides allocator that doesn't throw an exception on failure + static void operator delete ( void* p ) { free( p ); } + static void* operator new ( size_t s ) UNRAR_NOTHROW { return malloc( s ); } + static void* operator new ( size_t, void* p ) UNRAR_NOTHROW { return p; } +}; + +//// os.hpp +#undef STRICT_ALIGNMENT_REQUIRED +#undef LITTLE_ENDIAN +#define NM 1024 + +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + // Optimizations mostly only apply to x86 + #define LITTLE_ENDIAN + #define ALLOW_NOT_ALIGNED_INT +#endif + +#if defined(__sparc) || defined(sparc) || defined(__sparcv9) +/* prohibit not aligned access to data structures in text comression + algorithm, increases memory requirements */ + #define STRICT_ALIGNMENT_REQUIRED +#endif + +//// rartypes.hpp +#if INT_MAX == 0x7FFFFFFF && UINT_MAX == 0xFFFFFFFF + typedef unsigned int uint32; //32 bits exactly + typedef int sint32; //signed 32 bits exactly + #define PRESENT_INT32 +#endif + +typedef unsigned char byte; //8 bits +typedef unsigned short ushort; //preferably 16 bits, but can be more +typedef unsigned int uint; //32 bits or more + +typedef wchar_t wchar; + +#define SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) +#define UINT32(x) (sizeof(uint )==4 ? (uint )(x):((x)&0xffffffff)) + +//// rardefs.hpp +#define Min(x,y) (((x)<(y)) ? (x):(y)) +#define Max(x,y) (((x)>(y)) ? (x):(y)) + +//// int64.hpp +typedef unrar_long_long Int64; + +#define int64to32(x) ((uint)(x)) +#define int32to64(high,low) ((((Int64)(high))<<31<<1)+(low)) +#define is64plus(x) (x>=0) + +#define INT64MAX int32to64(0x7fffffff,0) + +//// crc.hpp +extern uint CRCTab[256]; +void InitCRC(); +uint CRC(uint StartCRC,const void *Addr,size_t Size); +ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size); + +//// rartime.hpp +struct RarTime +{ + unsigned time; + void SetDos(uint DosTime) { time = DosTime; } +}; + +//// rdwrfn.hpp +class ComprDataIO + : public Rar_Error_Handler +{ +public: + unrar_read_func user_read; + unrar_write_func user_write; + void* user_read_data; + void* user_write_data; + unrar_err_t write_error; // once write error occurs, no more writes are made + Int64 Tell_; + bool OldFormat; + +private: + Int64 UnpPackedSize; + bool SkipUnpCRC; + +public: + int UnpRead(byte *Addr,uint Count); + void UnpWrite(byte *Addr,uint Count); + void SetSkipUnpCRC( bool b ) { SkipUnpCRC = b; } + void SetPackedSizeToRead( Int64 n ) { UnpPackedSize = n; } + + uint UnpFileCRC; + + void Seek(Int64 Offset, int Method = 0 ) { (void)Method; Tell_ = Offset; } + Int64 Tell() { return Tell_; } + int Read( void* p, int n ); +}; + +//// rar.hpp +class Unpack; +#include "array.hpp" +#include "headers.hpp" +#include "getbits.hpp" +#include "archive.hpp" +#include "rawread.hpp" +#include "encname.hpp" +#include "compress.hpp" +#include "rarvm.hpp" +#include "model.hpp" +#include "unpack.hpp" + +//// extract.hpp +/** RAR archive */ +struct unrar_t + : public Rar_Allocator +{ + unrar_info_t info; + unrar_pos_t begin_pos; + unrar_pos_t solid_pos; + unrar_pos_t first_file_pos; + void const* data_; + void* own_data_; + void (*close_file)( void* ); // func ptr to avoid linking fclose() in unnecessarily + bool done; + long FileCount; + Unpack* Unp; + Array Buffer; + // large items last + Archive Arc; + + unrar_t(); + ~unrar_t(); + void UnstoreFile( Int64 ); + unrar_err_t ExtractCurrentFile( bool SkipSolid = false, bool check_compatibility_only = false ); + void update_first_file_pos() + { + if ( FileCount == 0 ) + first_file_pos = Arc.CurBlockPos; + } +}; + +typedef unrar_t CmdExtract; + +#endif diff --git a/snesreader/unrar/rarvm.cpp b/snesreader/unrar/rarvm.cpp new file mode 100644 index 00000000..d13e6264 --- /dev/null +++ b/snesreader/unrar/rarvm.cpp @@ -0,0 +1,1158 @@ +#include "rar.hpp" + +#include "rarvmtbl.cpp" + +// avoids warning of enumeration and non-enumeration in ?: expressions +#define VM_FC ((unsigned) VM_FC) +#define VM_FZ ((unsigned) VM_FZ) +#define VM_FS ((unsigned) VM_FS) + +RarVM::RarVM() +{ + Mem=NULL; +} + + +RarVM::~RarVM() +{ + rarfree( Mem ); +} + + +void RarVM::Init() +{ + if (Mem==NULL) + Mem = (byte*) rarmalloc( VM_MEMSIZE+4 ); +} + +void RarVM::handle_mem_error( Rar_Error_Handler& ErrHandler ) +{ + BitInput::handle_mem_error( ErrHandler ); + if ( !Mem ) + ErrHandler.MemoryError(); +} + +/********************************************************************* + IS_VM_MEM macro checks if address belongs to VM memory pool (Mem). + Only Mem data are always low endian regardless of machine architecture, + so we need to convert them to native format when reading or writing. + VM registers have endianness of host machine. +**********************************************************************/ +#define IS_VM_MEM(a) (((byte*)a)>=Mem && ((byte*)a)>8); + ((byte *)Addr)[2]=(byte)(Value>>16); + ((byte *)Addr)[3]=(byte)(Value>>24); + } + else + *(uint *)Addr=Value; +#else + *(uint32 *)Addr=Value; +#endif + } +} + +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) +#define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint *)Addr,Value) +#else + #define SET_VALUE(ByteMode,Addr,Value) ((ByteMode) ? (*(byte *)(Addr)=(Value)):(*(uint32 *)(Addr)=((uint32)(Value)))) +#endif + + +void RarVM::SetLowEndianValue(uint *Addr,uint Value) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) + ((byte *)Addr)[0]=(byte)Value; + ((byte *)Addr)[1]=(byte)(Value>>8); + ((byte *)Addr)[2]=(byte)(Value>>16); + ((byte *)Addr)[3]=(byte)(Value>>24); +#else + *(uint32 *)Addr=Value; +#endif +} + + +inline uint* RarVM::GetOperand(VM_PreparedOperand *CmdOp) +{ + if (CmdOp->Type==VM_OPREGMEM) + return((uint *)&Mem[(*CmdOp->Addr+CmdOp->Base)&VM_MEMMASK]); + else + return(CmdOp->Addr); +} + + +void RarVM::Execute(VM_PreparedProgram *Prg) +{ + memcpy(R,Prg->InitR,sizeof(Prg->InitR)); + unsigned int GlobalSize=Min(Prg->GlobalData.Size(),VM_GLOBALMEMSIZE); + if (GlobalSize) + memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); + unsigned int StaticSize=Min(Prg->StaticData.Size(),VM_GLOBALMEMSIZE-GlobalSize); + if (StaticSize) + memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); + + R[7]=VM_MEMSIZE; + Flags=0; + + VM_PreparedCommand *PreparedCode=Prg->AltCmd ? Prg->AltCmd:&Prg->Cmd[0]; + if (!ExecuteCode(PreparedCode,Prg->CmdCount)) + PreparedCode[0].OpCode=VM_RET; + uint NewBlockPos=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20])&VM_MEMMASK; + uint NewBlockSize=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c])&VM_MEMMASK; + if (NewBlockPos+NewBlockSize>=VM_MEMSIZE) + NewBlockPos=NewBlockSize=0; + Prg->FilteredData=Mem+NewBlockPos; + Prg->FilteredDataSize=NewBlockSize; + + Prg->GlobalData.Reset(); + + uint DataSize=Min(GET_VALUE(false,(uint*)&Mem[VM_GLOBALMEMADDR+0x30]),VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE); + if (DataSize!=0) + { + Prg->GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE); + memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); + } +} + + +/* +Note: + Due to performance considerations RAR VM may set VM_FS, VM_FC, VM_FZ + incorrectly for byte operands. These flags are always valid only + for 32-bit operands. Check implementation of concrete VM command + to see if it sets flags right. +*/ + +#define SET_IP(IP) \ + if ((IP)>=CodeSize) \ + return(true); \ + if (--MaxOpCount<=0) \ + return(false); \ + Cmd=PreparedCode+(IP); + +bool RarVM::ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize) +{ + int MaxOpCount=25000000; + VM_PreparedCommand *Cmd=PreparedCode; + while (1) + { +#ifndef NORARVM + // Get addresses to quickly access operands. + uint *Op1=GetOperand(&Cmd->Op1); + uint *Op2=GetOperand(&Cmd->Op2); +#endif + switch(Cmd->OpCode) + { +#ifndef NORARVM + case VM_MOV: + SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); + break; +#ifdef VM_OPTIMIZE + case VM_MOVB: + SET_VALUE(true,Op1,GET_VALUE(true,Op2)); + break; + case VM_MOVD: + SET_VALUE(false,Op1,GET_VALUE(false,Op2)); + break; +#endif + case VM_CMP: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; +#ifdef VM_OPTIMIZE + case VM_CMPB: + { + uint Value1=GET_VALUE(true,Op1); + uint Result=UINT32(Value1-GET_VALUE(true,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; + case VM_CMPD: + { + uint Value1=GET_VALUE(false,Op1); + uint Result=UINT32(Value1-GET_VALUE(false,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; +#endif + case VM_ADD: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)); + if (Cmd->ByteMode) + { + Result&=0xff; + Flags=(ResultByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_ADDB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)+GET_VALUE(true,Op2)); + break; + case VM_ADDD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)+GET_VALUE(false,Op2)); + break; +#endif + case VM_SUB: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_SUBB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)-GET_VALUE(true,Op2)); + break; + case VM_SUBD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)-GET_VALUE(false,Op2)); + break; +#endif + case VM_JZ: + if ((Flags & VM_FZ)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JNZ: + if ((Flags & VM_FZ)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_INC: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)+1); + if (Cmd->ByteMode) + Result&=0xff; + SET_VALUE(Cmd->ByteMode,Op1,Result); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; +#ifdef VM_OPTIMIZE + case VM_INCB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)+1); + break; + case VM_INCD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)+1); + break; +#endif + case VM_DEC: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)-1); + SET_VALUE(Cmd->ByteMode,Op1,Result); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; +#ifdef VM_OPTIMIZE + case VM_DECB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)-1); + break; + case VM_DECD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)-1); + break; +#endif + case VM_JMP: + SET_IP(GET_VALUE(false,Op1)); + continue; + case VM_XOR: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)^GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_AND: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_OR: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)|GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_TEST: + { + uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; + case VM_JS: + if ((Flags & VM_FS)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JNS: + if ((Flags & VM_FS)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JB: + if ((Flags & VM_FC)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JBE: + if ((Flags & (VM_FC|VM_FZ))!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JA: + if ((Flags & (VM_FC|VM_FZ))==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JAE: + if ((Flags & VM_FC)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_PUSH: + R[7]-=4; + SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],GET_VALUE(false,Op1)); + break; + case VM_POP: + SET_VALUE(false,Op1,GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); + R[7]+=4; + break; + case VM_CALL: + R[7]-=4; + SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Cmd-PreparedCode+1); + SET_IP(GET_VALUE(false,Op1)); + continue; + case VM_NOT: + SET_VALUE(Cmd->ByteMode,Op1,~GET_VALUE(Cmd->ByteMode,Op1)); + break; + case VM_SHL: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=UINT32(Value1<ByteMode,Op1,Result); + } + break; + case VM_SHR: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=UINT32(Value1>>Value2); + Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_SAR: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=UINT32(((int)Value1)>>Value2); + Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_NEG: + { + uint Result=UINT32(-GET_VALUE(Cmd->ByteMode,Op1)); + Flags=Result==0 ? VM_FZ:VM_FC|(Result&VM_FS); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_NEGB: + SET_VALUE(true,Op1,-GET_VALUE(true,Op1)); + break; + case VM_NEGD: + SET_VALUE(false,Op1,-GET_VALUE(false,Op1)); + break; +#endif + case VM_PUSHA: + { + const int RegCount=sizeof(R)/sizeof(R[0]); + for (int I=0,SP=R[7]-4;IByteMode,Op1); + SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); + SET_VALUE(Cmd->ByteMode,Op2,Value1); + } + break; + case VM_MUL: + { + uint Result=GET_VALUE(Cmd->ByteMode,Op1)*GET_VALUE(Cmd->ByteMode,Op2); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_DIV: + { + uint Divider=GET_VALUE(Cmd->ByteMode,Op2); + if (Divider!=0) + { + uint Result=GET_VALUE(Cmd->ByteMode,Op1)/Divider; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + } + break; + case VM_ADC: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint FC=(Flags&VM_FC); + uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)+FC); + if (Cmd->ByteMode) + Result&=0xff; + Flags=(ResultByteMode,Op1,Result); + } + break; + case VM_SBB: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint FC=(Flags&VM_FC); + uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)-FC); + if (Cmd->ByteMode) + Result&=0xff; + Flags=(Result>Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#endif // for #ifndef NORARVM + case VM_RET: + if (R[7]>=VM_MEMSIZE) + return(true); + SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); + R[7]+=4; + continue; +#ifdef VM_STANDARDFILTERS + case VM_STANDARD: + ExecuteStandardFilter((VM_StandardFilters)Cmd->Op1.Data); + break; +#endif + case VM_PRINT: + break; + } + Cmd++; + --MaxOpCount; + } +} + + + + +void RarVM::Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg) +{ + InitBitInput(); + memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); + + // Calculate the single byte XOR checksum to check validity of VM code. + byte XorSum=0; + { + for (int I=1;ICmdCount=0; + if (XorSum==Code[0]) // VM code is valid if equal. + { +#ifdef VM_STANDARDFILTERS + VM_StandardFilters FilterType=IsStandardFilter(Code,CodeSize); + if (FilterType!=VMSF_NONE) + { + // VM code is found among standard filters. + Prg->Cmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; + CurCmd->OpCode=VM_STANDARD; + CurCmd->Op1.Data=FilterType; + CurCmd->Op1.Addr=&CurCmd->Op1.Data; + CurCmd->Op2.Addr=&CurCmd->Op2.Data; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + CodeSize=0; + } +#endif + uint DataFlag=fgetbits(); + faddbits(1); + + // Read static data contained in DB operators. This data cannot be + // changed, it is a part of VM code, not a filter parameter. + + if (DataFlag&0x8000) + { + int DataSize=ReadData(*this)+1; + for (int I=0;InAddrStaticData.Add(1); + Prg->StaticData[I]=fgetbits()>>8; + faddbits(8); + } + } + + while (InAddrCmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount]; + uint Data=fgetbits(); + if ((Data&0x8000)==0) + { + CurCmd->OpCode=(VM_Commands)(Data>>12); + faddbits(4); + } + else + { + CurCmd->OpCode=(VM_Commands)((Data>>10)-24); + faddbits(6); + } + if (VM_CmdFlags[CurCmd->OpCode] & VMCF_BYTEMODE) + { + CurCmd->ByteMode=fgetbits()>>15; + faddbits(1); + } + else + CurCmd->ByteMode=0; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + int OpNum=(VM_CmdFlags[CurCmd->OpCode] & VMCF_OPMASK); + CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; + if (OpNum>0) + { + DecodeArg(CurCmd->Op1,CurCmd->ByteMode); // reading the first operand + if (OpNum==2) + DecodeArg(CurCmd->Op2,CurCmd->ByteMode); // reading the second operand + else + { + if (CurCmd->Op1.Type==VM_OPINT && (VM_CmdFlags[CurCmd->OpCode]&(VMCF_JUMP|VMCF_PROC))) + { + // Calculating jump distance. + int Distance=CurCmd->Op1.Data; + if (Distance>=256) + Distance-=256; + else + { + if (Distance>=136) + Distance-=264; + else + if (Distance>=16) + Distance-=8; + else + if (Distance>=8) + Distance-=16; + Distance+=Prg->CmdCount; + } + CurCmd->Op1.Data=Distance; + } + } + } + Prg->CmdCount++; + } + } + + // Adding RET command at the end of program. + Prg->Cmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; + CurCmd->OpCode=VM_RET; + CurCmd->Op1.Addr=&CurCmd->Op1.Data; + CurCmd->Op2.Addr=&CurCmd->Op2.Data; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + + // If operand 'Addr' field has not been set by DecodeArg calls above, + // let's set it to point to operand 'Data' field. It is necessary for + // VM_OPINT type operands (usual integers) or maybe if something was + // not set properly for other operands. 'Addr' field is required + // for quicker addressing of operand data. + for (int I=0;ICmdCount;I++) + { + VM_PreparedCommand *Cmd=&Prg->Cmd[I]; + if (Cmd->Op1.Addr==NULL) + Cmd->Op1.Addr=&Cmd->Op1.Data; + if (Cmd->Op2.Addr==NULL) + Cmd->Op2.Addr=&Cmd->Op2.Data; + } + +#ifdef VM_OPTIMIZE + if (CodeSize!=0) + Optimize(Prg); +#endif +} + + +void RarVM::DecodeArg(VM_PreparedOperand &Op,bool ByteMode) +{ + uint Data=fgetbits(); + if (Data & 0x8000) + { + Op.Type=VM_OPREG; // Operand is register (R[0]..R[7]) + Op.Data=(Data>>12)&7; // Register number + Op.Addr=&R[Op.Data]; // Register address + faddbits(4); // 1 flag bit and 3 register number bits + } + else + if ((Data & 0xc000)==0) + { + Op.Type=VM_OPINT; // Operand is integer + if (ByteMode) + { + Op.Data=(Data>>6) & 0xff; // Byte integer. + faddbits(10); + } + else + { + faddbits(2); + Op.Data=ReadData(*this); // 32 bit integer. + } + } + else + { + // Operand is data addressed by register data, base address or both. + Op.Type=VM_OPREGMEM; + if ((Data & 0x2000)==0) + { + // Base address is zero, just use the address from register. + Op.Data=(Data>>10)&7; + Op.Addr=&R[Op.Data]; + Op.Base=0; + faddbits(6); + } + else + { + if ((Data & 0x1000)==0) + { + // Use both register and base address. + Op.Data=(Data>>9)&7; + Op.Addr=&R[Op.Data]; + faddbits(7); + } + else + { + // Use base address only. Access memory by fixed address. + Op.Data=0; + faddbits(4); + } + Op.Base=ReadData(*this); // Read base address. + } + } +} + + +uint RarVM::ReadData(BitInput &Inp) +{ + uint Data=Inp.fgetbits(); + switch(Data&0xc000) + { + case 0: + Inp.faddbits(6); + return((Data>>10)&0xf); + case 0x4000: + if ((Data&0x3c00)==0) + { + Data=0xffffff00|((Data>>2)&0xff); + Inp.faddbits(14); + } + else + { + Data=(Data>>6)&0xff; + Inp.faddbits(10); + } + return(Data); + case 0x8000: + Inp.faddbits(2); + Data=Inp.fgetbits(); + Inp.faddbits(16); + return(Data); + default: + Inp.faddbits(2); + Data=(Inp.fgetbits()<<16); + Inp.faddbits(16); + Data|=Inp.fgetbits(); + Inp.faddbits(16); + return(Data); + } +} + + +void RarVM::SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize) +{ + if (PosCmd[0]; + int CodeSize=Prg->CmdCount; + + for (int I=0;IOpCode) + { + case VM_MOV: + Cmd->OpCode=Cmd->ByteMode ? VM_MOVB:VM_MOVD; + continue; + case VM_CMP: + Cmd->OpCode=Cmd->ByteMode ? VM_CMPB:VM_CMPD; + continue; + } + if ((VM_CmdFlags[Cmd->OpCode] & VMCF_CHFLAGS)==0) + continue; + + // If we do not have jump commands between the current operation + // and next command which will modify processor flags, we can replace + // the current command with faster version which does not need to + // modify flags. + bool FlagsRequired=false; + for (int J=I+1;JOpCode) + { + case VM_ADD: + Cmd->OpCode=Cmd->ByteMode ? VM_ADDB:VM_ADDD; + continue; + case VM_SUB: + Cmd->OpCode=Cmd->ByteMode ? VM_SUBB:VM_SUBD; + continue; + case VM_INC: + Cmd->OpCode=Cmd->ByteMode ? VM_INCB:VM_INCD; + continue; + case VM_DEC: + Cmd->OpCode=Cmd->ByteMode ? VM_DECB:VM_DECD; + continue; + case VM_NEG: + Cmd->OpCode=Cmd->ByteMode ? VM_NEGB:VM_NEGD; + continue; + } + } +} +#endif + + +#ifdef VM_STANDARDFILTERS +VM_StandardFilters RarVM::IsStandardFilter(byte *Code,int CodeSize) +{ + static const + struct StandardFilterSignature + { + int Length; + uint CRC; + VM_StandardFilters Type; + } StdList[]={ + { + 53, 0xad576887, VMSF_E8, + },{ + 57, 0x3cd7e57e, VMSF_E8E9, + },{ + 120, 0x3769893f, VMSF_ITANIUM, + },{ + 29, 0x0e06077d, VMSF_DELTA, + },{ + 149, 0x1c2c5dc8, VMSF_RGB, + },{ + 216, 0xbc85e701, VMSF_AUDIO, + },{ + 40, 0x46b9c560, VMSF_UPCASE + } + }; + uint CodeCRC=CRC(0xffffffff,Code,CodeSize)^0xffffffff; + for (int I=0;I=VM_GLOBALMEMADDR || DataSize<4) + break; + + const int FileSize=0x1000000; + byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; + for (int CurPos=0;CurPos=0) + SET_VALUE(false,Data,Addr+FileSize); + } + else + if (Addr=VM_GLOBALMEMADDR || DataSize<21) + break; + + int CurPos=0; + + FileOffset>>=4; + + while (CurPos=0) + { + const + static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0}; + byte CmdMask=Masks[Byte]; + if (CmdMask!=0) + for (int I=0;I<=2;I++) + if (CmdMask & (1<=VM_GLOBALMEMADDR/2) + break; + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (int CurChannel=0;CurChannel=VM_GLOBALMEMADDR/2 || PosR<0) + break; + for (int CurChannel=0;CurChannel=3) + { + byte *UpperData=DestData+UpperPos; + unsigned int UpperByte=*UpperData; + unsigned int UpperLeftByte=*(UpperData-3); + Predicted=PrevByte+UpperByte-UpperLeftByte; + int pa=abs((int)(Predicted-PrevByte)); + int pb=abs((int)(Predicted-UpperByte)); + int pc=abs((int)(Predicted-UpperLeftByte)); + if (pa<=pb && pa<=pc) + Predicted=PrevByte; + else + if (pb<=pc) + Predicted=UpperByte; + else + Predicted=UpperLeftByte; + } + else + Predicted=PrevByte; + DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); + } + } + for (int I=PosR,Border=DataSize-2;I=VM_GLOBALMEMADDR/2) + break; + for (int CurChannel=0;CurChannel>3) & 0xff; + + unsigned int CurByte=*(SrcData++); + + Predicted-=CurByte; + DestData[I]=Predicted; + PrevDelta=(signed char)(Predicted-PrevByte); + PrevByte=Predicted; + + int D=((signed char)CurByte)<<3; + + Dif[0]+=abs(D); + Dif[1]+=abs(D-D1); + Dif[2]+=abs(D+D1); + Dif[3]+=abs(D-D2); + Dif[4]+=abs(D+D2); + Dif[5]+=abs(D-D3); + Dif[6]+=abs(D+D3); + + if ((ByteCount & 0x1f)==0) + { + unsigned int MinDif=Dif[0],NumMinDif=0; + Dif[0]=0; + for (int J=1;J=-16) K1--; break; + case 2: if (K1 < 16) K1++; break; + case 3: if (K2>=-16) K2--; break; + case 4: if (K2 < 16) K2++; break; + case 5: if (K3>=-16) K3--; break; + case 6: if (K3 < 16) K3++; break; + } + } + } + } + } + break; + case VMSF_UPCASE: + { + int DataSize=R[4],SrcPos=0,DestPos=DataSize; + if (DataSize>=VM_GLOBALMEMADDR/2) + break; + while (SrcPos>= InBit; + return(BitField & (0xffffffff>>(32-BitCount))); +} + + +void RarVM::FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos, + int BitCount) +{ + int InAddr=BitPos/8; + int InBit=BitPos&7; + unsigned int AndMask=0xffffffff>>(32-BitCount); + AndMask=~(AndMask<>8)|0xff000000; + BitField>>=8; + } +} +#endif diff --git a/snesreader/unrar/rarvm.hpp b/snesreader/unrar/rarvm.hpp new file mode 100644 index 00000000..835e5299 --- /dev/null +++ b/snesreader/unrar/rarvm.hpp @@ -0,0 +1,112 @@ +#ifndef _RAR_VM_ +#define _RAR_VM_ + +#define VM_STANDARDFILTERS + +#ifndef SFX_MODULE +#define VM_OPTIMIZE +#endif + + +#define VM_MEMSIZE 0x40000 +#define VM_MEMMASK (VM_MEMSIZE-1) +#define VM_GLOBALMEMADDR 0x3C000 +#define VM_GLOBALMEMSIZE 0x2000 +#define VM_FIXEDGLOBALSIZE 64 + +enum VM_Commands +{ + VM_MOV, VM_CMP, VM_ADD, VM_SUB, VM_JZ, VM_JNZ, VM_INC, VM_DEC, + VM_JMP, VM_XOR, VM_AND, VM_OR, VM_TEST, VM_JS, VM_JNS, VM_JB, + VM_JBE, VM_JA, VM_JAE, VM_PUSH, VM_POP, VM_CALL, VM_RET, VM_NOT, + VM_SHL, VM_SHR, VM_SAR, VM_NEG, VM_PUSHA,VM_POPA, VM_PUSHF,VM_POPF, + VM_MOVZX,VM_MOVSX,VM_XCHG, VM_MUL, VM_DIV, VM_ADC, VM_SBB, VM_PRINT, + +#ifdef VM_OPTIMIZE + VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, + + VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, + VM_NEGB, VM_NEGD, +#endif + + VM_STANDARD +}; + +enum VM_StandardFilters { + VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO, + VMSF_DELTA, VMSF_UPCASE +}; + +enum VM_Flags {VM_FC=1,VM_FZ=2,VM_FS=0x80000000}; + +enum VM_OpType {VM_OPREG,VM_OPINT,VM_OPREGMEM,VM_OPNONE}; + +struct VM_PreparedOperand +{ + VM_OpType Type; + uint Data; + uint Base; + uint *Addr; +}; + +struct VM_PreparedCommand +{ + VM_Commands OpCode; + bool ByteMode; + VM_PreparedOperand Op1,Op2; +}; + + +struct VM_PreparedProgram +{ + VM_PreparedProgram( Rar_Error_Handler* eh ) : Cmd( eh ), GlobalData( eh ), StaticData( eh ) + {AltCmd=NULL;} + + Array Cmd; + VM_PreparedCommand *AltCmd; + int CmdCount; + + Array GlobalData; + Array StaticData; // static data contained in DB operators + uint InitR[7]; + + byte *FilteredData; + unsigned int FilteredDataSize; +}; + +class RarVM:private BitInput +{ + private: + inline uint GetValue(bool ByteMode,uint *Addr); + inline void SetValue(bool ByteMode,uint *Addr,uint Value); + inline uint* GetOperand(VM_PreparedOperand *CmdOp); + void DecodeArg(VM_PreparedOperand &Op,bool ByteMode); +#ifdef VM_OPTIMIZE + void Optimize(VM_PreparedProgram *Prg); +#endif + bool ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize); +#ifdef VM_STANDARDFILTERS + VM_StandardFilters IsStandardFilter(byte *Code,int CodeSize); + void ExecuteStandardFilter(VM_StandardFilters FilterType); + unsigned int FilterItanium_GetBits(byte *Data,int BitPos,int BitCount); + void FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos, + int BitCount); +#endif + + byte *Mem; + uint R[8]; + uint Flags; + public: + RarVM(); + ~RarVM(); + void Init(); + void handle_mem_error( Rar_Error_Handler& ); + friend class Unpack; + void Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg); + void Execute(VM_PreparedProgram *Prg); + void SetLowEndianValue(uint *Addr,uint Value); + void SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize); + static uint ReadData(BitInput &Inp); +}; + +#endif diff --git a/snesreader/unrar/rarvmtbl.cpp b/snesreader/unrar/rarvmtbl.cpp new file mode 100644 index 00000000..abfdbeeb --- /dev/null +++ b/snesreader/unrar/rarvmtbl.cpp @@ -0,0 +1,57 @@ +// #included by rarvm.cpp +#ifdef RAR_COMMON_HPP +#define VMCF_OP0 0 +#define VMCF_OP1 1 +#define VMCF_OP2 2 +#define VMCF_OPMASK 3 +#define VMCF_BYTEMODE 4 +#define VMCF_JUMP 8 +#define VMCF_PROC 16 +#define VMCF_USEFLAGS 32 +#define VMCF_CHFLAGS 64 + +const +static byte VM_CmdFlags[]= +{ + /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JMP */ VMCF_OP1 | VMCF_JUMP , + /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , + /* VM_PUSH */ VMCF_OP1 , + /* VM_POP */ VMCF_OP1 , + /* VM_CALL */ VMCF_OP1 | VMCF_PROC , + /* VM_RET */ VMCF_OP0 | VMCF_PROC , + /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE , + /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , + /* VM_PUSHA */ VMCF_OP0 , + /* VM_POPA */ VMCF_OP0 , + /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS , + /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS , + /* VM_MOVZX */ VMCF_OP2 , + /* VM_MOVSX */ VMCF_OP2 , + /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE , + /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , + /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , + /* VM_PRINT */ VMCF_OP0 +}; +#endif diff --git a/snesreader/unrar/rawread.cpp b/snesreader/unrar/rawread.cpp new file mode 100644 index 00000000..afe6b06f --- /dev/null +++ b/snesreader/unrar/rawread.cpp @@ -0,0 +1,86 @@ +#include "rar.hpp" + +RawRead::RawRead(ComprDataIO *SrcFile) : Data( SrcFile ) +{ + RawRead::SrcFile=SrcFile; + ReadPos=0; + DataSize=0; +} + +void RawRead::Reset() +{ + ReadPos=0; + DataSize=0; + Data.Reset(); +} + +void RawRead::Read(int Size) +{ + // (removed decryption) + if (Size!=0) + { + Data.Add(Size); + DataSize+=SrcFile->Read(&Data[DataSize],Size); + } +} + + + + +void RawRead::Get(byte &Field) +{ + if (ReadPos2 ? CRC(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2):0xffffffff); +} diff --git a/snesreader/unrar/rawread.hpp b/snesreader/unrar/rawread.hpp new file mode 100644 index 00000000..dc37c304 --- /dev/null +++ b/snesreader/unrar/rawread.hpp @@ -0,0 +1,25 @@ +#ifndef _RAR_RAWREAD_ +#define _RAR_RAWREAD_ + +class RawRead +{ +private: + Array Data; + File *SrcFile; + int DataSize; + int ReadPos; + friend class Archive; +public: + RawRead(File *SrcFile); + void Reset(); + void Read(int Size); + void Get(byte &Field); + void Get(ushort &Field); + void Get(uint &Field); + void Get(byte *Field,int Size); + uint GetCRC(bool ProcessedOnly); + int Size() {return DataSize;} + int PaddedSize() {return Data.Size()-DataSize;} +}; + +#endif diff --git a/snesreader/unrar/readme.txt b/snesreader/unrar/readme.txt new file mode 100644 index 00000000..20e70c77 --- /dev/null +++ b/snesreader/unrar/readme.txt @@ -0,0 +1,63 @@ + + Portable UnRAR version + + + 1. General + + This package includes freeware Unrar C++ source and a few makefiles + (makefile.bcc, makefile.msc+msc.dep, makefile.unix). Unrar source + is subset of RAR and generated from RAR source automatically, + by a small program removing blocks like '#ifndef UNRAR ... #endif'. + Such method is not perfect and you may find some RAR related + stuff unnecessary in Unrar, especially in header files. + + If you wish to port Unrar to a new platform, you may need to edit + '#define LITTLE_ENDIAN' in os.hpp and data type definitions + in rartypes.hpp. + + if computer architecture does not allow not aligned data access, + you need to undefine ALLOW_NOT_ALIGNED_INT and define + STRICT_ALIGNMENT_REQUIRED in os.h. Note that it will increase memory + requirements. + + If you use Borland C++ makefile (makefile.bcc), you need to define + BASEPATHCC environment (or makefile) variable containing + the path to Borland C++ installation. + + Makefile.unix contains numerous compiler option sets. + GCC Linux is selected by default. If you need to compile Unrar + for other platforms, uncomment corresponding lines. + + + 2. Unrar binaries + + If you compiled Unrar for OS, which is not present in "Downloads" + and "RAR extras" on www.rarlab.com, we will appreciate if you send + us the compiled executable to place it to our site. + + + 3. Acknowledgements + + This source includes parts of code written by the following authors: + + Dmitry Shkarin PPMII v.H text compression + Dmitry Subbotin Carryless rangecoder + Szymon Stefanek AES encryption + Brian Gladman AES encryption + Steve Reid SHA-1 hash function + Marcus Herbert makefile.unix file + Tomasz Klim fixes for libunrar.so + Robert Riebisch makefile.dj and patches for DJGPP + + + 4. Legal stuff + + Unrar source may be used in any software to handle RAR archives + without limitations free of charge, but cannot be used to re-create + the RAR compression algorithm, which is proprietary. Distribution + of modified Unrar source in separate form or as a part of other + software is permitted, provided that it is clearly stated in + the documentation and source comments that the code may not be used + to develop a RAR (WinRAR) compatible archiver. + + More detailed license text is available in license.txt. diff --git a/snesreader/unrar/suballoc.cpp b/snesreader/unrar/suballoc.cpp new file mode 100644 index 00000000..66d49d55 --- /dev/null +++ b/snesreader/unrar/suballoc.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** + * This file is part of PPMd project * + * Written and distributed to public domain by Dmitry Shkarin 1997, * + * 1999-2000 * + * Contents: memory allocation routines * + ****************************************************************************/ + +// #included by unpack.cpp +#ifdef RAR_COMMON_HPP +SubAllocator::SubAllocator() +{ + Clean(); +} + + +void SubAllocator::Clean() +{ + SubAllocatorSize=0; +} + + +inline void SubAllocator::InsertNode(void* p,int indx) +{ + ((RAR_NODE*) p)->next=FreeList[indx].next; + FreeList[indx].next=(RAR_NODE*) p; +} + + +inline void* SubAllocator::RemoveNode(int indx) +{ + RAR_NODE* RetVal=FreeList[indx].next; + FreeList[indx].next=RetVal->next; + return RetVal; +} + + +inline uint SubAllocator::U2B(int NU) +{ + return /*8*NU+4*NU*/UNIT_SIZE*NU; +} + + +/* + calculate RAR_MEM_BLK + Items address. Real RAR_MEM_BLK size must be + equal to UNIT_SIZE, so we cannot just add Items to RAR_MEM_BLK address +*/ +inline RAR_MEM_BLK* SubAllocator::MBPtr(RAR_MEM_BLK *BasePtr,int Items) +{ + return((RAR_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); +} + + +inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx) +{ + int i, UDiff=Indx2Units[OldIndx]-Indx2Units[NewIndx]; + byte* p=((byte*) pv)+U2B(Indx2Units[NewIndx]); + if (Indx2Units[i=Units2Indx[UDiff-1]] != UDiff) + { + InsertNode(p,--i); + p += U2B(i=Indx2Units[i]); + UDiff -= i; + } + InsertNode(p,Units2Indx[UDiff-1]); +} + + + + +void SubAllocator::StopSubAllocator() +{ + if ( SubAllocatorSize ) + { + SubAllocatorSize=0; + rarfree(HeapStart); + } +} + + +bool SubAllocator::StartSubAllocator(int SASize) +{ + uint t=SASize << 20; + if (SubAllocatorSize == t) + return true; + StopSubAllocator(); + uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE; +#ifdef STRICT_ALIGNMENT_REQUIRED + AllocSize+=UNIT_SIZE; +#endif + if ((HeapStart=(byte *)rarmalloc(AllocSize)) == NULL) + { + ErrHandler->MemoryError(); + return false; + } + HeapEnd=HeapStart+AllocSize-UNIT_SIZE; + SubAllocatorSize=t; + return true; +} + + +void SubAllocator::InitSubAllocator() +{ + int i, k; + memset(FreeList,0,sizeof(FreeList)); + pText=HeapStart; + uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7); + uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE; + uint Size1=SubAllocatorSize-Size2; + uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+Size1%FIXED_UNIT_SIZE; +#ifdef STRICT_ALIGNMENT_REQUIRED + if (Size1%FIXED_UNIT_SIZE!=0) + RealSize1+=UNIT_SIZE-Size1%FIXED_UNIT_SIZE; +#endif + HiUnit=HeapStart+SubAllocatorSize; + LoUnit=UnitsStart=HeapStart+RealSize1; + FakeUnitsStart=HeapStart+Size1; + HiUnit=LoUnit+RealSize2; + for (i=0,k=1;i < N1 ;i++,k += 1) + Indx2Units[i]=k; + for (k++;i < N1+N2 ;i++,k += 2) + Indx2Units[i]=k; + for (k++;i < N1+N2+N3 ;i++,k += 3) + Indx2Units[i]=k; + for (k++;i < N1+N2+N3+N4;i++,k += 4) + Indx2Units[i]=k; + for (GlueCount=k=i=0;k < 128;k++) + { + i += (Indx2Units[i] < k+1); + Units2Indx[k]=i; + } +} + + +inline void SubAllocator::GlueFreeBlocks() +{ + RAR_MEM_BLK s0, * p, * p1; + int i, k, sz; + if (LoUnit != HiUnit) + *LoUnit=0; + for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++) + while ( FreeList[i].next ) + { + p=(RAR_MEM_BLK*)RemoveNode(i); + p->insertAt(&s0); + p->Stamp=0xFFFF; + p->NU=Indx2Units[i]; + } + for (p=s0.next;p != &s0;p=p->next) + while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU < 0x10000) + { + p1->remove(); + p->NU += p1->NU; + } + while ((p=s0.next) != &s0) + { + for (p->remove(), sz=p->NU;sz > 128;sz -= 128, p=MBPtr(p,128)) + InsertNode(p,N_INDEXES-1); + if (Indx2Units[i=Units2Indx[sz-1]] != sz) + { + k=sz-Indx2Units[--i]; + InsertNode(MBPtr(p,sz-k),k-1); + } + InsertNode(p,i); + } +} + +void* SubAllocator::AllocUnitsRare(int indx) +{ + if ( !GlueCount ) + { + GlueCount = 255; + GlueFreeBlocks(); + if ( FreeList[indx].next ) + return RemoveNode(indx); + } + int i=indx; + do + { + if (++i == N_INDEXES) + { + GlueCount--; + i=U2B(Indx2Units[indx]); + int j=FIXED_UNIT_SIZE*Indx2Units[indx]; + if (FakeUnitsStart-pText > j) + { + FakeUnitsStart-=j; + UnitsStart -= i; + return(UnitsStart); + } + return(NULL); + } + } while ( !FreeList[i].next ); + void* RetVal=RemoveNode(i); + SplitBlock(RetVal,i,indx); + return RetVal; +} + + +inline void* SubAllocator::AllocUnits(int NU) +{ + int indx=Units2Indx[NU-1]; + if ( FreeList[indx].next ) + return RemoveNode(indx); + void* RetVal=LoUnit; + LoUnit += U2B(Indx2Units[indx]); + if (LoUnit <= HiUnit) + return RetVal; + LoUnit -= U2B(Indx2Units[indx]); + return AllocUnitsRare(indx); +} + + +void* SubAllocator::AllocContext() +{ + if (HiUnit != LoUnit) + return (HiUnit -= UNIT_SIZE); + if ( FreeList->next ) + return RemoveNode(0); + return AllocUnitsRare(0); +} + + +void* SubAllocator::ExpandUnits(void* OldPtr,int OldNU) +{ + int i0=Units2Indx[OldNU-1], i1=Units2Indx[OldNU-1+1]; + if (i0 == i1) + return OldPtr; + void* ptr=AllocUnits(OldNU+1); + if ( ptr ) + { + memcpy(ptr,OldPtr,U2B(OldNU)); + InsertNode(OldPtr,i0); + } + return ptr; +} + + +void* SubAllocator::ShrinkUnits(void* OldPtr,int OldNU,int NewNU) +{ + int i0=Units2Indx[OldNU-1], i1=Units2Indx[NewNU-1]; + if (i0 == i1) + return OldPtr; + if ( FreeList[i1].next ) + { + void* ptr=RemoveNode(i1); + memcpy(ptr,OldPtr,U2B(NewNU)); + InsertNode(OldPtr,i0); + return ptr; + } + else + { + SplitBlock(OldPtr,i0,i1); + return OldPtr; + } +} + + +void SubAllocator::FreeUnits(void* ptr,int OldNU) +{ + InsertNode(ptr,Units2Indx[OldNU-1]); +} +#endif diff --git a/snesreader/unrar/suballoc.hpp b/snesreader/unrar/suballoc.hpp new file mode 100644 index 00000000..1ea9f217 --- /dev/null +++ b/snesreader/unrar/suballoc.hpp @@ -0,0 +1,88 @@ +/**************************************************************************** + * This file is part of PPMd project * + * Written and distributed to public domain by Dmitry Shkarin 1997, * + * 1999-2000 * + * Contents: interface to memory allocation routines * + ****************************************************************************/ +#if !defined(_SUBALLOC_H_) +#define _SUBALLOC_H_ + +const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; +const int N_INDEXES=N1+N2+N3+N4; + +#if defined(__GNUC__) && !defined(STRICT_ALIGNMENT_REQUIRED) +#define _PACK_ATTR __attribute__ ((packed)) +#else +#define _PACK_ATTR +#endif /* defined(__GNUC__) */ + +#ifndef STRICT_ALIGNMENT_REQUIRED +#pragma pack(1) +#endif + +struct RAR_MEM_BLK +{ + ushort Stamp, NU; + RAR_MEM_BLK* next, * prev; + void insertAt(RAR_MEM_BLK* p) + { + next=(prev=p)->next; + p->next=next->prev=this; + } + void remove() + { + prev->next=next; + next->prev=prev; + } +} _PACK_ATTR; + +#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef _AIX +#pragma pack(pop) +#else +#pragma pack() +#endif +#endif + + +struct RAR_NODE +{ + RAR_NODE* next; +}; + +class SubAllocator +{ + private: + inline void InsertNode(void* p,int indx); + inline void* RemoveNode(int indx); + inline uint U2B(int NU); + inline void SplitBlock(void* pv,int OldIndx,int NewIndx); + uint GetUsedMemory(); + inline void GlueFreeBlocks(); + void* AllocUnitsRare(int indx); + inline RAR_MEM_BLK* MBPtr(RAR_MEM_BLK *BasePtr,int Items); + + long SubAllocatorSize; + byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; + byte *HeapStart,*LoUnit, *HiUnit; + struct RAR_NODE FreeList[N_INDEXES]; + public: + Rar_Error_Handler* ErrHandler; + SubAllocator(); + ~SubAllocator() {StopSubAllocator();} + void Clean(); + bool StartSubAllocator(int SASize); + void StopSubAllocator(); + void InitSubAllocator(); + inline void* AllocContext(); + inline void* AllocUnits(int NU); + inline void* ExpandUnits(void* ptr,int OldNU); + inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU); + inline void FreeUnits(void* ptr,int OldNU); + long GetAllocatedMemory() {return(SubAllocatorSize);}; + + byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; +}; + + +#endif /* !defined(_SUBALLOC_H_) */ diff --git a/snesreader/unrar/technote.txt b/snesreader/unrar/technote.txt new file mode 100644 index 00000000..15e57593 --- /dev/null +++ b/snesreader/unrar/technote.txt @@ -0,0 +1,275 @@ + + RAR version 3.80 - Technical information + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + THE ARCHIVE FORMAT DESCRIBED BELOW IS ONLY VALID FOR VERSIONS SINCE 1.50 + + ========================================================================== + RAR archive file format + ========================================================================== + + Archive file consists of variable length blocks. The order of these +blocks may vary, but the first block must be a marker block followed by +an archive header block. + + Each block begins with the following fields: + +HEAD_CRC 2 bytes CRC of total block or block part +HEAD_TYPE 1 byte Block type +HEAD_FLAGS 2 bytes Block flags +HEAD_SIZE 2 bytes Block size +ADD_SIZE 4 bytes Optional field - added block size + + Field ADD_SIZE present only if (HEAD_FLAGS & 0x8000) != 0 + + Total block size is HEAD_SIZE if (HEAD_FLAGS & 0x8000) == 0 +and HEAD_SIZE+ADD_SIZE if the field ADD_SIZE is present - when +(HEAD_FLAGS & 0x8000) != 0. + + In each block the followings bits in HEAD_FLAGS have the same meaning: + + 0x4000 - if set, older RAR versions will ignore the block + and remove it when the archive is updated. + if clear, the block is copied to the new archive + file when the archive is updated; + + 0x8000 - if set, ADD_SIZE field is present and the full block + size is HEAD_SIZE+ADD_SIZE. + + Declared block types: + +HEAD_TYPE=0x72 marker block +HEAD_TYPE=0x73 archive header +HEAD_TYPE=0x74 file header +HEAD_TYPE=0x75 old style comment header +HEAD_TYPE=0x76 old style authenticity information +HEAD_TYPE=0x77 old style subblock +HEAD_TYPE=0x78 old style recovery record +HEAD_TYPE=0x79 old style authenticity information +HEAD_TYPE=0x7a subblock + + Comment block is actually used only within other blocks and doesn't +exist separately. + + Archive processing is made in the following manner: + +1. Read and check marker block +2. Read archive header +3. Read or skip HEAD_SIZE-sizeof(MAIN_HEAD) bytes +4. If end of archive encountered then terminate archive processing, + else read 7 bytes into fields HEAD_CRC, HEAD_TYPE, HEAD_FLAGS, + HEAD_SIZE. +5. Check HEAD_TYPE. + if HEAD_TYPE==0x74 + read file header ( first 7 bytes already read ) + read or skip HEAD_SIZE-sizeof(FILE_HEAD) bytes + if (HEAD_FLAGS & 0x100) + read or skip HIGH_PACK_SIZE*0x100000000+PACK_SIZE bytes + else + read or skip PACK_SIZE bytes + else + read corresponding HEAD_TYPE block: + read HEAD_SIZE-7 bytes + if (HEAD_FLAGS & 0x8000) + read ADD_SIZE bytes +6. go to 4. + + + ========================================================================== + Block Formats + ========================================================================== + + + Marker block ( MARK_HEAD ) + + +HEAD_CRC Always 0x6152 +2 bytes + +HEAD_TYPE Header type: 0x72 +1 byte + +HEAD_FLAGS Always 0x1a21 +2 bytes + +HEAD_SIZE Block size = 0x0007 +2 bytes + + The marker block is actually considered as a fixed byte +sequence: 0x52 0x61 0x72 0x21 0x1a 0x07 0x00 + + + + Archive header ( MAIN_HEAD ) + + +HEAD_CRC CRC of fields HEAD_TYPE to RESERVED2 +2 bytes + +HEAD_TYPE Header type: 0x73 +1 byte + +HEAD_FLAGS Bit flags: +2 bytes + 0x0001 - Volume attribute (archive volume) + 0x0002 - Archive comment present + RAR 3.x uses the separate comment block + and does not set this flag. + + 0x0004 - Archive lock attribute + 0x0008 - Solid attribute (solid archive) + 0x0010 - New volume naming scheme ('volname.partN.rar') + 0x0020 - Authenticity information present + RAR 3.x does not set this flag. + + 0x0040 - Recovery record present + 0x0080 - Block headers are encrypted + 0x0100 - First volume (set only by RAR 3.0 and later) + + other bits in HEAD_FLAGS are reserved for + internal use + +HEAD_SIZE Archive header total size including archive comments +2 bytes + +RESERVED1 Reserved +2 bytes + +RESERVED2 Reserved +4 bytes + + + + File header (File in archive) + + +HEAD_CRC CRC of fields from HEAD_TYPE to FILEATTR +2 bytes and file name + +HEAD_TYPE Header type: 0x74 +1 byte + +HEAD_FLAGS Bit flags: +2 bytes + 0x01 - file continued from previous volume + 0x02 - file continued in next volume + 0x04 - file encrypted with password + + 0x08 - file comment present + RAR 3.x uses the separate comment block + and does not set this flag. + + 0x10 - information from previous files is used (solid flag) + (for RAR 2.0 and later) + + bits 7 6 5 (for RAR 2.0 and later) + + 0 0 0 - dictionary size 64 KB + 0 0 1 - dictionary size 128 KB + 0 1 0 - dictionary size 256 KB + 0 1 1 - dictionary size 512 KB + 1 0 0 - dictionary size 1024 KB + 1 0 1 - dictionary size 2048 KB + 1 1 0 - dictionary size 4096 KB + 1 1 1 - file is directory + + 0x100 - HIGH_PACK_SIZE and HIGH_UNP_SIZE fields + are present. These fields are used to archive + only very large files (larger than 2Gb), + for smaller files these fields are absent. + + 0x200 - FILE_NAME contains both usual and encoded + Unicode name separated by zero. In this case + NAME_SIZE field is equal to the length + of usual name plus encoded Unicode name plus 1. + + If this flag is present, but FILE_NAME does not + contain zero bytes, it means that file name + is encoded using UTF-8. + + 0x400 - the header contains additional 8 bytes + after the file name, which are required to + increase encryption security (so called 'salt'). + + 0x800 - Version flag. It is an old file version, + a version number is appended to file name as ';n'. + + 0x1000 - Extended time field present. + + 0x8000 - this bit always is set, so the complete + block size is HEAD_SIZE + PACK_SIZE + (and plus HIGH_PACK_SIZE, if bit 0x100 is set) + +HEAD_SIZE File header full size including file name and comments +2 bytes + +PACK_SIZE Compressed file size +4 bytes + +UNP_SIZE Uncompressed file size +4 bytes + +HOST_OS Operating system used for archiving +1 byte 0 - MS DOS + 1 - OS/2 + 2 - Win32 + 3 - Unix + 4 - Mac OS + 5 - BeOS + +FILE_CRC File CRC +4 bytes + +FTIME Date and time in standard MS DOS format +4 bytes + +UNP_VER RAR version needed to extract file +1 byte + Version number is encoded as + 10 * Major version + minor version. + +METHOD Packing method +1 byte + 0x30 - storing + 0x31 - fastest compression + 0x32 - fast compression + 0x33 - normal compression + 0x34 - good compression + 0x35 - best compression + +NAME_SIZE File name size +2 bytes + +ATTR File attributes +4 bytes + +HIGH_PACK_SIZE High 4 bytes of 64 bit value of compressed file size. +4 bytes Optional value, presents only if bit 0x100 in HEAD_FLAGS + is set. + +HIGH_UNP_SIZE High 4 bytes of 64 bit value of uncompressed file size. +4 bytes Optional value, presents only if bit 0x100 in HEAD_FLAGS + is set. + +FILE_NAME File name - string of NAME_SIZE bytes size + +SALT present if (HEAD_FLAGS & 0x400) != 0 +8 bytes + +EXT_TIME present if (HEAD_FLAGS & 0x1000) != 0 +variable size + +other new fields may appear here. + + + ========================================================================== + Application notes + ========================================================================== + + 1. To process an SFX archive you need to skip the SFX module searching +for the marker block in the archive. There is no marker block sequence (0x52 +0x61 0x72 0x21 0x1a 0x07 0x00) in the SFX module itself. + + 2. The CRC is calculated using the standard polynomial 0xEDB88320. In +case the size of the CRC is less than 4 bytes, only the low order bytes +are used. diff --git a/snesreader/unrar/unicode.cpp b/snesreader/unrar/unicode.cpp new file mode 100644 index 00000000..3853752c --- /dev/null +++ b/snesreader/unrar/unicode.cpp @@ -0,0 +1,106 @@ +#include "rar.hpp" + +#include "unicode.hpp" + +bool WideToChar(const wchar *Src,char *Dest,int DestSize) +{ + bool RetCode=true; +#ifdef _WIN_32 + if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,DestSize,NULL,NULL)==0) + RetCode=false; +#else +#ifdef _APPLE + WideToUtf(Src,Dest,DestSize); +#else +#ifdef MBFUNCTIONS + + size_t ResultingSize=wcstombs(Dest,Src,DestSize); + if (ResultingSize==(size_t)-1) + RetCode=false; + if (ResultingSize==0 && *Src!=0) + RetCode=false; + + if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlenw(Src)>5)==6) + { + if ((*Src&0xc0)!=0x80) + break; + d=((c&0x1f)<<6)|(*Src&0x3f); + Src++; + } + else + if ((c>>4)==14) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) + break; + d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); + Src+=2; + } + else + if ((c>>3)==30) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) + break; + d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); + Src+=3; + } + else + break; + if (--DestSize<0) + break; + if (d>0xffff) + { + if (--DestSize<0 || d>0x10ffff) + break; + *(Dest++)=((d-0x10000)>>10)+0xd800; + *(Dest++)=(d&0x3ff)+0xdc00; + } + else + *(Dest++)=d; + } + *Dest=0; +} + + +// strfn.cpp +void ExtToInt(const char *Src,char *Dest) +{ +#if defined(_WIN_32) + CharToOem(Src,Dest); +#else + if (Dest!=Src) + strcpy(Dest,Src); +#endif +} diff --git a/snesreader/unrar/unicode.hpp b/snesreader/unrar/unicode.hpp new file mode 100644 index 00000000..2ed90e6a --- /dev/null +++ b/snesreader/unrar/unicode.hpp @@ -0,0 +1,10 @@ +#ifndef _RAR_UNICODE_ +#define _RAR_UNICODE_ + +bool WideToChar(const wchar *Src,char *Dest,int DestSize=0x1000000); +void UtfToWide(const char *Src,wchar *Dest,int DestSize); + +// strfn.cpp +void ExtToInt(const char *Src,char *Dest); + +#endif diff --git a/snesreader/unrar/unpack.cpp b/snesreader/unrar/unpack.cpp new file mode 100644 index 00000000..3d9bcf84 --- /dev/null +++ b/snesreader/unrar/unpack.cpp @@ -0,0 +1,1065 @@ +#include "rar.hpp" + +#include "coder.cpp" +#include "suballoc.cpp" +#include "model.cpp" +#ifndef SFX_MODULE +#include "unpack15.cpp" +#include "unpack20.cpp" +#endif + +Unpack::Unpack(ComprDataIO *DataIO) + : VMCode( DataIO ), Filters( DataIO ), PrgStack( DataIO ), OldFilterLengths( DataIO ), ErrHandler( *DataIO ) +{ + PPM.SubAlloc.ErrHandler = DataIO; + LastStackFilter = NULL; + UnpIO=DataIO; + Window=NULL; + ExternalWindow=false; + UnpAllBuf=false; + UnpSomeRead=false; +} + + +Unpack::~Unpack() +{ + if (Window!=NULL && !ExternalWindow) + rarfree( Window ); + InitFilters(); +} + + +void Unpack::Init(byte *Window) +{ + if (Window==NULL) + { + Unpack::Window = (byte*) rarmalloc( MAXWINSIZE ); + if (Unpack::Window==NULL) + ErrHandler.MemoryError(); + } + else + { + Unpack::Window=Window; + ExternalWindow=true; + } + UnpInitData(false); + BitInput::handle_mem_error( ErrHandler ); + Inp.handle_mem_error( ErrHandler ); + + // Only check BitInput, as VM's memory isn't allocated yet + VM.BitInput::handle_mem_error( ErrHandler ); + +#ifndef SFX_MODULE + // RAR 1.5 decompression initialization + OldUnpInitData(false); + InitHuff(); +#endif +} + + +void Unpack::DoUnpack(int Method,bool Solid) +{ + switch(Method) + { +#ifndef SFX_MODULE + case 15: // rar 1.5 compression + Unpack15(Solid); + break; + case 20: // rar 2.x compression + case 26: // files larger than 2GB + Unpack20(Solid); + break; +#endif + case 29: // rar 3.x compression + case 36: // alternative hash + Unpack29(Solid); + break; + } +} + + +inline void Unpack::InsertOldDist(unsigned int Distance) +{ + OldDist[3]=OldDist[2]; + OldDist[2]=OldDist[1]; + OldDist[1]=OldDist[0]; + OldDist[0]=Distance; +} + + +inline void Unpack::InsertLastMatch(unsigned int Length,unsigned int Distance) +{ + LastDist=Distance; + LastLength=Length; +} + + +// These optimizations give 22% speedup on x86, 44% speedup on PowerPC +void Unpack::CopyString(unsigned int Length,unsigned int Distance) +{ + unsigned UnpPtr = this->UnpPtr; // cache in register + byte* const Window = this->Window; // cache in register + + unsigned int DestPtr=UnpPtr-Distance; + if (UnpPtrUnpPtr += Length; + if ( Distance < Length ) // can't use memcpy when source and dest overlap + { + // Length always >= 1 + do + { + Window[UnpPtr++]=Window[DestPtr++]; + } + while (--Length>0) + ; + } + else + { + memcpy( &Window[UnpPtr], &Window[DestPtr], Length ); + } + } + else + { + while (Length--) + { + Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; + UnpPtr=(UnpPtr+1) & MAXWINMASK; + } + + this->UnpPtr = UnpPtr; + } +} + + +int Unpack::DecodeNumber(struct Decode *Dec) +{ + unsigned int Bits; + unsigned int BitField=getbits() & 0xfffe; + if (BitFieldDecodeLen[8]) + if (BitFieldDecodeLen[4]) + if (BitFieldDecodeLen[2]) + if (BitFieldDecodeLen[1]) + Bits=1; + else + Bits=2; + else + if (BitFieldDecodeLen[3]) + Bits=3; + else + Bits=4; + else + if (BitFieldDecodeLen[6]) + if (BitFieldDecodeLen[5]) + Bits=5; + else + Bits=6; + else + if (BitFieldDecodeLen[7]) + Bits=7; + else + Bits=8; + else + if (BitFieldDecodeLen[12]) + if (BitFieldDecodeLen[10]) + if (BitFieldDecodeLen[9]) + Bits=9; + else + Bits=10; + else + if (BitFieldDecodeLen[11]) + Bits=11; + else + Bits=12; + else + if (BitFieldDecodeLen[14]) + if (BitFieldDecodeLen[13]) + Bits=13; + else + Bits=14; + else + Bits=15; + + unsigned int N=Dec->DecodePos[Bits]+((BitField-Dec->DecodeLen[Bits-1])>>(16-Bits)); + if (N>=Dec->MaxNum) + N=0; + // do after reading values, to allow better instruction scheduling + addbits(Bits); + return(Dec->DecodeNum[N]); +} + +const +static unsigned char LDecode[]={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}; +const +static unsigned char LBits[]= {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}; +static int DDecode[DC]; +static byte DBits[DC]; +const +static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; +const +static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; +const +static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; + +void Unpack::init_tables() +{ + if (DDecode[1]==0) + { + int Dist=0,BitLength=0,Slot=0; + for (int I=0;IReadBorder) + { + if (!UnpReadBuf()) + break; + } + if (((WrPtr-UnpPtr) & MAXWINMASK)<260 && WrPtr!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + if (UnpBlockType==BLOCK_PPM) + { + int Ch=PPM.DecodeChar(); + if (Ch==-1) + { + PPM.CleanUp(); + + // turn off PPM compression mode in case of error, so UnRAR will + // call PPM.DecodeInit in case it needs to turn it on back later. + UnpBlockType=BLOCK_LZ; + break; + } + if (Ch==PPMEscChar) + { + int NextCh=PPM.DecodeChar(); + if (NextCh==0) + { + if (!ReadTables()) + break; + continue; + } + if (NextCh==2 || NextCh==-1) + break; + if (NextCh==3) + { + if (!ReadVMCodePPM()) + break; + continue; + } + if (NextCh==4) + { + unsigned int Distance=0,Length; + Length = 0; // avoids warning + bool Failed=false; + for (int I=0;I<4 && !Failed;I++) + { + int Ch=PPM.DecodeChar(); + if (Ch==-1) + Failed=true; + else + if (I==3) + Length=(byte)Ch; + else + Distance=(Distance<<8)+(byte)Ch; + } + if (Failed) + break; + +#ifdef _MSC_VER + // avoid a warning about uninitialized 'Length' variable + #pragma warning( disable : 4701 ) +#endif + CopyString(Length+32,Distance+2); + continue; + } + if (NextCh==5) + { + int Length=PPM.DecodeChar(); + if (Length==-1) + break; + CopyString(Length+4,1); + continue; + } + } + Window[UnpPtr++]=Ch; + continue; + } + + int Number=DecodeNumber((struct Decode *)&LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + continue; + } + if (Number>=271) + { + int Length=LDecode[Number-=271]+3; + if ((Bits=LBits[Number])>0) + { + Length+=getbits()>>(16-Bits); + addbits(Bits); + } + + int DistNumber=DecodeNumber((struct Decode *)&DD); + unsigned int Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + if (DistNumber>9) + { + if (Bits>4) + { + Distance+=((getbits()>>(20-Bits))<<4); + addbits(Bits-4); + } + if (LowDistRepCount>0) + { + LowDistRepCount--; + Distance+=PrevLowDist; + } + else + { + int LowDist=DecodeNumber((struct Decode *)&LDD); + if (LowDist==16) + { + LowDistRepCount=LOW_DIST_REP_COUNT-1; + Distance+=PrevLowDist; + } + else + { + Distance+=LowDist; + PrevLowDist=LowDist; + } + } + } + else + { + Distance+=getbits()>>(16-Bits); + addbits(Bits); + } + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000L) + Length++; + } + + InsertOldDist(Distance); + InsertLastMatch(Length,Distance); + CopyString(Length,Distance); + continue; + } + if (Number==256) + { + if (!ReadEndOfBlock()) + break; + continue; + } + if (Number==257) + { + if (!ReadVMCode()) + break; + continue; + } + if (Number==258) + { + if (LastLength!=0) + CopyString(LastLength,LastDist); + continue; + } + if (Number<263) + { + int DistNum=Number-259; + unsigned int Distance=OldDist[DistNum]; + for (int I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + int LengthNumber=DecodeNumber((struct Decode *)&RD); + int Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=getbits()>>(16-Bits); + addbits(Bits); + } + InsertLastMatch(Length,Distance); + CopyString(Length,Distance); + continue; + } + if (Number<272) + { + unsigned int Distance=SDDecode[Number-=263]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=getbits()>>(16-Bits); + addbits(Bits); + } + InsertOldDist(Distance); + InsertLastMatch(2,Distance); + CopyString(2,Distance); + continue; + } + } + UnpWriteBuf(); +} + + +bool Unpack::ReadEndOfBlock() +{ + unsigned int BitField=getbits(); + bool NewTable,NewFile=false; + if (BitField & 0x8000) + { + NewTable=true; + addbits(1); + } + else + { + NewFile=true; + NewTable=(BitField & 0x4000); + addbits(2); + } + TablesRead=!NewTable; + return !(NewFile || NewTable && !ReadTables()); +} + + +bool Unpack::ReadVMCode() +{ + unsigned int FirstByte=getbits()>>8; + addbits(8); + int Length=(FirstByte & 7)+1; + if (Length==7) + { + Length=(getbits()>>8)+7; + addbits(8); + } + else + if (Length==8) + { + Length=getbits(); + addbits(16); + } + VMCode.Alloc( Length ); + for (int I=0;I=ReadTop-1 && !UnpReadBuf() && I>8; + addbits(8); + } + return(AddVMCode(FirstByte,&VMCode[0],Length)); +} + + +bool Unpack::ReadVMCodePPM() +{ + unsigned int FirstByte=PPM.DecodeChar(); + if ((int)FirstByte==-1) + return(false); + int Length=(FirstByte & 7)+1; + if (Length==7) + { + int B1=PPM.DecodeChar(); + if (B1==-1) + return(false); + Length=B1+7; + } + else + if (Length==8) + { + int B1=PPM.DecodeChar(); + if (B1==-1) + return(false); + int B2=PPM.DecodeChar(); + if (B2==-1) + return(false); + Length=B1*256+B2; + } + VMCode.Alloc( Length ); + for (int I=0;IFilters.Size() || FiltPos>OldFilterLengths.Size()) + return(false); + LastFilter=FiltPos; + bool NewFilter=(FiltPos==Filters.Size()); + + delete LastStackFilter; + LastStackFilter = NULL; + UnpackFilter *StackFilter=new UnpackFilter(&ErrHandler); + LastStackFilter = StackFilter; + if ( !StackFilter ) + ErrHandler.MemoryError(); + + UnpackFilter *Filter; + if (NewFilter) // new filter code, never used before since VM reset + { + // too many different filters, corrupt archive + if (FiltPos>1024) + return(false); + + Filters.Add(1); + Filters[Filters.Size()-1]=Filter=new UnpackFilter(&ErrHandler); + if ( !Filter ) + ErrHandler.MemoryError(); + StackFilter->ParentFilter=Filters.Size()-1; + OldFilterLengths.Add(1); + Filter->ExecCount=0; + } + else // filter was used in the past + { + Filter=Filters[FiltPos]; + StackFilter->ParentFilter=FiltPos; + Filter->ExecCount++; + } + + int EmptyCount=0; + { + for (int I=0;I0) + PrgStack[I]=NULL; + } + } + if (EmptyCount==0) + { + PrgStack.Add(1); + EmptyCount=1; + } + int StackPos=PrgStack.Size()-EmptyCount; + PrgStack[StackPos]=StackFilter; + LastStackFilter = NULL; + StackFilter->ExecCount=Filter->ExecCount; + + uint BlockStart=RarVM::ReadData(Inp); + if (FirstByte & 0x40) + BlockStart+=258; + StackFilter->BlockStart=(BlockStart+UnpPtr)&MAXWINMASK; + if (FirstByte & 0x20) + StackFilter->BlockLength=RarVM::ReadData(Inp); + else + StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MAXWINMASK)<=BlockStart; + +// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + + OldFilterLengths[FiltPos]=StackFilter->BlockLength; + + memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); + StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR; + StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + StackFilter->Prg.InitR[5]=StackFilter->ExecCount; + + if (FirstByte & 0x10) // set registers to optional parameters if any + { + unsigned int InitMask=Inp.fgetbits()>>9; + Inp.faddbits(7); + for (int I=0;I<7;I++) + if (InitMask & (1<Prg.InitR[I]=RarVM::ReadData(Inp); + } + + if (NewFilter) + { + uint VMCodeSize=RarVM::ReadData(Inp); + if (VMCodeSize>=0x10000 || VMCodeSize==0) + return(false); + VMCode.Alloc( VMCodeSize ); + for (int I=0;I>8; + Inp.faddbits(8); + } + VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + VMCode.Reset(); + } + StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0]; + StackFilter->Prg.CmdCount=Filter->Prg.CmdCount; + + int StaticDataSize=Filter->Prg.StaticData.Size(); + if (StaticDataSize>0 && StaticDataSizePrg.StaticData.Add(StaticDataSize); + memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize); + } + + if (StackFilter->Prg.GlobalData.Size()Prg.GlobalData.Reset(); + StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE); + } + byte *GlobalData=&StackFilter->Prg.GlobalData[0]; + for (int I=0;I<7;I++) + VM.SetLowEndianValue((uint *)&GlobalData[I*4],StackFilter->Prg.InitR[I]); + VM.SetLowEndianValue((uint *)&GlobalData[0x1c],StackFilter->BlockLength); + VM.SetLowEndianValue((uint *)&GlobalData[0x20],0); + VM.SetLowEndianValue((uint *)&GlobalData[0x2c],StackFilter->ExecCount); + memset(&GlobalData[0x30],0,16); + + if (FirstByte & 8) // put data block passed as parameter if any + { + if (Inp.Overflow(3)) + return(false); + uint DataSize=RarVM::ReadData(Inp); + if (DataSize>VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE) + return(false); + unsigned int CurSize=StackFilter->Prg.GlobalData.Size(); + if (CurSizePrg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize); + byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE]; + for (int I=0;I>8; + Inp.faddbits(8); + } + } + Inp.InitBitInput(); + return(true); +} + + +bool Unpack::UnpReadBuf() +{ + int DataSize=ReadTop-InAddr; + if (DataSize<0) + return(false); + if (InAddr>BitInput::MAX_SIZE/2) + { + if (DataSize>0) + memmove(InBuf,InBuf+InAddr,DataSize); + InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf); + if (ReadCode>0) + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + return(ReadCode!=-1); +} + + +void Unpack::UnpWriteBuf() +{ + unsigned int WrittenBorder=WrPtr; + unsigned int WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; + for (int I=0;INextWindow) + { + flt->NextWindow=false; + continue; + } + unsigned int BlockStart=flt->BlockStart; + unsigned int BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MAXWINMASK)ParentFilter]->Prg; + VM_PreparedProgram *Prg=&flt->Prg; + + if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) + { + // copy global data from previous script execution if any + Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); + memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + } + + ExecuteCode(Prg); + + if (Prg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) + { + // save global data for next script execution + if (ParentPrg->GlobalData.Size()GlobalData.Size()) + ParentPrg->GlobalData.Alloc(Prg->GlobalData.Size()); + memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + } + else + ParentPrg->GlobalData.Reset(); + + byte *FilteredData=Prg->FilteredData; + unsigned int FilteredDataSize=Prg->FilteredDataSize; + + delete PrgStack[I]; + PrgStack[I]=NULL; + while (I+1BlockStart!=BlockStart || + NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) + break; + + // apply several filters to same data block + + VM.SetMemory(0,FilteredData,FilteredDataSize); + + VM_PreparedProgram *ParentPrg=&Filters[NextFilter->ParentFilter]->Prg; + VM_PreparedProgram *NextPrg=&NextFilter->Prg; + + if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) + { + // copy global data from previous script execution if any + NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); + memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + } + + ExecuteCode(NextPrg); + + if (NextPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) + { + // save global data for next script execution + if (ParentPrg->GlobalData.Size()GlobalData.Size()) + ParentPrg->GlobalData.Alloc(NextPrg->GlobalData.Size()); + memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + } + else + ParentPrg->GlobalData.Reset(); + + FilteredData=NextPrg->FilteredData; + FilteredDataSize=NextPrg->FilteredDataSize; + I++; + delete PrgStack[I]; + PrgStack[I]=NULL; + } + UnpIO->UnpWrite(FilteredData,FilteredDataSize); + UnpSomeRead=true; + WrittenFileSize+=FilteredDataSize; + WrittenBorder=BlockEnd; + WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; + } + else + { + for (int J=I;JNextWindow) + flt->NextWindow=false; + } + WrPtr=WrittenBorder; + return; + } + } + } + + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; +} + + +void Unpack::ExecuteCode(VM_PreparedProgram *Prg) +{ + if (Prg->GlobalData.Size()>0) + { + Prg->InitR[6]=int64to32(WrittenFileSize); + VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize)); + VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>31>>1)); + VM.Execute(Prg); + } +} + + +void Unpack::UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr) +{ + if (EndPtr!=StartPtr) + UnpSomeRead=true; + if (EndPtr=DestUnpSize) + return; + int WriteSize=Size; + Int64 LeftToWrite=DestUnpSize-WrittenFileSize; + if (WriteSize>LeftToWrite) + WriteSize=int64to32(LeftToWrite); + UnpIO->UnpWrite(Data,WriteSize); + WrittenFileSize+=Size; +} + + +bool Unpack::ReadTables() +{ + byte BitLength[BC]; + unsigned char Table[HUFF_TABLE_SIZE]; + if (InAddr>ReadTop-25) + if (!UnpReadBuf()) + return(false); + faddbits((8-InBit)&7); + unsigned int BitField=fgetbits(); + if (BitField & 0x8000) + { + UnpBlockType=BLOCK_PPM; + return(PPM.DecodeInit(this,PPMEscChar)); + } + UnpBlockType=BLOCK_LZ; + + PrevLowDist=0; + LowDistRepCount=0; + + if (!(BitField & 0x4000)) + memset(UnpOldTable,0,sizeof(UnpOldTable)); + faddbits(2); + { + for (int I=0;I> 12); + faddbits(4); + if (Length==15) + { + int ZeroCount=(byte)(fgetbits() >> 12); + faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && IReadTop-5) + if (!UnpReadBuf()) + return(false); + int Number=DecodeNumber((struct Decode *)&BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable[I]) & 0xf; + I++; + } + else + if (Number<18) + { + int N; + if (Number==16) + { + N=(fgetbits() >> 13)+3; + faddbits(3); + } + else + { + N=(fgetbits() >> 9)+11; + faddbits(7); + } + while (N-- > 0 && I> 13)+3; + faddbits(3); + } + else + { + N=(fgetbits() >> 9)+11; + faddbits(7); + } + while (N-- > 0 && IReadTop) + return(false); + MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC); + MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC); + MakeDecodeTables(&Table[NC+DC],(struct Decode *)&LDD,LDC); + MakeDecodeTables(&Table[NC+DC+LDC],(struct Decode *)&RD,RC); + memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + return(true); +} + + +void Unpack::UnpInitData(int Solid) +{ + if (!Solid) + { + TablesRead=false; + memset(OldDist,0,sizeof(OldDist)); + OldDistPtr=0; + LastDist=LastLength=0; +// memset(Window,0,MAXWINSIZE); + memset(UnpOldTable,0,sizeof(UnpOldTable)); + memset(&LD,0,sizeof(LD)); + memset(&DD,0,sizeof(DD)); + memset(&LDD,0,sizeof(LDD)); + memset(&RD,0,sizeof(RD)); + memset(&BD,0,sizeof(BD)); + UnpPtr=WrPtr=0; + PPMEscChar=2; + UnpBlockType=BLOCK_LZ; + + InitFilters(); + } + InitBitInput(); + WrittenFileSize=0; + ReadTop=0; + ReadBorder=0; +#ifndef SFX_MODULE + UnpInitData20(Solid); +#endif +} + + +void Unpack::InitFilters() +{ + delete LastStackFilter; + LastStackFilter = NULL; + + OldFilterLengths.Reset(); + LastFilter=0; + { + for (int I=0;IDecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + for (I=0;IDecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++) + { + N=2*(N+LenCount[I]); + M=N<<(15-I); + if (M>0xFFFF) + M=0xFFFF; + Dec->DecodeLen[I]=(unsigned int)M; + TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1]; + } + + for (I=0;IDecodeNum[TmpPos[LenTab[I] & 0xF]++]=I; + Dec->MaxNum=Size; +} diff --git a/snesreader/unrar/unpack.hpp b/snesreader/unrar/unpack.hpp new file mode 100644 index 00000000..918fdb6c --- /dev/null +++ b/snesreader/unrar/unpack.hpp @@ -0,0 +1,227 @@ +#ifndef _RAR_UNPACK_ +#define _RAR_UNPACK_ + +enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; + +struct Decode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[2]; +}; + +struct LitDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[NC]; +}; + +struct DistDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[DC]; +}; + +struct LowDistDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[LDC]; +}; + +struct RepDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[RC]; +}; + +struct BitDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[BC]; +}; + +struct UnpackFilter + : Rar_Allocator +{ + unsigned int BlockStart; + unsigned int BlockLength; + unsigned int ExecCount; + bool NextWindow; + + // position of parent filter in Filters array used as prototype for filter + // in PrgStack array. Not defined for filters in Filters array. + unsigned int ParentFilter; + + VM_PreparedProgram Prg; + UnpackFilter( Rar_Error_Handler* eh ) : Prg( eh ) { } +}; + +/***************************** Unpack v 2.0 *********************************/ +struct MultDecode +{ + unsigned int MaxNum; + unsigned int DecodeLen[16]; + unsigned int DecodePos[16]; + unsigned int DecodeNum[MC20]; +}; + +struct AudioVariables +{ + int K1,K2,K3,K4,K5; + int D1,D2,D3,D4; + int LastDelta; + unsigned int Dif[11]; + unsigned int ByteCount; + int LastChar; +}; +/***************************** Unpack v 2.0 *********************************/ + + +// public so operator new/delete will be accessible, argh +class Unpack:public BitInput +{ +private: + friend class Pack; + + void Unpack29(bool Solid); + bool UnpReadBuf(); + void UnpWriteBuf(); + void ExecuteCode(VM_PreparedProgram *Prg); + void UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr); + void UnpWriteData(byte *Data,int Size); + bool ReadTables(); + void MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size); + int DecodeNumber(struct Decode *Dec); + void CopyString(); + inline void InsertOldDist(unsigned int Distance); + inline void InsertLastMatch(unsigned int Length,unsigned int Distance); + void UnpInitData(int Solid); + void CopyString(unsigned int Length,unsigned int Distance); + bool ReadEndOfBlock(); + bool ReadVMCode(); + bool ReadVMCodePPM(); + bool AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize); + void InitFilters(); + + ComprDataIO *UnpIO; + ModelPPM PPM; + int PPMEscChar; + + Array VMCode; // here to avoid leaks + BitInput Inp; // here to avoid leaks + + RarVM VM; + + UnpackFilter* LastStackFilter; // avoids leak for stack-based filter + + /* Filters code, one entry per filter */ + Array Filters; + + /* Filters stack, several entrances of same filter are possible */ + Array PrgStack; + + /* lengths of preceding blocks, one length per filter. Used to reduce + size required to write block length if lengths are repeating */ + Array OldFilterLengths; + + int LastFilter; + + bool TablesRead; + struct LitDecode LD; + struct DistDecode DD; + struct LowDistDecode LDD; + struct RepDecode RD; + struct BitDecode BD; + + unsigned int OldDist[4],OldDistPtr; + unsigned int LastDist,LastLength; + + unsigned int UnpPtr,WrPtr; + + int ReadTop; + int ReadBorder; + + unsigned char UnpOldTable[HUFF_TABLE_SIZE]; + + int UnpBlockType; + + byte *Window; + bool ExternalWindow; + + + Int64 DestUnpSize; + + enum { Suspended = false }; // original source could never set to true + bool UnpAllBuf; + bool UnpSomeRead; + Int64 WrittenFileSize; + bool FileExtracted; + + int PrevLowDist,LowDistRepCount; + + /***************************** Unpack v 1.5 *********************************/ + void Unpack15(bool Solid); + void ShortLZ(); + void LongLZ(); + void HuffDecode(); + void GetFlagsBuf(); + void OldUnpInitData(int Solid); + void InitHuff(); + void CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace); + void OldCopyString(unsigned int Distance,unsigned int Length); + unsigned int DecodeNum(int Num,unsigned int StartPos, + const unsigned int *DecTab,const unsigned int *PosTab); + void OldUnpWriteBuf(); + + unsigned int ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; + unsigned int Place[256],PlaceA[256],PlaceB[256],PlaceC[256]; + unsigned int NToPl[256],NToPlB[256],NToPlC[256]; + unsigned int FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; + int Buf60,NumHuf,StMode,LCount,FlagsCnt; + unsigned int Nhfb,Nlzb,MaxDist3; + /***************************** Unpack v 1.5 *********************************/ + + /***************************** Unpack v 2.0 *********************************/ + void Unpack20(bool Solid); + struct MultDecode MD[4]; + unsigned char UnpOldTable20[MC20*4]; + int UnpAudioBlock,UnpChannels,UnpCurChannel,UnpChannelDelta; + void CopyString20(unsigned int Length,unsigned int Distance); + bool ReadTables20(); + void UnpInitData20(int Solid); + void ReadLastTables(); + byte DecodeAudio(int Delta); + struct AudioVariables AudV[4]; + /***************************** Unpack v 2.0 *********************************/ + +public: + Rar_Error_Handler& ErrHandler; + byte const* window_wrptr() const { return &Window [WrPtr & MAXWINMASK]; } + + static void init_tables(); + Unpack(ComprDataIO *DataIO); + ~Unpack(); + void Init(byte *Window=NULL); + void DoUnpack(int Method,bool Solid); + void SetDestSize(Int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} + + unsigned int GetChar() + { + if (InAddr>BitInput::MAX_SIZE-30) + UnpReadBuf(); + return(InBuf[InAddr++]); + } +}; + +#endif diff --git a/snesreader/unrar/unpack15.cpp b/snesreader/unrar/unpack15.cpp new file mode 100644 index 00000000..b2a63c05 --- /dev/null +++ b/snesreader/unrar/unpack15.cpp @@ -0,0 +1,532 @@ +// #included by unpack.cpp +#ifdef RAR_COMMON_HPP +#define STARTL1 2 +const +static unsigned int DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, + 0xee00,0xf000,0xf200,0xf200,0xffff}; +const +static unsigned int PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; + +#define STARTL2 3 +const +static unsigned int DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, + 0xf000,0xf200,0xf240,0xffff}; +const +static unsigned int PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; + +#define STARTHF0 4 +const +static unsigned int DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, + 0xf200,0xf200,0xffff}; +const +static unsigned int PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; + + +#define STARTHF1 5 +const +static unsigned int DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, + 0xf7e0,0xffff}; +const +static unsigned int PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; + + +#define STARTHF2 5 +const +static unsigned int DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, + 0xffff,0xffff}; +const +static unsigned int PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; + + +#define STARTHF3 6 +const +static unsigned int DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, + 0xffff}; +const +static unsigned int PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; + + +#define STARTHF4 8 +const +static unsigned int DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; +const +static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; + + +void Unpack::Unpack15(bool Solid) +{ + if (Suspended) + UnpPtr=WrPtr; + else + { + UnpInitData(Solid); + OldUnpInitData(Solid); + UnpReadBuf(); + if (!Solid) + { + InitHuff(); + UnpPtr=0; + } + else + UnpPtr=WrPtr; + --DestUnpSize; + } + if (DestUnpSize>=0) + { + GetFlagsBuf(); + FlagsCnt=8; + } + + while (DestUnpSize>=0) + { + UnpPtr&=MAXWINMASK; + + if (InAddr>ReadTop-30 && !UnpReadBuf()) + break; + if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) + { + OldUnpWriteBuf(); + if (Suspended) + return; + } + if (StMode) + { + HuffDecode(); + continue; + } + + if (--FlagsCnt < 0) + { + GetFlagsBuf(); + FlagsCnt=7; + } + + if (FlagBuf & 0x80) + { + FlagBuf<<=1; + if (Nlzb > Nhfb) + LongLZ(); + else + HuffDecode(); + } + else + { + FlagBuf<<=1; + if (--FlagsCnt < 0) + { + GetFlagsBuf(); + FlagsCnt=7; + } + if (FlagBuf & 0x80) + { + FlagBuf<<=1; + if (Nlzb > Nhfb) + HuffDecode(); + else + LongLZ(); + } + else + { + FlagBuf<<=1; + ShortLZ(); + } + } + } + OldUnpWriteBuf(); +} + + +void Unpack::OldUnpWriteBuf() +{ + if (UnpPtr!=WrPtr) + UnpSomeRead=true; + if (UnpPtrUnpWrite(&Window[WrPtr],-WrPtr & MAXWINMASK); + UnpIO->UnpWrite(Window,UnpPtr); + UnpAllBuf=true; + } + else + UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); + WrPtr=UnpPtr; +} + + +#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) +#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) + +void Unpack::ShortLZ() +{ + const + static unsigned int ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; + const + static unsigned int ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, + 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; + const + static unsigned int ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; + const + static unsigned int ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, + 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; + + + unsigned int Length,SaveLength; + unsigned int LastDistance; + unsigned int Distance; + int DistancePlace; + NumHuf=0; + + unsigned int BitField=fgetbits(); + if (LCount==2) + { + faddbits(1); + if (BitField >= 0x8000) + { + OldCopyString((unsigned int)LastDist,LastLength); + return; + } + BitField <<= 1; + LCount=0; + } + + BitField>>=8; + +// not thread safe, replaced by GetShortLen1 and GetShortLen2 macro +// ShortLen1[1]=ShortLen2[3]=Buf60+3; + + if (AvrLn1<37) + { + for (Length=0;;Length++) + if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) + break; + faddbits(GetShortLen1(Length)); + } + else + { + for (Length=0;;Length++) + if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) + break; + faddbits(GetShortLen2(Length)); + } + + if (Length >= 9) + { + if (Length == 9) + { + LCount++; + OldCopyString((unsigned int)LastDist,LastLength); + return; + } + if (Length == 14) + { + LCount=0; + Length=DecodeNum(fgetbits(),STARTL2,DecL2,PosL2)+5; + Distance=(fgetbits()>>1) | 0x8000; + faddbits(15); + LastLength=Length; + LastDist=Distance; + OldCopyString(Distance,Length); + return; + } + + LCount=0; + SaveLength=Length; + Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; + Length=DecodeNum(fgetbits(),STARTL1,DecL1,PosL1)+2; + if (Length==0x101 && SaveLength==10) + { + Buf60 ^= 1; + return; + } + if (Distance > 256) + Length++; + if (Distance >= MaxDist3) + Length++; + + OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + OldCopyString(Distance,Length); + return; + } + + LCount=0; + AvrLn1 += Length; + AvrLn1 -= AvrLn1 >> 4; + + DistancePlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; + Distance=ChSetA[DistancePlace]; + if (--DistancePlace != -1) + { + PlaceA[Distance]--; + LastDistance=ChSetA[DistancePlace]; + PlaceA[LastDistance]++; + ChSetA[DistancePlace+1]=LastDistance; + ChSetA[DistancePlace]=Distance; + } + Length+=2; + OldDist[OldDistPtr++] = ++Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + OldCopyString(Distance,Length); +} + + +void Unpack::LongLZ() +{ + unsigned int Length; + unsigned int Distance; + unsigned int DistancePlace,NewDistancePlace; + unsigned int OldAvr2,OldAvr3; + + NumHuf=0; + Nlzb+=16; + if (Nlzb > 0xff) + { + Nlzb=0x90; + Nhfb >>= 1; + } + OldAvr2=AvrLn2; + + unsigned int BitField=fgetbits(); + if (AvrLn2 >= 122) + Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); + else + if (AvrLn2 >= 64) + Length=DecodeNum(BitField,STARTL1,DecL1,PosL1); + else + if (BitField < 0x100) + { + Length=BitField; + faddbits(16); + } + else + { + for (Length=0;((BitField<> 5; + + BitField=fgetbits(); + if (AvrPlcB > 0x28ff) + DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); + else + if (AvrPlcB > 0x6ff) + DistancePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); + else + DistancePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); + + AvrPlcB += DistancePlace; + AvrPlcB -= AvrPlcB >> 8; + while (1) + { + Distance = ChSetB[DistancePlace & 0xff]; + NewDistancePlace = NToPlB[Distance++ & 0xff]++; + if (!(Distance & 0xff)) + CorrHuff(ChSetB,NToPlB); + else + break; + } + + ChSetB[DistancePlace]=ChSetB[NewDistancePlace]; + ChSetB[NewDistancePlace]=Distance; + + Distance=((Distance & 0xff00) | (fgetbits() >> 8)) >> 1; + faddbits(7); + + OldAvr3=AvrLn3; + if (Length!=1 && Length!=4) + if (Length==0 && Distance <= MaxDist3) + { + AvrLn3++; + AvrLn3 -= AvrLn3 >> 8; + } + else + if (AvrLn3 > 0) + AvrLn3--; + Length+=3; + if (Distance >= MaxDist3) + Length++; + if (Distance <= 256) + Length+=8; + if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) + MaxDist3=0x7f00; + else + MaxDist3=0x2001; + OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + OldCopyString(Distance,Length); +} + + +void Unpack::HuffDecode() +{ + unsigned int CurByte,NewBytePlace; + unsigned int Length; + unsigned int Distance; + int BytePlace; + + unsigned int BitField=fgetbits(); + + if (AvrPlc > 0x75ff) + BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); + else + if (AvrPlc > 0x5dff) + BytePlace=DecodeNum(BitField,STARTHF3,DecHf3,PosHf3); + else + if (AvrPlc > 0x35ff) + BytePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); + else + if (AvrPlc > 0x0dff) + BytePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); + else + BytePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); + BytePlace&=0xff; + if (StMode) + { + if (BytePlace==0 && BitField > 0xfff) + BytePlace=0x100; + if (--BytePlace==-1) + { + BitField=fgetbits(); + faddbits(1); + if (BitField & 0x8000) + { + NumHuf=StMode=0; + return; + } + else + { + Length = (BitField & 0x4000) ? 4 : 3; + faddbits(1); + Distance=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); + Distance = (Distance << 5) | (fgetbits() >> 11); + faddbits(5); + OldCopyString(Distance,Length); + return; + } + } + } + else + if (NumHuf++ >= 16 && FlagsCnt==0) + StMode=1; + AvrPlc += BytePlace; + AvrPlc -= AvrPlc >> 8; + Nhfb+=16; + if (Nhfb > 0xff) + { + Nhfb=0x90; + Nlzb >>= 1; + } + + Window[UnpPtr++]=(byte)(ChSet[BytePlace]>>8); + --DestUnpSize; + + while (1) + { + CurByte=ChSet[BytePlace]; + NewBytePlace=NToPl[CurByte++ & 0xff]++; + if ((CurByte & 0xff) > 0xa1) + CorrHuff(ChSet,NToPl); + else + break; + } + + ChSet[BytePlace]=ChSet[NewBytePlace]; + ChSet[NewBytePlace]=CurByte; +} + + +void Unpack::GetFlagsBuf() +{ + unsigned int Flags,NewFlagsPlace; + unsigned int FlagsPlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); + + while (1) + { + Flags=ChSetC[FlagsPlace]; + FlagBuf=Flags>>8; + NewFlagsPlace=NToPlC[Flags++ & 0xff]++; + if ((Flags & 0xff) != 0) + break; + CorrHuff(ChSetC,NToPlC); + } + + ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace]; + ChSetC[NewFlagsPlace]=Flags; +} + + +void Unpack::OldUnpInitData(int Solid) +{ + if (!Solid) + { + AvrPlcB=AvrLn1=AvrLn2=AvrLn3=NumHuf=Buf60=0; + AvrPlc=0x3500; + MaxDist3=0x2001; + Nhfb=Nlzb=0x80; + } + FlagsCnt=0; + FlagBuf=0; + StMode=0; + LCount=0; + ReadTop=0; +} + + +void Unpack::InitHuff() +{ + for (unsigned int I=0;I<256;I++) + { + Place[I]=PlaceA[I]=PlaceB[I]=I; + PlaceC[I]=(~I+1) & 0xff; + ChSet[I]=ChSetB[I]=I<<8; + ChSetA[I]=I; + ChSetC[I]=((~I+1) & 0xff)<<8; + } + memset(NToPl,0,sizeof(NToPl)); + memset(NToPlB,0,sizeof(NToPlB)); + memset(NToPlC,0,sizeof(NToPlC)); + CorrHuff(ChSetB,NToPlB); +} + + +void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace) +{ + int I,J; + for (I=7;I>=0;I--) + for (J=0;J<32;J++,CharSet++) + *CharSet=(*CharSet & ~0xff) | I; + memset(NumToPlace,0,sizeof(NToPl)); + for (I=6;I>=0;I--) + NumToPlace[I]=(7-I)*32; +} + + +void Unpack::OldCopyString(unsigned int Distance,unsigned int Length) +{ + DestUnpSize-=Length; + while (Length--) + { + Window[UnpPtr]=Window[(UnpPtr-Distance) & MAXWINMASK]; + UnpPtr=(UnpPtr+1) & MAXWINMASK; + } +} + + +unsigned int Unpack::DecodeNum(int Num,unsigned int StartPos, + const unsigned int *DecTab,const unsigned int *PosTab) +{ + int I; + for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) + StartPos++; + faddbits(StartPos); + return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); +} +#endif diff --git a/snesreader/unrar/unpack20.cpp b/snesreader/unrar/unpack20.cpp new file mode 100644 index 00000000..0896d1ce --- /dev/null +++ b/snesreader/unrar/unpack20.cpp @@ -0,0 +1,394 @@ +// #included by unpack.cpp +#ifdef RAR_COMMON_HPP +#include "rar.hpp" + +// Presumably these optimizations give similar speedup as those for CopyString in unpack.cpp +void Unpack::CopyString20(unsigned int Length,unsigned int Distance) +{ + LastDist=OldDist[OldDistPtr++ & 3]=Distance; + LastLength=Length; + DestUnpSize-=Length; + + unsigned UnpPtr = this->UnpPtr; // cache in register + byte* const Window = this->Window; // cache in register + + unsigned int DestPtr=UnpPtr-Distance; + if (UnpPtrUnpPtr += Length; + if ( Distance < Length ) // can't use memcpy when source and dest overlap + { + Window[UnpPtr++]=Window[DestPtr++]; + Window[UnpPtr++]=Window[DestPtr++]; + while (Length>2) + { + Length--; + Window[UnpPtr++]=Window[DestPtr++]; + } + } + else + { + memcpy( &Window[UnpPtr], &Window[DestPtr], Length ); + } + } + else + { + while (Length--) + { + Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; + UnpPtr=(UnpPtr+1) & MAXWINMASK; + } + this->UnpPtr = UnpPtr; + } +} + + +void Unpack::Unpack20(bool Solid) +{ + const + static unsigned char LDecode[]={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}; + const + static unsigned char LBits[]= {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}; + const + static int DDecode[]={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,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; + const + static unsigned char DBits[]= {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, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + const + static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; + const + static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; + unsigned int Bits; + + if (Suspended) + UnpPtr=WrPtr; + else + { + UnpInitData(Solid); + if (!UnpReadBuf()) + return; + if (!Solid) + if (!ReadTables20()) + return; + --DestUnpSize; + } + + while (is64plus(DestUnpSize)) + { + UnpPtr&=MAXWINMASK; + + if (InAddr>ReadTop-30) + if (!UnpReadBuf()) + break; + if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) + { + OldUnpWriteBuf(); + if (Suspended) + return; + } + if (UnpAudioBlock) + { + int AudioNumber=DecodeNumber((struct Decode *)&MD[UnpCurChannel]); + + if (AudioNumber==256) + { + if (!ReadTables20()) + break; + continue; + } + Window[UnpPtr++]=DecodeAudio(AudioNumber); + if (++UnpCurChannel==UnpChannels) + UnpCurChannel=0; + --DestUnpSize; + continue; + } + + int Number=DecodeNumber((struct Decode *)&LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + --DestUnpSize; + continue; + } + if (Number>269) + { + int Length=LDecode[Number-=270]+3; + if ((Bits=LBits[Number])>0) + { + Length+=getbits()>>(16-Bits); + addbits(Bits); + } + + int DistNumber=DecodeNumber((struct Decode *)&DD); + unsigned int Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + Distance+=getbits()>>(16-Bits); + addbits(Bits); + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000L) + Length++; + } + + CopyString20(Length,Distance); + continue; + } + if (Number==269) + { + if (!ReadTables20()) + break; + continue; + } + if (Number==256) + { + CopyString20(LastLength,LastDist); + continue; + } + if (Number<261) + { + unsigned int Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; + int LengthNumber=DecodeNumber((struct Decode *)&RD); + int Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=getbits()>>(16-Bits); + addbits(Bits); + } + if (Distance>=0x101) + { + Length++; + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000) + Length++; + } + } + CopyString20(Length,Distance); + continue; + } + if (Number<270) + { + unsigned int Distance=SDDecode[Number-=261]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=getbits()>>(16-Bits); + addbits(Bits); + } + CopyString20(2,Distance); + continue; + } + } + ReadLastTables(); + OldUnpWriteBuf(); +} + + +bool Unpack::ReadTables20() +{ + byte BitLength[BC20]; + unsigned char Table[MC20*4]; + int TableSize,N,I; + if (InAddr>ReadTop-25) + if (!UnpReadBuf()) + return(false); + unsigned int BitField=getbits(); + UnpAudioBlock=(BitField & 0x8000); + + if (!(BitField & 0x4000)) + memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + addbits(2); + + if (UnpAudioBlock) + { + UnpChannels=((BitField>>12) & 3)+1; + if (UnpCurChannel>=UnpChannels) + UnpCurChannel=0; + addbits(2); + TableSize=MC20*UnpChannels; + } + else + TableSize=NC20+DC20+RC20; + + for (I=0;I> 12); + addbits(4); + } + MakeDecodeTables(BitLength,(struct Decode *)&BD,BC20); + I=0; + while (IReadTop-5) + if (!UnpReadBuf()) + return(false); + int Number=DecodeNumber((struct Decode *)&BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable20[I]) & 0xf; + I++; + } + else + if (Number==16) + { + N=(getbits() >> 14)+3; + addbits(2); + while (N-- > 0 && I> 13)+3; + addbits(3); + } + else + { + N=(getbits() >> 9)+11; + addbits(7); + } + while (N-- > 0 && IReadTop) + return(true); + if (UnpAudioBlock) + for (I=0;I=InAddr+5) + if (UnpAudioBlock) + { + if (DecodeNumber((struct Decode *)&MD[UnpCurChannel])==256) + ReadTables20(); + } + else + if (DecodeNumber((struct Decode *)&LD)==269) + ReadTables20(); +} + + +void Unpack::UnpInitData20(int Solid) +{ + if (!Solid) + { + UnpAudioBlock=UnpChannelDelta=UnpCurChannel=0; + UnpChannels=1; + + memset(AudV,0,sizeof(AudV)); + memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + memset(MD,0,sizeof(MD)); + } +} + + +byte Unpack::DecodeAudio(int Delta) +{ + struct AudioVariables *V=&AudV[UnpCurChannel]; + V->ByteCount++; + V->D4=V->D3; + V->D3=V->D2; + V->D2=V->LastDelta-V->D1; + V->D1=V->LastDelta; + int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; + PCh=(PCh>>3) & 0xFF; + + unsigned int Ch=PCh-Delta; + + int D=((signed char)Delta)<<3; + + V->Dif[0]+=abs(D); + V->Dif[1]+=abs(D-V->D1); + V->Dif[2]+=abs(D+V->D1); + V->Dif[3]+=abs(D-V->D2); + V->Dif[4]+=abs(D+V->D2); + V->Dif[5]+=abs(D-V->D3); + V->Dif[6]+=abs(D+V->D3); + V->Dif[7]+=abs(D-V->D4); + V->Dif[8]+=abs(D+V->D4); + V->Dif[9]+=abs(D-UnpChannelDelta); + V->Dif[10]+=abs(D+UnpChannelDelta); + + UnpChannelDelta=V->LastDelta=(signed char)(Ch-V->LastChar); + V->LastChar=Ch; + + if ((V->ByteCount & 0x1F)==0) + { + unsigned int MinDif=V->Dif[0],NumMinDif=0; + V->Dif[0]=0; + for (int I=1;IDif)/sizeof(V->Dif[0]);I++) + { + if (V->Dif[I]Dif[I]; + NumMinDif=I; + } + V->Dif[I]=0; + } + switch(NumMinDif) + { + case 1: + if (V->K1>=-16) + V->K1--; + break; + case 2: + if (V->K1<16) + V->K1++; + break; + case 3: + if (V->K2>=-16) + V->K2--; + break; + case 4: + if (V->K2<16) + V->K2++; + break; + case 5: + if (V->K3>=-16) + V->K3--; + break; + case 6: + if (V->K3<16) + V->K3++; + break; + case 7: + if (V->K4>=-16) + V->K4--; + break; + case 8: + if (V->K4<16) + V->K4++; + break; + case 9: + if (V->K5>=-16) + V->K5--; + break; + case 10: + if (V->K5<16) + V->K5++; + break; + } + } + return((byte)Ch); +} +#endif diff --git a/snesreader/unrar/unrar.cpp b/snesreader/unrar/unrar.cpp new file mode 100644 index 00000000..2c3baa7b --- /dev/null +++ b/snesreader/unrar/unrar.cpp @@ -0,0 +1,350 @@ +// unrar_core 3.8.5. http://www.slack.net/~ant/ + +#include "unrar.h" + +#include "rar.hpp" +#include + +// This source code is a heavily modified version based on the unrar package. +// It may not be used to develop a RAR (WinRAR) compatible archiver. +// See unrar/license.txt for copyright and licensing. + +// Same as printf when debugging, otherwise 0 +#ifndef debug_printf + #define debug_printf 1 ? (void)0 : (void) +#endif + +// If expr != unrar_ok, returns its value +#define RETURN_ERR( expr ) \ + do {\ + unrar_err_t err_;\ + if ( (err_ = (expr)) != unrar_ok )\ + return err_;\ + } while ( 0 ) + + +// Receives errors reported from deep within library. +// MUST be macro. +#define NONLOCAL_ERROR( p ) \ + setjmp( p->Arc.jmp_env ) + +void Rar_Error_Handler::ReportError( unrar_err_t err ) +{ + if ( err ) + longjmp( jmp_env, err ); +} + +void Rar_Error_Handler::MemoryError() +{ + ReportError( unrar_err_memory ); +} + + +//// Internal + +unrar_t::unrar_t() : + Buffer( &Arc ) +{ + Arc.user_read = NULL; + Arc.user_write = NULL; + Arc.Tell_ = 0; + Arc.write_error = unrar_ok; + data_ = NULL; + own_data_ = NULL; + close_file = NULL; + FileCount = 0; + Unp = NULL; + + unrar_init(); +} + +unrar_t::~unrar_t() +{ + if ( Arc.write_error ) { } + + if ( close_file ) + close_file( Arc.user_read_data ); + + delete Unp; + + free( own_data_ ); +} + +// True if current file is compressed in way that affects solid extraction state +static inline bool solid_file( const unrar_t* p ) +{ + return p->Arc.Solid && + p->Arc.NewLhd.Method != 0x30 && + p->Arc.NewLhd.FullPackSize != 0; +} + +static void update_solid_pos( unrar_t* p ) +{ + if ( p->solid_pos == p->Arc.CurBlockPos ) + p->solid_pos = p->Arc.NextBlockPos; +} + +static unrar_err_t extract_( unrar_t* p, unrar_write_func user_write, void* user_data ) +{ + assert( !p->done ); + assert( !solid_file( p ) || p->solid_pos == p->Arc.CurBlockPos ); + + if ( p->Arc.write_error ) { } + p->Arc.write_error = unrar_ok; + p->Arc.user_write = user_write; + p->Arc.user_write_data = user_data; + RETURN_ERR( p->ExtractCurrentFile( user_write == NULL ) ); + p->Arc.user_write = NULL; + RETURN_ERR( p->Arc.write_error ); + + update_solid_pos( p ); + + return unrar_ok; +} + +static unrar_err_t skip_solid( unrar_t* p ) +{ + if ( !solid_file( p ) ) + { + update_solid_pos( p ); + return unrar_ok; + } + + return extract_( p, NULL, NULL ); +} + +static inline bool IsLink(uint Attr) +{ + return((Attr & 0xF000)==0xA000); +} + +static unrar_err_t next_( unrar_t* p, bool skipping_solid ) +{ + if ( p->done ) + return unrar_err_arc_eof; + + free( p->own_data_ ); + p->own_data_ = NULL; + p->data_ = NULL; + + for (;;) + { + p->Arc.SeekToNext(); + unrar_err_t const err = p->Arc.ReadHeader(); + if ( err != unrar_err_arc_eof ) + RETURN_ERR( err ); + //else + // debug_printf( "unrar: Didn't end with ENDARC_HEAD\n" ); // rar -en causes this + + HEADER_TYPE const type = (HEADER_TYPE) p->Arc.GetHeaderType(); + + if ( err != unrar_ok || type == ENDARC_HEAD ) + { + p->done = true; + break; + } + + if ( type != FILE_HEAD ) + { + // Skip non-files + if ( type != NEWSUB_HEAD && type != PROTECT_HEAD && type != SIGN_HEAD && type != SUB_HEAD ) + debug_printf( "unrar: Skipping unknown block type: %X\n", (unsigned) type ); + + update_solid_pos( p ); + } + else + { + // Update even for non-solid files, in case it's not extracted + if ( !solid_file( p ) ) + update_solid_pos( p ); + + if ( p->Arc.IsArcLabel() ) + { + // Ignore labels + } + else if ( IsLink( p->Arc.NewLhd.FileAttr ) ) + { + // Ignore links + + p->update_first_file_pos(); + p->FileCount++; // Links are treated as files + } + else if ( p->Arc.IsArcDir() ) + { + // Ignore directories + } + else + { + p->info.size = p->Arc.NewLhd.UnpSize; + p->info.name = p->Arc.NewLhd.FileName; + p->info.name_w = p->Arc.NewLhd.FileNameW; + p->info.is_unicode = (p->Arc.NewLhd.Flags & LHD_UNICODE) != 0; + p->info.dos_date = p->Arc.NewLhd.mtime.time; + p->info.crc = p->Arc.NewLhd.FileCRC; + p->info.is_crc32 = !p->Arc.OldFormat; + + // Stop for files + break; + } + + // Original code assumed that non-file items were never solid compressed + check( !solid_file( p ) ); + + // Skip non-file solid-compressed items (original code assumed there were none) + if ( skipping_solid ) + RETURN_ERR( skip_solid( p ) ); + } + } + + return unrar_ok; +} + +static unrar_err_t open_( unrar_t* p, unrar_read_func read, void* user_data ) +{ + p->Arc.user_read = read; + p->Arc.user_read_data = user_data; + + RETURN_ERR( p->Arc.IsArchive() ); + + p->begin_pos = p->Arc.NextBlockPos; + p->solid_pos = p->Arc.NextBlockPos; + p->first_file_pos = INT_MAX; + p->done = false; + + return unrar_ok; +} + + +//// Interface + + // Needed when user read throws exception + struct unrar_ptr { + unrar_t* p; + unrar_ptr() { p = NULL; } + ~unrar_ptr() { delete p; } + }; + +unrar_err_t unrar_open_custom( unrar_t** impl_out, unrar_read_func read, void* user_data ) +{ + *impl_out = NULL; + + unrar_ptr ptr; + ptr.p = new unrar_t; + if ( !ptr.p ) + return unrar_err_memory; + + RETURN_ERR( NONLOCAL_ERROR( ptr.p ) ); + RETURN_ERR( open_( ptr.p, read, user_data ) ); + RETURN_ERR( next_( ptr.p, false ) ); + + *impl_out = ptr.p; + ptr.p = NULL; + + //delete ptr.p; // done automatically at end of function + + return unrar_ok; +} + +void unrar_close( unrar_t* ar ) +{ + delete ar; +} + +unrar_bool unrar_done( const unrar_t* p ) +{ + return p->done; +} + +unrar_err_t unrar_next( unrar_t* p ) +{ + assert( !unrar_done( p ) ); + + RETURN_ERR( NONLOCAL_ERROR( p ) ); + return next_( p, false ); +} + +const unrar_info_t* unrar_info( unrar_t const* p ) +{ + assert( !unrar_done( p ) ); + + return &p->info; +} + +unrar_pos_t unrar_tell( const unrar_t* p ) +{ + return p->Arc.CurBlockPos; +} + +unrar_err_t unrar_seek( unrar_t* p, unrar_pos_t n ) +{ + p->Arc.NextBlockPos = n; + p->done = false; + p->FileCount = (n <= p->first_file_pos ? 0 : 1); + + return unrar_next( p ); +} + +unrar_err_t unrar_rewind( unrar_t* p ) +{ + return unrar_seek( p, p->begin_pos ); +} + +unrar_err_t unrar_try_extract( const unrar_t* p ) +{ + assert( !unrar_done( p ) ); + + return ((unrar_t*) p)->ExtractCurrentFile( true, true ); +} + + static unrar_err_t reopen( unrar_t* p ) + { + // Save and restore archive reader + unrar_read_func read = p->Arc.user_read; + void* user_data = p->Arc.user_read_data; + + void (*close_file)( void* ) = p->close_file; + p->close_file = NULL; + + p->~unrar_t(); + new (p) unrar_t; + + p->close_file = close_file; + + return open_( p, read, user_data ); + } + +unrar_err_t unrar_extract_custom( unrar_t* p, unrar_write_func user_write, void* user_data ) +{ + assert( !unrar_done( p ) ); + + RETURN_ERR( NONLOCAL_ERROR( p ) ); + + if ( solid_file( p ) ) + { + unrar_pos_t pos = p->Arc.CurBlockPos; + if ( p->solid_pos != pos ) + { + // Next file to solid extract isn't current one + + if ( p->solid_pos > pos ) + RETURN_ERR( reopen( p ) ); + else + p->Arc.NextBlockPos = p->solid_pos; + + RETURN_ERR( next_( p, true ) ); + + // Keep extracting until solid position is at desired file + while ( !p->done && p->solid_pos < pos ) + { + RETURN_ERR( skip_solid( p ) ); + RETURN_ERR( next_( p, true ) ); + } + + // Be sure we're at right file + if ( p->solid_pos != pos || p->Arc.CurBlockPos != pos ) + return unrar_err_corrupt; + } + } + + return extract_( p, user_write, user_data ); +} diff --git a/snesreader/unrar/unrar.h b/snesreader/unrar/unrar.h new file mode 100644 index 00000000..470bc146 --- /dev/null +++ b/snesreader/unrar/unrar.h @@ -0,0 +1,164 @@ +/** RAR archive scanning and extraction \file */ + +/* unrar_core 3.8.5 */ +#ifndef UNRAR_H +#define UNRAR_H + +#include +#include + +#if !defined (UNRAR_NO_LONG_LONG) && defined (LLONG_MAX) + typedef long long unrar_long_long; +#else + typedef long unrar_long_long; +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + +/** Error code, or 0 if function was successful. See Errors for more. Except +where noted, once an operation returns an error, that archive should not be +used any further, other than with unrar_close(). */ +#ifndef unrar_err_t /* (#ifndef allows better testing of library) */ + typedef int unrar_err_t; +#endif + +/** First parameter of most functions is unrar_t*, or const unrar_t* if nothing +is changed. */ +typedef struct unrar_t unrar_t; + +/** File position */ +typedef unrar_long_long unrar_pos_t; + +/** Boolean, where 0 is false and 1 is true */ +typedef int unrar_bool; + + +/******** Open/close ********/ + +/** Initializes static tables used by library. Automatically called by +unrar_open(). OK to call more than once. */ +void unrar_init( void ); + +/** Opens archive and points *out at it. If error, sets *out to NULL. */ +unrar_err_t unrar_open( unrar_t** out, const char path [] ); + +/** User archive read callback. When called, user_data is a copy of that passed +to unrar_open_custom(). Callback must do the following: Read avail bytes from +file at offset pos and set *count to avail, where avail is the lesser of *count +and file_size-pos. Put read bytes into *out and return unrar_ok. If fewer than +avail bytes could be read successfully, return a non-zero error code. */ +typedef unrar_err_t (*unrar_read_func)( void* user_data, + void* out, int* count, unrar_pos_t pos ); + +/** Same as unrar_open(), except data is read using supplied function rather +than from file. */ +unrar_err_t unrar_open_custom( unrar_t** unrar_out, + unrar_read_func, void* user_data ); + +/** Closes archive and frees memory. OK to pass NULL. */ +void unrar_close( unrar_t* ); + + +/******** Scanning ********/ + +/** True if at end of archive. Must be called after unrar_open() or +unrar_rewind(), as an archive might contain no files. */ +unrar_bool unrar_done( const unrar_t* ); + +/** Goes to next file in archive. If there are no more files, unrar_done() will +now return true. */ +unrar_err_t unrar_next( unrar_t* ); + +/** Goes back to first file in archive, as if it were just opened with +unrar_open(). */ +unrar_err_t unrar_rewind( unrar_t* ); + +/** Position of current file in archive. Will never return zero. */ +unrar_pos_t unrar_tell( const unrar_t* ); + +/** Returns to file at previously-saved position. */ +unrar_err_t unrar_seek( unrar_t*, unrar_pos_t ); + + +/**** Info ****/ + +/** Information about current file */ +typedef struct unrar_info_t +{ + unrar_pos_t size; /**< Uncompressed size */ + const char* name; /**< Name, in Unicode if is_unicode is true */ + const wchar_t* name_w; /**< Name in Unicode, "" if unavailable */ + unrar_bool is_unicode; /**< True if name is Unicode (UTF-8) */ + unsigned int dos_date; /**< Date in DOS-style format, 0 if unavailable */ + unsigned int crc; /**< Checksum; algorithm depends on archive */ + unrar_bool is_crc32; /**< True if crc is CRC-32 */ +} unrar_info_t; + +/** Information about current file. Pointer is valid until unrar_next(), +unrar_rewind(), unrar_seek(), or unrar_close(). */ +const unrar_info_t* unrar_info( const unrar_t* ); + + +/**** Extraction ****/ + +/** Returns unrar_ok if current file can be extracted, otherwise error +indicating why it can't be extracted (too new/old compression algorithm, +encrypted, segmented). Archive is still usable if this returns error, +just the current file can't be extracted. */ +unrar_err_t unrar_try_extract( const unrar_t* ); + +/** Extracts at most size bytes from current file into out. If file is larger, +discards excess bytes. If file is smaller, only writes unrar_size() bytes. */ +unrar_err_t unrar_extract( unrar_t*, void* out, unrar_pos_t size ); + +/** Extracts data to memory and returns pointer to it in *out. Pointer is +valid until unrar_next(), unrar_rewind(), unrar_seek(), or unrar_close(). OK to +call more than once for same file. Optimized to avoid allocating memory when +entire file will already be kept in internal window. */ +unrar_err_t unrar_extract_mem( unrar_t* p, void const** out ); + +/** User extracted data write callback. When called, user_data is a copy of +that passed to unrar_extract_custom(). Callback must do the following: Write +count bytes from *in to wherever extracted data goes and return unrar_ok. If +data cannot be written successfully, return a non-zero error code. */ +typedef unrar_err_t (*unrar_write_func)( void* user_data, + const void* in, int count ); + +/** Extracts current file and writes data using supplied function. Any error +it returns will be returned by this function, and archive will still be +usable. */ +unrar_err_t unrar_extract_custom( unrar_t*, + unrar_write_func, void* user_data ); + + +/******** Errors ********/ + +/** Error string associated with unrar error code. Always returns valid +pointer to a C string; never returns NULL. Returns "" for unrar_ok. */ +const char* unrar_err_str( unrar_err_t ); + +enum { + unrar_ok = 0,/**< No error; success. Guaranteed to be zero. */ + unrar_err_memory = 1,/**< Out of memory */ + unrar_err_open = 2,/**< Couldn't open file (not found/permissions) */ + unrar_err_not_arc = 3,/**< Not a RAR archive */ + unrar_err_corrupt = 4,/**< Archive is corrupt */ + unrar_err_io = 5,/**< Read failed */ + unrar_err_arc_eof = 6,/**< At end of archive; no more files */ + unrar_err_encrypted = 7,/**< Encryption not supported */ + unrar_err_segmented = 8,/**< Segmentation not supported */ + unrar_err_huge = 9,/**< Huge (2GB+) archives not supported */ + unrar_err_old_algo = 10,/**< Compressed with unsupported old algorithm */ + unrar_err_new_algo = 11,/**< Compressed with unsupported new algorithm */ + unrar_next_err = 100/**< Errors range from 0 to unrar_next_err-1 */ +}; + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snesreader/unrar/unrar_misc.cpp b/snesreader/unrar/unrar_misc.cpp new file mode 100644 index 00000000..a0a6551a --- /dev/null +++ b/snesreader/unrar/unrar_misc.cpp @@ -0,0 +1,170 @@ +// Misc functions outside the core interface + +#include "unrar.h" + +#include "rar.hpp" +#include + +// This source code is a heavily modified version based on the unrar package. +// It may not be used to develop a RAR (WinRAR) compatible archiver. +// See unrar/license.txt for copyright and licensing. + +void unrar_init() +{ + if (CRCTab[1]==0) + InitCRC(); + + Unpack::init_tables(); +} + +struct unrar_extract_mem_t +{ + char* out; + char* end; +}; + +extern "C" { + static unrar_err_t extract_write( void* user_data, const void* in, int count ) + { + unrar_extract_mem_t* p = (unrar_extract_mem_t*) user_data; + + unrar_pos_t remain = p->end - p->out; + if ( remain > 0 ) + { + if ( count > remain ) + count = remain; + + memcpy( p->out, in, count ); + p->out += count; + } + + return unrar_ok; + } +} + +unrar_err_t unrar_extract( unrar_t* p, void* out, unrar_pos_t size ) +{ + assert( !unrar_done( p ) ); + + unrar_extract_mem_t m; + m.out = (char*) out; + m.end = m.out + size; + return unrar_extract_custom( p, &extract_write, &m ); +} + +inline +static bool is_entire_file( const unrar_t* p, const void* in, int count ) +{ + return (count == p->Arc.NewLhd.UnpSize && p->Unp && in == p->Unp->window_wrptr()); +} + +extern "C" { + static unrar_err_t extract_mem( void* data, void const* in, int count ) + { + unrar_t* p = (unrar_t*) data; + + // We might have pointer to entire file + if ( !p->data_ && is_entire_file( p, in, count ) ) + { + p->data_ = in; + return unrar_ok; + } + + // We don't have it, so allocate memory to read entire file into + if ( !p->own_data_ ) + { + assert( !p->data_ ); + + unrar_pos_t size = unrar_info( p )->size; + p->own_data_ = malloc( size ? size : 1 ); + if ( !p->own_data_ ) + return unrar_err_memory; + + p->data_ = p->own_data_; + } + + memcpy( (void*) p->data_, in, count ); + p->data_ = (char*) p->data_ + count; + + return unrar_ok; + } +} + +unrar_err_t unrar_extract_mem( unrar_t* p, void const** out ) +{ + assert( !unrar_done( p ) ); + + *out = NULL; + + if ( !p->data_ ) + { + unrar_err_t err = unrar_extract_custom( p, &extract_mem, p ); + if ( err ) + return err; + } + + *out = (p->own_data_ ? p->own_data_ : p->data_); + return unrar_ok; +} + +const char* unrar_err_str( unrar_err_t err ) +{ + switch ( err ) + { + case unrar_ok: return ""; + case unrar_err_memory: return "out of memory"; + case unrar_err_open: return "couldn't open RAR archive"; + case unrar_err_not_arc: return "not a RAR archive"; + case unrar_err_corrupt: return "RAR archive is corrupt"; + case unrar_err_io: return "couldn't read/write"; + case unrar_err_arc_eof: return "unexpected end of archive"; + case unrar_err_encrypted: return "encryption not supported"; + case unrar_err_segmented: return "segmentation not supported"; + case unrar_err_huge: return "huge (2GB+) archives are not supported"; + case unrar_err_old_algo: return "compressed using older algorithm than supported"; + case unrar_err_new_algo: return "compressed using newer algorithm than supported"; + } + + assert( false ); + return "problem with RAR"; +} + +int ComprDataIO::Read( void* p, int n ) +{ + unrar_err_t err = user_read( user_read_data, p, &n, Tell_ ); + if ( err ) + ReportError( err ); + + Tell_ += n; + if ( Tell_ < 0 ) + ReportError( unrar_err_huge ); + + return n; +} + +void ComprDataIO::UnpWrite( byte* out, uint count ) +{ + if ( !SkipUnpCRC ) + { + if ( write_error == unrar_ok ) + write_error = user_write( user_write_data, out, count ); + + if ( OldFormat ) + UnpFileCRC = OldCRC( (ushort) UnpFileCRC, out, count ); + else + UnpFileCRC = CRC( UnpFileCRC, out, count ); + } +} + +int ComprDataIO::UnpRead( byte* out, uint count ) +{ + if ( count <= 0 ) + return 0; + + if ( count > (uint) UnpPackedSize ) + count = UnpPackedSize; + + int result = Read( out, count ); + UnpPackedSize -= result; + return result; +} diff --git a/snesreader/unrar/unrar_open.cpp b/snesreader/unrar/unrar_open.cpp new file mode 100644 index 00000000..f9b0c40d --- /dev/null +++ b/snesreader/unrar/unrar_open.cpp @@ -0,0 +1,45 @@ +// Separate file to avoid linking to f* functions unless user calls unrar_open_file() + +#include "unrar.h" +#include "rar.hpp" +#include + +extern "C" { + static unrar_err_t unrar_read_file( void* user_data, void* out, int* count, unrar_pos_t pos ) + { + FILE* file = (FILE*) user_data; + + // most of the time, seeking won't be necessary + if ( pos != ftell( file ) && fseek( file, pos, SEEK_SET ) != 0 ) + return unrar_err_corrupt; + + *count = (int) fread( out, 1, *count, file ); + + if ( ferror( file ) != 0 ) + return unrar_err_io; + + return unrar_ok; + } +} + +static void unrar_close_file( void* user_data ) +{ + fclose( (FILE*) user_data ); +} + +unrar_err_t unrar_open( unrar_t** arc_out, const char path [] ) +{ + *arc_out = NULL; + + FILE* file = fopen( path, "rb" ); + if ( file == NULL ) + return unrar_err_open; + + unrar_err_t err = unrar_open_custom( arc_out, &unrar_read_file, file ); + if ( err != unrar_ok ) + fclose( file ); + else + (*arc_out)->close_file = &unrar_close_file; + + return err; +} diff --git a/snesreader/unrar/whatsnew.txt b/snesreader/unrar/whatsnew.txt new file mode 100644 index 00000000..38012e9a --- /dev/null +++ b/snesreader/unrar/whatsnew.txt @@ -0,0 +1,267 @@ + + + WinRAR - What's new in the latest version + + + Version 3.80 + + 1. Added support for ZIP archives containing Unicode file names + in UTF-8 format. When creating ZIP archive, WinRAR stores + names in Unicode only if they cannot be stored correctly using + the current single byte character set. + + 2. Added decompression support for WinZip AES encrypted ZIP archives. + + 3. Improved Unicode support for RAR and ZIP archive names. + + 4. "Ask before overwrite" and "Skip existing files" update modes + are now available in archiving dialog. They allow to specify + WinRAR behavior when updating already existing files in archive. + Unlike already available "Fresh existing files only" and + "Add and update files", these new modes ignore file date + and compare only file names. + + Command line equivalents of these modes are: + + a) switch -o enables "Ask before overwrite" archiving mode; + + b) switch -o- enables "Skip existing files" archiving mode; + + c) switch -o+ enables "Overwrite all" mode (default for archiving). + + 5. New "Add to context menu" option in "Profile parameters" dialog. + If this option is on, the profile name will be displayed in Explorer + context menus allowing to activate a profile from context menu. + + 6. New -cp switch allows to select a compression profile + in command line mode. It is supported only by GUI WinRAR.exe, + not by rar.exe. + + 7. New "Options" page of archiving dialog contains the group of + settings modifying the behavior of "Delete files after archiving" + option from "General" page: + + a) Delete files. Delete files normally like in previous WinRAR + versions. + + b) Move files to Recycle Bin. Deleted files are placed to + Recycle Bin. + + Command line equivalent of this option is -dr switch. + + c) Wipe files. Before deleting file data are overwritten by + zero bytes to prevent recovery of deleted files. + + Command line equivalent of this option is -dw switch. + + All these options have an effect only if "Delete files + after archiving" is on. You can enable any of these options + in the default compression profile to change the default + behavior of "Delete files after archiving". + + 8. WinRAR "Extraction path and options" dialog is now resizable. + You can use the mouse to drag its border to the desired size + and provide more space for folder tree pane. WinRAR will store + new dimensions of this dialog. + + 9. New "Update" SFX script command and "Update mode" group + of options in "Update" page of "Advanced SFX options" dialog. + These command and options allow to check time and implement + file time based updating; + + 10. SFX script "Shortcut" command and "Add shortcut..." command + in "Advanced SFX options" dialog now allow to specify + an icon file containing an icon associated with shortcut. + + 11. New "Wipe temporary files" option in "Settings/Security" dialog + provides more secure, though slower, way to delete temporary + WinRAR files. + + 12. WinRAR and RAR display the total progress bar when unpacking + a multivolume RAR archive if all volumes are present + in the same folder. + + 13. WinRAR and RAR automatically expand names of environment + variables in list files. For example, a list file can contain + lines like: + + %windir%\*.exe + %USERPROFILE%\Desktop + + This feature is available only in Windows RAR version. + + 14. Added support of TAR archives with non-zero "extra field" data. + + 15. Added support of TAR archives, which does not contain + the end of archive entry consisting of 512 zero bytes. + + 16. Improved Unicode support when dragging files from WinRAR window. + + 17. Shift+Tab key combination can be used in main WinRAR window to + switch the input focus between interface elements (files, comment, + tree, address) in reverse order. In previous versions Shift+Tab + used the same order as Tab. + + 18. Corrected a possible WinRAR crash when opening truncated + UDF ISO files. + + + Version 3.71 + + 1. Archive names in rar.log error log file always include + the full path. + + 2. WinRAR tray icon is compatible with high DPI display modes. + + 3. If you modified a file in archive with encrypted names using + an external editor, WinRAR will not ask for archive password again + when prompting to update a file. It will use a password which + you entered when opening an archive, + + 4. Bugs fixed: + + a) switch -tl and "Set archive time to latest file time" option + could fail in previous version. Sometimes they set archive time + to current system time instead of latest file time; + + b) if -ag switch mask contained archive number, month and minute + characters, WinRAR placed 'I' character instead of minute value + into generated archive name for archive numbers exceeding 1; + + c) high ASCII names in ISO files using ISO 9660 format without + Joliet format extension were displayed incorrectly; + + d) WinRAR could crash when decompressing some of corrupt RAR archives; + + e) if "Turn PC off when done" option was set in "Convert archives" + command, WinRAR turned PC off after converting the first archive + in selected group instead of after converting the entire group; + + f) if user specified a non-existent destination path in SFX archive + in Vista, SFX could enter into infinite "create new SFX window" + loop; + + g) WinRAR could fail to unpack an individual file from subfolder + of ACE archive using the drag and drop. + + + Version 3.70 + + 1. Numerous Windows Vista compatibility changes: + + a) help format changed from old HLP to newer HTML based CHM; + + b) GUI self-extracting modules attempt to request for + administrator permissions if they cannot create destination + folder under current user account; + + c) Log file rar.log and WinRAR theme files are stored + in %APPDATA%\WinRAR folder instead of WinRAR program files folder. + + Exported settings file settings.reg is also stored + in %APPDATA%\WinRAR folder by default, but it is possible to + select another folder in "Save WinRAR settings" and "Load WinRAR + settings" dialogs. + + WinRAR searches for registration key and settings.reg + both in its program files folder and in %APPDATA%\WinRAR; + + It is possible to set the string value "AppData" in Registry key + HKEY_CURRENT_USER\Software\WinRAR\Paths to override the default + %appdata%\WinRAR path for WinRAR settings. + + For example, if you wish to store theme files in WinRAR folder, + set this value to "c:\Program Files\WinRAR". + + d) Vista compatibility changes in WinRAR shell integration; + + e) New "Request administrative access" option in "Advanced" page + of "Advanced SFX options" allows to create SFX archive, + which will request the administrative access when started + in Windows Vista. + + Command line equivalent of this option is -iadm switch. + + 2. Added support for ISO 13346 (UDF) file format. This format + is frequently used in ISO images of DVD disks. + + 3. Added Unicode support for ISO 9660 files, so WinRAR should + handle non-English file names in .iso files better. + + 4. Design changes in window displaying archiving and extraction + progress: + + a) it provides more space for file names, allowing lengthy names; + + b) it displays the current archive name in separate line, + allowing much longer archive names than before; + + c) when archiving, it displays the current compression ratio + in separate line; + + d) it can use both standard Windows and classic WinRAR progress bars. + Turn on "Windows progress bars" option in WinRAR "Settings/General" + dialog to use standard progress bars. By default this option is + on if some Windows visual style is active and off if Windows Classic + theme is selected. + + Windows progress bars are two color only, so they do not indicate + the current compression ratio. But now the ratio is displayed + in separate line; + + e) "Mode..." button moved to bottom of window. + + 5. GUI self-extracting modules support following command line + switches: + + -d set the destination path + -p specify a password + -s silent mode, hide all + -s1 same as -s + -s2 silent mode, hide start dialog + -sp specify parameters for setup program + + 6. GUI self-extracting modules do not pass the entire command line + to setup program like they did in previous versions. + If you need to get access to entire command line of SFX archive, + parse sfxcmd environment variable which contains this command line. + + 7. New switch -sc[objects] allowing to select character + sets for archive comments and list files. It replaces -fcu switch + introduced in RAR 3.60, which was removed from list of supported + switches. Now you need to specify -scuc instead of -fcu to use + Unicode comments. Unlike -fcu, -sc also supports OEM and ANSI charset. + + 8. New "Save archive copy as..." command in "File" menu. + This command may be useful if you opened an archive from Internet + directly in WinRAR and then decided to save it on local disk. + + 9. "Word wrap" command added to "View" menu of WinRAR internal viewer, + so you can change the wrapping mode of already opened viewer window. + + State of this option is not stored between viewing sessions. + If you need to change the default word wrap mode, use WinRAR + "Settings/Viewer" dialog. + + 10. Buttons "Up" and "Down" added to "Organize profiles" dialog. + Using these buttons you can change position of selected profile + in the list. + + 11. Operation progress is displayed when adding the recovery record. + + 12. If WinRAR is minimized to tray and mouse is over its icon, + WinRAR diplays a message about the current operation progress. + In previous versions it included only percent done, now it also + contains the time left information. + + 13. Console RAR displays "Calculating the control sum" message + when calculating CRC32 control sum for newly created RAR volume. + Previous versions also calculated the volume control sum, + but did it silently. + + 14. Archives history list in "File" menu allows Unicode names, + providing more reliable support for non-English archive names. + + 15. Stack overflow vulnerability has been corrected in password + processing module of console RAR and UnRAR. GUI WinRAR is not + affected. We are thankful to the iDEFENSE LABS for reporting this bug. diff --git a/snesreader/xml.cpp b/snesreader/xml.cpp new file mode 100644 index 00000000..f173d5cc --- /dev/null +++ b/snesreader/xml.cpp @@ -0,0 +1,752 @@ +#include "xml.hpp" +XML xml; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +void XML::generate(string &xml, const uint8_t *data, unsigned size) { + read_header(data, size); + + xml = "\n"; + + if(type == TypeBsx) { + xml << ""; + return; + } else if(type == TypeSufamiTurbo) { + xml << ""; + return; + } else if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + return; + } + + xml << "\n"; + + if(type == TypeSuperGameBoy1Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(type == TypeSuperGameBoy2Bios) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(has_spc7110) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + xml << " \n"; + } else { + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == ExLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + if((rom_size > 0x200000) || (ram_size > 32 * 1024)) { + xml << " \n"; + } else { + xml << " \n"; + } + xml << " \n"; + } + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_sdd1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_cx4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + //ST-0011 addresses not verified; chip is unsupported + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; +} + +void XML::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + const unsigned index = find_header(data, size); + const uint8 mapperid = data[index + Mapper]; + const uint8 rom_type = data[index + RomType]; + const uint8 rom_size = data[index + RomSize]; + const uint8 company = data[index + Company]; + const uint8 regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8 n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned XML::find_header(const uint8_t *data, unsigned size) const { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned XML::score_header(const uint8_t *data, unsigned size, unsigned addr) const { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16 resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16 checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16 complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8 mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned XML::gameboy_ram_size(const uint8_t *data, unsigned size) const { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool XML::gameboy_has_rtc(const uint8_t *data, unsigned size) const { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} diff --git a/snesreader/xml.hpp b/snesreader/xml.hpp new file mode 100644 index 00000000..c75f8e11 --- /dev/null +++ b/snesreader/xml.hpp @@ -0,0 +1,103 @@ +class XML { +public: + void generate(nall::string &xml, const uint8_t *data, unsigned size); + +private: + void read_header(const uint8_t *data, unsigned size); + unsigned find_header(const uint8_t *data, unsigned size) const; + unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; + + unsigned gameboy_ram_size(const uint8_t *data, unsigned size) const; + bool gameboy_has_rtc(const uint8_t *data, unsigned size) const; + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +extern XML xml; diff --git a/snesreader/zlib/adler32.c b/snesreader/zlib/adler32.c new file mode 100644 index 00000000..007ba262 --- /dev/null +++ b/snesreader/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$ */ + +#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/snesreader/zlib/crc32.c b/snesreader/zlib/crc32.c new file mode 100644 index 00000000..f658a9ef --- /dev/null +++ b/snesreader/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$ */ + +/* + 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/snesreader/zlib/crc32.h b/snesreader/zlib/crc32.h new file mode 100644 index 00000000..8053b611 --- /dev/null +++ b/snesreader/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/snesreader/zlib/inffast.c b/snesreader/zlib/inffast.c new file mode 100644 index 00000000..7a148eb7 --- /dev/null +++ b/snesreader/zlib/inffast.c @@ -0,0 +1,318 @@ +/* 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" + +#ifndef ASMINF + +/* 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 = "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 = "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 = "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 = "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 /* !ASMINF */ diff --git a/snesreader/zlib/inffast.h b/snesreader/zlib/inffast.h new file mode 100644 index 00000000..1e88d2d9 --- /dev/null +++ b/snesreader/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/snesreader/zlib/inffixed.h b/snesreader/zlib/inffixed.h new file mode 100644 index 00000000..75ed4b59 --- /dev/null +++ b/snesreader/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/snesreader/zlib/inflate.c b/snesreader/zlib/inflate.c new file mode 100644 index 00000000..37744b3e --- /dev/null +++ b/snesreader/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 = "incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = "unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = "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 = "unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = "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 = "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 = "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/snesreader/zlib/inflate.h b/snesreader/zlib/inflate.h new file mode 100644 index 00000000..07bd3e78 --- /dev/null +++ b/snesreader/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/snesreader/zlib/inftrees.c b/snesreader/zlib/inftrees.c new file mode 100644 index 00000000..8a9c13ff --- /dev/null +++ b/snesreader/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/snesreader/zlib/inftrees.h b/snesreader/zlib/inftrees.h new file mode 100644 index 00000000..b1104c87 --- /dev/null +++ b/snesreader/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/snesreader/zlib/readme.txt b/snesreader/zlib/readme.txt new file mode 100644 index 00000000..d1331635 --- /dev/null +++ b/snesreader/zlib/readme.txt @@ -0,0 +1,10 @@ +This is NOT the complete zlib distribution; it is just a subset of the +source needed by the File_Extractor library. I've made some minor +changes: + +* Enabled DYNAMIC_CRC_TABLE in zconf.h, to reduce executable size +slightly. +* Made z_stream_s's msg const char* to eliminate many warnings. + +You can remove these sources and link to your own copy of zlib if +desired. diff --git a/snesreader/zlib/zconf.h b/snesreader/zlib/zconf.h new file mode 100644 index 00000000..ee5a9181 --- /dev/null +++ b/snesreader/zlib/zconf.h @@ -0,0 +1,335 @@ +/* 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$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* added for file_extractor; OK to remove, just increases executable size */ +#define DYNAMIC_CRC_TABLE + +/* + * 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/snesreader/zlib/zlib.h b/snesreader/zlib/zlib.h new file mode 100644 index 00000000..e4768717 --- /dev/null +++ b/snesreader/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 */ + + const 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/snesreader/zlib/zlib.txt b/snesreader/zlib/zlib.txt new file mode 100644 index 00000000..80f71ae8 --- /dev/null +++ b/snesreader/zlib/zlib.txt @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. 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). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 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 + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/snesreader/zlib/zutil.c b/snesreader/zlib/zutil.c new file mode 100644 index 00000000..d55f5948 --- /dev/null +++ b/snesreader/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$ */ + +#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/snesreader/zlib/zutil.h b/snesreader/zlib/zutil.h new file mode 100644 index 00000000..b7d5eff8 --- /dev/null +++ b/snesreader/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$ */ + +#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/base.hpp b/src/base.hpp index f59902cc..017c8cc7 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,4 +1,4 @@ -static const char bsnesVersion[] = "062.10"; +static const char bsnesVersion[] = "063"; static const char bsnesTitle[] = "bsnes"; static const unsigned bsnesSerializerVersion = 8; diff --git a/src/cpu/scpu/dma/dma.cpp b/src/cpu/scpu/dma/dma.cpp index 5f34b147..5a8f8d02 100644 --- a/src/cpu/scpu/dma/dma.cpp +++ b/src/cpu/scpu/dma/dma.cpp @@ -17,8 +17,8 @@ bool sCPU::dma_addr_valid(uint32 abus) { } uint8 sCPU::dma_read(uint32 abus) { - if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR - return bus.read(abus); + if(dma_addr_valid(abus) == false) return regs.mdr = 0x00; + return regs.mdr = bus.read(abus); } void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { diff --git a/src/ui_qt/debugger/tools/disassembler.cpp b/src/ui_qt/debugger/tools/disassembler.cpp index 08b14a18..28f8b62f 100644 --- a/src/ui_qt/debugger/tools/disassembler.cpp +++ b/src/ui_qt/debugger/tools/disassembler.cpp @@ -52,7 +52,7 @@ Disassembler::Disassembler() { } void Disassembler::refresh(Source source, unsigned addr) { - uint8 *usage; + uint8_t *usage; unsigned mask; if(source == CPU) { usage = SNES::cpu.usage; mask = (1 << 24) - 1; } if(source == SMP) { usage = SNES::smp.usage; mask = (1 << 16) - 1; } diff --git a/src/ui_qt/debugger/tools/memory.cpp b/src/ui_qt/debugger/tools/memory.cpp index 71811212..f9db487a 100644 --- a/src/ui_qt/debugger/tools/memory.cpp +++ b/src/ui_qt/debugger/tools/memory.cpp @@ -156,10 +156,10 @@ void MemoryEditor::importMemory(SNES::Memory &memory, const string &filename) co } } -uint8 MemoryEditor::reader(unsigned addr) { +uint8_t MemoryEditor::reader(unsigned addr) { return SNES::debugger.read(memorySource, addr); } -void MemoryEditor::writer(unsigned addr, uint8 data) { +void MemoryEditor::writer(unsigned addr, uint8_t data) { SNES::debugger.write(memorySource, addr, data); } diff --git a/src/ui_qt/debugger/tools/memory.moc.hpp b/src/ui_qt/debugger/tools/memory.moc.hpp index 2313bb1b..a81332e4 100644 --- a/src/ui_qt/debugger/tools/memory.moc.hpp +++ b/src/ui_qt/debugger/tools/memory.moc.hpp @@ -17,8 +17,8 @@ public: void synchronize(); SNES::Debugger::MemorySource memorySource; - uint8 reader(unsigned addr); - void writer(unsigned addr, uint8 data); + uint8_t reader(unsigned addr); + void writer(unsigned addr, uint8_t data); MemoryEditor(); diff --git a/src/ui_qt/debugger/tracer.cpp b/src/ui_qt/debugger/tracer.cpp index 8249de13..c0ca2e8d 100644 --- a/src/ui_qt/debugger/tracer.cpp +++ b/src/ui_qt/debugger/tracer.cpp @@ -60,8 +60,8 @@ Tracer::Tracer() { traceSmp = false; traceMask = false; - traceMaskCPU = new uint8[(1 << 24) >> 3](); - traceMaskSMP = new uint8[(1 << 16) >> 3](); + traceMaskCPU = new uint8_t[(1 << 24) >> 3](); + traceMaskSMP = new uint8_t[(1 << 16) >> 3](); SNES::cpu.step_event = bind(&Tracer::stepCpu, this); SNES::smp.step_event = bind(&Tracer::stepSmp, this); diff --git a/src/ui_qt/debugger/tracer.moc.hpp b/src/ui_qt/debugger/tracer.moc.hpp index 06fad5c8..e67b855f 100644 --- a/src/ui_qt/debugger/tracer.moc.hpp +++ b/src/ui_qt/debugger/tracer.moc.hpp @@ -19,8 +19,8 @@ private: bool traceSmp; bool traceMask; - uint8 *traceMaskCPU; - uint8 *traceMaskSMP; + uint8_t *traceMaskCPU; + uint8_t *traceMaskSMP; }; extern Tracer *tracer; diff --git a/supergameboy/Makefile b/supergameboy/Makefile new file mode 100644 index 00000000..60409afa --- /dev/null +++ b/supergameboy/Makefile @@ -0,0 +1,126 @@ +include nall/Makefile + +c := $(compiler) -std=gnu99 +cpp := $(subst cc,++,$(compiler)) -std=gnu++0x +flags := -O3 -fomit-frame-pointer -I. -Icommon -Ilibgambatte/include -Ilibgambatte/src +link := + +ifeq ($(platform),osx) + flags := -fPIC $(flags) +else ifeq ($(platform),x) + flags := -fPIC $(flags) + link += -s +endif + +objects := supergameboy +objects += bitmap_font colorconversion cpu gambatte initstate interrupter +objects += memory rtc sound state_osd_elements statesaver video +objects += channel1 channel2 channel3 channel4 duty_unit envelope_unit length_counter +objects += basic_add_event break_event irq_event ly_counter lyc_irq +objects += m3_extra_cycles mode3_event mode0_irq mode1_irq mode2_irq +objects += sc_reader scx_reader sprite_mapper we_master_checker we wx_reader wy +objects += catrom2x catrom3x kreed2xsai maxsthq2x maxsthq3x file + +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 -c $< -o $@ \ + ) \ + ) \ + ) + +%.o: $<; $(call compile) + +all: build; + +objects := $(patsubst %,obj/%.o,$(objects)) + +#################### +### supergameboy ### +#################### + +obj/supergameboy.o: supergameboy.cpp *.cpp *.hpp $(call rwildcard,interface/) + +################### +### libgambatte ### +################### + +obj/bitmap_font.o: libgambatte/src/bitmap_font.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/colorconversion.o: libgambatte/src/colorconversion.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/cpu.o: libgambatte/src/cpu.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/gambatte.o: libgambatte/src/gambatte.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/initstate.o: libgambatte/src/initstate.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/interrupter.o: libgambatte/src/interrupter.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/memory.o: libgambatte/src/memory.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/rtc.o: libgambatte/src/rtc.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/sound.o: libgambatte/src/sound.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/state_osd_elements.o: libgambatte/src/state_osd_elements.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/statesaver.o: libgambatte/src/statesaver.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/video.o: libgambatte/src/video.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) + +obj/channel1.o: libgambatte/src/sound/channel1.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/channel2.o: libgambatte/src/sound/channel2.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/channel3.o: libgambatte/src/sound/channel3.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/channel4.o: libgambatte/src/sound/channel4.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/duty_unit.o: libgambatte/src/sound/duty_unit.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/envelope_unit.o: libgambatte/src/sound/envelope_unit.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/length_counter.o: libgambatte/src/sound/length_counter.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) + +obj/basic_add_event.o: libgambatte/src/video/basic_add_event.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/break_event.o: libgambatte/src/video/break_event.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/irq_event.o: libgambatte/src/video/irq_event.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/ly_counter.o: libgambatte/src/video/ly_counter.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/lyc_irq.o: libgambatte/src/video/lyc_irq.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/m3_extra_cycles.o: libgambatte/src/video/m3_extra_cycles.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/mode3_event.o: libgambatte/src/video/mode3_event.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/mode0_irq.o: libgambatte/src/video/mode0_irq.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/mode1_irq.o: libgambatte/src/video/mode1_irq.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/mode2_irq.o: libgambatte/src/video/mode2_irq.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/sc_reader.o: libgambatte/src/video/sc_reader.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/scx_reader.o: libgambatte/src/video/scx_reader.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/sprite_mapper.o: libgambatte/src/video/sprite_mapper.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/we_master_checker.o: libgambatte/src/video/we_master_checker.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/we.o: libgambatte/src/video/we.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/wx_reader.o: libgambatte/src/video/wx_reader.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/wy.o: libgambatte/src/video/wy.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) + +obj/catrom2x.o: libgambatte/src/video/filters/catrom2x.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/catrom3x.o: libgambatte/src/video/filters/catrom3x.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/kreed2xsai.o: libgambatte/src/video/filters/kreed2xsai.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/maxsthq2x.o: libgambatte/src/video/filters/maxsthq2x.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) +obj/maxsthq3x.o: libgambatte/src/video/filters/maxsthq3x.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) + +obj/file.o: libgambatte/src/file/file.cpp $(call rwildcard,common/) $(call rwildcard,libgambatte/) + +############### +### targets ### +############### + +build: $(objects) +ifeq ($(platform),win) + $(cpp) $(link) -o supergameboy.dll -shared -Wl,--out-implib,libsupergameboy.a $(objects) $(qtlib) +else ifeq ($(platform),osx) + ar rcs libsupergameboy.a $(objects) + $(cpp) $(link) -o libsupergameboy.dylib -shared -dynamiclib $(objects) $(qtlib) +else + ar rcs libsupergameboy.a $(objects) + $(cpp) $(link) -o libsupergameboy.so -shared -Wl,-soname,libsupergameboy.so.1 $(objects) $(qtlib) +endif + +install: +ifeq ($(platform),osx) + cp libsupergameboy.dylib /usr/local/lib/libsupergameboy.dylib +else + install -D -m 755 libsupergameboy.a $(DESTDIR)$(prefix)/lib + install -D -m 755 libsupergameboy.so $(DESTDIR)$(prefix)/lib + ldconfig -n $(DESTDIR)$(prefix)/lib +endif + +clean: + -@$(call delete,obj/*.o) + -@$(call delete,libsupergameboy.a) + -@$(call delete,supergameboy.dll) + -@$(call delete,libsupergameboy.dylib) + -@$(call delete,libsupergameboy.so) diff --git a/supergameboy/cc.bat b/supergameboy/cc.bat new file mode 100644 index 00000000..7e2f36ad --- /dev/null +++ b/supergameboy/cc.bat @@ -0,0 +1,2 @@ +@mingw32-make +@pause diff --git a/supergameboy/clean.bat b/supergameboy/clean.bat new file mode 100644 index 00000000..d8bb7e0b --- /dev/null +++ b/supergameboy/clean.bat @@ -0,0 +1 @@ +@mingw32-make clean diff --git a/supergameboy/common/adaptivesleep.cpp b/supergameboy/common/adaptivesleep.cpp new file mode 100644 index 00000000..48c40979 --- /dev/null +++ b/supergameboy/common/adaptivesleep.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "adaptivesleep.h" + +usec_t AdaptiveSleep::sleepUntil(usec_t base, usec_t inc) { + usec_t now = getusecs(); + usec_t diff = now - base; + + if (diff >= inc) + return diff - inc; + + diff = inc - diff; + + if (diff > oversleep + oversleepVar) { + diff -= oversleep + oversleepVar; + usecsleep(diff); + const usec_t ideal = now + diff; + now = getusecs(); + + { + usec_t curOversleep = now - ideal; + + if (negate(curOversleep) < curOversleep) + curOversleep = 0; + + oversleepVar = (oversleepVar * 15 + (curOversleep < oversleep ? oversleep - curOversleep : curOversleep - oversleep)) >> 4; + oversleep = (oversleep * 15 + curOversleep) >> 4; + } + + noSleep = 60; + } else if (--noSleep == 0) { + noSleep = 60; + oversleep = oversleepVar = 0; + } + + while (now - base < inc) + now = getusecs(); + + return 0; +} diff --git a/supergameboy/common/adaptivesleep.h b/supergameboy/common/adaptivesleep.h new file mode 100644 index 00000000..de2010a0 --- /dev/null +++ b/supergameboy/common/adaptivesleep.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ADAPTIVE_SLEEP_H +#define ADAPTIVE_SLEEP_H + +#include "usec.h" + +class AdaptiveSleep { + usec_t oversleep; + usec_t oversleepVar; + unsigned noSleep; + +public: + AdaptiveSleep() : oversleep(0), oversleepVar(0), noSleep(60) {} + usec_t sleepUntil(usec_t base, usec_t inc); +}; + +#endif diff --git a/supergameboy/common/array.h b/supergameboy/common/array.h new file mode 100644 index 00000000..f01806ea --- /dev/null +++ b/supergameboy/common/array.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARRAY_H +#define ARRAY_H + +#include + +template +class Array { + T *a; + std::size_t sz; + + Array(const Array &ar); + +public: + Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {} + ~Array() { delete []a; } + void reset(const std::size_t size) { delete []a; a = size ? new T[size] : 0; sz = size; } + std::size_t size() const { return sz; } + operator T*() { return a; } + operator const T*() const { return a; } +}; + +#endif diff --git a/supergameboy/common/rateest.cpp b/supergameboy/common/rateest.cpp new file mode 100644 index 00000000..c1feba6c --- /dev/null +++ b/supergameboy/common/rateest.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "rateest.h" +#include + +void RateEst::SumQueue::reset() { + q.clear(); + samples_ = usecs_ = 0; +} + +void RateEst::SumQueue::push(const long samples, const usec_t usecs) { + q.push_back(pair_t(samples, usecs)); + samples_ += samples; + usecs_ += usecs; +} + +void RateEst::SumQueue::pop() { + const pair_t &f = q.front(); + samples_ -= f.first; + usecs_ -= f.second; + q.pop_front(); +} + +static usec_t sampleUsecs(long samples, long rate) { + return static_cast((samples * 1000000.0f) / (rate ? rate : 1) + 0.5f); +} + +static long limit(long est, const long reference) { + if (est > reference + (reference >> 6)) + est = reference + (reference >> 6); + else if (est < reference - (reference >> 6)) + est = reference - (reference >> 6); + + return est; +} + +void RateEst::init(long srate, long reference, const long maxSamplePeriod) { + maxPeriod = sampleUsecs(maxSamplePeriod, reference); + + srate <<= UPSHIFT; + reference <<= UPSHIFT; + + this->srate.est = limit(srate, reference); + this->srate.var = srate >> 12; + last = 0; + this->reference = reference; + samples = ((this->srate.est >> UPSHIFT) * 12) << 5; + usecs = 12000000 << 5; + sumq.reset(); +} + +void RateEst::feed(long samplesIn, const usec_t now) { + usec_t usecsIn = now - last; + + if (last && usecsIn < maxPeriod) { + sumq.push(samplesIn, usecsIn); + + while ((usecsIn = sumq.usecs()) > 100000) { + samplesIn = sumq.samples(); + sumq.pop(); + + if (std::abs(static_cast(samplesIn * (1000000.0f * UP) / usecsIn) - reference) < reference >> 1) { + samples += (samplesIn - sumq.samples()) << 5; + usecs += (usecsIn - sumq.usecs()) << 5; + + long est = static_cast(samples * (1000000.0f * UP) / usecs + 0.5f); + est = limit((srate.est * 31 + est + 16) >> 5, reference); + srate.var = (srate.var * 15 + std::abs(est - srate.est) + 8) >> 4; + srate.est = est; + + if (usecs > 16000000 << 5) { + samples = (samples * 3 + 2) >> 2; + usecs = (usecs * 3 + 2) >> 2; + } + } + } + } + + last = now; +} diff --git a/supergameboy/common/rateest.h b/supergameboy/common/rateest.h new file mode 100644 index 00000000..3e109541 --- /dev/null +++ b/supergameboy/common/rateest.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RATEEST_H +#define RATEEST_H + +#include "usec.h" +#include +#include + +class RateEst { +public: + struct Result { + long est; + long var; + }; + +private: + class SumQueue { + typedef std::pair pair_t; + typedef std::deque q_t; + + q_t q; + long samples_; + usec_t usecs_; + + public: + SumQueue() : samples_(0), usecs_(0) {} + void reset(); + long samples() const { return samples_; } + usec_t usecs() const { return usecs_; } + void push(long samples, usec_t usecs); + void pop(); + }; + + enum { UPSHIFT = 5 }; + enum { UP = 1 << UPSHIFT }; + + Result srate; + SumQueue sumq; + usec_t last; + usec_t usecs; + usec_t maxPeriod; + long reference; + long samples; + +public: + RateEst(long srate = 0) { init(srate); } + RateEst(long srate, long reference) { init(srate, reference); } + void init(long srate) { init(srate, srate); } + void init(long srate, long reference) { init(srate, reference, reference); } + void init(long srate, long reference, long maxSamplePeriod); + void reset() { last = 0; } + void feed(long samples, usec_t usecs = getusecs()); + const Result result() const { const Result res = { (srate.est + UP / 2) >> UPSHIFT, (srate.var + UP / 2) >> UPSHIFT }; return res; } +}; + +#endif diff --git a/supergameboy/common/resample/blackmansinc.h b/supergameboy/common/resample/blackmansinc.h new file mode 100644 index 00000000..86578239 --- /dev/null +++ b/supergameboy/common/resample/blackmansinc.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BLACKMANSINC_H +#define BLACKMANSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic4.h" +#include +#include + +template +class BlackmanSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double blackmanWin(const long i, const long M) { + static const double PI = 3.14159265358979323846; + return 0.42 - 0.5 * std::cos(2 * PI * i / M) + 0.08 * std::cos(4 * PI * i / M); + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic4 Cic; + static float cicLimit() { return 4.7f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 4.5f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 1.69f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + BlackmanSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + BlackmanSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~BlackmanSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void BlackmanSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, blackmanWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t BlackmanSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void BlackmanSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/supergameboy/common/resample/chainresampler.cpp b/supergameboy/common/resample/chainresampler.cpp new file mode 100644 index 00000000..6836a05b --- /dev/null +++ b/supergameboy/common/resample/chainresampler.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "chainresampler.h" + +float ChainResampler::get2ChainMidRatio(const float ratio, const float rollOff) { + return std::sqrt(0.5f * rollOff * ratio) + 1; +} + +float ChainResampler::get2ChainCost(const float ratio, const float rollOff, const float midRatio) { + return midRatio * ratio / ((midRatio - 1) * 2) + midRatio / rollOff; +} + +float ChainResampler::get3ChainRatio1(float ratio1, const float rollOff, const float ratio) { + for (unsigned n = 8; n--;) { + ratio1 = std::sqrt(ratio - ratio / get3ChainRatio2(ratio1, rollOff)) + 1; + } + + return ratio1; +} + +float ChainResampler::get3ChainCost(const float ratio, const float rollOff, const float ratio1, const float ratio2) { + return ratio1 * ratio / ((ratio1 - 1) * 2) + ratio2 * ratio1 / ((ratio2 - 1) * 2) + ratio2 / rollOff; +} + +std::size_t ChainResampler::reallocateBuffer() { + std::size_t bufSz[2] = { 0, 0 }; + std::size_t inSz = periodSize; + int i = -1; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) { + inSz = (inSz * (*it)->mul() - 1) / (*it)->div() + 1; + + ++i; + + if (inSz > bufSz[i&1]) + bufSz[i&1] = inSz; + } + + if (inSz >= bufSz[i&1]) + bufSz[i&1] = 0; + + if (bufferSize < bufSz[0] + bufSz[1]) { + delete[] buffer; + buffer = (bufferSize = bufSz[0] + bufSz[1]) ? new short[bufferSize * channels] : NULL; + } + + buffer2 = bufSz[1] ? buffer + bufSz[0] * channels : NULL; + + return (maxOut_ = inSz); +} + +void ChainResampler::adjustRate(const long inRate, const long outRate) { + unsigned long mul, div; + + exactRatio(mul, div); + + bigSinc->adjustDiv(static_cast(inRate) * mul / (static_cast(div / bigSinc->div()) * outRate) + 0.5); + + reallocateBuffer(); + setRate(inRate, outRate); +} + +void ChainResampler::exactRatio(unsigned long &mul, unsigned long &div) const { + mul = 1; + div = 1; + + for (list_t::const_iterator it = list.begin(); it != list.end(); ++it) { + mul *= (*it)->mul(); + div *= (*it)->div(); + } +} + +std::size_t ChainResampler::resample(short *const out, const short *const in, std::size_t inlen) { + assert(inlen <= periodSize); + + short *const buf = buffer != buffer2 ? buffer : out; + short *const buf2 = buffer2 ? buffer2 : out; + + const short *inbuf = in; + short *outbuf = NULL; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) { + outbuf = ++list_t::iterator(it) == list.end() ? out : (inbuf == buf ? buf2 : buf); + inlen = (*it)->resample(outbuf, inbuf, inlen); + inbuf = outbuf; + } + + return inlen; +} + +void ChainResampler::uninit() { + delete[] buffer; + buffer2 = buffer = NULL; + bufferSize = 0; + periodSize = 0; + bigSinc = NULL; + + for (list_t::iterator it = list.begin(); it != list.end(); ++it) + delete *it; + + list.clear(); +} diff --git a/supergameboy/common/resample/chainresampler.h b/supergameboy/common/resample/chainresampler.h new file mode 100644 index 00000000..aeb52d6c --- /dev/null +++ b/supergameboy/common/resample/chainresampler.h @@ -0,0 +1,189 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CHAINRESAMPLER_H +#define CHAINRESAMPLER_H + +#include +#include +#include +#include +#include +#include "subresampler.h" +#include "resampler.h" +#include "upsampler.h" + +class ChainResampler : public Resampler { + enum { channels = 2 }; + + typedef std::list list_t; + + list_t list; + SubResampler *bigSinc; + short *buffer; + short *buffer2; + std::size_t bufferSize; + std::size_t periodSize; + std::size_t maxOut_; + + static float get1ChainCost(const float ratio, const float rollOff) { + return ratio / rollOff; + } + + static float get2ChainMidRatio(float ratio, float rollOff); + static float get2ChainCost(float ratio, float rollOff, float midRatio); + + static float get3ChainRatio2(const float ratio1, const float rollOff) { + return get2ChainMidRatio(ratio1, rollOff); + } + + static float get3ChainRatio1(float ratio1, float rollOff, float ratio); + static float get3ChainCost(float ratio, float rollOff, float ratio1, float ratio2); + + template class Sinc> + std::size_t downinit(long inRate, long outRate, std::size_t periodSize); + + std::size_t reallocateBuffer(); + + template class Sinc> + std::size_t upinit(long inRate, long outRate, std::size_t periodSize); + +public: + ChainResampler() : bigSinc(NULL), buffer(NULL), buffer2(NULL), bufferSize(0), periodSize(0) {} + ~ChainResampler() { uninit(); } + + void adjustRate(long inRate, long outRate); + void exactRatio(unsigned long &mul, unsigned long &div) const; + + template class Sinc> + std::size_t init(long inRate, long outRate, std::size_t periodSize); + std::size_t maxOut(std::size_t /*inlen*/) const { return maxOut_; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void uninit(); +}; + +template class Sinc> +std::size_t ChainResampler::init(const long inRate, const long outRate, const std::size_t periodSize) { + setRate(inRate, outRate); + + if (outRate > inRate) + return upinit(inRate, outRate, periodSize); + else + return downinit(inRate, outRate, periodSize); +} + +template class Sinc> +std::size_t ChainResampler::downinit(const long inRate, const long outRate, const std::size_t periodSize) { + typedef Sinc BigSinc; + typedef Sinc SmallSinc; + + uninit(); + this->periodSize = periodSize; + + + const float rollOff = std::max((outRate - 36000.0f + outRate - 40000.0f) / outRate, 0.2f); + + double ratio = static_cast(inRate) / outRate; + + while (ratio >= BigSinc::cicLimit() * 2) { + const int div = std::min(static_cast(ratio / BigSinc::cicLimit()), BigSinc::Cic::MAX_DIV); + + list.push_back(new typename BigSinc::Cic(div)); + ratio /= div; + } + + { + int div_2c = ratio * SmallSinc::MUL / get2ChainMidRatio(ratio, rollOff) + 0.5f; + double ratio_2c = ratio * SmallSinc::MUL / div_2c; + float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c); + + if (cost_2c < get1ChainCost(ratio, rollOff)) { + const int div1_3c = ratio * SmallSinc::MUL / get3ChainRatio1(ratio_2c, rollOff, ratio) + 0.5f; + const double ratio1_3c = ratio * SmallSinc::MUL / div1_3c; + const int div2_3c = ratio1_3c * SmallSinc::MUL / get3ChainRatio2(ratio1_3c, rollOff) + 0.5f; + const double ratio2_3c = ratio1_3c * SmallSinc::MUL / div2_3c; + + if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) { + list.push_back(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio, (ratio1_3c - 1) / ratio))); + ratio = ratio1_3c; + div_2c = div2_3c; + ratio_2c = ratio2_3c; + } + + list.push_back(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio, (ratio_2c - 1) / ratio))); + ratio = ratio_2c; + } + } + + list.push_back(bigSinc = new BigSinc(BigSinc::MUL * ratio + 0.5, + typename BigSinc::RollOff(0.5f * (1 + std::max((outRate - 40000.0f) / outRate, 0.0f) - rollOff) / ratio, 0.5f * rollOff / ratio))); + + return reallocateBuffer(); +} + +template class Sinc> +std::size_t ChainResampler::upinit(const long inRate, const long outRate, const std::size_t periodSize) { + typedef Sinc BigSinc; + typedef Sinc SmallSinc; + + uninit(); + this->periodSize = periodSize; + + const float rollOff = std::max((inRate - 36000.0f) / inRate, 0.2f); + + double ratio = static_cast(outRate) / inRate; + + // Spectral images above 20 kHz assumed inaudible + { + const int div = outRate / std::max(inRate, 40000l); + + if (div >= 2) { + list.push_front(new Upsampler(div)); + ratio /= div; + } + } + + { + int div_2c = get2ChainMidRatio(ratio, rollOff) * SmallSinc::MUL / ratio + 0.5f; + double ratio_2c = ratio * div_2c / SmallSinc::MUL; + float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c); + + if (cost_2c < get1ChainCost(ratio, rollOff)) { + const int div1_3c = get3ChainRatio1(ratio_2c, rollOff, ratio) * SmallSinc::MUL / ratio + 0.5f; + const double ratio1_3c = ratio * div1_3c / SmallSinc::MUL; + const int div2_3c = get3ChainRatio2(ratio1_3c, rollOff) * SmallSinc::MUL / ratio1_3c + 0.5f; + const double ratio2_3c = ratio1_3c * div2_3c / SmallSinc::MUL; + + if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) { + list.push_front(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio1_3c, (ratio1_3c - 1) / ratio1_3c))); + ratio = ratio1_3c; + div_2c = div2_3c; + ratio_2c = ratio2_3c; + } + + list.push_front(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio_2c, (ratio_2c - 1) / ratio_2c))); + ratio = ratio_2c; + } + } + + list.push_front(bigSinc = new BigSinc(BigSinc::MUL / ratio + 0.5, typename BigSinc::RollOff(0.5f * (1 - rollOff), 0.5f * rollOff))); + + return reallocateBuffer(); +} + +#endif diff --git a/supergameboy/common/resample/cic2.h b/supergameboy/common/resample/cic2.h new file mode 100644 index 00000000..1f12bfc9 --- /dev/null +++ b/supergameboy/common/resample/cic2.h @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC2_H +#define CIC2_H + +#include "subresampler.h" + +template +class Cic2Core { +// enum { BUFLEN = 64 }; +// unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long prev1; + unsigned long prev2; + unsigned div_; + unsigned nextdivn; +// unsigned bufpos; + +public: + Cic2Core(const unsigned div = 2) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic2Core::reset(const unsigned div) { + sum2 = sum1 = 0; + prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +// bufpos = div - 1; +} + +template +std::size_t Cic2Core::filter(short *out, const short *const in, std::size_t inlen) { +// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; + const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + + while (inlen >> 2) { + unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2); + const unsigned end = n * 4; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += s1; + buf[i++] = sm2 += sm1; + s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += s1; + buf[i++] = sm2 += sm1; + } while (--n); + + while (bufpos < end) { + const unsigned long out2 = buf[bufpos] - prev2; + prev2 = buf[bufpos]; + bufpos += div_; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + buf[i++] = sm2 += sm1; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out2 = buf[bufpos] - prev2; + prev2 = buf[bufpos]; + bufpos += div_; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2;*/ + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + + if (inlen >= nextdivn) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + } while (--divn); + + const unsigned long out2 = sm2 - prev2; + prev2 = sm2; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + } + } + + sum1 = sm1; + sum2 = sm2; + + return produced; +} + +template +class Cic2 : public SubResampler { + Cic2Core cics[channels]; + +public: + enum { MAX_DIV = 64 }; + Cic2(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic2::Cic2(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic2::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/supergameboy/common/resample/cic3.h b/supergameboy/common/resample/cic3.h new file mode 100644 index 00000000..85b9dcee --- /dev/null +++ b/supergameboy/common/resample/cic3.h @@ -0,0 +1,382 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC3_H +#define CIC3_H + +#include "subresampler.h" + +template +class Cic3Core { +// enum { BUFLEN = 64 }; +// unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned div_; + unsigned nextdivn; +// unsigned bufpos; + +public: + Cic3Core(const unsigned div = 1) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic3Core::reset(const unsigned div) { + sum3 = sum2 = sum1 = 0; + prev3 = prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +// bufpos = div - 1; +} + +template +std::size_t Cic3Core::filter(short *out, const short *const in, std::size_t inlen) { +// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; + const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (inlen >> 1) { + unsigned n = (inlen < BUFLEN ? inlen >> 1 : BUFLEN >> 1); + const unsigned end = n * 2; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + buf[i++] = sm3 += s2; + buf[i++] = sm3 += sm2; + } while (--n); + + while (bufpos < end) { + const unsigned long out3 = buf[bufpos] - prev3; + prev3 = buf[bufpos]; + bufpos += div_; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + buf[i++] = sm3 += sm2; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out3 = buf[bufpos] - prev3; + prev3 = buf[bufpos]; + bufpos += div_; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3;*/ + + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + if (inlen >= nextdivn) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + sm2 += sm1; + sm3 += sm2; + s += channels; + } while (--divn); + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + sm2 += sm1; + sm3 += sm2; + s += channels; + } + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + + return produced; +} + +/*template +class Cic3EvenOddCore { + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned div_; + unsigned nextdivn; + + static int getMul(unsigned div) { + return 0x10000 / (div * div * div); // trouble if div is too large, may be better to only support power of 2 div + } + + void filterEven(short *out, const short *s, std::size_t n); + void filterOdd(short *out, const short *s, std::size_t n); + +public: + Cic3EvenOddCore(const unsigned div = 2) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic3EvenOddCore::reset(const unsigned div) { + sum3 = sum2 = sum1 = 0; + prev3 = prev2 = prev1 = 0; + this->div_ = div; + nextdivn = div; +} + +template +void Cic3EvenOddCore::filterEven(short *out, const short *s, std::size_t n) { + const int mul = getMul(div_); + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (n--) { + { + unsigned sn = div_ >> 1; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + sm3 += s2; + sm3 += sm2; + } while (--sn); + } + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; +} + +template +void Cic3EvenOddCore::filterOdd(short *out, const short *s, std::size_t n) { + const int mul = getMul(div_); + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + + while (n--) { + { + unsigned sn = div_ >> 1; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + sm3 += s2; + sm3 += sm2; + } while (--sn); + } + + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + + const unsigned long out3 = sm3 - prev3; + prev3 = sm3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; +} + +template +std::size_t Cic3EvenOddCore::filter(short *out, const short *const in, std::size_t inlen) { + short *const outStart = out; + const short *s = in; + + if (inlen >= nextdivn) { + { + { + unsigned divn = nextdivn; + + do { + sum1 += static_cast(*s); + s += channels; + sum2 += sum1; + sum3 += sum2; + } while (--divn); + } + + const unsigned long out3 = sum3 - prev3; + prev3 = sum3; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * getMul(div_) / 0x10000; + prev1 = out2; + out += channels; + } + + std::size_t n = (inlen - nextdivn) / div_; + + if (div_ & 1) + filterOdd(out, s, n); + else + filterEven(out, s, n); + + s += n * div_ * channels; + out += n * channels; + nextdivn = div_; + } + + { + unsigned divn = inlen - (s - in) / channels; + nextdivn -= divn; + + while (divn--) { + sum1 += static_cast(*s); + s += channels; + sum2 += sum1; + sum3 += sum2; + } + } + + return (out - outStart) / channels; +}*/ + +template +class Cic3 : public SubResampler { + Cic3Core cics[channels]; + +public: + enum { MAX_DIV = 23 }; + Cic3(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic3::Cic3(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic3::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/supergameboy/common/resample/cic4.h b/supergameboy/common/resample/cic4.h new file mode 100644 index 00000000..430cb03d --- /dev/null +++ b/supergameboy/common/resample/cic4.h @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CIC4_H +#define CIC4_H + +#include "subresampler.h" + +template +class Cic4Core { + enum { BUFLEN = 64 }; + unsigned long buf[BUFLEN]; + unsigned long sum1; + unsigned long sum2; + unsigned long sum3; + unsigned long sum4; + unsigned long prev1; + unsigned long prev2; + unsigned long prev3; + unsigned long prev4; + unsigned div_; +// unsigned nextdivn; + unsigned bufpos; + +public: + Cic4Core(const unsigned div = 1) { + reset(div); + } + + unsigned div() const { return div_; } + std::size_t filter(short *out, const short *in, std::size_t inlen); + void reset(unsigned div); +}; + +template +void Cic4Core::reset(const unsigned div) { + sum4 = sum3 = sum2 = sum1 = 0; + prev4 = prev3 = prev2 = prev1 = 0; + this->div_ = div; +// nextdivn = div; + bufpos = div - 1; +} + +template +std::size_t Cic4Core::filter(short *out, const short *const in, std::size_t inlen) { + const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_; +// const std::size_t produced = (inlen + div_ - nextdivn) / div_; + const long mul = 0x10000 / (div_ * div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div + const short *s = in; + + unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + unsigned long sm4 = sum4; + + while (inlen >> 2) { + unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2); + const unsigned end = n * 4; + unsigned i = 0; + + do { + unsigned long s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + unsigned long s2 = sm2 += s1; + sm2 += sm1; + unsigned long s3 = sm3 += s2; + sm3 += sm2; + buf[i++] = sm4 += s3; + buf[i++] = sm4 += sm3; + s1 = sm1 += static_cast(*s); + s += channels; + sm1 += static_cast(*s); + s += channels; + s2 = sm2 += s1; + sm2 += sm1; + s3 = sm3 += s2; + sm3 += sm2; + buf[i++] = sm4 += s3; + buf[i++] = sm4 += sm3; + } while (--n); + + while (bufpos < end) { + const unsigned long out4 = buf[bufpos] - prev4; + prev4 = buf[bufpos]; + bufpos += div_; + + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= end; + inlen -= end; + } + + if (inlen) { + unsigned n = inlen; + unsigned i = 0; + + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + buf[i++] = sm4 += sm3; + } while (--n); + + while (bufpos < inlen) { + const unsigned long out4 = buf[bufpos] - prev4; + prev4 = buf[bufpos]; + bufpos += div_; + + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + } + + bufpos -= inlen; + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + sum4 = sm4; + + /*unsigned long sm1 = sum1; + unsigned long sm2 = sum2; + unsigned long sm3 = sum3; + unsigned long sm4 = sum4; + + if (produced) { + unsigned divn = nextdivn; + std::size_t n = produced; + + do { + do { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + sm4 += sm3; + } while (--divn); + + const unsigned long out4 = sm4 - prev4; + prev4 = sm4; + const unsigned long out3 = out4 - prev3; + prev3 = out4; + const unsigned long out2 = out3 - prev2; + prev2 = out3; + *out = static_cast(out2 - prev1) * mul / 0x10000; + prev1 = out2; + out += channels; + + divn = div_; + } while (--n); + + nextdivn = div_; + } + + { + unsigned divn = (in + inlen * channels - s) / channels; + nextdivn -= divn; + + while (divn--) { + sm1 += static_cast(*s); + s += channels; + sm2 += sm1; + sm3 += sm2; + sm4 += sm3; + } + } + + sum1 = sm1; + sum2 = sm2; + sum3 = sm3; + sum4 = sm4;*/ + + return produced; +} + +template +class Cic4 : public SubResampler { + Cic4Core cics[channels]; + +public: + enum { MAX_DIV = 13 }; + Cic4(unsigned div); + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return 1; } + unsigned div() const { return cics[0].div(); } +}; + +template +Cic4::Cic4(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + cics[i].reset(div); +} + +template +std::size_t Cic4::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) { + samplesOut = cics[i].filter(out + i, in + i, inlen); + } + + return samplesOut; +} + +#endif diff --git a/supergameboy/common/resample/convoluter.h b/supergameboy/common/resample/convoluter.h new file mode 100644 index 00000000..41fab0d0 --- /dev/null +++ b/supergameboy/common/resample/convoluter.h @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CONVOLUTER_H +#define CONVOLUTER_H + +#include +#include + +template +class PolyPhaseConvoluter { + const short *kernel; + short *prevbuf; + + unsigned phaseLen; + unsigned div_; + unsigned x_; + +public: + PolyPhaseConvoluter() : kernel(NULL), prevbuf(NULL), phaseLen(0), div_(0), x_(0) {} + PolyPhaseConvoluter(const short *kernel, unsigned phaseLen, unsigned div) { reset(kernel, phaseLen, div); } + ~PolyPhaseConvoluter() { delete[] prevbuf; } + void reset(const short *kernel, unsigned phaseLen, unsigned div); + std::size_t filter(short *out, const short *in, std::size_t inlen); + void adjustDiv(const unsigned div) { this->div_ = div; } + unsigned div() const { return div_; } +}; + +template +void PolyPhaseConvoluter::reset(const short *const kernel, const unsigned phaseLen, const unsigned div) { + this->kernel = kernel; + this->phaseLen = phaseLen; + this->div_ = div; + x_ = 0; + delete[] prevbuf; + prevbuf = new short[phaseLen]; + std::fill(prevbuf, prevbuf + phaseLen, 0); +} + +template +std::size_t PolyPhaseConvoluter::filter(short *out, const short *const in, std::size_t inlen) { + if (!kernel || !inlen) + return 0; + + /*for (std::size_t x = 0; x < inlen + M; ++x) { + const int end = x < inlen ? M + 1 : inlen + M - x; + int j = x < M ? M - x : 0; + j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample + + for (; j < end; j += phases) { + buffer[x] += kernel[j] * start[(x - M + j) / phases]; + } + }*/ + + /*for (std::size_t x = 0; x < inlen + M; ++x) { + const int end = x < inlen ? M + 1 : inlen + M - x; + int j = x < M ? M - x : 0; + j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample + const short *k = kernel + (j % phases) * phaseLen + j / phases; + const short *s = start + (x - M + j) / phases; + int n = ((end - j) + phases - 1) / phases; + + do { + buffer[x] += *k++ * *s++; + } while (--n); + }*/ + + const std::size_t M = phaseLen * phases - 1; + inlen *= phases; + std::size_t x = x_; + + for (; x < (M < inlen ? M : inlen); x += div_) { + long acc = 0; + const unsigned phase = (phases - (x + 1) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample + const short *s = prevbuf + (x + 1 + phase) / phases; + const short *k = kernel + phase * phaseLen; + unsigned n = prevbuf + phaseLen - s; + + while (n--) { + acc += *k++ * *s++; + } + + s = in; + n = x / phases + 1; + + do { + acc += *k++ * *s; + s += channels; + } while (--n); + + *out = acc / 0x10000; + out += channels; + } + + for (; x < inlen; x += div_) { + long acc = 0; + const unsigned phase = (phases - (x - M) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample + const short *s = in + ((x - M + phase) / phases) * channels; + const short *k = kernel + phase * phaseLen; +// unsigned n = (M + 1/* - phase + phases - 1*/) / phases; + unsigned n = phaseLen; + + do { + acc += *k++ * *s; + s += channels; + } while (--n); + + *out = acc / 0x10000; + out += channels; + } + + const std::size_t produced = (x - x_) / div_; + x_ = x - inlen; + + inlen /= phases; + + { + short *p = prevbuf; + const short *s = in + (inlen - phaseLen) * channels; + unsigned n = phaseLen; + + if (inlen < phaseLen) { + const unsigned i = phaseLen - inlen; + + std::memmove(p, p + inlen, i * sizeof(short)); + + p += i; + n -= i; + s = in; + } + + do { + *p++ = *s; + s += channels; + } while (--n); + } + + return produced; +} + +#endif diff --git a/supergameboy/common/resample/hammingsinc.h b/supergameboy/common/resample/hammingsinc.h new file mode 100644 index 00000000..bb50daee --- /dev/null +++ b/supergameboy/common/resample/hammingsinc.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef HAMMINGSINC_H +#define HAMMINGSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic3.h" +#include +#include + +template +class HammingSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double hammingWin(const long i, const long M) { + static const double PI = 3.14159265358979323846; + return 0.53836 - 0.46164 * std::cos(2 * PI * i / M); + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic3 Cic; + static float cicLimit() { return 4.2f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 3.0f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 1.27f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + HammingSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + HammingSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~HammingSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void HammingSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, hammingWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t HammingSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void HammingSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/supergameboy/common/resample/linint.h b/supergameboy/common/resample/linint.h new file mode 100644 index 00000000..0c6d8cb2 --- /dev/null +++ b/supergameboy/common/resample/linint.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LININT_H +#define LININT_H + +#include +#include "resampler.h" +#include "u48div.h" + +template +class LinintCore { + unsigned long ratio; + std::size_t pos_; + unsigned fracPos_; + int prevSample_; + +public: + LinintCore(long inRate = 1, long outRate = 1) { init(inRate, outRate); } + void adjustRate(long inRate, long outRate) { ratio = (static_cast(inRate) / outRate) * 0x10000 + 0.5; } + void exactRatio(unsigned long &mul, unsigned long &div) const { mul = 0x10000; div = ratio; } + void init(long inRate, long outRate); + std::size_t maxOut(std::size_t inlen) const { return inlen ? u48div(inlen - 1, 0xFFFF, ratio) + 1 : 0; } + std::size_t resample(short *out, const short *in, std::size_t inlen); +}; + +template +void LinintCore::init(const long inRate, const long outRate) { + adjustRate(inRate, outRate); + pos_ = (ratio >> 16) + 1; + fracPos_ = ratio & 0xFFFF; + prevSample_ = 0; +} + +template +std::size_t LinintCore::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t opos = 0; + std::size_t pos = pos_; + unsigned fracPos = fracPos_; + int prevSample = prevSample_; + + if (pos < inlen) { + if (pos != 0) + prevSample = in[(pos-1) * channels]; + + for (;;) { + out[opos] = prevSample + (in[pos * channels] - prevSample) * static_cast(fracPos) / 0x10000; + opos += channels; + + { + const unsigned long next = ratio + fracPos; + + pos += next >> 16; + fracPos = next & 0xFFFF; + } + + if (pos < inlen) { + prevSample = in[(pos-1) * channels]; + } else + break; + } + + if (pos == inlen) + prevSample = in[(pos-1) * channels]; + } + +// const std::size_t produced = ((pos - pos_) * 0x10000 + fracPos - fracPos_) / ratio; + + pos_ = pos - inlen; + fracPos_ = fracPos; + prevSample_ = prevSample; + + return opos / channels; +} + +template +class Linint : public Resampler { + LinintCore cores[channels]; + +public: + Linint(long inRate, long outRate); + void adjustRate(long inRate, long outRate); + void exactRatio(unsigned long &mul, unsigned long &div) const { cores[0].exactRatio(mul, div); } + std::size_t maxOut(std::size_t inlen) const { return cores[0].maxOut(inlen); } + std::size_t resample(short *out, const short *in, std::size_t inlen); +}; + +template +Linint::Linint(const long inRate, const long outRate) { + setRate(inRate, outRate); + + for (unsigned i = 0; i < channels; ++i) + cores[i].init(inRate, outRate); +} + +template +void Linint::adjustRate(const long inRate, const long outRate) { + setRate(inRate, outRate); + + for (unsigned i = 0; i < channels; ++i) + cores[i].adjustRate(inRate, outRate); +} + +template +std::size_t Linint::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t outlen = 0; + + for (unsigned i = 0; i < channels; ++i) + outlen = cores[i].resample(out + i, in + i, inlen); + + return outlen; +} + +#endif diff --git a/supergameboy/common/resample/makesinckernel.h b/supergameboy/common/resample/makesinckernel.h new file mode 100644 index 00000000..c6515f2d --- /dev/null +++ b/supergameboy/common/resample/makesinckernel.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAKE_SINC_KERNEL_H +#define MAKE_SINC_KERNEL_H + +#include +#include + +template +void makeSincKernel(short *const kernel, const unsigned phases, const unsigned phaseLen, double fc, Window win) { + static const double PI = 3.14159265358979323846; + fc /= phases; + + /*{ + double *const dkernel = new double[phaseLen * phases]; + const long M = static_cast(phaseLen) * phases - 1; + + for (long i = 0; i < M + 1; ++i) { + const double sinc = i * 2 == M ? + PI * fc : + std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M); + + dkernel[(i % phases) * phaseLen + i / phases] = win(i, M) * sinc; + } + + double maxabsgain = 0; + + for (unsigned ph = 0; ph < phases; ++ph) { + double gain = 0; + double absgain = 0; + + for (unsigned i = 0; i < phaseLen; ++i) { + gain += dkernel[ph * phaseLen + i]; + absgain += std::abs(dkernel[ph * phaseLen + i]); + } + + gain = 1.0 / gain; + + // Per phase normalization to avoid DC fluctuations. + for (unsigned i = 0; i < phaseLen; ++i) + dkernel[ph * phaseLen + i] *= gain; + + absgain *= gain; + + if (absgain > maxabsgain) + maxabsgain = absgain; + } + + const double gain = 0x10000 / maxabsgain; + + for (long i = 0; i < M + 1; ++i) + kernel[i] = std::floor(dkernel[i] * gain + 0.5); + + delete[] dkernel; + }*/ + + // The following is equivalent to the more readable version above + + const long M = static_cast(phaseLen) * phases - 1; + + double *const dkernel = new double[M / 2 + 1]; + + { + double *dk = dkernel; + + for (unsigned ph = 0; ph < phases; ++ph) { + for (long i = ph; i < M / 2 + 1; i += phases) { + const double sinc = i * 2 == M ? + PI * fc : + std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M); + + *dk++ = win(i, M) * sinc; + } + } + } + + double maxabsgain = 0.0; + + { + double *dkp1 = dkernel; + double *dkp2 = dkernel + M / 2; + + for (unsigned ph = 0; ph < (phases + 1) / 2; ++ph) { + double gain = 0.0; + double absgain = 0.0; + + { + const double *kp1 = dkp1; + const double *kp2 = dkp2; + long i = ph; + + for (; i < M / 2 + 1; i += phases) { + gain += *kp1; + absgain += std::abs(*kp1++); + } + + for (; i < M + 1; i += phases) { + gain += *kp2; + absgain += std::abs(*kp2--); + } + } + + gain = 1.0 / gain; + + long i = ph; + + for (; i < M / 2 + 1; i += phases) + *dkp1++ *= gain; + + if (dkp1 < dkp2) { + for (; i < M + 1; i += phases) + *dkp2-- *= gain; + } + + absgain *= gain; + + if (absgain > maxabsgain) + maxabsgain = absgain; + } + } + + const double gain = 0x10000 / maxabsgain; + const double *dk = dkernel; + + for (unsigned ph = 0; ph < phases; ++ph) { + short *k = kernel + ph * phaseLen; + short *km = kernel + M - ph * phaseLen; + + for (long i = ph; i < M / 2 + 1; i += phases) + *km-- = *k++ = std::floor(*dk++ * gain + 0.5); + } + + delete[] dkernel; +} + +#endif diff --git a/supergameboy/common/resample/rectsinc.h b/supergameboy/common/resample/rectsinc.h new file mode 100644 index 00000000..9f99ed6b --- /dev/null +++ b/supergameboy/common/resample/rectsinc.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RECTSINC_H +#define RECTSINC_H + +#include "convoluter.h" +#include "subresampler.h" +#include "makesinckernel.h" +#include "cic2.h" +#include +#include + +template +class RectSinc : public SubResampler { + PolyPhaseConvoluter convoluters[channels]; + short *kernel; + + static double rectWin(const long /*i*/, const long /*M*/) { + return 1; + } + + void init(unsigned div, unsigned phaseLen, double fc); + +public: + enum { MUL = phases }; + + typedef Cic2 Cic; + static float cicLimit() { return 2.0f; } + + class RollOff { + static unsigned toTaps(const float rollOffWidth) { + static const float widthTimesTaps = 0.9f; + return std::ceil(widthTimesTaps / rollOffWidth); + } + + static float toFc(const float rollOffStart, const int taps) { + static const float startToFcDeltaTimesTaps = 0.43f; + return startToFcDeltaTimesTaps / taps + rollOffStart; + } + + public: + const unsigned taps; + const float fc; + + RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {} + }; + + RectSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); } + RectSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); } + ~RectSinc() { delete[] kernel; } + std::size_t resample(short *out, const short *in, std::size_t inlen); + void adjustDiv(unsigned div); + unsigned mul() const { return MUL; } + unsigned div() const { return convoluters[0].div(); } +}; + +template +void RectSinc::init(const unsigned div, const unsigned phaseLen, const double fc) { + kernel = new short[phaseLen * phases]; + + makeSincKernel(kernel, phases, phaseLen, fc, rectWin); + + for (unsigned i = 0; i < channels; ++i) + convoluters[i].reset(kernel, phaseLen, div); +} + +template +std::size_t RectSinc::resample(short *const out, const short *const in, const std::size_t inlen) { + std::size_t samplesOut; + + for (unsigned i = 0; i < channels; ++i) + samplesOut = convoluters[i].filter(out + i, in + i, inlen); + + return samplesOut; +} + +template +void RectSinc::adjustDiv(const unsigned div) { + for (unsigned i = 0; i < channels; ++i) + convoluters[i].adjustDiv(div); +} + +#endif diff --git a/supergameboy/common/resample/resampler.h b/supergameboy/common/resample/resampler.h new file mode 100644 index 00000000..f3d448d9 --- /dev/null +++ b/supergameboy/common/resample/resampler.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RESAMPLER_H +#define RESAMPLER_H + +#include + +class Resampler { + long inRate_; + long outRate_; + +protected: + void setRate(const long inRate, const long outRate) { inRate_ = inRate; outRate_ = outRate; } + +public: + Resampler() : inRate_(0), outRate_(0) {} + long inRate() const { return inRate_; } + long outRate() const { return outRate_; } + + virtual void adjustRate(long inRate, long outRate) = 0; + virtual void exactRatio(unsigned long &mul, unsigned long &div) const = 0; + virtual std::size_t maxOut(std::size_t inlen) const = 0; + virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0; + virtual ~Resampler() {} +}; + +#endif diff --git a/supergameboy/common/resample/resamplerinfo.cpp b/supergameboy/common/resample/resamplerinfo.cpp new file mode 100644 index 00000000..3abcdaf8 --- /dev/null +++ b/supergameboy/common/resample/resamplerinfo.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "resamplerinfo.h" +#include "chainresampler.h" +#include "hammingsinc.h" +#include "blackmansinc.h" +#include "rectsinc.h" +#include "linint.h" + +struct LinintInfo { + static Resampler* create(long inRate, long outRate, std::size_t) { return new Linint<2>(inRate, outRate); } +}; + +struct RectsincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +struct HammingsincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +struct BlackmansincInfo { + static Resampler* create(long inRate, long outRate, std::size_t periodSz) { + ChainResampler *r = new ChainResampler; + r->init(inRate, outRate, periodSz); + return r; + } +}; + +const ResamplerInfo ResamplerInfo::resamplers[] = { + { "2-tap linear interpolation", LinintInfo::create }, + { "Rectangular windowed sinc (~20 dB SNR)", RectsincInfo::create }, + { "Hamming windowed sinc (~50 dB SNR)", HammingsincInfo::create }, + { "Blackman windowed sinc (~70 dB SNR)", BlackmansincInfo::create } +}; + +const unsigned ResamplerInfo::num_ = sizeof(ResamplerInfo::resamplers) / sizeof(ResamplerInfo); diff --git a/supergameboy/common/resample/resamplerinfo.h b/supergameboy/common/resample/resamplerinfo.h new file mode 100644 index 00000000..23f4a545 --- /dev/null +++ b/supergameboy/common/resample/resamplerinfo.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RESAMPLER_INFO_H +#define RESAMPLER_INFO_H + +#include "resampler.h" + +struct ResamplerInfo { + const char *desc; + Resampler* (*create)(long inRate, long outRate, std::size_t periodSz); + + static unsigned num() { return num_; } + static const ResamplerInfo& get(unsigned n) { return resamplers[n]; } + +private: + static const ResamplerInfo resamplers[]; + static const unsigned num_; +}; + +#endif diff --git a/supergameboy/common/resample/subresampler.h b/supergameboy/common/resample/subresampler.h new file mode 100644 index 00000000..134ec80b --- /dev/null +++ b/supergameboy/common/resample/subresampler.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SUBRESAMPLER_H +#define SUBRESAMPLER_H + +#include + +class SubResampler { +public: + virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0; + virtual unsigned mul() const = 0; + virtual unsigned div() const = 0; + virtual void adjustDiv(unsigned /*div*/) {} + virtual ~SubResampler() {} +}; + +#endif diff --git a/supergameboy/common/resample/u48div.cpp b/supergameboy/common/resample/u48div.cpp new file mode 100644 index 00000000..077ddfd9 --- /dev/null +++ b/supergameboy/common/resample/u48div.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "u48div.h" + +unsigned long u48div(unsigned long num1, unsigned num2, const unsigned long den) { + unsigned long res = 0; + unsigned s = 16; + + do { + if (num1 < 0x10000) { + num1 <<= s; + num1 |= num2 & ((1 << s) - 1); + s = 0; + } else { + if (num1 < 0x1000000) { + const unsigned maxs = s < 8 ? s : 8; + num1 <<= maxs; + num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1); + } + + if (num1 < 0x10000000) { + const unsigned maxs = s < 4 ? s : 4; + num1 <<= maxs; + num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1); + } + + while (num1 < den && s) { + num1 <<= 1; // if this overflows we're screwed + num1 |= num2 >> --s & 1; + } + } + + res += (num1 / den) << s; + num1 = (num1 % den); + } while (s); + + return res; +} diff --git a/supergameboy/common/resample/u48div.h b/supergameboy/common/resample/u48div.h new file mode 100644 index 00000000..26b16af4 --- /dev/null +++ b/supergameboy/common/resample/u48div.h @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef U48DIV_H +#define U48DIV_H + +unsigned long u48div(unsigned long num1, unsigned num2, unsigned long den); + +#endif diff --git a/supergameboy/common/resample/upsampler.h b/supergameboy/common/resample/upsampler.h new file mode 100644 index 00000000..8bf88d8a --- /dev/null +++ b/supergameboy/common/resample/upsampler.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef UPSAMPLER_H +#define UPSAMPLER_H + +#include "subresampler.h" +#include + +template +class Upsampler : public SubResampler { + unsigned mul_; + +public: + Upsampler(const unsigned mul) : mul_(mul) {} + std::size_t resample(short *out, const short *in, std::size_t inlen); + unsigned mul() const { return mul_; } + unsigned div() const { return 1; } +}; + +template +std::size_t Upsampler::resample(short *out, const short *in, std::size_t inlen) { + if (inlen) { + std::memset(out, 0, inlen * mul_ * sizeof(short) * channels); + + do { + std::memcpy(out, in, sizeof(short) * channels); + in += channels; + out += mul_ * channels; + } while (--inlen); + } + + return inlen * mul_; +} + +#endif diff --git a/supergameboy/common/ringbuffer.h b/supergameboy/common/ringbuffer.h new file mode 100644 index 00000000..34f22bfe --- /dev/null +++ b/supergameboy/common/ringbuffer.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include "array.h" +#include +#include +#include + +template +class RingBuffer { + Array buf; + std::size_t sz; + std::size_t rpos; + std::size_t wpos; + +public: + RingBuffer(const std::size_t sz_in = 0) : sz(0), rpos(0), wpos(0) { reset(sz_in); } + + std::size_t avail() const { + return (wpos < rpos ? 0 : sz) + rpos - wpos - 1; + } + + void clear() { + wpos = rpos = 0; + } + + void fill(T value); + + void read(T *out, std::size_t num); + + void reset(std::size_t sz_in); + + std::size_t size() const { + return sz - 1; + } + + std::size_t used() const { + return (wpos < rpos ? sz : 0) + wpos - rpos; + } + + void write(const T *in, std::size_t num); +}; + +template +void RingBuffer::fill(const T value) { + std::fill(buf + 0, buf + sz, value); + rpos = 0; + wpos = sz - 1; +} + +template +void RingBuffer::read(T *out, std::size_t num) { + if (rpos + num > sz) { + const std::size_t n = sz - rpos; + + std::memcpy(out, buf + rpos, n * sizeof(T)); + + rpos = 0; + num -= n; + out += n; + } + + std::memcpy(out, buf + rpos, num * sizeof(T)); + + if ((rpos += num) == sz) + rpos = 0; +} + +template +void RingBuffer::reset(const std::size_t sz_in) { + sz = sz_in + 1; + rpos = wpos = 0; + buf.reset(sz_in ? sz : 0); +} + +template +void RingBuffer::write(const T *in, std::size_t num) { + if (wpos + num > sz) { + const std::size_t n = sz - wpos; + + std::memcpy(buf + wpos, in, n * sizeof(T)); + + wpos = 0; + num -= n; + in += n; + } + + std::memcpy(buf + wpos, in, num * sizeof(T)); + + if ((wpos += num) == sz) + wpos = 0; +} + +#endif diff --git a/supergameboy/common/usec.h b/supergameboy/common/usec.h new file mode 100644 index 00000000..2bc889cf --- /dev/null +++ b/supergameboy/common/usec.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef USEC_H +#define USEC_H + +typedef unsigned long usec_t; + +static inline usec_t negate(usec_t t) { + return usec_t(0) - t; +} + +usec_t getusecs(); +void usecsleep(usec_t usecs); + +#endif diff --git a/supergameboy/interface/interface.cpp b/supergameboy/interface/interface.cpp new file mode 100644 index 00000000..b726b147 --- /dev/null +++ b/supergameboy/interface/interface.cpp @@ -0,0 +1,373 @@ +SuperGameBoy supergameboy; + +//==================== +//SuperGameBoy::Packet +//==================== + +const char SuperGameBoy::command_name[32][64] = { + "PAL01", "PAL23", "PAL03", "PAL12", + "ATTR_BLK", "ATTR_LIN", "ATTR_DIV", "ATTR_CHR", + "SOUND", "SOU_TRN", "PAL_SET", "PAL_TRN", + "ATRC_EN", "TEST_EN", "ICON_EN", "DATA_SND", + "DATA_TRN", "MLT_REQ", "JUMP", "CHR_TRN", + "PCT_TRN", "ATTR_TRN", "ATTR_SET", "MASK_EN", + "OBJ_TRN", "19_???", "1A_???", "1B_???", + "1C_???", "1D_???", "1E_ROM", "1F_???", +}; + +void SuperGameBoy::joyp_write(bool p15, bool p14) { + //=============== + //joypad handling + //=============== + + if(p15 == 1 && p14 == 1) { + if(joyp15lock == 0 && joyp14lock == 0) { + joyp15lock = 1; + joyp14lock = 1; + joyp_id = (joyp_id + 1) & 3; + } + } + + if(p15 == 0 && p14 == 1) joyp15lock = 0; + if(p15 == 1 && p14 == 0) joyp14lock = 0; + + //=============== + //packet handling + //=============== + + if(p15 == 0 && p14 == 0) { + //pulse + pulselock = false; + packetoffset = 0; + bitoffset = 0; + strobelock = true; + packetlock = false; + return; + } + + if(pulselock) return; + + if(p15 == 1 && p14 == 1) { + strobelock = false; + return; + } + + if(strobelock) { + if(p15 == 1 || p14 == 1) { + //malformed packet + packetlock = false; + pulselock = true; + bitoffset = 0; + packetoffset = 0; + } else { + return; + } + } + + //p15:1, p14:0 = 0 + //p15:0, p14:1 = 1 + bool bit = (p15 == 0); + strobelock = true; + + if(packetlock) { + if(p15 == 1 && p14 == 0) { + if((joyp_packet[0] >> 3) == 0x11) { + mmio.mlt_req = joyp_packet[1] & 3; + if(mmio.mlt_req == 2) mmio.mlt_req = 3; + joyp_id = 0; + } + + if(packetsize < 64) packet[packetsize++] = joyp_packet; + packetlock = false; + pulselock = true; + } + return; + } + + bitdata = (bit << 7) | (bitdata >> 1); + if(++bitoffset < 8) return; + + bitoffset = 0; + joyp_packet[packetoffset] = bitdata; + if(++packetoffset < 16) return; + packetlock = true; +} + +//================== +//SuperGameBoy::Core +//================== + +static uint8_t null_rom[32768]; + +bool SuperGameBoy::init(bool version_) { + if(!romdata) { romdata = null_rom; romsize = 32768; } + version = version_; + + gambatte = new Gambatte::GB; + gambatte->setVideoBlitter(this); + gambatte->setInputStateGetter(this); + + return true; +} + +void SuperGameBoy::term() { + if(gambatte) { + delete gambatte; + gambatte = 0; + } +} + +unsigned SuperGameBoy::run(uint32_t *samplebuffer, unsigned samples) { + if((mmio.r6003 & 0x80) == 0) { + //Gameboy is inactive + samplebuffer[0] = 0; + return 1; + } + + return gambatte->runFor(samplebuffer, samples); +} + +void SuperGameBoy::save() { + gambatte->saveSavedata(); +} + +void SuperGameBoy::serialize(nall::serializer &s) { + s.integer(vram_row); + s.array(vram); + + s.integer(mmio.r6000); + s.integer(mmio.r6003); + s.integer(mmio.r6004); + s.integer(mmio.r6005); + s.integer(mmio.r6006); + s.integer(mmio.r6007); + s.array(mmio.r7000); + s.integer(mmio.r7800); + s.integer(mmio.mlt_req); + + for(unsigned i = 0; i < 64; i++) s.array(packet[i].data); + s.integer(packetsize); + + s.integer(joyp_id); + s.integer(joyp15lock); + s.integer(joyp14lock); + s.integer(pulselock); + s.integer(strobelock); + s.integer(packetlock); + s.array(joyp_packet.data); + s.integer(packetoffset); + s.integer(bitdata); + s.integer(bitoffset); + + uint8_t *savestate = new uint8_t[256 * 1024]; + if(s.mode() == serializer::Load) { + s.array(savestate, 256 * 1024); + + file fp; + if(fp.open("supergameboy-state.tmp", file::mode_write)) { + fp.write(savestate, 256 * 1024); + fp.close(); + + gambatte->loadState("supergameboy-state.tmp"); + unlink("supergameboy-state.tmp"); + } + } else if(s.mode() == serializer::Save) { + gambatte->saveState("supergameboy-state.tmp"); + + file fp; + if(fp.open("supergameboy-state.tmp", file::mode_read)) { + fp.read(savestate, fp.size() < 256 * 1024 ? fp.size() : 256 * 1024); + fp.close(); + } + + unlink("supergameboy-state.tmp"); + s.array(savestate, 256 * 1024); + } else if(s.mode() == serializer::Size) { + s.array(savestate, 256 * 1024); + } + delete[] savestate; +} + +void SuperGameBoy::power() { + gambatte->load(true); + mmio_reset(); +} + +void SuperGameBoy::reset() { + gambatte->reset(); + mmio_reset(); +} + +void SuperGameBoy::row(unsigned row) { + mmio.r7800 = 0; + vram_row = row; + render(vram_row); +} + +uint8_t SuperGameBoy::read(uint16_t addr) { + //LY counter + if(addr == 0x6000) { + return gambatte->lyCounter(); + } + + //command ready port + if(addr == 0x6002) { + bool data = packetsize > 0; + if(data) { + for(unsigned i = 0; i < 16; i++) mmio.r7000[i] = packet[0][i]; + packetsize--; + for(unsigned i = 0; i < packetsize; i++) packet[i] = packet[i + 1]; + } + return data; + } + + //command port + if((addr & 0xfff0) == 0x7000) { + return mmio.r7000[addr & 15]; + } + + if(addr == 0x7800) { + uint8_t data = vram[mmio.r7800]; + mmio.r7800 = (mmio.r7800 + 1) % 320; + return data; + } + + return 0x00; +} + +void SuperGameBoy::write(uint16_t addr, uint8_t data) { + //control port + //d7 = /RESET line (0 = stop, 1 = run) + if(addr == 0x6003) { + if((mmio.r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) { + reset(); + command_1e(); + } + + mmio.r6003 = data; + return; + } + + if(addr == 0x6004) { mmio.r6004 = data; return; } //joypad 1 state + if(addr == 0x6005) { mmio.r6005 = data; return; } //joypad 2 state + if(addr == 0x6006) { mmio.r6006 = data; return; } //joypad 3 state + if(addr == 0x6007) { mmio.r6007 = data; return; } //joypad 4 state +} + +void SuperGameBoy::mmio_reset() { + mmio.r6000 = 0x00; + mmio.r6003 = 0x00; + mmio.r6004 = 0xff; + mmio.r6005 = 0xff; + mmio.r6006 = 0xff; + mmio.r6007 = 0xff; + for(unsigned n = 0; n < 16; n++) mmio.r7000[n] = 0; + mmio.r7800 = 0; + mmio.mlt_req = 0; + + packetsize = 0; + + vram_row = 0; + memset(vram, 0, 320); + + joyp_id = 3; + joyp15lock = 0; + joyp14lock = 0; + pulselock = true; +} + +//simulate 256-byte internal SGB BIOS on /RESET +void SuperGameBoy::command_1e() { + for(unsigned i = 0; i < 6; i++) { + Packet p; + p[0] = 0xf1 + (i << 1); + p[1] = 0; + for(unsigned n = 2; n < 16; n++) { + uint8_t data = romdata[0x0104 + (i * 14) + (n - 2)]; + p[1] += data; + p[n] = data; + } + if(packetsize < 64) packet[packetsize++] = p; + } +} + +void SuperGameBoy::render(unsigned row) { + gambatte->updateVideo(); + + uint32_t *source = buffer + row * 160 * 8; + memset(vram, 0x00, 320); + + for(unsigned y = row * 8; y < row * 8 + 8; y++) { + for(unsigned x = 0; x < 160; x++) { + unsigned pixel = *source++ / 0x555555; + pixel ^= 3; + + unsigned addr = (x / 8 * 16) + ((y & 7) * 2); + vram[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); + vram[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); + } + } +} + +//====================== +//Gambatte::VideoBlitter +//====================== + +//should always be 160x144, as no filters are used +void SuperGameBoy::setBufferDimensions(unsigned width, unsigned height) { + if(buffer) delete[] buffer; + buffer = new uint32_t[width * height]; + bufferWidth = width; + bufferHeight = height; +} + +const Gambatte::PixelBuffer SuperGameBoy::inBuffer() { + Gambatte::PixelBuffer pixelBuffer; + pixelBuffer.pixels = (void*)buffer; + pixelBuffer.format = Gambatte::PixelBuffer::RGB32; + pixelBuffer.pitch = bufferWidth; + return pixelBuffer; +} + +void SuperGameBoy::blit() { +} + +//========================== +//Gambatte::InputStateGetter +//========================== + +const Gambatte::InputState& SuperGameBoy::operator()() { + inputState.joypadId = 0x0f - (joyp_id & mmio.mlt_req); + + unsigned data = 0x00; + switch(joyp_id & mmio.mlt_req) { + case 0: data = mmio.r6004; break; + case 1: data = mmio.r6005; break; + case 2: data = mmio.r6006; break; + case 3: data = mmio.r6007; break; + } + + inputState.startButton = !(data & 0x80); + inputState.selectButton = !(data & 0x40); + inputState.bButton = !(data & 0x20); + inputState.aButton = !(data & 0x10); + inputState.dpadDown = !(data & 0x08); + inputState.dpadUp = !(data & 0x04); + inputState.dpadLeft = !(data & 0x02); + inputState.dpadRight = !(data & 0x01); + + return inputState; +} + +//========================== +//SuperGameBoy::Construction +//========================== + +SuperGameBoy::SuperGameBoy() : gambatte(0), buffer(0) { + romdata = ramdata = rtcdata = 0; + romsize = ramsize = rtcsize = 0; +} + +SuperGameBoy::~SuperGameBoy() { + if(buffer) delete[] buffer; +} diff --git a/supergameboy/interface/interface.hpp b/supergameboy/interface/interface.hpp new file mode 100644 index 00000000..d369a281 --- /dev/null +++ b/supergameboy/interface/interface.hpp @@ -0,0 +1,80 @@ +class SuperGameBoy : public Gambatte::VideoBlitter, public Gambatte::InputStateGetter { +public: + Gambatte::GB *gambatte; + +//SuperGameBoy::MMIO + unsigned vram_row; + uint8_t vram[320]; + + struct MMIO { + uint8_t r6000; + uint8_t r6003; + uint8_t r6004; + uint8_t r6005; + uint8_t r6006; + uint8_t r6007; + uint8_t r7000[16]; + unsigned r7800; + uint8_t mlt_req; + } mmio; + +//SuperGameBoy::Packet + static const char command_name[32][64]; + + struct Packet { + uint8_t data[16]; + uint8_t& operator[](unsigned addr) { return data[addr & 15]; } + }; + Packet packet[64]; + unsigned packetsize; + + unsigned joyp_id; + bool joyp15lock; + bool joyp14lock; + bool pulselock; + bool strobelock; + bool packetlock; + Packet joyp_packet; + uint8_t packetoffset; + uint8_t bitdata, bitoffset; + + void joyp_write(bool p15, bool p14); + +//SuperGameBoy::Core + uint8_t *romdata, *ramdata, *rtcdata; + unsigned romsize, ramsize, rtcsize; + bool version; + + bool init(bool version); + void term(); + unsigned run(uint32_t *samplebuffer, unsigned samples); + void save(); + void serialize(nall::serializer &s); + void power(); + void reset(); + void row(unsigned row); + uint8_t read(uint16_t addr); + void write(uint16_t addr, uint8_t data); + + void mmio_reset(); + void command_1e(); + void render(unsigned row); + + SuperGameBoy(); + ~SuperGameBoy(); + +//Gambatte::VideoBlitter + unsigned bufferWidth, bufferHeight; + uint32_t *buffer; + + void setBufferDimensions(unsigned width, unsigned height); + const Gambatte::PixelBuffer inBuffer(); + void blit(); + +//Gambatte::InputStateGetter + Gambatte::InputState inputState; + + const Gambatte::InputState& operator()(); +}; + +extern SuperGameBoy supergameboy; diff --git a/supergameboy/libgambatte/SConstruct b/supergameboy/libgambatte/SConstruct new file mode 100644 index 00000000..47087ba0 --- /dev/null +++ b/supergameboy/libgambatte/SConstruct @@ -0,0 +1,64 @@ +global_cflags = ARGUMENTS.get('CFLAGS', '-Wall -Wextra -O2 -fomit-frame-pointer') +global_cxxflags = ARGUMENTS.get('CXXFLAGS', global_cflags + ' -fno-exceptions -fno-rtti') +global_defines = ' -DHAVE_STDINT_H -DCHAR_WIDTH_8' + +env = Environment(CPPPATH = ['src', 'include', '../common'], + CFLAGS = global_cflags + global_defines, + CXXFLAGS = global_cxxflags + global_defines) + +sourceFiles = Split(''' + src/bitmap_font.cpp + src/colorconversion.cpp + src/cpu.cpp + src/gambatte.cpp + src/initstate.cpp + src/interrupter.cpp + src/memory.cpp + src/rtc.cpp + src/sound.cpp + src/state_osd_elements.cpp + src/statesaver.cpp + src/video.cpp + src/sound/channel1.cpp + src/sound/channel2.cpp + src/sound/channel3.cpp + src/sound/channel4.cpp + src/sound/duty_unit.cpp + src/sound/envelope_unit.cpp + src/sound/length_counter.cpp + src/video/basic_add_event.cpp + src/video/break_event.cpp + src/video/irq_event.cpp + src/video/ly_counter.cpp + src/video/lyc_irq.cpp + src/video/m3_extra_cycles.cpp + src/video/mode3_event.cpp + src/video/mode0_irq.cpp + src/video/mode1_irq.cpp + src/video/mode2_irq.cpp + src/video/sc_reader.cpp + src/video/scx_reader.cpp + src/video/sprite_mapper.cpp + src/video/we_master_checker.cpp + src/video/we.cpp + src/video/wx_reader.cpp + src/video/wy.cpp + src/video/filters/catrom2x.cpp + src/video/filters/catrom3x.cpp + src/video/filters/kreed2xsai.cpp + src/video/filters/maxsthq2x.cpp + src/video/filters/maxsthq3x.cpp + ''') + +conf = env.Configure() + +if conf.CheckHeader('zlib.h') and conf.CheckLib('z'): + sourceFiles.append('src/file/unzip/unzip.c') + sourceFiles.append('src/file/unzip/ioapi.c') + sourceFiles.append('src/file/file_zip.cpp') +else: + sourceFiles.append('src/file/file.cpp') + +conf.Finish() + +env.Library('gambatte', sourceFiles) diff --git a/supergameboy/libgambatte/include/filterinfo.h b/supergameboy/libgambatte/include/filterinfo.h new file mode 100644 index 00000000..ab5a4726 --- /dev/null +++ b/supergameboy/libgambatte/include/filterinfo.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_FILTERINFO_H +#define GAMBATTE_FILTERINFO_H + +#include + +namespace Gambatte { +struct FilterInfo { + std::string handle; + unsigned int outWidth; + unsigned int outHeight; +}; +} + +#endif diff --git a/supergameboy/libgambatte/include/gambatte.h b/supergameboy/libgambatte/include/gambatte.h new file mode 100644 index 00000000..fc787d76 --- /dev/null +++ b/supergameboy/libgambatte/include/gambatte.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_H +#define GAMBATTE_H + +class CPU; + +#include "videoblitter.h" +#include "inputstate.h" +#include "inputstategetter.h" +#include "filterinfo.h" +#include "int.h" +#include + +namespace Gambatte { +class GB { + CPU *const z80; + int stateNo; + + void loadState(const char *filepath, bool osdMessage); + +public: + GB(); + ~GB(); + bool load(bool forceDmg = false); + + /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer. + * There are 35112 stereo sound samples in a video frame. + * May run for uptil 2064 stereo samples too long. + * A stereo sample consists of two native endian 2s complement 16-bit PCM samples, + * with the left sample preceding the right one. Usually casting soundBuf to/from + * short* is OK and recommended. The reason for not using a short* in the interface + * is to avoid implementation defined behaviour without compromising performance. + * + * @param soundBuf buffer with space >= samples + 2064 + * @param samples number of stereo samples to produce + * @return actual number of samples produced + */ + unsigned runFor(Gambatte::uint_least32_t *soundBuf, unsigned samples); + void updateVideo(); + unsigned lyCounter(); + + void reset(); + void setVideoBlitter(VideoBlitter *vb); + void videoBufferChange(); + unsigned videoWidth() const; + unsigned videoHeight() const; + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32); + + void setVideoFilter(unsigned n); + std::vector filterInfo() const; + void setInputStateGetter(InputStateGetter *getInput); + + void set_savedir(const char *sdir); + bool isCgb() const; + void saveSavedata(); + void saveState(); + void loadState(); + void saveState(const char *filepath); + void loadState(const char *filepath); + void selectState(int n); + int currentState() const { return stateNo; } +}; +} + +#endif diff --git a/supergameboy/libgambatte/include/inputstate.h b/supergameboy/libgambatte/include/inputstate.h new file mode 100644 index 00000000..bdfec44f --- /dev/null +++ b/supergameboy/libgambatte/include/inputstate.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INPUTSTATE_H +#define GAMBATTE_INPUTSTATE_H + +namespace Gambatte { +struct InputState { + unsigned joypadId; + bool startButton, selectButton, bButton, aButton; + bool dpadDown, dpadUp, dpadLeft, dpadRight; +}; +} + +#endif diff --git a/supergameboy/libgambatte/include/inputstategetter.h b/supergameboy/libgambatte/include/inputstategetter.h new file mode 100644 index 00000000..375dad5e --- /dev/null +++ b/supergameboy/libgambatte/include/inputstategetter.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INPUTSTATEGETTER_H +#define GAMBATTE_INPUTSTATEGETTER_H + +namespace Gambatte { +class InputStateGetter { +public: + virtual ~InputStateGetter() {}; + virtual const InputState& operator()() = 0; +}; +} + +#endif diff --git a/supergameboy/libgambatte/include/int.h b/supergameboy/libgambatte/include/int.h new file mode 100644 index 00000000..116ab8b7 --- /dev/null +++ b/supergameboy/libgambatte/include/int.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_INT_H +#define GAMBATTE_INT_H + +#include + +namespace Gambatte { +typedef uint32_t uint_least32_t; +typedef uint16_t uint_least16_t; +} + +#endif diff --git a/supergameboy/libgambatte/include/videoblitter.h b/supergameboy/libgambatte/include/videoblitter.h new file mode 100644 index 00000000..d2a9c335 --- /dev/null +++ b/supergameboy/libgambatte/include/videoblitter.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GAMBATTE_VIDEOBLITTER_H +#define GAMBATTE_VIDEOBLITTER_H + +namespace Gambatte { + +struct PixelBuffer { + enum Format { RGB32, RGB16, UYVY }; + + void *pixels; + Format format; + unsigned pitch; + + PixelBuffer() : pixels(0), format(RGB32), pitch(0) {} +}; + +class VideoBlitter { +public: + virtual void setBufferDimensions(unsigned width, unsigned height) = 0; + virtual const PixelBuffer inBuffer() = 0; + virtual void blit() = 0; + virtual ~VideoBlitter() {} +}; + +} + +#endif diff --git a/supergameboy/libgambatte/src/bitmap_font.cpp b/supergameboy/libgambatte/src/bitmap_font.cpp new file mode 100644 index 00000000..b644c06f --- /dev/null +++ b/supergameboy/libgambatte/src/bitmap_font.cpp @@ -0,0 +1,328 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* + The following font bitmaps (static const unsigned char *_bits[]), only used + as data and included in this source file for convenience, are derived from + the Bitstream Vera Sans font, which is distributed under the following + copyright: + + Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera + is a trademark of Bitstream, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of the fonts accompanying this license ("Fonts") and associated + documentation files (the "Font Software"), to reproduce and distribute the + Font Software, including without limitation the rights to use, copy, merge, + publish, distribute, and/or sell copies of the Font Software, and to permit + persons to whom the Font Software is furnished to do so, subject to the + following conditions: + + The above copyright and trademark notices and this permission notice shall + be included in all copies of one or more of the Font Software typefaces. + + The Font Software may be modified, altered, or added to, and in particular + the designs of glyphs or characters in the Fonts may be modified and + additional glyphs or characters may be added to the Fonts, only if the fonts + are renamed to names not containing either the words "Bitstream" or the word + "Vera". + + This License becomes null and void to the extent applicable to Fonts or Font + Software that has been modified and is distributed under the "Bitstream Vera" + names. + + The Font Software may be sold as part of a larger software package but no + copy of one or more of the Font Software typefaces may be sold by itself. + + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM + OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM + OTHER DEALINGS IN THE FONT SOFTWARE. + + Except as contained in this notice, the names of Gnome, the Gnome + Foundation, and Bitstream Inc., shall not be used in advertising or + otherwise to promote the sale, use or other dealings in this Font Software + without prior written authorization from the Gnome Foundation or + Bitstream Inc., respectively. For further information, contact: fonts at + gnome dot org. +*/ + +#include "bitmap_font.h" + +static const unsigned char n0_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c }; + +static const unsigned char n1_bits[] = { 0x68, + 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e }; + +static const unsigned char n2_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x20, 0x10, 0x08, 0x04, 0x3e }; + +static const unsigned char n3_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x20, 0x1c, 0x20, 0x22, 0x1c }; + +static const unsigned char n4_bits[] = { 0x68, + 0x00, 0x18, 0x18, 0x14, 0x12, 0x3e, 0x10, 0x10 }; + +static const unsigned char n5_bits[] = { 0x68, + 0x00, 0x1e, 0x02, 0x1e, 0x20, 0x20, 0x20, 0x1e }; + +static const unsigned char n6_bits[] = { 0x68, + 0x00, 0x3c, 0x06, 0x02, 0x1e, 0x22, 0x22, 0x1c }; + +static const unsigned char n7_bits[] = { 0x68, + 0x00, 0x3e, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04 }; + +static const unsigned char n8_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c }; + +static const unsigned char n9_bits[] = { 0x68, + 0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x30, 0x1e }; + +static const unsigned char A_bits[] = { 0x78, + 0x00, 0x08, 0x14, 0x14, 0x22, 0x3e, 0x22, 0x41 }; + +static const unsigned char a_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3e }; + +static const unsigned char B_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e }; + +static const unsigned char b_bits[] = { 0x68, + 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e }; + +static const unsigned char C_bits[] = { 0x88, + 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x44, 0x38 }; + +static const unsigned char c_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1c, 0x02, 0x02, 0x02, 0x1c }; + +static const unsigned char D_bits[] = { 0x88, + 0x00, 0x3e, 0x62, 0x42, 0x42, 0x42, 0x62, 0x3e }; + +static const unsigned char d_bits[] = { 0x68, + 0x20, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c }; + +static const unsigned char E_bits[] = { 0x78, + 0x00, 0x3e, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x3e }; + +static const unsigned char e_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x3c }; + +static const unsigned char F_bits[] = { 0x68, + 0x00, 0x1e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02 }; + +static const unsigned char f_bits[] = { 0x48, + 0x0e, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char G_bits[] = { 0x88, + 0x00, 0x3c, 0x46, 0x02, 0x72, 0x42, 0x46, 0x3c }; + +static const unsigned char g_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c }; + +static const unsigned char H_bits[] = { 0x88, + 0x00, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42 }; + +static const unsigned char h_bits[] = { 0x68, + 0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22 }; + +static const unsigned char I_bits[] = { 0x38, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char i_bits[] = { 0x28, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char J_bits[] = { 0x4a, + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03 }; + +static const unsigned char j_bits[] = { 0x2a, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03 }; + +static const unsigned char K_bits[] = { 0x78, + 0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22 }; + +static const unsigned char k_bits[] = { 0x58, + 0x02, 0x02, 0x02, 0x12, 0x0a, 0x06, 0x0a, 0x12 }; + +static const unsigned char L_bits[] = { 0x68, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e }; + +static const unsigned char l_bits[] = { 0x28, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char M_bits[] = { 0x98, + 0x00, 0x00, 0x82, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x92, 0x00, 0x82, 0x00 }; + +static const unsigned char m_bits[] = { 0xa8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x01, 0x22, 0x02, 0x22, 0x02, + 0x22, 0x02, 0x22, 0x02 }; + +static const unsigned char N_bits[] = { 0x88, + 0x00, 0x42, 0x46, 0x4a, 0x4a, 0x52, 0x62, 0x42 }; + +static const unsigned char n_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22 }; + +static const unsigned char O_bits[] = { 0x88, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x66, 0x3c }; + +static const unsigned char o_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c }; + +static const unsigned char P_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02 }; + +static const unsigned char p_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x02 }; + +static const unsigned char Q_bits[] = { 0x89, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x26, 0x1c, 0x20 }; + +static const unsigned char q_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20 }; + +static const unsigned char R_bits[] = { 0x78, + 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x12, 0x22, 0x42 }; + +static const unsigned char r_bits[] = { 0x48, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x02, 0x02, 0x02 }; + +static const unsigned char S_bits[] = { 0x78, + 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c }; + +static const unsigned char s_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1e, 0x02, 0x1c, 0x10, 0x1e }; + +static const unsigned char T_bits[] = { 0x58, + 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + +static const unsigned char t_bits[] = { 0x48, + 0x00, 0x02, 0x02, 0x0f, 0x02, 0x02, 0x02, 0x0e }; + +static const unsigned char U_bits[] = { 0x88, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c }; + +static const unsigned char u_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3c }; + +static const unsigned char V_bits[] = { 0x78, + 0x00, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08 }; + +static const unsigned char v_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08 }; + +static const unsigned char W_bits[] = { 0x98, + 0x00, 0x00, 0x11, 0x01, 0x11, 0x01, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x44, 0x00, 0x44, 0x00 }; + +static const unsigned char w_bits[] = { 0x88, + 0x00, 0x00, 0x00, 0x92, 0xaa, 0xaa, 0x44, 0x44 }; + +static const unsigned char X_bits[] = { 0x68, + 0x00, 0x21, 0x12, 0x0c, 0x0c, 0x0c, 0x12, 0x21 }; + +static const unsigned char x_bits[] = { 0x68, + 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22 }; + +static const unsigned char Y_bits[] = { 0x78, + 0x00, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08 }; + +static const unsigned char y_bits[] = { 0x6a, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x06 }; + +static const unsigned char Z_bits[] = { 0x68, + 0x00, 0x3f, 0x10, 0x08, 0x0c, 0x04, 0x02, 0x3f }; + +static const unsigned char z_bits[] = { 0x58, + 0x00, 0x00, 0x00, 0x1e, 0x10, 0x08, 0x04, 0x1e }; + +static const unsigned char SPC_bits[] = { 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +namespace BitmapFont { +const unsigned char *const font[] = { + 0, + n0_bits, n1_bits, n2_bits, n3_bits, n4_bits, n5_bits, n6_bits, n7_bits, n8_bits, n9_bits, + A_bits, B_bits, C_bits, D_bits, E_bits, F_bits, G_bits, H_bits, I_bits, J_bits, K_bits, L_bits, M_bits, + N_bits, O_bits, P_bits, Q_bits, R_bits, S_bits, T_bits, U_bits, V_bits, W_bits, X_bits, Y_bits, Z_bits, + a_bits, b_bits, c_bits, d_bits, e_bits, f_bits, g_bits, h_bits, i_bits, j_bits, k_bits, l_bits, m_bits, + n_bits, o_bits, p_bits, q_bits, r_bits, s_bits, t_bits, u_bits, v_bits, w_bits, x_bits, y_bits, z_bits, + SPC_bits +}; + +unsigned getWidth(const char *chars) { + unsigned w = 0; + + while (const int character = *chars++) { + w += *font[character] >> 4; + } + + return w; +} + +class Rgb32Fill { + const unsigned long color; + +public: + Rgb32Fill(unsigned long color) : color(color) {} + + void operator()(Gambatte::uint_least32_t *dest, unsigned /*pitch*/) { + *dest = color; + } +}; + +void print(Gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) { + print(dest, pitch, Rgb32Fill(color), chars); +} + +static void reverse(char *first, char *last) { + while (first < last) { + const int tmp = *first; + + *first = *last; + *last = tmp; + + ++first; + --last; + } +} + +void utoa(unsigned u, char *a) { + char *aa = a; + + while (u > 9) { + const unsigned div = u / 10; + const unsigned rem = u % 10; + + u = div; + *aa++ = rem + N0; + } + + *aa = u + N0; + + reverse(a, aa); +} +} diff --git a/supergameboy/libgambatte/src/bitmap_font.h b/supergameboy/libgambatte/src/bitmap_font.h new file mode 100644 index 00000000..8217cf61 --- /dev/null +++ b/supergameboy/libgambatte/src/bitmap_font.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BITMAP_FONT_H +#define BITMAP_FONT_H + +#include "int.h" + +namespace BitmapFont { +enum Char { + NUL, + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z, + SPC +}; + +enum { HEIGHT = 10 }; +enum { MAX_WIDTH = 9 }; +enum { NUMBER_WIDTH = 6 }; + +unsigned getWidth(const char *chars); + +// struct Fill { void operator()(RandomAccessIterator dest, unsigned pitch) { fill pixels at dest } } +template +void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars); + +void print(Gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars); +void utoa(unsigned u, char *a); + +// --- INTERFACE END --- + + + +extern const unsigned char *const font[]; + +template +void print(RandomAccessIterator dest, const unsigned pitch, Fill fill, const char *chars) { + while (const int character = *chars++) { + RandomAccessIterator dst = dest; + const unsigned char *s = font[character]; + + const unsigned width = *s >> 4; + unsigned h = *s++ & 0xF; + + while (h--) { + RandomAccessIterator d = dst; + + unsigned line = *s++; + + if (width > 8) + line |= *s++ << 8; + + while (line) { + if (line & 1) + fill(d, pitch); + + line >>= 1; + ++d; + } + + dst += pitch; + } + + dest += width; + } +} +} + +#endif diff --git a/supergameboy/libgambatte/src/colorconversion.cpp b/supergameboy/libgambatte/src/colorconversion.cpp new file mode 100644 index 00000000..d76b0aee --- /dev/null +++ b/supergameboy/libgambatte/src/colorconversion.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "colorconversion.h" +#include + +Rgb32ToUyvy::Rgb32ToUyvy() { +#ifdef WORDS_BIGENDIAN + const CacheUnit c = { 0, 128ul << 24 | 16ul << 16 | 128 << 8 | 16 }; +#else + const CacheUnit c = { 0, 16ul << 24 | 128ul << 16 | 16 << 8 | 128 }; +#endif + std::fill(cache, cache + cache_size, c); +} + +void Rgb32ToUyvy::operator()(const Gambatte::uint_least32_t *s, Gambatte::uint_least32_t *d, const unsigned w, unsigned h, const unsigned d_pitch) { + while (h--) { + for (const Gambatte::uint_least32_t *const ends = s + w; s != ends;) { + if ((cache[*s & cache_mask].rgb32 - *s) | (cache[*(s+1) & cache_mask].rgb32 - *(s+1))) { + cache[*s & cache_mask].rgb32 = *s; + cache[*(s+1) & cache_mask].rgb32 = *(s+1); + + const unsigned long r = (*s >> 16 & 0x000000FF) | (*(s+1) & 0x00FF0000); + const unsigned long g = (*s >> 8 & 0x000000FF) | (*(s+1) << 8 & 0x00FF0000); + const unsigned long b = (*s & 0x000000FF) | (*(s+1) << 16 & 0x00FF0000); + + const unsigned long y = r * 66 + g * 129 + b * 25 + (16 * 256 + 128) * 0x00010001ul; + const unsigned long u = b * 112 - r * 38 - g * 74 + (128 * 256 + 128) * 0x00010001ul; + const unsigned long v = r * 112 - g * 94 - b * 18 + (128 * 256 + 128) * 0x00010001ul; + +#ifdef WORDS_BIGENDIAN + *d++ = cache[*s & cache_mask].uyvy = (u << 16 & 0xFF000000) | (y << 8 & 0x00FF0000) | (v & 0x0000FF00) | (y >> 8 & 0x000000FF); + *d++ = cache[*(s+1) & cache_mask].uyvy = (u & 0xFF000000) | (y >> 8 & 0x00FF0000) | (v >> 16 & 0x0000FF00) | y >> 24; +#else + *d++ = cache[*s & cache_mask].uyvy = (y << 16 & 0xFF000000) | (v << 8 & 0x00FF0000) | (y & 0x0000FF00) | (u >> 8 & 0x000000FF); + *d++ = cache[*(s+1) & cache_mask].uyvy = (y & 0xFF000000) | (v >> 8 & 0x00FF0000) | (y >> 16 & 0x0000FF00) | u >> 24; +#endif + } else { + *d++ = cache[*s & cache_mask].uyvy; + *d++ = cache[*(s+1) & cache_mask].uyvy; + } + + s += 2; + } + + d += d_pitch - w; + } +} + +unsigned long rgb32ToUyvy(unsigned long rgb32) { + const unsigned r = rgb32 >> 16 & 0xFF; + const unsigned g = rgb32 >> 8 & 0xFF; + const unsigned b = rgb32 & 0xFF; + + const unsigned long y = (r * 66 + g * 129 + b * 25 + 16 * 256 + 128) >> 8; + const unsigned long u = (b * 112 - r * 38 - g * 74 + 128 * 256 + 128) >> 8; + const unsigned long v = (r * 112 - g * 94 - b * 18 + 128 * 256 + 128) >> 8; + +#ifdef WORDS_BIGENDIAN + return u << 24 | y << 16 | v << 8 | y; +#else + return y << 24 | v << 16 | y << 8 | u; +#endif +} + +void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, const unsigned w, unsigned h, const unsigned dstPitch) { + do { + unsigned n = w; + + do { + *d++ = (*s >> 8 & 0xF800) | (*s >> 5 & 0x07E0) | (*s >> 3 & 0x001F); + ++s; + } while (--n); + + d += dstPitch - w; + } while (--h); +} + +unsigned rgb32ToRgb16(const unsigned long rgb32) { + return (rgb32 >> 8 & 0xF800) | (rgb32 >> 5 & 0x07E0) | (rgb32 >> 3 & 0x001F); +} diff --git a/supergameboy/libgambatte/src/colorconversion.h b/supergameboy/libgambatte/src/colorconversion.h new file mode 100644 index 00000000..9323015e --- /dev/null +++ b/supergameboy/libgambatte/src/colorconversion.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef COLORCONVERSION_H +#define COLORCONVERSION_H + +#include "int.h" +#include + +class Rgb32ToUyvy { + struct CacheUnit { + Gambatte::uint_least32_t rgb32; + Gambatte::uint_least32_t uyvy; + }; + + enum { cache_size = 0x100 }; + enum { cache_mask = cache_size - 1 }; + + CacheUnit cache[cache_size]; + +public: + Rgb32ToUyvy(); + void operator()(const Gambatte::uint_least32_t *s, Gambatte::uint_least32_t *d, unsigned w, unsigned h, unsigned dstPitch); +}; + +unsigned long rgb32ToUyvy(unsigned long rgb32); + +void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, unsigned w, unsigned h, unsigned dstPitch); +unsigned rgb32ToRgb16(unsigned long rgb32); + +#endif diff --git a/supergameboy/libgambatte/src/cpu.cpp b/supergameboy/libgambatte/src/cpu.cpp new file mode 100644 index 00000000..c44b1239 --- /dev/null +++ b/supergameboy/libgambatte/src/cpu.cpp @@ -0,0 +1,2842 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "cpu.h" +#include "memory.h" +#include "savestate.h" + +CPU::CPU() : +memory(Interrupter(SP, PC_, halted)), +cycleCounter_(0), +PC_(0x100), +SP(0xFFFE), +HF1(0xF), +HF2(0xF), +ZF(0), +CF(0x100), +A_(0x01), +B(0x00), +C(0x13), +D(0x00), +E(0xD8), +H(0x01), +L(0x4D), +skip(false), +halted(false) +{} + +void CPU::runFor(const unsigned long cycles) { + process(cycles/* << memory.isDoubleSpeed()*/); + + if (cycleCounter_ & 0x80000000) + cycleCounter_ = memory.resetCounters(cycleCounter_); +} + +bool CPU::load(const bool forceDmg) { + bool tmp = memory.loadROM(forceDmg); + + return tmp; +} + +/*void CPU::halt() { + while (halted) { + const uint_fast32_t cycles = memory.next_eventtime - memory.CycleCounter; + memory.CycleCounter += cycles + ((4 - (cycles & 3)) & 3); + memory.event(); + } +}*/ + +//Push address of next instruction onto stack and then jump to interrupt address (0x40-0x60): +/*unsigned CPU::interrupt(const unsigned address, unsigned cycleCounter) { + if (halted && memory.isCgb()) + cycleCounter += 4; + + halted = false; + cycleCounter += 8; + memory.write(--SP, PC_ >> 8, cycleCounter); + cycleCounter += 4; + memory.write(--SP, PC_ & 0xFF, cycleCounter); + PC_ = address; + cycleCounter += 8; + + return cycleCounter; +}*/ + +// (HF2 & 0x200) == true means HF is set. +// (HF2 & 0x400) marks the subtract flag. +// (HF2 & 0x800) is set for inc/dec. +// (HF2 & 0x100) is set if there's a carry to add. +static void calcHF(const unsigned HF1, unsigned& HF2) { + unsigned arg1 = HF1 & 0xF; + unsigned arg2 = (HF2 & 0xF) + (HF2 >> 8 & 1); + + if (HF2 & 0x800) { + arg1 = arg2; + arg2 = 1; + } + + if (HF2 & 0x400) + arg1 -= arg2; + else + arg1 = (arg1 + arg2) << 5; + + HF2 |= arg1 & 0x200; +} + +#define F() (((HF2 & 0x600) | (CF & 0x100)) >> 4 | ((ZF & 0xFF) ? 0 : 0x80)) + +#define FROM_F(f_in) do { \ + unsigned from_f_var = f_in; \ +\ + ZF = ~from_f_var & 0x80; \ + HF2 = from_f_var << 4 & 0x600; \ + CF = from_f_var << 4 & 0x100; \ +} while (0) + +void CPU::setStatePtrs(SaveState &state) { + memory.setStatePtrs(state); +} + +void CPU::saveState(SaveState &state) { + cycleCounter_ = memory.saveState(state, cycleCounter_); + + calcHF(HF1, HF2); + + state.cpu.cycleCounter = cycleCounter_; + state.cpu.PC = PC_; + state.cpu.SP = SP; + state.cpu.A = A_; + state.cpu.B = B; + state.cpu.C = C; + state.cpu.D = D; + state.cpu.E = E; + state.cpu.F = F(); + state.cpu.H = H; + state.cpu.L = L; + state.cpu.skip = skip; + state.cpu.halted = halted; +} + +void CPU::loadState(const SaveState &state) { + memory.loadState(state, cycleCounter_); + + cycleCounter_ = state.cpu.cycleCounter; + PC_ = state.cpu.PC; + SP = state.cpu.SP; + A_ = state.cpu.A; + B = state.cpu.B; + C = state.cpu.C; + D = state.cpu.D; + E = state.cpu.E; + FROM_F(state.cpu.F); + H = state.cpu.H; + L = state.cpu.L; + skip = state.cpu.skip; + halted = state.cpu.halted; +} + +#define BC() ( B << 8 | C ) +#define DE() ( D << 8 | E ) +#define HL() ( H << 8 | L ) + +#define READ(dest, addr) do { (dest) = memory.read(addr, cycleCounter); cycleCounter += 4; } while (0) +// #define PC_READ(dest, addr) do { (dest) = memory.pc_read(addr, cycleCounter); cycleCounter += 4; } while (0) +#define PC_READ(dest) do { (dest) = memory.read(PC, cycleCounter); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) +#define FF_READ(dest, addr) do { (dest) = memory.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0) + +#define WRITE(addr, data) do { memory.write(addr, data, cycleCounter); cycleCounter += 4; } while (0) +#define FF_WRITE(addr, data) do { memory.ff_write(addr, data, cycleCounter); cycleCounter += 4; } while (0) + +#define PC_MOD(data) do { PC = data; cycleCounter += 4; } while (0) + +#define PUSH(r1, r2) do { \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r1)); \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r2)); \ +} while (0) + +//CB OPCODES (Shifts, rotates and bits): +//swap r (8 cycles): +//Swap upper and lower nibbles of 8-bit register, reset flags, check zero flag: +#define swap_r(r) do { \ + CF = HF2 = 0; \ + ZF = (r); \ + (r) = (ZF << 4 | ZF >> 4) & 0xFF; \ +} while (0) + +//rlc r (8 cycles): +//Rotate 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define rlc_r(r) do { \ + CF = (r) << 1; \ + ZF = CF | CF >> 8; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rl r (8 cycles): +//Rotate 8-bit register left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF and HCF, Check ZF: +#define rl_r(r) do { \ + const unsigned rl_r_var_oldcf = CF >> 8 & 1; \ + CF = (r) << 1; \ + ZF = CF | rl_r_var_oldcf; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rrc r (8 cycles): +//Rotate 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define rrc_r(r) do { \ + ZF = (r); \ + CF = ZF << 8; \ + (r) = (ZF | CF) >> 1 & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rr r (8 cycles): +//Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF: +#define rr_r(r) do { \ + const unsigned rr_r_var_oldcf = CF & 0x100; \ + CF = (r) << 8; \ + (r) = ZF = ((r) | rr_r_var_oldcf) >> 1; \ + HF2 = 0; \ +} while (0) + +//sla r (8 cycles): +//Shift 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define sla_r(r) do { \ + ZF = CF = (r) << 1; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//sra r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF: +#define sra_r(r) do { \ + CF = (r) << 8; \ + ZF = (r) >> 1; \ + (r) = ZF | ((r) & 0x80); \ + HF2 = 0; \ +} while (0) + +//srl r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define srl_r(r) do { \ + ZF = (r); \ + CF = (r) << 8; \ + ZF >>= 1; \ + (r) = ZF; \ + HF2 = 0; \ +} while (0) + +//bit n,r (8 cycles): +//bit n,(hl) (12 cycles): +//Test bitn in 8-bit value, check ZF, unset SF, set HCF: +#define bitn_u8(bitmask, u8) do { \ + ZF = (u8) & (bitmask); \ + HF2 = 0x200; \ +} while (0) + +#define bit0_u8(u8) bitn_u8(1, (u8)) +#define bit1_u8(u8) bitn_u8(2, (u8)) +#define bit2_u8(u8) bitn_u8(4, (u8)) +#define bit3_u8(u8) bitn_u8(8, (u8)) +#define bit4_u8(u8) bitn_u8(0x10, (u8)) +#define bit5_u8(u8) bitn_u8(0x20, (u8)) +#define bit6_u8(u8) bitn_u8(0x40, (u8)) +#define bit7_u8(u8) bitn_u8(0x80, (u8)) + +//set n,r (8 cycles): +//Set bitn of 8-bit register: +#define set0_r(r) ( (r) |= 0x1 ) +#define set1_r(r) ( (r) |= 0x2 ) +#define set2_r(r) ( (r) |= 0x4 ) +#define set3_r(r) ( (r) |= 0x8 ) +#define set4_r(r) ( (r) |= 0x10 ) +#define set5_r(r) ( (r) |= 0x20 ) +#define set6_r(r) ( (r) |= 0x40 ) +#define set7_r(r) ( (r) |= 0x80 ) + +//set n,(hl) (16 cycles): +//Set bitn of value at address stored in HL: +#define setn_mem_hl(n) do { \ + const unsigned setn_mem_hl_var_addr = HL(); \ + unsigned setn_mem_hl_var_tmp; \ +\ + READ(setn_mem_hl_var_tmp, setn_mem_hl_var_addr); \ + setn_mem_hl_var_tmp |= 1 << (n); \ +\ + WRITE(setn_mem_hl_var_addr, setn_mem_hl_var_tmp); \ +} while (0) + +//res n,r (8 cycles): +//Unset bitn of 8-bit register: +#define res0_r(r) ( (r) &= 0xFE ) +#define res1_r(r) ( (r) &= 0xFD ) +#define res2_r(r) ( (r) &= 0xFB ) +#define res3_r(r) ( (r) &= 0xF7 ) +#define res4_r(r) ( (r) &= 0xEF ) +#define res5_r(r) ( (r) &= 0xDF ) +#define res6_r(r) ( (r) &= 0xBF ) +#define res7_r(r) ( (r) &= 0x7F ) + +//res n,(hl) (16 cycles): +//Unset bitn of value at address stored in HL: +#define resn_mem_hl(n) do { \ + const unsigned resn_mem_hl_var_addr = HL(); \ + unsigned resn_mem_hl_var_tmp; \ +\ + READ(resn_mem_hl_var_tmp, resn_mem_hl_var_addr); \ + resn_mem_hl_var_tmp &= ~(1 << (n)); \ +\ + WRITE(resn_mem_hl_var_addr, resn_mem_hl_var_tmp); \ +} while (0) + + +//16-BIT LOADS: +//ld rr,nn (12 cycles) +//set rr to 16-bit value of next 2 bytes in memory +#define ld_rr_nn(r1, r2) do { \ + PC_READ(r2); \ + PC_READ(r1); \ +} while (0) + +//push rr (16 cycles): +//Push value of register pair onto stack: +#define push_rr(r1, r2) do { \ + PUSH(r1, r2); \ + cycleCounter += 4; \ +} while (0) + +//pop rr (12 cycles): +//Pop two bytes off stack into register pair: +#define pop_rr(r1, r2) do { \ + READ(r2, SP); \ + SP = (SP + 1) & 0xFFFF; \ + READ(r1, SP); \ + SP = (SP + 1) & 0xFFFF; \ +} while (0) + +//8-BIT ALU: +//add a,r (4 cycles): +//add a,(addr) (8 cycles): +//Add 8-bit value to A, check flags: +#define add_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A + HF2; \ + A = ZF & 0xFF; \ +} while (0) + +//adc a,r (4 cycles): +//adc a,(addr) (8 cycles): +//Add 8-bit value+CF to A, check flags: +#define adc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = (CF & 0x100) | (u8); \ + ZF = CF = (CF >> 8 & 1) + (u8) + A; \ + A = ZF & 0xFF; \ +} while (0) + +//sub a,r (4 cycles): +//sub a,(addr) (8 cycles): +//Subtract 8-bit value from A, check flags: +#define sub_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + A = ZF & 0xFF; \ + HF2 |= 0x400; \ +} while (0) + +//sbc a,r (4 cycles): +//sbc a,(addr) (8 cycles): +//Subtract CF and 8-bit value from A, check flags: +#define sbc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = 0x400 | (CF & 0x100) | (u8); \ + ZF = CF = A - ((CF >> 8) & 1) - (u8); \ + A = ZF & 0xFF; \ +} while (0) + +//and a,r (4 cycles): +//and a,(addr) (8 cycles): +//bitwise and 8-bit value into A, check flags: +#define and_a_u8(u8) do { \ + HF2 = 0x200; \ + CF = 0; \ + A &= (u8); \ + ZF = A; \ +} while (0) + +//or a,r (4 cycles): +//or a,(hl) (8 cycles): +//bitwise or 8-bit value into A, check flags: +#define or_a_u8(u8) do { \ + CF = HF2 = 0; \ + A |= (u8); \ + ZF = A; \ +} while (0) + +//xor a,r (4 cycles): +//xor a,(hl) (8 cycles): +//bitwise xor 8-bit value into A, check flags: +#define xor_a_u8(u8) do { \ + CF = HF2 = 0; \ + A ^= (u8); \ + ZF = A; \ +} while (0) + +//cp a,r (4 cycles): +//cp a,(addr) (8 cycles): +//Compare (subtract without storing result) 8-bit value to A, check flags: +#define cp_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + HF2 |= 0x400; \ +} while (0) + +//inc r (4 cycles): +//Increment value of 8-bit register, check flags except CF: +#define inc_r(r) do { \ + HF2 = (r) | 0x800; \ + ZF = (r) + 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//dec r (4 cycles): +//Decrement value of 8-bit register, check flags except CF: +#define dec_r(r) do { \ + HF2 = (r) | 0xC00; \ + ZF = (r) - 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//16-BIT ARITHMETIC +//add hl,rr (8 cycles): +//add 16-bit register to HL, check flags except ZF: +/*#define add_hl_rr(rh, rl) do { \ + L = HF1 = L + (rl); \ + HF1 >>= 8; \ + HF1 += H; \ + HF2 = (rh); \ + H = CF = HF1 + (rh); \ + cycleCounter += 4; \ +} while (0)*/ + +#define add_hl_rr(rh, rl) do { \ + CF = L + (rl); \ + L = CF & 0xFF; \ + HF1 = H; \ + HF2 = (CF & 0x100) | (rh); \ + CF = H + (CF >> 8) + (rh); \ + H = CF & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//inc rr (8 cycles): +//Increment 16-bit register: +#define inc_rr(rh, rl) do { \ + const unsigned inc_rr_var_tmp = (rl) + 1; \ + (rl) = inc_rr_var_tmp & 0xFF; \ + (rh) = ((rh) + (inc_rr_var_tmp >> 8)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//dec rr (8 cycles): +//Decrement 16-bit register: +#define dec_rr(rh, rl) do { \ + const unsigned dec_rr_var_tmp = (rl) - 1; \ + (rl) = dec_rr_var_tmp & 0xFF; \ + (rh) = ((rh) - (dec_rr_var_tmp >> 8 & 1)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +#define sp_plus_n(sumout) do { \ + unsigned sp_plus_n_var_n; \ + PC_READ(sp_plus_n_var_n); \ + sp_plus_n_var_n = (sp_plus_n_var_n ^ 0x80) - 0x80; \ + \ + const unsigned sp_plus_n_var_sum = SP + sp_plus_n_var_n; \ + CF = SP ^ sp_plus_n_var_n ^ sp_plus_n_var_sum; \ + HF2 = CF << 5 & 0x200; \ + ZF = 1; \ + cycleCounter += 4; \ + (sumout) = sp_plus_n_var_sum & 0xFFFF; \ +} while (0) + +//JUMPS: +//jp nn (16 cycles): +//Jump to address stored in the next two bytes in memory: +#define jp_nn() do { \ + unsigned jp_nn_var_l, jp_nn_var_h; \ +\ + PC_READ(jp_nn_var_l); \ + PC_READ(jp_nn_var_h); \ +\ + PC_MOD(jp_nn_var_h << 8 | jp_nn_var_l); \ +} while (0) + +//jr disp (12 cycles): +//Jump to value of next (signed) byte in memory+current address: +#define jr_disp() do { \ + unsigned jr_disp_var_tmp; \ +\ + PC_READ(jr_disp_var_tmp); \ + jr_disp_var_tmp = (jr_disp_var_tmp ^ 0x80) - 0x80; \ +\ + PC_MOD((PC + jr_disp_var_tmp) & 0xFFFF); \ +} while (0) + +//CALLS, RESTARTS AND RETURNS: +//call nn (24 cycles): +//Push address of next instruction onto stack and then jump to address stored in next two bytes in memory: +#define call_nn() do { \ + PUSH(((PC + 2) >> 8) & 0xFF, (PC + 2) & 0xFF); \ + jp_nn(); \ +} while (0) + +//rst n (16 Cycles): +//Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h): +#define rst_n(n) do { \ + PUSH(PC >> 8, PC & 0xFF); \ + PC_MOD(n); \ +} while (0) + +//ret (16 cycles): +//Pop two bytes from the stack and jump to that address: +#define ret() do { \ + unsigned ret_var_l, ret_var_h; \ +\ + pop_rr(ret_var_h, ret_var_l); \ +\ + PC_MOD(ret_var_h << 8 | ret_var_l); \ +} while (0) + +void CPU::process(const unsigned long cycles) { + memory.setEndtime(cycleCounter_, cycles); + + unsigned char A = A_; + unsigned long cycleCounter = cycleCounter_; + + while (memory.isActive()) { + unsigned short PC = PC_; + + if (halted) { + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } else while (cycleCounter < memory.getNextEventTime()) { + unsigned char opcode; + + PC_READ(opcode); + + if (skip) { + PC = (PC - 1) & 0xFFFF; + skip = false; + } + + switch (opcode) { + //nop (4 cycles): + //Do nothing for 4 cycles: + case 0x00: + break; + case 0x01: + ld_rr_nn(B, C); + break; + case 0x02: + WRITE(BC(), A); + break; + case 0x03: + inc_rr(B, C); + break; + case 0x04: + inc_r(B); + break; + case 0x05: + dec_r(B); + break; + case 0x06: + PC_READ(B); + break; + + //rlca (4 cycles): + //Rotate 8-bit register A left, store old bit7 in CF. Reset SF, HCF, ZF: + case 0x07: + CF = A << 1; + A = (CF | CF >> 8) & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //ld (nn),SP (20 cycles): + //Put value of SP into address given by next 2 bytes in memory: + case 0x08: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + const unsigned addr = h << 8 | l; + + WRITE(addr, SP & 0xFF); + WRITE((addr + 1) & 0xFFFF, SP >> 8); + } + break; + + case 0x09: + add_hl_rr(B, C); + break; + case 0x0A: + READ(A, BC()); + break; + case 0x0B: + dec_rr(B, C); + break; + case 0x0C: + inc_r(C); + break; + case 0x0D: + dec_r(C); + break; + case 0x0E: + PC_READ(C); + break; + + //rrca (4 cycles): + //Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF: + case 0x0F: + CF = A << 8 | A; + A = CF >> 1 & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //stop (4 cycles): + //Halt CPU and LCD display until button pressed: + case 0x10: + memory.speedChange(cycleCounter); + PC = (PC + 1) & 0xFFFF; + break; + case 0x11: + ld_rr_nn(D, E); + break; + case 0x12: + WRITE(DE(), A); + break; + case 0x13: + inc_rr(D, E); + break; + case 0x14: + inc_r(D); + break; + case 0x15: + dec_r(D); + break; + case 0x16: + PC_READ(D); + break; + + //rla (4 cycles): + //Rotate 8-bit register A left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF, HCF, ZF: + case 0x17: + { + const unsigned oldcf = CF >> 8 & 1; + CF = A << 1; + A = (CF | oldcf) & 0xFF; + } + + HF2 = 0; + ZF = 1; + break; + + case 0x18: + jr_disp(); + break; + case 0x19: + add_hl_rr(D, E); + break; + case 0x1A: + READ(A, DE()); + break; + case 0x1B: + dec_rr(D, E); + break; + case 0x1C: + inc_r(E); + break; + case 0x1D: + dec_r(E); + break; + case 0x1E: + PC_READ(E); + break; + + //rra (4 cycles): + //Rotate 8-bit register A right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF, HCF, ZF: + case 0x1F: + { + const unsigned oldcf = CF & 0x100; + CF = A << 8; + A = (A | oldcf) >> 1; + } + + HF2 = 0; + ZF = 1; + break; + + //jr nz,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is unset: + case 0x20: + if (ZF & 0xFF) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + case 0x21: + ld_rr_nn(H, L); + break; + + //ldi (hl),a (8 cycles): + //Put A into memory address in hl. Increment HL: + case 0x22: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x23: + inc_rr(H, L); + break; + case 0x24: + inc_r(H); + break; + case 0x25: + dec_r(H); + break; + case 0x26: + PC_READ(H); + break; + + + //daa (4 cycles): + //Adjust register A to correctly represent a BCD. Check ZF, HF and CF: + case 0x27: + /*{ + unsigned correction = ((A > 0x99) || (CF & 0x100)) ? 0x60 : 0x00; + + calcHF(HF1, HF2); + + if ((A & 0x0F) > 0x09 || (HF2 & 0x200)) + correction |= 0x06; + + HF1 = A; + HF2 = (HF2 & 0x400) | correction; + CF = (correction & 0x40) << 2; + A = (HF2 & 0x400) ? A - correction : (A + correction); + ZF = A; + }*/ + + calcHF(HF1, HF2); + + { + unsigned correction = (CF & 0x100) ? 0x60 : 0x00; + + if (HF2 & 0x200) + correction |= 0x06; + + if (!(HF2 &= 0x400)) { + if ((A & 0x0F) > 0x09) + correction |= 0x06; + + if (A > 0x99) + correction |= 0x60; + + A += correction; + } else + A -= correction; + + CF = correction << 2 & 0x100; + ZF = A; + A &= 0xFF; + } + break; + + //jr z,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is set: + case 0x28: + if (ZF & 0xFF) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //add hl,hl (8 cycles): + //add 16-bit register HL to HL, check flags except ZF: + case 0x29: + add_hl_rr(H, L); + break; + + //ldi a,(hl) (8 cycles): + //Put value at address in hl into A. Increment HL: + case 0x2A: + { + unsigned addr = HL(); + + READ(A, addr); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x2B: + dec_rr(H, L); + break; + case 0x2C: + inc_r(L); + break; + case 0x2D: + dec_r(L); + break; + case 0x2E: + PC_READ(L); + break; + + //cpl (4 cycles): + //Complement register A. (Flip all bits), set SF and HCF: + case 0x2F: /*setSubtractFlag(); setHalfCarryFlag();*/ + HF2 = 0x600; + A ^= 0xFF; + break; + + //jr nc,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is unset: + case 0x30: + if (CF & 0x100) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //ld sp,nn (12 cycles) + //set sp to 16-bit value of next 2 bytes in memory + case 0x31: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + SP = h << 8 | l; + } + break; + + //ldd (hl),a (8 cycles): + //Put A into memory address in hl. Decrement HL: + case 0x32: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x33: + SP = (SP + 1) & 0xFFFF; + cycleCounter += 4; + break; + + //inc (hl) (12 cycles): + //Increment value at address in hl, check flags except CF: + case 0x34: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 + 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0x800; + } + break; + + //dec (hl) (12 cycles): + //Decrement value at address in hl, check flags except CF: + case 0x35: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 - 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0xC00; + } + break; + + //ld (hl),n (12 cycles): + //set memory at address in hl to value of next byte in memory: + case 0x36: + { + unsigned tmp; + + PC_READ(tmp); + WRITE(HL(), tmp); + } + break; + + //scf (4 cycles): + //Set CF. Unset SF and HCF: + case 0x37: /*setCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF = 0x100; + HF2 = 0; + break; + + //jr c,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is set: + case 0x38: //PC+=(((int8_t)memory.read(PC++))*CarryFlag()); Cycles(8); break; + if (CF & 0x100) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + //add hl,sp (8 cycles): + //add SP to HL, check flags except ZF: + case 0x39: /*add_hl_rr(SP>>8, SP); break;*/ + CF = L + SP; + L = CF & 0xFF; + HF1 = H; + HF2 = ((CF ^ SP) & 0x100) | SP >> 8; + CF >>= 8; + CF += H; + H = CF & 0xFF; + cycleCounter += 4; + break; + + //ldd a,(hl) (8 cycles): + //Put value at address in hl into A. Decrement HL: + case 0x3A: + { + unsigned addr = HL(); + + A = memory.read(addr, cycleCounter); + cycleCounter += 4; + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x3B: + SP = (SP - 1) & 0xFFFF; + cycleCounter += 4; + break; + case 0x3C: + inc_r(A); + break; + case 0x3D: + dec_r(A); + break; + case 0x3E: + PC_READ(A); + break; + + //ccf (4 cycles): + //Complement CF (unset if set vv.) Unset SF and HCF. + case 0x3F: /*complementCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF ^= 0x100; + HF2 = 0; + break; + + //ld r,r (4 cycles):next_irqEventTime + //ld r,(r) (8 cycles): + case 0x40: + B = B; + break; + case 0x41: + B = C; + break; + case 0x42: + B = D; + break; + case 0x43: + B = E; + break; + case 0x44: + B = H; + break; + case 0x45: + B = L; + break; + case 0x46: + READ(B, HL()); + break; + case 0x47: + B = A; + break; + case 0x48: + C = B; + break; + case 0x49: + C = C; + break; + case 0x4A: + C = D; + break; + case 0x4B: + C = E; + break; + case 0x4C: + C = H; + break; + case 0x4D: + C = L; + break; + case 0x4E: + READ(C, HL()); + break; + case 0x4F: + C = A; + break; + case 0x50: + D = B; + break; + case 0x51: + D = C; + break; + case 0x52: + D = D; + break; + case 0x53: + D = E; + break; + case 0x54: + D = H; + break; + case 0x55: + D = L; + break; + case 0x56: + READ(D, HL()); + break; + case 0x57: + D = A; + break; + case 0x58: + E = B; + break; + case 0x59: + E = C; + break; + case 0x5A: + E = D; + break; + case 0x5B: + E = E; + break; + case 0x5C: + E = H; + break; + case 0x5D: + E = L; + break; + case 0x5E: + READ(E, HL()); + break; + case 0x5F: + E = A; + break; + case 0x60: + H = B; + break; + case 0x61: + H = C; + break; + case 0x62: + H = D; + break; + case 0x63: + H = E; + break; + case 0x64: + H = H; + break; + case 0x65: + H = L; + break; + case 0x66: + READ(H, HL()); + break; + case 0x67: + H = A; + break; + case 0x68: + L = B; + break; + case 0x69: + L = C; + break; + case 0x6A: + L = D; + break; + case 0x6B: + L = E; + break; + case 0x6C: + L = H; + break; + case 0x6D: + L = L; + break; + case 0x6E: + READ(L, HL()); + break; + case 0x6F: + L = A; + break; + case 0x70: + WRITE(HL(), B); + break; + case 0x71: + WRITE(HL(), C); + break; + case 0x72: + WRITE(HL(), D); + break; + case 0x73: + WRITE(HL(), E); + break; + case 0x74: + WRITE(HL(), H); + break; + case 0x75: + WRITE(HL(), L); + break; + + //halt (4 cycles): + case 0x76: +// printf("halt\n"); + if (memory.getIME()/* || memory.next_eitime*/) { + halted = 1; + + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } else { + if ((memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter)) & 0x1F) { + if (memory.isCgb()) + cycleCounter += 8; //two nops. + else + skip = true; + } else { + memory.schedule_unhalt(); + halted = 1; + + if (cycleCounter < memory.getNextEventTime()) { + const unsigned long cycles = memory.getNextEventTime() - cycleCounter; + cycleCounter += cycles + ((4 - (cycles & 3)) & 3); + } + } + } + break; + case 0x77: + WRITE(HL(), A); + break; + case 0x78: + A = B; + break; + case 0x79: + A = C; + break; + case 0x7A: + A = D; + break; + case 0x7B: + A = E; + break; + case 0x7C: + A = H; + break; + case 0x7D: + A = L; + break; + case 0x7E: + READ(A, HL()); + break; + case 0x7F: + A = A; + break; + case 0x80: + add_a_u8(B); + break; + case 0x81: + add_a_u8(C); + break; + case 0x82: + add_a_u8(D); + break; + case 0x83: + add_a_u8(E); + break; + case 0x84: + add_a_u8(H); + break; + case 0x85: + add_a_u8(L); + break; + case 0x86: + { + unsigned data; + + READ(data, HL()); + + add_a_u8(data); + } + break; + case 0x87: + add_a_u8(A); + break; + case 0x88: + adc_a_u8(B); + break; + case 0x89: + adc_a_u8(C); + break; + case 0x8A: + adc_a_u8(D); + break; + case 0x8B: + adc_a_u8(E); + break; + case 0x8C: + adc_a_u8(H); + break; + case 0x8D: + adc_a_u8(L); + break; + case 0x8E: + { + unsigned data; + + READ(data, HL()); + + adc_a_u8(data); + } + break; + case 0x8F: + adc_a_u8(A); + break; + case 0x90: + sub_a_u8(B); + break; + case 0x91: + sub_a_u8(C); + break; + case 0x92: + sub_a_u8(D); + break; + case 0x93: + sub_a_u8(E); + break; + case 0x94: + sub_a_u8(H); + break; + case 0x95: + sub_a_u8(L); + break; + case 0x96: + { + unsigned data; + + READ(data, HL()); + + sub_a_u8(data); + } + break; + //A-A is always 0: + case 0x97: + HF2 = 0x400; + CF = ZF = A = 0; + break; + case 0x98: + sbc_a_u8(B); + break; + case 0x99: + sbc_a_u8(C); + break; + case 0x9A: + sbc_a_u8(D); + break; + case 0x9B: + sbc_a_u8(E); + break; + case 0x9C: + sbc_a_u8(H); + break; + case 0x9D: + sbc_a_u8(L); + break; + case 0x9E: + { + unsigned data; + + READ(data, HL()); + + sbc_a_u8(data); + } + break; + case 0x9F: + sbc_a_u8(A); + break; + case 0xA0: + and_a_u8(B); + break; + case 0xA1: + and_a_u8(C); + break; + case 0xA2: + and_a_u8(D); + break; + case 0xA3: + and_a_u8(E); + break; + case 0xA4: + and_a_u8(H); + break; + case 0xA5: + and_a_u8(L); + break; + case 0xA6: + { + unsigned data; + + READ(data, HL()); + + and_a_u8(data); + } + break; + //A&A will always be A: + case 0xA7: + ZF = A; + CF = 0; + HF2 = 0x200; + break; + case 0xA8: + xor_a_u8(B); + break; + case 0xA9: + xor_a_u8(C); + break; + case 0xAA: + xor_a_u8(D); + break; + case 0xAB: + xor_a_u8(E); + break; + case 0xAC: + xor_a_u8(H); + break; + case 0xAD: + xor_a_u8(L); + break; + case 0xAE: + { + unsigned data; + + READ(data, HL()); + + xor_a_u8(data); + } + break; + //A^A will always be 0: + case 0xAF: + CF = HF2 = ZF = A = 0; + break; + case 0xB0: + or_a_u8(B); + break; + case 0xB1: + or_a_u8(C); + break; + case 0xB2: + or_a_u8(D); + break; + case 0xB3: + or_a_u8(E); + break; + case 0xB4: + or_a_u8(H); + break; + case 0xB5: + or_a_u8(L); + break; + case 0xB6: + { + unsigned data; + + READ(data, HL()); + + or_a_u8(data); + } + break; + //A|A will always be A: + case 0xB7: + ZF = A; + HF2 = CF = 0; + break; + case 0xB8: + cp_a_u8(B); + break; + case 0xB9: + cp_a_u8(C); + break; + case 0xBA: + cp_a_u8(D); + break; + case 0xBB: + cp_a_u8(E); + break; + case 0xBC: + cp_a_u8(H); + break; + case 0xBD: + cp_a_u8(L); + break; + case 0xBE: + { + unsigned data; + + READ(data, HL()); + + cp_a_u8(data); + } + break; + //A always equals A: + case 0xBF: + CF = ZF = 0; + HF2 = 0x400; + break; + + //ret nz (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is unset: + case 0xC0: + cycleCounter += 4; + + if (ZF & 0xFF) { + ret(); + } + break; + + case 0xC1: + pop_rr(B, C); + break; + + //jp nz,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is unset: + case 0xC2: + if (ZF & 0xFF) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC3: + jp_nn(); + break; + + //call nz,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is unset: + case 0xC4: + if (ZF & 0xFF) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC5: + push_rr(B, C); + break; + case 0xC6: + { + unsigned data; + + PC_READ(data); + + add_a_u8(data); + } + break; + case 0xC7: + rst_n(0x00); + break; + + //ret z (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is set: + case 0xC8: + cycleCounter += 4; + + if (!(ZF & 0xFF)) { + ret(); + } + + break; + + //ret (16 cycles): + //Pop two bytes from the stack and jump to that address: + case 0xC9: + ret(); + break; + + //jp z,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is set: + case 0xCA: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + + //CB OPCODES (Shifts, rotates and bits): + case 0xCB: + PC_READ(opcode); + + switch (opcode) { + case 0x00: + rlc_r(B); + break; + case 0x01: + rlc_r(C); + break; + case 0x02: + rlc_r(D); + break; + case 0x03: + rlc_r(E); + break; + case 0x04: + rlc_r(H); + break; + case 0x05: + rlc_r(L); + break; + //rlc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x06: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF | (CF >> 8); + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x07: + rlc_r(A); + break; + case 0x08: + rrc_r(B); + break; + case 0x09: + rrc_r(C); + break; + case 0x0A: + rrc_r(D); + break; + case 0x0B: + rrc_r(E); + break; + case 0x0C: + rrc_r(H); + break; + case 0x0D: + rrc_r(L); + break; + //rrc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x0E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + CF = ZF << 8; + + WRITE(addr, (ZF | CF) >> 1 & 0xFF); + + HF2 = 0; + } + break; + case 0x0F: + rrc_r(A); + break; + case 0x10: + rl_r(B); + break; + case 0x11: + rl_r(C); + break; + case 0x12: + rl_r(D); + break; + case 0x13: + rl_r(E); + break; + case 0x14: + rl_r(H); + break; + case 0x15: + rl_r(L); + break; + //rl (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left thorugh CF, store old bit7 in CF, old CF value becoms bit0. Reset SF and HCF. Check ZF: + case 0x16: + { + const unsigned addr = HL(); + const unsigned oldcf = CF >> 8 & 1; + + READ(CF, addr); + CF <<= 1; + + ZF = CF | oldcf; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x17: + rl_r(A); + break; + case 0x18: + rr_r(B); + break; + case 0x19: + rr_r(C); + break; + case 0x1A: + rr_r(D); + break; + case 0x1B: + rr_r(E); + break; + case 0x1C: + rr_r(H); + break; + case 0x1D: + rr_r(L); + break; + //rr (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right thorugh CF, store old bit0 in CF, old CF value becoms bit7. Reset SF and HCF. Check ZF: + case 0x1E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + const unsigned oldcf = CF & 0x100; + CF = ZF << 8; + ZF = (ZF | oldcf) >> 1; + + WRITE(addr, ZF); + + HF2 = 0; + } + break; + case 0x1F: + rr_r(A); + break; + case 0x20: + sla_r(B); + break; + case 0x21: + sla_r(C); + break; + case 0x22: + sla_r(D); + break; + case 0x23: + sla_r(E); + break; + case 0x24: + sla_r(H); + break; + case 0x25: + sla_r(L); + break; + //sla (hl) (16 cycles): + //Shift 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x26: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x27: + sla_r(A); + break; + case 0x28: + sra_r(B); + break; + case 0x29: + sra_r(C); + break; + case 0x2A: + sra_r(D); + break; + case 0x2B: + sra_r(E); + break; + case 0x2C: + sra_r(H); + break; + case 0x2D: + sra_r(L); + break; + //sra (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF, bit7=old bit7. Reset SF and HCF. Check ZF: + case 0x2E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF | (CF & 0x80)); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x2F: + sra_r(A); + break; + case 0x30: + swap_r(B); + break; + case 0x31: + swap_r(C); + break; + case 0x32: + swap_r(D); + break; + case 0x33: + swap_r(E); + break; + case 0x34: + swap_r(H); + break; + case 0x35: + swap_r(L); + break; + //swap (hl) (16 cycles): + //Swap upper and lower nibbles of 8-bit value stored at address in HL, reset flags, check zero flag: + case 0x36: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + WRITE(addr, (ZF << 4 | ZF >> 4) & 0xFF); + + CF = HF2 = 0; + } + break; + case 0x37: + swap_r(A); + break; + case 0x38: + srl_r(B); + break; + case 0x39: + srl_r(C); + break; + case 0x3A: + srl_r(D); + break; + case 0x3B: + srl_r(E); + break; + case 0x3C: + srl_r(H); + break; + case 0x3D: + srl_r(L); + break; + //srl (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x3E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x3F: + srl_r(A); + break; + case 0x40: + bit0_u8(B); + break; + case 0x41: + bit0_u8(C); + break; + case 0x42: + bit0_u8(D); + break; + case 0x43: + bit0_u8(E); + break; + case 0x44: + bit0_u8(H); + break; + case 0x45: + bit0_u8(L); + break; + case 0x46: + { + unsigned data; + + READ(data, HL()); + + bit0_u8(data); + } + break; + case 0x47: + bit0_u8(A); + break; + case 0x48: + bit1_u8(B); + break; + case 0x49: + bit1_u8(C); + break; + case 0x4A: + bit1_u8(D); + break; + case 0x4B: + bit1_u8(E); + break; + case 0x4C: + bit1_u8(H); + break; + case 0x4D: + bit1_u8(L); + break; + case 0x4E: + { + unsigned data; + + READ(data, HL()); + + bit1_u8(data); + } + break; + case 0x4F: + bit1_u8(A); + break; + case 0x50: + bit2_u8(B); + break; + case 0x51: + bit2_u8(C); + break; + case 0x52: + bit2_u8(D); + break; + case 0x53: + bit2_u8(E); + break; + case 0x54: + bit2_u8(H); + break; + case 0x55: + bit2_u8(L); + break; + case 0x56: + { + unsigned data; + + READ(data, HL()); + + bit2_u8(data); + } + break; + case 0x57: + bit2_u8(A); + break; + case 0x58: + bit3_u8(B); + break; + case 0x59: + bit3_u8(C); + break; + case 0x5A: + bit3_u8(D); + break; + case 0x5B: + bit3_u8(E); + break; + case 0x5C: + bit3_u8(H); + break; + case 0x5D: + bit3_u8(L); + break; + case 0x5E: + { + unsigned data; + + READ(data, HL()); + + bit3_u8(data); + } + break; + case 0x5F: + bit3_u8(A); + break; + case 0x60: + bit4_u8(B); + break; + case 0x61: + bit4_u8(C); + break; + case 0x62: + bit4_u8(D); + break; + case 0x63: + bit4_u8(E); + break; + case 0x64: + bit4_u8(H); + break; + case 0x65: + bit4_u8(L); + break; + case 0x66: + { + unsigned data; + + READ(data, HL()); + + bit4_u8(data); + } + break; + case 0x67: + bit4_u8(A); + break; + case 0x68: + bit5_u8(B); + break; + case 0x69: + bit5_u8(C); + break; + case 0x6A: + bit5_u8(D); + break; + case 0x6B: + bit5_u8(E); + break; + case 0x6C: + bit5_u8(H); + break; + case 0x6D: + bit5_u8(L); + break; + case 0x6E: + { + unsigned data; + + READ(data, HL()); + + bit5_u8(data); + } + break; + case 0x6F: + bit5_u8(A); + break; + case 0x70: + bit6_u8(B); + break; + case 0x71: + bit6_u8(C); + break; + case 0x72: + bit6_u8(D); + break; + case 0x73: + bit6_u8(E); + break; + case 0x74: + bit6_u8(H); + break; + case 0x75: + bit6_u8(L); + break; + case 0x76: + { + unsigned data; + + READ(data, HL()); + + bit6_u8(data); + } + break; + case 0x77: + bit6_u8(A); + break; + case 0x78: + bit7_u8(B); + break; + case 0x79: + bit7_u8(C); + break; + case 0x7A: + bit7_u8(D); + break; + case 0x7B: + bit7_u8(E); + break; + case 0x7C: + bit7_u8(H); + break; + case 0x7D: + bit7_u8(L); + break; + case 0x7E: + { + unsigned data; + + READ(data, HL()); + + bit7_u8(data); + } + break; + case 0x7F: + bit7_u8(A); + break; + case 0x80: + res0_r(B); + break; + case 0x81: + res0_r(C); + break; + case 0x82: + res0_r(D); + break; + case 0x83: + res0_r(E); + break; + case 0x84: + res0_r(H); + break; + case 0x85: + res0_r(L); + break; + case 0x86: + resn_mem_hl(0); + break; + case 0x87: + res0_r(A); + break; + case 0x88: + res1_r(B); + break; + case 0x89: + res1_r(C); + break; + case 0x8A: + res1_r(D); + break; + case 0x8B: + res1_r(E); + break; + case 0x8C: + res1_r(H); + break; + case 0x8D: + res1_r(L); + break; + case 0x8E: + resn_mem_hl(1); + break; + case 0x8F: + res1_r(A); + break; + case 0x90: + res2_r(B); + break; + case 0x91: + res2_r(C); + break; + case 0x92: + res2_r(D); + break; + case 0x93: + res2_r(E); + break; + case 0x94: + res2_r(H); + break; + case 0x95: + res2_r(L); + break; + case 0x96: + resn_mem_hl(2); + break; + case 0x97: + res2_r(A); + break; + case 0x98: + res3_r(B); + break; + case 0x99: + res3_r(C); + break; + case 0x9A: + res3_r(D); + break; + case 0x9B: + res3_r(E); + break; + case 0x9C: + res3_r(H); + break; + case 0x9D: + res3_r(L); + break; + case 0x9E: + resn_mem_hl(3); + break; + case 0x9F: + res3_r(A); + break; + case 0xA0: + res4_r(B); + break; + case 0xA1: + res4_r(C); + break; + case 0xA2: + res4_r(D); + break; + case 0xA3: + res4_r(E); + break; + case 0xA4: + res4_r(H); + break; + case 0xA5: + res4_r(L); + break; + case 0xA6: + resn_mem_hl(4); + break; + case 0xA7: + res4_r(A); + break; + case 0xA8: + res5_r(B); + break; + case 0xA9: + res5_r(C); + break; + case 0xAA: + res5_r(D); + break; + case 0xAB: + res5_r(E); + break; + case 0xAC: + res5_r(H); + break; + case 0xAD: + res5_r(L); + break; + case 0xAE: + resn_mem_hl(5); + break; + case 0xAF: + res5_r(A); + break; + case 0xB0: + res6_r(B); + break; + case 0xB1: + res6_r(C); + break; + case 0xB2: + res6_r(D); + break; + case 0xB3: + res6_r(E); + break; + case 0xB4: + res6_r(H); + break; + case 0xB5: + res6_r(L); + break; + case 0xB6: + resn_mem_hl(6); + break; + case 0xB7: + res6_r(A); + break; + case 0xB8: + res7_r(B); + break; + case 0xB9: + res7_r(C); + break; + case 0xBA: + res7_r(D); + break; + case 0xBB: + res7_r(E); + break; + case 0xBC: + res7_r(H); + break; + case 0xBD: + res7_r(L); + break; + case 0xBE: + resn_mem_hl(7); + break; + case 0xBF: + res7_r(A); + break; + case 0xC0: + set0_r(B); + break; + case 0xC1: + set0_r(C); + break; + case 0xC2: + set0_r(D); + break; + case 0xC3: + set0_r(E); + break; + case 0xC4: + set0_r(H); + break; + case 0xC5: + set0_r(L); + break; + case 0xC6: + setn_mem_hl(0); + break; + case 0xC7: + set0_r(A); + break; + case 0xC8: + set1_r(B); + break; + case 0xC9: + set1_r(C); + break; + case 0xCA: + set1_r(D); + break; + case 0xCB: + set1_r(E); + break; + case 0xCC: + set1_r(H); + break; + case 0xCD: + set1_r(L); + break; + case 0xCE: + setn_mem_hl(1); + break; + case 0xCF: + set1_r(A); + break; + case 0xD0: + set2_r(B); + break; + case 0xD1: + set2_r(C); + break; + case 0xD2: + set2_r(D); + break; + case 0xD3: + set2_r(E); + break; + case 0xD4: + set2_r(H); + break; + case 0xD5: + set2_r(L); + break; + case 0xD6: + setn_mem_hl(2); + break; + case 0xD7: + set2_r(A); + break; + case 0xD8: + set3_r(B); + break; + case 0xD9: + set3_r(C); + break; + case 0xDA: + set3_r(D); + break; + case 0xDB: + set3_r(E); + break; + case 0xDC: + set3_r(H); + break; + case 0xDD: + set3_r(L); + break; + case 0xDE: + setn_mem_hl(3); + break; + case 0xDF: + set3_r(A); + break; + case 0xE0: + set4_r(B); + break; + case 0xE1: + set4_r(C); + break; + case 0xE2: + set4_r(D); + break; + case 0xE3: + set4_r(E); + break; + case 0xE4: + set4_r(H); + break; + case 0xE5: + set4_r(L); + break; + case 0xE6: + setn_mem_hl(4); + break; + case 0xE7: + set4_r(A); + break; + case 0xE8: + set5_r(B); + break; + case 0xE9: + set5_r(C); + break; + case 0xEA: + set5_r(D); + break; + case 0xEB: + set5_r(E); + break; + case 0xEC: + set5_r(H); + break; + case 0xED: + set5_r(L); + break; + case 0xEE: + setn_mem_hl(5); + break; + case 0xEF: + set5_r(A); + break; + case 0xF0: + set6_r(B); + break; + case 0xF1: + set6_r(C); + break; + case 0xF2: + set6_r(D); + break; + case 0xF3: + set6_r(E); + break; + case 0xF4: + set6_r(H); + break; + case 0xF5: + set6_r(L); + break; + case 0xF6: + setn_mem_hl(6); + break; + case 0xF7: + set6_r(A); + break; + case 0xF8: + set7_r(B); + break; + case 0xF9: + set7_r(C); + break; + case 0xFA: + set7_r(D); + break; + case 0xFB: + set7_r(E); + break; + case 0xFC: + set7_r(H); + break; + case 0xFD: + set7_r(L); + break; + case 0xFE: + setn_mem_hl(7); + break; + case 0xFF: + set7_r(A); + break; +// default: break; + } + break; + + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is set: + case 0xCC: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xCD: + call_nn(); + break; + case 0xCE: + { + unsigned data; + + PC_READ(data); + + adc_a_u8(data); + } + break; + case 0xCF: + rst_n(0x08); + break; + + //ret nc (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is unset: + case 0xD0: + cycleCounter += 4; + + if (!(CF & 0x100)) { + ret(); + } + + break; + + case 0xD1: + pop_rr(D, E); + break; + + //jp nc,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is unset: + case 0xD2: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + case 0xD3: /*doesn't exist*/ + break; + + //call nc,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is unset: + case 0xD4: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xD5: + push_rr(D, E); + break; + case 0xD6: + { + unsigned data; + + PC_READ(data); + + sub_a_u8(data); + } + break; + case 0xD7: + rst_n(0x10); + break; + + //ret c (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is set: + case 0xD8: + cycleCounter += 4; + + if (CF & 0x100) { + ret(); + } + + break; + + //reti (16 cycles): + //Pop two bytes from the stack and jump to that address, then enable interrupts: + case 0xD9: + { + unsigned l, h; + + pop_rr(h, l); + + memory.ei(cycleCounter); + + PC_MOD(h << 8 | l); + } + break; + + //jp c,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is set: + case 0xDA: //PC=( ((PC+2)*(1-CarryFlag())) + (((memory.read(PC+1)<<8)+memory.read(PC))*CarryFlag()) ); Cycles(12); break; + if (CF & 0x100) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDB: /*doesn't exist*/ + break; + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is set: + case 0xDC: + if (CF & 0x100) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDE: + { + unsigned data; + + PC_READ(data); + + sbc_a_u8(data); + } + break; + case 0xDF: + rst_n(0x18); + break; + + //ld ($FF00+n),a (12 cycles): + //Put value in A into address (0xFF00 + next byte in memory): + case 0xE0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_WRITE(0xFF00 | tmp, A); + } + break; + + case 0xE1: + pop_rr(H, L); + break; + + //ld ($FF00+C),a (8 ycles): + //Put A into address (0xFF00 + register C): + case 0xE2: + FF_WRITE(0xFF00 | C, A); + break; + case 0xE3: /*doesn't exist*/ + break; + case 0xE4: /*doesn't exist*/ + break; + case 0xE5: + push_rr(H, L); + break; + case 0xE6: + { + unsigned data; + + PC_READ(data); + + and_a_u8(data); + } + break; + case 0xE7: + rst_n(0x20); + break; + + //add sp,n (16 cycles): + //Add next (signed) byte in memory to SP, reset ZF and SF, check HCF and CF: + case 0xE8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + SP = CF; + CF >>= 8; + ZF = 1; + cycleCounter += 12; + }*/ + sp_plus_n(SP); + cycleCounter += 4; + break; + + //jp hl (4 cycles): + //Jump to address in hl: + case 0xE9: + PC = HL(); + break; + + //ld (nn),a (16 cycles): + //set memory at address given by the next 2 bytes to value in A: + //Incrementing PC before call, because of possible interrupt. + case 0xEA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + WRITE(h << 8 | l, A); + } + break; + + case 0xEB: /*doesn't exist*/ + break; + case 0xEC: /*doesn't exist*/ + break; + case 0xED: /*doesn't exist*/ + break; + case 0xEE: + { + unsigned data; + + PC_READ(data); + + xor_a_u8(data); + } + break; + case 0xEF: + rst_n(0x28); + break; + + //ld a,($FF00+n) (12 cycles): + //Put value at address (0xFF00 + next byte in memory) into A: + case 0xF0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_READ(A, 0xFF00 | tmp); + } + break; + + case 0xF1: /*pop_rr(A, F); Cycles(12); break;*/ + { + unsigned F; + + pop_rr(A, F); + + FROM_F(F); + } + break; + + //ld a,($FF00+C) (8 cycles): + //Put value at address (0xFF00 + register C) into A: + case 0xF2: + FF_READ(A, 0xFF00 | C); + break; + + //di (4 cycles): + case 0xF3: + memory.di(); + break; + + case 0xF4: /*doesn't exist*/ + break; + case 0xF5: /*push_rr(A, F); Cycles(16); break;*/ + calcHF(HF1, HF2); + + { + unsigned F = F(); + + push_rr(A, F); + } + break; + + case 0xF6: + { + unsigned data; + + PC_READ(data); + + or_a_u8(data); + } + break; + case 0xF7: + rst_n(0x30); + break; + + //ldhl sp,n (12 cycles): + //Put (sp+next (signed) byte in memory) into hl (unsets ZF and SF, may enable HF and CF): + case 0xF8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + L = CF; + CF >>= 8; + H = CF; + ZF = 1; + cycleCounter += 8; + }*/ + { + unsigned sum; + sp_plus_n(sum); + L = sum & 0xFF; + H = sum >> 8; + } + break; + + //ld sp,hl (8 cycles): + //Put value in HL into SP + case 0xF9: + SP = HL(); + cycleCounter += 4; + break; + + //ld a,(nn) (16 cycles): + //set A to value in memory at address given by the 2 next bytes. + case 0xFA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + READ(A, h << 8 | l); + } + break; + + //ei (4 cycles): + //Enable Interrupts after next instruction: + case 0xFB: + memory.ei(cycleCounter); + break; + + case 0xFC: /*doesn't exist*/ + break; + case 0xFD: /*doesn't exist*/ + break; + case 0xFE: + { + unsigned data; + + PC_READ(data); + + cp_a_u8(data); + } + break; + case 0xFF: + rst_n(0x38); + break; +// default: break; + } + } + + PC_ = PC; + cycleCounter = memory.event(cycleCounter); + } + + A_ = A; + cycleCounter_ = cycleCounter; +} diff --git a/supergameboy/libgambatte/src/cpu.h b/supergameboy/libgambatte/src/cpu.h new file mode 100644 index 00000000..300ba5fb --- /dev/null +++ b/supergameboy/libgambatte/src/cpu.h @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CPU_H +#define CPU_H + +class SaveState; + +#include "int.h" +#include "memory.h" + +class CPU { + Memory memory; + + unsigned long cycleCounter_; + + unsigned short PC_; + unsigned short SP; + + unsigned HF1, HF2, ZF, CF; + + unsigned char A_, B, C, D, E, /*F,*/ H, L; + + bool skip; + bool halted; + + void process(unsigned long cycles); + +public: + + CPU(); +// void halt(); + +// unsigned interrupt(unsigned address, unsigned cycleCounter); + + void updateVideo() { memory.updateVideo(cycleCounter_); } + unsigned lyCounter() { return memory.lyCounter(cycleCounter_); } + void setAccumulator(unsigned char value) { A_ = value; } + + void runFor(unsigned long cycles); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state); + void loadState(const SaveState &state); + + void loadSavedata() { memory.loadSavedata(); } + void saveSavedata() { memory.saveSavedata(); } + + void setVideoBlitter(Gambatte::VideoBlitter *vb) { + memory.setVideoBlitter(vb); + } + + void videoBufferChange() { + memory.videoBufferChange(); + } + + unsigned int videoWidth() const { + return memory.videoWidth(); + } + + unsigned int videoHeight() const { + return memory.videoHeight(); + } + + void setVideoFilter(const unsigned int n) { + memory.setVideoFilter(n); + } + + std::vector filterInfo() const { + return memory.filterInfo(); + } + + void setInputStateGetter(Gambatte::InputStateGetter *getInput) { + memory.setInputStateGetter(getInput); + } + + void set_savedir(const char *sdir) { + memory.set_savedir(sdir); + } + + const std::string saveBasePath() const { + return memory.saveBasePath(); + } + + void setOsdElement(std::auto_ptr osdElement) { + memory.setOsdElement(osdElement); + } + + bool load(bool forceDmg); + + void setSoundBuffer(Gambatte::uint_least32_t *const buf) { memory.setSoundBuffer(buf); } + unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); } + + bool isCgb() const { return memory.isCgb(); } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { + memory.setDmgPaletteColor(palNum, colorNum, rgb32); + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/event_queue.h b/supergameboy/libgambatte/src/event_queue.h new file mode 100644 index 00000000..94fbebcf --- /dev/null +++ b/supergameboy/libgambatte/src/event_queue.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include + +template +class event_queue { + Comparer comparer; + T *const a; + const std::size_t capacity_; + std::size_t size_; + + + std::size_t indexOf(T e); + void internalDec(std::size_t i, T e); + template void internalInc(std::size_t i, T e); + +public: + event_queue(std::size_t capacity, const Comparer &comparer); + ~event_queue(); + + std::size_t capacity() const { + return capacity_; + } + + void clear() { + size_ = 0; + } + + void dec(const T oldE, const T newE) { + internalDec(indexOf(oldE), newE); + } + + bool empty() const { + return size_ == 0; + } + + void inc(const T oldE, const T newE) { + internalInc(indexOf(oldE), newE); + } + + void modify_root(const T newRoot) { + internalInc(0, newRoot); + } + + void pop() { + internalInc(0, a[--size_]); + } + + void push(const T e) { + internalDec(size_++, e); + } + + void remove(T e); + + std::size_t size() const { + return size_; + } + + T top() const { + return a[0]; + } +}; + +template +event_queue::event_queue(const std::size_t capacity, const Comparer &comparer_in) : + comparer(comparer_in), + a(new T[capacity]), + capacity_(capacity), + size_(0) +{} + +template +event_queue::~event_queue() { + delete[] a; +} + +template +std::size_t event_queue::indexOf(const T e) { + std::size_t i = 0; + + while (a[i] != e) + ++i; + + return i; +} + +template +void event_queue::internalDec(std::size_t i, const T e) { + a[i] = e; + + while (i != 0) { + const std::size_t parentI = (i - 1) >> 1; + + if (!comparer.less(e, a[parentI])) + break; + + a[i] = a[parentI]; + a[parentI] = e; + i = parentI; + } +} + +template +template +void event_queue::internalInc(std::size_t i, const T e) { + a[i] = e; + + for (;;) { + std::size_t childI = i * 2 + 1; + + if (childI >= size_) + break; + + if ((!child2BoundsCheck || childI + 1 < size_) && comparer.less(a[childI + 1], a[childI])) + ++childI; + + if (!comparer.less(a[childI], e)) + break; + + a[i] = a[childI]; + a[childI] = e; + i = childI; + } +} + +template +void event_queue::remove(const T e) { + std::size_t i = indexOf(e); + + while (i != 0) { + const std::size_t parentI = (i - 1) >> 1; + + a[i] = a[parentI]; + a[parentI] = e; + i = parentI; + } + + pop(); +} + +#endif diff --git a/supergameboy/libgambatte/src/file/file.cpp b/supergameboy/libgambatte/src/file/file.cpp new file mode 100644 index 00000000..7a8f9966 --- /dev/null +++ b/supergameboy/libgambatte/src/file/file.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +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 version 2 as +published by the Free Software Foundation. + +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 version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include "file.h" + +using namespace std; + +static const unsigned int MAX_FILE_NAME = 512; + +File::File(const char *filename) : stream(filename, ios::in | ios::binary), is_zip(false), fsize(0), count(0) +{ + if (stream) + { + stream.seekg(0, ios::end); + fsize = stream.tellg(); + stream.seekg(0, ios::beg); + } +} + +File::~File() +{ + close(); +} + +void File::rewind() +{ + if (is_open()) + { + stream.seekg(0, ios::beg); + } +} + +bool File::is_open() +{ + return(stream.is_open()); +} + +void File::close() +{ + if (is_open()) + { + stream.close(); + } +} + +void File::read(char *buffer, size_t amount) +{ + if (is_open()) + { + stream.read(buffer, amount); + count = stream.gcount(); + } + else + { + count = 0; + } +} diff --git a/supergameboy/libgambatte/src/file/file.h b/supergameboy/libgambatte/src/file/file.h new file mode 100644 index 00000000..3435ef16 --- /dev/null +++ b/supergameboy/libgambatte/src/file/file.h @@ -0,0 +1,42 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +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 version 2 as +published by the Free Software Foundation. + +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 version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include + +class File { + private: + std::ifstream stream; + bool is_zip; //Change this to an enum later + std::size_t fsize, count; + void *zipfile; + bool zip_sub_open; + + void zip(const char *filename); + + public: + File(const char *filename); + ~File(); + void rewind(); + bool is_open(); + void close(); + std::size_t size() const { return fsize; }; + void read(char *buffer, std::size_t amount); + std::size_t gcount() const { return count; } + bool fail() const { return stream.fail(); } +}; diff --git a/supergameboy/libgambatte/src/file/file_zip.cpp b/supergameboy/libgambatte/src/file/file_zip.cpp new file mode 100644 index 00000000..c7fae6db --- /dev/null +++ b/supergameboy/libgambatte/src/file/file_zip.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** +Copyright (C) 2007 by Nach +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 version 2 as +published by the Free Software Foundation. + +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 version 2 for more details. + +You should have received a copy of the GNU General Public License +version 2 along with this program; if not, write to the +Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +***************************************************************************/ + +#include "file.h" + +#include + +namespace zlib { +#include "unzip/unzip.h" +} + +using namespace std; +using namespace zlib; + +static const unsigned int MAX_FILE_NAME = 512; + +File::File(const char *filename) : stream(filename, ios::in | ios::binary), is_zip(false), fsize(0), count(0) +{ + if (stream) + { + char temp[4]; + stream.read(temp, sizeof(temp)); + + //check for standard zip 'magic number' + if ((temp[0] == 'P') && (temp[1] == 'K') && (temp[2] == 3) && (temp[3] == 4)) + { + stream.close(); + is_zip = true; + zip(filename); + } + else + { + stream.seekg(0, ios::end); + fsize = stream.tellg(); + stream.seekg(0, ios::beg); + } + } +} + +void File::zip(const char *filename) +{ + zipfile = unzOpen(filename); + if (zipfile) + { + zip_sub_open = false; + + unz_file_info cFileInfo; + char ourFile[MAX_FILE_NAME] = { '\n' }; + + for (int cFile = unzGoToFirstFile((unzFile)zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile((unzFile)zipfile)) + { + //Temporary char array for file name + char cFileName[MAX_FILE_NAME]; + + //Gets info on current file, and places it in cFileInfo + unzGetCurrentFileInfo((unzFile)zipfile, &cFileInfo, cFileName, MAX_FILE_NAME, 0, 0, 0, 0); + + //Check for largest file which should be the ROM + if ((size_t)cFileInfo.uncompressed_size > fsize) + { + strcpy(ourFile, cFileName); + fsize = (size_t)cFileInfo.uncompressed_size; + } + } + + if (ourFile[0] != '\n') + { + //Sets current file to the file we liked before + unzLocateFile((unzFile)zipfile, ourFile, 1); + + if (unzOpenCurrentFile((unzFile)zipfile) == UNZ_OK) + { + zip_sub_open = true; + } + } + + if (!zip_sub_open) + { + unzClose((unzFile)zipfile); + zipfile = 0; + } + } +} + +File::~File() +{ + close(); +} + +void File::rewind() +{ + if (is_open()) + { + if (!is_zip) + { + stream.seekg(0, ios::beg); + } + else + { + unzCloseCurrentFile((unzFile)zipfile); + unzOpenCurrentFile((unzFile)zipfile); + } + } +} + +bool File::is_open() +{ + if (!is_zip) + { + return(stream.is_open()); + } + return(zipfile && zip_sub_open); +} + +void File::close() +{ + if (is_open()) + { + if (!is_zip) + { + stream.close(); + } + else + { + unzOpenCurrentFile((unzFile)zipfile); + unzClose((unzFile)zipfile); + zipfile = 0; + zip_sub_open = false; + } + } +} + +void File::read(char *buffer, size_t amount) +{ + if (is_open()) + { + if (!is_zip) + { + stream.read(buffer, amount); + count = stream.gcount(); + } + else + { + count = (size_t)unzReadCurrentFile((unzFile)zipfile, buffer, amount); + } + } + else + { + count = 0; + } +} diff --git a/supergameboy/libgambatte/src/file/unzip/crypt.h b/supergameboy/libgambatte/src/file/unzip/crypt.h new file mode 100644 index 00000000..622f4bc2 --- /dev/null +++ b/supergameboy/libgambatte/src/file/unzip/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/supergameboy/libgambatte/src/file/unzip/ioapi.c b/supergameboy/libgambatte/src/file/unzip/ioapi.c new file mode 100644 index 00000000..05b5ef15 --- /dev/null +++ b/supergameboy/libgambatte/src/file/unzip/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 +#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/supergameboy/libgambatte/src/file/unzip/ioapi.h b/supergameboy/libgambatte/src/file/unzip/ioapi.h new file mode 100644 index 00000000..7d457baa --- /dev/null +++ b/supergameboy/libgambatte/src/file/unzip/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/supergameboy/libgambatte/src/file/unzip/unzip.c b/supergameboy/libgambatte/src/file/unzip/unzip.c new file mode 100644 index 00000000..325f3d08 --- /dev/null +++ b/supergameboy/libgambatte/src/file/unzip/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 +#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/supergameboy/libgambatte/src/file/unzip/unzip.h b/supergameboy/libgambatte/src/file/unzip/unzip.h new file mode 100644 index 00000000..5bb6a696 --- /dev/null +++ b/supergameboy/libgambatte/src/file/unzip/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 +#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/supergameboy/libgambatte/src/gambatte.cpp b/supergameboy/libgambatte/src/gambatte.cpp new file mode 100644 index 00000000..27354c91 --- /dev/null +++ b/supergameboy/libgambatte/src/gambatte.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "gambatte.h" +#include "cpu.h" +#include "savestate.h" +#include "statesaver.h" +#include "initstate.h" +#include "state_osd_elements.h" +#include +#include + +#include + +static const std::string itos(int i) { + std::stringstream ss; + + ss << i; + + std::string out; + + ss >> out; + + return out; +} + +static const std::string statePath(const std::string &basePath, int stateNo) { + return basePath + "_" + itos(stateNo) + ".gqs"; +} + +namespace Gambatte { +GB::GB() : z80(new CPU), stateNo(1) {} + +GB::~GB() { + delete z80; +} + +unsigned GB::runFor(Gambatte::uint_least32_t *const soundBuf, const unsigned samples) { + z80->setSoundBuffer(soundBuf); + z80->runFor(samples * 2); + + return z80->fillSoundBuffer(); +} + +void GB::updateVideo() { + z80->updateVideo(); +} + +unsigned GB::lyCounter() { + return z80->lyCounter(); +} + +void GB::reset() { + z80->saveSavedata(); + + SaveState state; + z80->setStatePtrs(state); + setInitState(state, z80->isCgb()); + z80->loadState(state); + z80->loadSavedata(); + + z80->setAccumulator(supergameboy.version == 0 ? 0x01 : 0xff); + +// z80->reset(); +} + +void GB::setVideoBlitter(VideoBlitter *vb) { + z80->setVideoBlitter(vb); +} + +void GB::videoBufferChange() { + z80->videoBufferChange(); +} + +unsigned GB::videoWidth() const { + return z80->videoWidth(); +} + +unsigned GB::videoHeight() const { + return z80->videoHeight(); +} + +void GB::setVideoFilter(const unsigned n) { + z80->setVideoFilter(n); +} + +std::vector GB::filterInfo() const { + return z80->filterInfo(); +} + +void GB::setInputStateGetter(InputStateGetter *getInput) { + z80->setInputStateGetter(getInput); +} + +void GB::set_savedir(const char *sdir) { + z80->set_savedir(sdir); +} + +bool GB::load(const bool forceDmg) { + const bool failed = z80->load(forceDmg); + + if (!failed) { + SaveState state; + z80->setStatePtrs(state); + setInitState(state, z80->isCgb()); + z80->loadState(state); + z80->loadSavedata(); + + z80->setAccumulator(supergameboy.version == 0 ? 0x01 : 0xff); + + stateNo = 1; + z80->setOsdElement(std::auto_ptr()); + } + + return failed; +} + +bool GB::isCgb() const { + return z80->isCgb(); +} + +void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { + z80->setDmgPaletteColor(palNum, colorNum, rgb32); +} + +void GB::saveSavedata() { + z80->saveSavedata(); +} + +void GB::loadState(const char *const filepath, const bool osdMessage) { + z80->saveSavedata(); + + SaveState state; + z80->setStatePtrs(state); + + if (StateSaver::loadState(state, filepath)) { + z80->loadState(state); + + if (osdMessage) + z80->setOsdElement(newStateLoadedOsdElement(stateNo)); + } +} + +void GB::saveState() { + saveState(statePath(z80->saveBasePath(), stateNo).c_str()); + z80->setOsdElement(newStateSavedOsdElement(stateNo)); +} + +void GB::loadState() { + loadState(statePath(z80->saveBasePath(), stateNo).c_str(), true); +} + +void GB::saveState(const char *filepath) { + SaveState state; + z80->setStatePtrs(state); + z80->saveState(state); + StateSaver::saveState(state, filepath); +} + +void GB::loadState(const char *const filepath) { + loadState(filepath, false); +} + +void GB::selectState(int n) { + n -= (n / 10) * 10; + stateNo = n < 0 ? n + 10 : n; + z80->setOsdElement(newSaveStateOsdElement(statePath(z80->saveBasePath(), stateNo).c_str(), stateNo)); +} +} diff --git a/supergameboy/libgambatte/src/initstate.cpp b/supergameboy/libgambatte/src/initstate.cpp new file mode 100644 index 00000000..c16d48b4 --- /dev/null +++ b/supergameboy/libgambatte/src/initstate.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "initstate.h" +#include "savestate.h" +#include +#include +#include "sound/sound_unit.h" +#include "memory.h" + +void setInitState(SaveState &state, const bool cgb) { + static const unsigned char feaxDump[0x60] = { + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x18, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x00, 0x90, 0xF7, 0x7F, 0xC0, 0xB1, 0xB4, 0xFB, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45, + 0x24, 0x1B, 0xFD, 0x3A, 0x10, 0x12, 0xAD, 0x45 + }; + + static const unsigned char ffxxDump[0x100] = { + 0xCF, 0x00, 0x7C, 0xFF, 0x43, 0x00, 0x00, 0xF8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, + 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, + 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF, + 0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7E, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0xFF, 0xC1, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, + 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, + 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, + 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, + 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, + 0x45, 0xEC, 0x52, 0xFA, 0x08, 0xB7, 0x07, 0x5D, + 0x01, 0xFD, 0xC0, 0xFF, 0x08, 0xFC, 0x00, 0xE5, + 0x0B, 0xF8, 0xC2, 0xCE, 0xF4, 0xF9, 0x0F, 0x7F, + 0x45, 0x6D, 0x3D, 0xFE, 0x46, 0x97, 0x33, 0x5E, + 0x08, 0xEF, 0xF1, 0xFF, 0x86, 0x83, 0x24, 0x74, + 0x12, 0xFC, 0x00, 0x9F, 0xB4, 0xB7, 0x06, 0xD5, + 0xD0, 0x7A, 0x00, 0x9E, 0x04, 0x5F, 0x41, 0x2F, + 0x1D, 0x77, 0x36, 0x75, 0x81, 0xAA, 0x70, 0x3A, + 0x98, 0xD1, 0x71, 0x02, 0x4D, 0x01, 0xC1, 0xFF, + 0x0D, 0x00, 0xD3, 0x05, 0xF9, 0x00, 0x0B, 0x00 + }; + + static const unsigned char cgbObjpDump[0x40] = { + 0x00, 0x00, 0xF2, 0xAB, + 0x61, 0xC2, 0xD9, 0xBA, + 0x88, 0x6E, 0xDD, 0x63, + 0x28, 0x27, 0xFB, 0x9F, + 0x35, 0x42, 0xD6, 0xD4, + 0x50, 0x48, 0x57, 0x5E, + 0x23, 0x3E, 0x3D, 0xCA, + 0x71, 0x21, 0x37, 0xC0, + 0xC6, 0xB3, 0xFB, 0xF9, + 0x08, 0x00, 0x8D, 0x29, + 0xA3, 0x20, 0xDB, 0x87, + 0x62, 0x05, 0x5D, 0xD4, + 0x0E, 0x08, 0xFE, 0xAF, + 0x20, 0x02, 0xD7, 0xFF, + 0x07, 0x6A, 0x55, 0xEC, + 0x83, 0x40, 0x0B, 0x77 + }; + + state.cpu.cycleCounter = 0x102A0; + state.cpu.PC = 0x100; + state.cpu.SP = 0xFFFE; + state.cpu.A = (cgb * 0x10) | 0x01; + state.cpu.B = 0x00; + state.cpu.C = 0x13; + state.cpu.D = 0x00; + state.cpu.E = 0xD8; + state.cpu.F = 0xB0; + state.cpu.H = 0x01; + state.cpu.L = 0x4D; + state.cpu.skip = false; + state.cpu.halted = false; + + + std::memset(state.mem.vram.ptr, 0, state.mem.vram.getSz()); + std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); + + for (unsigned addr = 0x0000; addr < 0x0800; addr += 0x10) { + std::memset(state.mem.wram.ptr + addr + 0x00, 0xFF, 0x08); + std::memset(state.mem.wram.ptr + addr + 0x08, 0x00, 0x08); + } + + for (unsigned addr = 0x0800; addr < 0x1000; addr += 0x10) { + std::memset(state.mem.wram.ptr + addr + 0x00, 0x00, 0x08); + std::memset(state.mem.wram.ptr + addr + 0x08, 0xFF, 0x08); + } + + for (unsigned addr = 0x0E00; addr < 0x1000; addr += 0x10) { + state.mem.wram.ptr[addr + 0x02] = 0xFF; + state.mem.wram.ptr[addr + 0x0A] = 0x00; + } + + for (unsigned addr = 0x1000; addr < state.mem.wram.getSz(); addr += 0x1000) + std::memcpy(state.mem.wram.ptr + addr, state.mem.wram.ptr, 0x1000); + + std::memset(state.mem.ioamhram.ptr, 0x00, state.mem.ioamhram.getSz()); + std::memcpy(state.mem.ioamhram.ptr + 0xA0, feaxDump, sizeof(feaxDump)); + std::memcpy(state.mem.ioamhram.ptr + 0x100, ffxxDump, sizeof(ffxxDump)); + + state.mem.ioamhram.ptr[0x104] = 0x1C; + state.mem.ioamhram.ptr[0x140] = 0x91; + state.mem.ioamhram.ptr[0x144] = 0x00; + + if (!cgb) { + state.mem.ioamhram.ptr[0x130] = 0xAC; + state.mem.ioamhram.ptr[0x131] = 0xDD; + state.mem.ioamhram.ptr[0x132] = 0xDA; + state.mem.ioamhram.ptr[0x133] = 0x48; + state.mem.ioamhram.ptr[0x134] = 0x36; + state.mem.ioamhram.ptr[0x135] = 0x02; + state.mem.ioamhram.ptr[0x136] = 0xCF; + state.mem.ioamhram.ptr[0x137] = 0x16; + state.mem.ioamhram.ptr[0x138] = 0x2C; + state.mem.ioamhram.ptr[0x139] = 0x04; + state.mem.ioamhram.ptr[0x13A] = 0xE5; + state.mem.ioamhram.ptr[0x13B] = 0x2C; + state.mem.ioamhram.ptr[0x13C] = 0xAC; + state.mem.ioamhram.ptr[0x13D] = 0xDD; + state.mem.ioamhram.ptr[0x13E] = 0xDA; + state.mem.ioamhram.ptr[0x13F] = 0x48; + + state.mem.ioamhram.ptr[0x14D] = 0xFF; + state.mem.ioamhram.ptr[0x14F] = 0xFF; + state.mem.ioamhram.ptr[0x156] = 0xFF; + state.mem.ioamhram.ptr[0x168] = 0xFF; + state.mem.ioamhram.ptr[0x16A] = 0xFF; + state.mem.ioamhram.ptr[0x16B] = 0xFF; + state.mem.ioamhram.ptr[0x16C] = 0xFF; + state.mem.ioamhram.ptr[0x170] = 0xFF; + state.mem.ioamhram.ptr[0x172] = 0xFF; + state.mem.ioamhram.ptr[0x173] = 0xFF; + state.mem.ioamhram.ptr[0x174] = 0xFF; + state.mem.ioamhram.ptr[0x175] = 0xFF; + state.mem.ioamhram.ptr[0x176] = 0xFF; + state.mem.ioamhram.ptr[0x177] = 0xFF; + } + + state.mem.div_lastUpdate = 0; + state.mem.tima_lastUpdate = 0; + state.mem.tmatime = Memory::COUNTER_DISABLED; + state.mem.next_serialtime = Memory::COUNTER_DISABLED; + state.mem.lastOamDmaUpdate = Memory::COUNTER_DISABLED; + state.mem.minIntTime = 0; + state.mem.rombank = 1; + state.mem.dmaSource = 0; + state.mem.dmaDestination = 0; + state.mem.rambank = 0; + state.mem.oamDmaPos = 0xFE; + state.mem.IME = false; + state.mem.enable_ram = false; + state.mem.rambank_mode = false; + state.mem.hdma_transfer = false; + + + for (unsigned i = 0x00; i < 0x40; i += 0x02) { + state.ppu.bgpData.ptr[i] = 0xFF; + state.ppu.bgpData.ptr[i + 1] = 0x7F; + } + + std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof(cgbObjpDump)); + + if (!cgb) { + state.ppu.bgpData.ptr[0] = state.mem.ioamhram.get()[0x147]; + state.ppu.objpData.ptr[0] = state.mem.ioamhram.get()[0x148]; + state.ppu.objpData.ptr[1] = state.mem.ioamhram.get()[0x149]; + } + + for (unsigned pos = 0; pos < 80; ++pos) + state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[((pos * 2) & ~3) | (pos & 1)]; + + std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); + + state.ppu.videoCycles = 144*456ul + 164; + state.ppu.enableDisplayM0Time = state.cpu.cycleCounter - state.ppu.videoCycles + 159; + state.ppu.winYPos = 0xFF; + state.ppu.drawStartCycle = 90; + state.ppu.scReadOffset = 90; + state.ppu.lcdc = state.mem.ioamhram.get()[0x140]; + state.ppu.scx[1] = state.ppu.scx[0] = 0; + state.ppu.scy[1] = state.ppu.scy[0] = 0; + state.ppu.scxAnd7 = 0; + state.ppu.weMaster = false; + state.ppu.wx = 0; + state.ppu.wy = 0; + state.ppu.lycIrqSkip = false; + + + state.spu.cycleCounter = 0x1000 | ((state.cpu.cycleCounter >> 1) & 0xFFF); // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. + + state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.sweep.shadow = 0; + state.spu.ch1.sweep.nr0 = 0; + state.spu.ch1.sweep.negging = false; + state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + state.spu.ch1.duty.nr3 = 0; + state.spu.ch1.duty.pos = 0; + state.spu.ch1.env.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.env.volume = 0; + state.spu.ch1.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.lcounter.lengthCounter = 0x40; + state.spu.ch1.nr4 = 0; + state.spu.ch1.master = true; + + state.spu.ch2.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + state.spu.ch2.duty.nr3 = 0; + state.spu.ch2.duty.pos = 0; + state.spu.ch2.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch2.env.volume = 0; + state.spu.ch2.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch2.lcounter.lengthCounter = 0x40; + state.spu.ch2.nr4 = 0; + state.spu.ch2.master = false; + + for (unsigned i = 0; i < 0x10; ++i) + state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i]; + + state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.lcounter.lengthCounter = 0x100; + state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.lastReadTime = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.nr3 = 0; + state.spu.ch3.nr4 = 0; + state.spu.ch3.wavePos = 0; + state.spu.ch3.sampleBuf = 0; + state.spu.ch3.master = false; + + state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4; + state.spu.ch4.lfsr.reg = 0xFF; + state.spu.ch4.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch4.env.volume = 0; + state.spu.ch4.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch4.lcounter.lengthCounter = 0x40; + state.spu.ch4.nr4 = 0; + state.spu.ch4.master = false; + + state.rtc.baseTime = std::time(0); + state.rtc.haltTime = state.rtc.baseTime; + state.rtc.index = 5; + state.rtc.dataDh = 0; + state.rtc.dataDl = 0; + state.rtc.dataH = 0; + state.rtc.dataM = 0; + state.rtc.dataS = 0; + state.rtc.lastLatchData = false; +} diff --git a/supergameboy/libgambatte/src/initstate.h b/supergameboy/libgambatte/src/initstate.h new file mode 100644 index 00000000..d550eed5 --- /dev/null +++ b/supergameboy/libgambatte/src/initstate.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INITSTATE_H +#define INITSTATE_H + +class SaveState; + +void setInitState(SaveState &state, bool cgb); + +#endif diff --git a/supergameboy/libgambatte/src/insertion_sort.h b/supergameboy/libgambatte/src/insertion_sort.h new file mode 100644 index 00000000..939ba074 --- /dev/null +++ b/supergameboy/libgambatte/src/insertion_sort.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef INSERTION_SORT_H +#define INSERTION_SORT_H + +#include + +template +void insertionSort(T *const start, T *const end, Less less) { + if (start >= end) + return; + + T *a = start; + + while (++a < end) { + const T e = *a; + + T *b = a; + + while (b != start && less(e, *(b - 1))) { + *b = *(b - 1); + b = b - 1; + } + + *b = e; + } +} + +template +inline void insertionSort(T *const start, T *const end) { + insertionSort(start, end, std::less()); +} + +#endif /*INSERTION_SORT_H*/ diff --git a/supergameboy/libgambatte/src/interrupter.cpp b/supergameboy/libgambatte/src/interrupter.cpp new file mode 100644 index 00000000..aea9df41 --- /dev/null +++ b/supergameboy/libgambatte/src/interrupter.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "interrupter.h" + +#include "memory.h" + +Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in, bool &halted_in) : + SP(SP_in), + PC(PC_in), + halted(halted_in) +{} + +unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) { + if (halted && memory.isCgb()) + cycleCounter += 4; + + halted = false; + cycleCounter += 8; + SP = (SP - 1) & 0xFFFF; + memory.write(SP, PC >> 8, cycleCounter); + cycleCounter += 4; + SP = (SP - 1) & 0xFFFF; + memory.write(SP, PC & 0xFF, cycleCounter); + PC = address; + cycleCounter += 8; + + return cycleCounter; +} diff --git a/supergameboy/libgambatte/src/interrupter.h b/supergameboy/libgambatte/src/interrupter.h new file mode 100644 index 00000000..18e0d9e1 --- /dev/null +++ b/supergameboy/libgambatte/src/interrupter.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INTERRUPTER_H +#define INTERRUPTER_H + +class Memory; + +class Interrupter { + unsigned short &SP; + unsigned short &PC; + bool &halted; + +public: + Interrupter(unsigned short &SP, unsigned short &PC, bool &halted); + unsigned long interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory); + + void unhalt() { + halted = false; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/memory.cpp b/supergameboy/libgambatte/src/memory.cpp new file mode 100644 index 00000000..2211733d --- /dev/null +++ b/supergameboy/libgambatte/src/memory.cpp @@ -0,0 +1,1867 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "memory.h" +#include "video.h" +#include "sound.h" +#include "inputstate.h" +#include "inputstategetter.h" +#include "savestate.h" +#include "file/file.h" +#include +#include + +// static const uint32_t timaClock[4]={ 1024, 16, 64, 256 }; +static const unsigned char timaClock[4] = { 10, 4, 6, 8 }; + +Memory::Memory(const Interrupter &interrupter_in) : +memchunk(NULL), +rambankdata(NULL), +rdisabled_ram(NULL), +wdisabled_ram(NULL), +oamDmaSrc(NULL), +vrambank(vram), +rsrambankptr(NULL), +wsrambankptr(NULL), +getInput(NULL), +div_lastUpdate(0), +tima_lastUpdate(0), +next_timatime(COUNTER_DISABLED), +next_blittime(144*456ul), +nextIntTime(COUNTER_DISABLED), +minIntTime(0), +next_dmatime(COUNTER_DISABLED), +next_hdmaReschedule(COUNTER_DISABLED), +next_unhalttime(COUNTER_DISABLED), +next_endtime(0), +tmatime(COUNTER_DISABLED), +next_serialtime(COUNTER_DISABLED), +lastOamDmaUpdate(COUNTER_DISABLED), +nextOamEventTime(COUNTER_DISABLED), +display(ioamhram, vram), +interrupter(interrupter_in), +romtype(plain), +rombanks(1), +rombank(1), +dmaSource(0), +dmaDestination(0), +rambank(0), +rambanks(1), +oamDmaArea1Lower(0), +oamDmaArea1Width(0), +oamDmaArea2Upper(0), +oamDmaPos(0xFE), +cgb(false), +doubleSpeed(false), +IME(false), +enable_ram(false), +rambank_mode(false), +battery(false), +rtcRom(false), +hdma_transfer(false), +active(false) +{ + romdata[1] = romdata[0] = NULL; + wramdata[1] = wramdata[0] = NULL; + std::fill_n(rmem, 0x10, static_cast(NULL)); + std::fill_n(wmem, 0x10, static_cast(NULL)); + set_irqEvent(); + set_event(); +} + +void Memory::setStatePtrs(SaveState &state) { + state.mem.vram.set(vram, sizeof vram); + state.mem.sram.set(rambankdata, rambanks * 0x2000ul); + state.mem.wram.set(wramdata[0], isCgb() ? 0x8000 : 0x2000); + state.mem.ioamhram.set(ioamhram, sizeof ioamhram); + + display.setStatePtrs(state); + sound.setStatePtrs(state); +} + +unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) { + cycleCounter = resetCounters(cycleCounter); + nontrivial_ff_read(0xFF0F, cycleCounter); + nontrivial_ff_read(0xFF26, cycleCounter); + + state.mem.div_lastUpdate = div_lastUpdate; + state.mem.tima_lastUpdate = tima_lastUpdate; + state.mem.tmatime = tmatime; + state.mem.next_serialtime = next_serialtime; + state.mem.lastOamDmaUpdate = lastOamDmaUpdate; + state.mem.minIntTime = minIntTime; + state.mem.rombank = rombank; + state.mem.dmaSource = dmaSource; + state.mem.dmaDestination = dmaDestination; + state.mem.rambank = rambank; + state.mem.oamDmaPos = oamDmaPos; + state.mem.IME = IME; + state.mem.enable_ram = enable_ram; + state.mem.rambank_mode = rambank_mode; + state.mem.hdma_transfer = hdma_transfer; + + rtc.saveState(state); + display.saveState(state); + sound.saveState(state); + + return cycleCounter; +} + +void Memory::loadState(const SaveState &state, const unsigned long oldCc) { + sound.loadState(state); + display.loadState(state, state.mem.oamDmaPos < 0xA0 ? rdisabled_ram : ioamhram); + rtc.loadState(state, rtcRom ? state.mem.enable_ram : false); + + div_lastUpdate = state.mem.div_lastUpdate; + tima_lastUpdate = state.mem.tima_lastUpdate; + tmatime = state.mem.tmatime; + next_serialtime = state.mem.next_serialtime; + lastOamDmaUpdate = state.mem.lastOamDmaUpdate; + minIntTime = state.mem.minIntTime; + rombank = state.mem.rombank & (rombanks - 1); + dmaSource = state.mem.dmaSource; + dmaDestination = state.mem.dmaDestination; + rambank = state.mem.rambank & (rambanks - 1); + oamDmaPos = state.mem.oamDmaPos; + IME = state.mem.IME; + enable_ram = state.mem.enable_ram; + rambank_mode = state.mem.rambank_mode; + hdma_transfer = state.mem.hdma_transfer; + + const bool oldDs = doubleSpeed; + doubleSpeed = isCgb() & ioamhram[0x14D] >> 7; + oamDmaArea2Upper = oamDmaArea1Width = oamDmaArea1Lower = 0; + vrambank = vram + (ioamhram[0x14F] & 0x01 & isCgb()) * 0x2000; + wramdata[1] = wramdata[0] + ((isCgb() && (ioamhram[0x170] & 0x07)) ? (ioamhram[0x170] & 0x07) : 1) * 0x1000; + std::fill_n(rmem, 0x10, static_cast(NULL)); + std::fill_n(wmem, 0x10, static_cast(NULL)); + setBanks(); + + if (lastOamDmaUpdate != COUNTER_DISABLED) { + oamDmaInitSetup(); + + unsigned oamEventPos = 0x100; + + if (oamDmaPos < 0xA0) { + setOamDmaArea(); + oamEventPos = 0xA0; + } + + nextOamEventTime = lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4; + setOamDmaSrc(); + } + + if (!IME && state.cpu.halted) + schedule_unhalt(); + + next_blittime = (ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : static_cast(COUNTER_DISABLED); + + const unsigned long cycleCounter = state.cpu.cycleCounter; + + if (hdma_transfer) { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } else { + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + } + + next_timatime = (ioamhram[0x107] & 4) ? tima_lastUpdate + ((256u - ioamhram[0x105]) << timaClock[ioamhram[0x107] & 3]) + 1 : static_cast(COUNTER_DISABLED); + set_irqEvent(); + rescheduleIrq(cycleCounter); + + if (oldDs != isDoubleSpeed()) + next_endtime = cycleCounter - (isDoubleSpeed() ?( oldCc - next_endtime) << 1 :( oldCc - next_endtime) >> 1); + else + next_endtime = cycleCounter - (oldCc - next_endtime); + +// set_event(); +} + +void Memory::schedule_unhalt() { + next_unhalttime = std::min(next_irqEventTime, display.nextIrqEvent()); + + if (next_unhalttime != COUNTER_DISABLED) + next_unhalttime += isCgb() * 4; + + set_event(); +} + +void Memory::rescheduleIrq(const unsigned long cycleCounter) { + if (IME) { + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + nextIntTime = (ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F) ? cycleCounter : std::min(next_irqEventTime, display.nextIrqEvent()); + + if (nextIntTime < minIntTime) + nextIntTime = minIntTime; + + set_event(); + } +} + +void Memory::rescheduleHdmaReschedule() { + if (hdma_transfer && (ioamhram[0x140] & 0x80)) { + const unsigned long newTime = display.nextHdmaTimeInvalid(); + + if (newTime < next_hdmaReschedule) { + next_hdmaReschedule = newTime; + + if (newTime < next_eventtime) { + next_eventtime = newTime; + next_event = HDMA_RESCHEDULE; + } + } + } +} + +void Memory::ei(const unsigned long cycleCounter) { + IME = 1; + minIntTime = cycleCounter + 1; + rescheduleIrq(cycleCounter); +} + +void Memory::incEndtime(const unsigned long inc) { + active = true; + next_endtime += inc << isDoubleSpeed(); + set_event(); +} + +void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { + next_endtime = cycleCounter; + incEndtime(inc); +} + +void Memory::set_irqEvent() { + next_irqEventTime = next_timatime; + next_irqEvent = TIMA; + + if (next_serialtime < next_irqEventTime) { + next_irqEvent = SERIAL; + next_irqEventTime = next_serialtime; + } +} + +void Memory::update_irqEvents(const unsigned long cc) { + while (next_irqEventTime <= cc) { + switch (next_irqEvent) { + case TIMA: + ioamhram[0x10F] |= 4; + next_timatime += (256u - ioamhram[0x106]) << timaClock[ioamhram[0x107] & 3]; + break; + case SERIAL: + next_serialtime = COUNTER_DISABLED; + ioamhram[0x101] = 0xFF; + ioamhram[0x102] &= 0x7F; + ioamhram[0x10F] |= 8; + break; + } + + set_irqEvent(); + } +} + +void Memory::set_event() { + next_event = INTERRUPTS; + next_eventtime = nextIntTime; + if (next_hdmaReschedule < next_eventtime) { + next_eventtime = next_hdmaReschedule; + next_event = HDMA_RESCHEDULE; + } + if (next_dmatime < next_eventtime) { + next_eventtime = next_dmatime; + next_event = DMA; + } + if (next_unhalttime < next_eventtime) { + next_eventtime = next_unhalttime; + next_event = UNHALT; + } + if (nextOamEventTime < next_eventtime) { + next_eventtime = nextOamEventTime; + next_event = OAM; + } + if (next_blittime < next_eventtime) { + next_event = BLIT; + next_eventtime = next_blittime; + } + if (next_endtime < next_eventtime) { + next_eventtime = next_endtime; + next_event = END; + } +} + +unsigned long Memory::event(unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (next_event) { + case HDMA_RESCHEDULE: +// printf("hdma_reschedule\n"); + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + break; + case DMA: +// printf("dma\n"); + { + const bool doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource; + unsigned dmaDest = dmaDestination; + unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; + + unsigned length = hdma_transfer ? 0x10 : dmaLength; + + if ((static_cast(dmaDest) + length) & 0x10000) { + length = 0x10000 - dmaDest; + ioamhram[0x155] |= 0x80; + } + + dmaLength -= length; + + if (!(ioamhram[0x140] & 0x80)) + dmaLength = 0; + + { + unsigned long lOamDmaUpdate = lastOamDmaUpdate; + lastOamDmaUpdate = COUNTER_DISABLED; + + while (length--) { + const unsigned src = dmaSrc++ & 0xFFFF; + const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter); + + cycleCounter += 2 << doubleSpeed; + + if (cycleCounter - 3 > lOamDmaUpdate) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lOamDmaUpdate += 4; + + if (oamDmaPos < 0xA0) { + if (oamDmaPos == 0) + startOamDma(lOamDmaUpdate - 2); + + ioamhram[src & 0xFF] = data; + } else if (oamDmaPos == 0xA0) { + endOamDma(lOamDmaUpdate - 2); + lOamDmaUpdate = COUNTER_DISABLED; + } + } + + nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); + } + + lastOamDmaUpdate = lOamDmaUpdate; + } + + cycleCounter += 4; + + dmaSource = dmaSrc; + dmaDestination = dmaDest; + ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); + + if (ioamhram[0x155] & 0x80) { + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + hdma_transfer = 0; + } + + if (hdma_transfer) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + next_dmatime = display.nextHdmaTime(cycleCounter); + } + } + + break; + case INTERRUPTS: +// printf("interrupts\n"); + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + { + /*unsigned interrupt = ioamhram[0x10F] & ioamhram[0x1FF]; + interrupt |= interrupt << 1; + interrupt |= interrupt << 2; + interrupt |= interrupt << 1; + interrupt = ~interrupt; + ++interrupt; + interrupt &= 0x1F; + + if (interrupt) { + ioamhram[0x10F] &= ~interrupt; + display.setIfReg(ioamhram[0x10F], CycleCounter); + IME = false; + + unsigned address = interrupt; + interrupt >>= 1; + address -= interrupt & 0x0C; + interrupt >>= 1; + address -= interrupt & 5; + address += interrupt >> 2; + + address <<= 3; + address += 0x38; + + z80.interrupt(address); + }*/ + + const unsigned interrupt = ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F; + + if (interrupt) { + unsigned n; + unsigned address; + + if ((n = interrupt & 0x01)) + address = 0x40; + else if ((n = interrupt & 0x02)) + address = 0x48; + else if ((n = interrupt & 0x04)) + address = 0x50; + else if ((n = interrupt & 0x08)) + address = 0x58; + else { + n = 0x10; + address = 0x60; + } + + ioamhram[0x10F] &= ~n; + display.setIfReg(ioamhram[0x10F], cycleCounter); + IME = false; + cycleCounter = interrupter.interrupt(address, cycleCounter, *this); + } + } + + nextIntTime = IME ? std::min(next_irqEventTime, display.nextIrqEvent()) : static_cast(COUNTER_DISABLED); + break; + case BLIT: +// printf("blit\n"); + display.updateScreen(next_blittime); + + if (ioamhram[0x140] & 0x80) + next_blittime += 70224 << isDoubleSpeed(); + else + next_blittime = COUNTER_DISABLED; + + break; + case UNHALT: +// printf("unhalt\n"); + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; + + if (ioamhram[0x10F] & ioamhram[0x1FF] & 0x1F) { + next_unhalttime = COUNTER_DISABLED; + interrupter.unhalt(); + } else + next_unhalttime = std::min(next_irqEventTime, display.nextIrqEvent()) + isCgb() * 4; + + break; + case OAM: + nextOamEventTime = lastOamDmaUpdate == COUNTER_DISABLED ? static_cast(COUNTER_DISABLED) : nextOamEventTime + 0xA0 * 4; + break; + case END: + { + const unsigned long endtime = next_endtime; + next_endtime = COUNTER_DISABLED; + set_event(); + + while (cycleCounter >= next_eventtime) + cycleCounter = event(cycleCounter); + + next_endtime = endtime; + active = false; + } + + break; + } + + set_event(); + + return cycleCounter; +} + +void Memory::speedChange(const unsigned long cycleCounter) { + if (isCgb() && (ioamhram[0x14D] & 0x1)) { + std::printf("speedChange\n"); + + update_irqEvents(cycleCounter); + sound.generate_samples(cycleCounter, isDoubleSpeed()); + display.preSpeedChange(cycleCounter); + + ioamhram[0x14D] = ~ioamhram[0x14D] & 0x80; + doubleSpeed = ioamhram[0x14D] >> 7; + + display.postSpeedChange(cycleCounter); + + if (hdma_transfer) { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } + + next_blittime = (ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : static_cast(COUNTER_DISABLED); + next_endtime = cycleCounter + (isDoubleSpeed() ?( next_endtime - cycleCounter) << 1 : ((next_endtime - cycleCounter) >> 1)); + set_irqEvent(); + rescheduleIrq(cycleCounter); + set_event(); + } +} + +static void decCycles(unsigned long &counter, const unsigned long dec) { + if (counter != Memory::COUNTER_DISABLED) + counter -= dec; +} + +unsigned long Memory::resetCounters(unsigned long cycleCounter) { + std::printf("resetting counters\n"); + + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + update_irqEvents(cycleCounter); + rescheduleIrq(cycleCounter); + display.preResetCounter(cycleCounter); + + const unsigned long oldCC = cycleCounter; + + { + const unsigned long divinc = (cycleCounter - div_lastUpdate) >> 8; + ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF; + div_lastUpdate += divinc << 8; + } + + if (ioamhram[0x107] & 0x04) { + update_tima(cycleCounter); + } + + const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000; + + minIntTime = minIntTime < cycleCounter ? 0 : minIntTime - dec; + + if (ioamhram[0x107] & 0x04) + decCycles(tima_lastUpdate, dec); + + decCycles(div_lastUpdate, dec); + decCycles(lastOamDmaUpdate, dec); + decCycles(next_eventtime, dec); + decCycles(next_irqEventTime, dec); + decCycles(next_timatime, dec); + decCycles(next_blittime, dec); + decCycles(nextOamEventTime, dec); + decCycles(next_endtime, dec); + decCycles(next_dmatime, dec); + decCycles(next_hdmaReschedule, dec); + decCycles(nextIntTime, dec); + decCycles(next_serialtime, dec); + decCycles(tmatime, dec); + decCycles(next_unhalttime, dec); + + cycleCounter -= dec; + + display.postResetCounter(oldCC, cycleCounter); + sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed()); + + return cycleCounter; +} + +void Memory::updateInput() { + unsigned joypadId = 0x0F; + unsigned button = 0xFF; + unsigned dpad = 0xFF; + + if (getInput) { + const Gambatte::InputState &is = (*getInput)(); + + joypadId = is.joypadId; + + button ^= is.startButton << 3; + button ^= is.selectButton << 2; + button ^= is.bButton << 1; + button ^= is.aButton; + + dpad ^= is.dpadDown << 3; + dpad ^= is.dpadUp << 2; + dpad ^= is.dpadLeft << 1; + dpad ^= is.dpadRight; + } + + ioamhram[0x100] |= 0xF; + + if ((ioamhram[0x100] & 0x30) == 0x30) { + ioamhram[0x100] &= 0xf0; + ioamhram[0x100] |= joypadId; + } else { + if (!(ioamhram[0x100] & 0x10)) + ioamhram[0x100] &= dpad; + + if (!(ioamhram[0x100] & 0x20)) + ioamhram[0x100] &= button; + } +} + +void Memory::setRombank() { + unsigned bank = rombank; + + if ((romtype == mbc1 && !(bank & 0x1F)) || (romtype == mbc5 && !bank)) + ++bank; + + romdata[1] = romdata[0] + bank * 0x4000ul - 0x4000; + + if (oamDmaArea1Lower != 0xA0) { + rmem[0x7] = rmem[0x6] = rmem[0x5] = rmem[0x4] = romdata[1]; + } else + setOamDmaSrc(); +} + +void Memory::setRambank() { + rmem[0xB] = rmem[0xA] = rsrambankptr = rdisabled_ram - 0xA000; + wmem[0xB] = wmem[0xA] = wsrambankptr = wdisabled_ram - 0xA000; + + if (enable_ram) { + if (rtc.getActive()) { + wmem[0xB] = wmem[0xA] = rmem[0xB] = rmem[0xA] = wsrambankptr = rsrambankptr = NULL; + } else if (rambanks) { + wmem[0xB] = rmem[0xB] = wmem[0xA] = rmem[0xA] = wsrambankptr = rsrambankptr = rambankdata + rambank * 0x2000ul - 0xA000; + } + } + + if (oamDmaArea1Lower == 0xA0) { + wmem[0xB] = wmem[0xA] = rmem[0xB] = rmem[0xA] = NULL; + setOamDmaSrc(); + } +} + +void Memory::setBanks() { + rmem[0x3] = rmem[0x2] = rmem[0x1] = rmem[0x0] = romdata[0]; + + setRombank(); + setRambank(); + + rmem[0xC] = wmem[0xC] = wramdata[0] - 0xC000; + rmem[0xD] = wmem[0xD] = wramdata[1] - 0xD000; + rmem[0xE] = wmem[0xE] = wramdata[0] - 0xE000; +} + +void Memory::updateOamDma(const unsigned long cycleCounter) { + unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; + + while (cycles--) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lastOamDmaUpdate += 4; + + //TODO: reads from vram while the ppu is reading vram should return whatever the ppu is reading. + if (oamDmaPos < 0xA0) { + if (oamDmaPos == 0) + startOamDma(lastOamDmaUpdate - 2); + + ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : *rtc.getActive(); + } else if (oamDmaPos == 0xA0) { + endOamDma(lastOamDmaUpdate - 2); + lastOamDmaUpdate = COUNTER_DISABLED; + break; + } + } +} + +void Memory::setOamDmaArea() { + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) != 0x80) + oamDmaArea2Upper = 0x80; + + oamDmaArea1Width = 0x20; + } else if (ioamhram[0x146] < 0xE0) + oamDmaArea1Width = 0x3E; +} + +void Memory::oamDmaInitSetup() { + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) == 0x80) { + oamDmaArea1Lower = 0x80; + } else { + oamDmaArea1Lower = 0xA0; + std::fill_n(rmem, 0x8, static_cast(NULL)); + rmem[0xB] = rmem[0xA] = NULL; + wmem[0xB] = wmem[0xA] = NULL; + } + } else if (ioamhram[0x146] < 0xE0) { + oamDmaArea1Lower = 0xC0; + rmem[0xE] = rmem[0xD] = rmem[0xC] = NULL; + wmem[0xE] = wmem[0xD] = wmem[0xC] = NULL; + } +} + +void Memory::setOamDmaSrc() { + oamDmaSrc = NULL; + + if (ioamhram[0x146] < 0xC0) { + if ((ioamhram[0x146] & 0xE0) == 0x80) { + oamDmaSrc = vrambank + (ioamhram[0x146] << 8 & 0x1FFF); + } else { + if (ioamhram[0x146] < 0x80) + oamDmaSrc = romdata[ioamhram[0x146] >> 6] + (ioamhram[0x146] << 8); + else if (rsrambankptr) + oamDmaSrc = rsrambankptr + (ioamhram[0x146] << 8); + } + } else if (ioamhram[0x146] < 0xE0) { + oamDmaSrc = wramdata[ioamhram[0x146] >> 4 & 1] + (ioamhram[0x146] << 8 & 0xFFF); + } else + oamDmaSrc = rdisabled_ram; +} + +void Memory::startOamDma(const unsigned long cycleCounter) { + setOamDmaArea(); + display.oamChange(rdisabled_ram, cycleCounter); + + if (next_unhalttime != COUNTER_DISABLED) + schedule_unhalt(); + else + rescheduleIrq(cycleCounter); + + rescheduleHdmaReschedule(); +} + +void Memory::endOamDma(const unsigned long cycleCounter) { + oamDmaArea2Upper = oamDmaArea1Width = oamDmaArea1Lower = 0; + oamDmaPos = 0xFE; + setBanks(); + display.oamChange(ioamhram, cycleCounter); + + if (next_unhalttime != COUNTER_DISABLED) + schedule_unhalt(); + else + rescheduleIrq(cycleCounter); + + rescheduleHdmaReschedule(); +} + +void Memory::update_tima(const unsigned long cycleCounter) { + const unsigned long ticks = (cycleCounter - tima_lastUpdate) >> timaClock[ioamhram[0x107] & 3]; + + tima_lastUpdate += ticks << timaClock[ioamhram[0x107] & 3]; + + if (cycleCounter >= tmatime) { + if (cycleCounter >= tmatime + 4) + tmatime = COUNTER_DISABLED; + + ioamhram[0x105] = ioamhram[0x106]; + } + + unsigned long tmp = ioamhram[0x105] + ticks; + + while (tmp > 0x100) + tmp -= 0x100 - ioamhram[0x106]; + + if (tmp == 0x100) { + tmp = 0; + tmatime = tima_lastUpdate + 3; + + if (cycleCounter >= tmatime) { + if (cycleCounter >= tmatime + 4) + tmatime = COUNTER_DISABLED; + + tmp = ioamhram[0x106]; + } + } + + ioamhram[0x105] = tmp; +} + +unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (P & 0x7F) { + case 0x00: + updateInput(); + break; + case 0x04: +// printf("div read\n"); + { + const unsigned long divcycles = (cycleCounter - div_lastUpdate) >> 8; + ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; + div_lastUpdate += divcycles << 8; + } + + break; + case 0x05: +// printf("tima read\n"); + if (ioamhram[0x107] & 0x04) + update_tima(cycleCounter); + + break; + case 0x0F: + update_irqEvents(cycleCounter); + ioamhram[0x10F] |= display.getIfReg(cycleCounter) & 3; +// rescheduleIrq(cycleCounter); + break; + case 0x26: +// printf("sound status read\n"); + if (ioamhram[0x126] & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + ioamhram[0x126] = 0xF0 | sound.getStatus(); + } else + ioamhram[0x126] = 0x70; + + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.waveRamRead(P & 0xF); + case 0x41: + return ioamhram[0x141] | display.get_stat(ioamhram[0x145], cycleCounter); + case 0x44: + return display.getLyReg(cycleCounter/*+4*/); + case 0x69: + return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); + case 0x6B: + return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); + default: break; + } + + return ioamhram[P - 0xFE00]; +} + +unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) { + if (P < 0xFF80) { + if (lastOamDmaUpdate != COUNTER_DISABLED) { + updateOamDma(cycleCounter); + + if ((P >> 8) - oamDmaArea1Lower < oamDmaArea1Width || P >> 8 < oamDmaArea2Upper) + return ioamhram[oamDmaPos]; + } + + if (P < 0xC000) { + if (P < 0x8000) + return romdata[P >> 14][P]; + + if (P < 0xA000) { + if (!display.vramAccessible(cycleCounter)) + return 0xFF; + + return vrambank[P & 0x1FFF]; + } + + if (rsrambankptr) + return rsrambankptr[P]; + + return *rtc.getActive(); + } + + if (P < 0xFE00) + return wramdata[P >> 12 & 1][P & 0xFFF]; + + if (P & 0x100) + return nontrivial_ff_read(P, cycleCounter); + + if (!display.oamAccessible(cycleCounter) || oamDmaPos < 0xA0) + return 0xFF; + } + + return ioamhram[P - 0xFE00]; +} + +void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { +// printf("mem[0x%X] = 0x%X\n", P, data); + + if (lastOamDmaUpdate != COUNTER_DISABLED) + updateOamDma(cycleCounter); + + switch (P & 0xFF) { + case 0x00: + data = (ioamhram[0x100] & 0xCF) | (data & 0xF0); + supergameboy.joyp_write(data & 0x20, data & 0x10); + break; + case 0x01: + update_irqEvents(cycleCounter); + break; + case 0x02: + update_irqEvents(cycleCounter); + + if ((data & 0x81) == 0x81) { + next_serialtime = cycleCounter; + next_serialtime += (isCgb() && (data & 0x2)) ? 128 : 4096; + set_irqEvent(); + } + + rescheduleIrq(cycleCounter); + data |= 0x7C; + break; + //If rom is trying to write to DIV register, reset it to 0. + case 0x04: +// printf("DIV write\n"); + ioamhram[0x104] = 0; + div_lastUpdate = cycleCounter; + return; + case 0x05: + // printf("tima write\n"); + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + + if (tmatime - cycleCounter < 4) + tmatime = COUNTER_DISABLED; + + next_timatime = tima_lastUpdate + ((256u - data) << timaClock[ioamhram[0x107] & 3]) + 1; + set_irqEvent(); + rescheduleIrq(cycleCounter); + } + + break; + case 0x06: + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + } + + break; + case 0x07: + // printf("tac write: %i\n", data); + data |= 0xF8; + + if (ioamhram[0x107] ^ data) { + if (ioamhram[0x107] & 0x04) { + update_irqEvents(cycleCounter); + update_tima(cycleCounter); + + tima_lastUpdate -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + tmatime -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + next_timatime -= (1u << (timaClock[ioamhram[0x107] & 3] - 1)) + 3; + set_irqEvent(); + update_tima(cycleCounter); + update_irqEvents(cycleCounter); + + tmatime = COUNTER_DISABLED; + next_timatime = COUNTER_DISABLED; + } + + if (data & 4) { + tima_lastUpdate = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; + next_timatime = tima_lastUpdate + ((256u - ioamhram[0x105]) << timaClock[data & 3]) + 1; + } + + set_irqEvent(); + rescheduleIrq(cycleCounter); + } + + break; + case 0x0F: + update_irqEvents(cycleCounter); + display.setIfReg(data, cycleCounter); + ioamhram[0x10F] = 0xE0 | data; + rescheduleIrq(cycleCounter); + return; + case 0x10: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr10(data); + data |= 0x80; + break; + case 0x11: + if(!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr11(data); + data |= 0x3F; + break; + case 0x12: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr12(data); + break; + case 0x13: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr13(data); + return; + case 0x14: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr14(data); + data |= 0xBF; + break; + case 0x16: + if(!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr21(data); + data |= 0x3F; + break; + case 0x17: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr22(data); + break; + case 0x18: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr23(data); + return; + case 0x19: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr24(data); + data |= 0xBF; + break; + case 0x1A: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr30(data); + data |= 0x7F; + break; + case 0x1B: + if(!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr31(data); + return; + case 0x1C: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr32(data); + data |= 0x9F; + break; + case 0x1D: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr33(data); + return; + case 0x1E: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr34(data); + data |= 0xBF; + break; + case 0x20: + if(!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr41(data); + return; + case 0x21: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr42(data); + break; + case 0x22: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr43(data); + break; + case 0x23: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr44(data); + data |= 0xBF; + break; + case 0x24: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_so_volume(data); + break; + case 0x25: + if(!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.map_so(data); + break; + case 0x26: + if ((ioamhram[0x126] ^ data) & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + + if (!(data & 0x80)) { + for (unsigned i = 0xFF10; i < 0xFF26; ++i) + ff_write(i, 0, cycleCounter); + +// std::memcpy(memory + 0xFF10, soundRegInitValues, sizeof(soundRegInitValues)); + sound.setEnabled(false); + } else { + sound.reset(/*memory + 0xFF00, isDoubleSpeed()*/); + sound.setEnabled(true); + } + } + + data = (data & 0x80) | (ioamhram[0x126] & 0x7F); + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.waveRamWrite(P & 0xF, data); + break; + case 0x40: + if (ioamhram[0x140] != data) { + if ((ioamhram[0x140] ^ data) & 0x80) { + update_irqEvents(cycleCounter); + const unsigned lyc = display.get_stat(ioamhram[0x145], cycleCounter) & 4; + display.enableChange(cycleCounter); + ioamhram[0x144] = 0; +// enable_display = bool(data & 0x80); + ioamhram[0x141] &= 0xF8; + + if (data & 0x80) { + next_blittime = display.nextMode1IrqTime() + (70224 << isDoubleSpeed()); + } else { + ioamhram[0x141] |= lyc; //Mr. Do! needs conicidence flag preserved. + next_blittime = cycleCounter + (456 * 4 << isDoubleSpeed()); + + if (hdma_transfer) + next_dmatime = cycleCounter; + + next_hdmaReschedule = COUNTER_DISABLED; + } + + set_event(); + } + + if ((ioamhram[0x140] ^ data) & 0x4) { + display.spriteSizeChange(data & 0x4, cycleCounter); + } + + if ((ioamhram[0x140] ^ data) & 0x20) { +// printf("%u: weChange to %u\n", CycleCounter, (data & 0x20) != 0); + display.weChange(data & 0x20, cycleCounter); + } + + if ((ioamhram[0x140] ^ data) & 0x40) + display.wdTileMapSelectChange(data & 0x40, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x08) + display.bgTileMapSelectChange(data & 0x08, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x10) + display.bgTileDataSelectChange(data & 0x10, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x02) + display.spriteEnableChange(data & 0x02, cycleCounter); + + if ((ioamhram[0x140] ^ data) & 0x01) + display.bgEnableChange(data & 0x01, cycleCounter); + + ioamhram[0x140] = data; + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + } + + return; + case 0x41: + display.lcdstatChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + data = (ioamhram[0x141] & 0x87) | (data & 0x78); + break; + case 0x42: + display.scyChange(data, cycleCounter); + break; + case 0x43: + display.scxChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + //If rom is trying to write to LY register, reset it to 0. + case 0x44: + if (ioamhram[0x140] & 0x80) { + std::printf("ly write\n"); + display.lyWrite(cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + } + + return; + case 0x45: + display.lycRegChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + break; + case 0x46: + if (lastOamDmaUpdate != COUNTER_DISABLED) + endOamDma(cycleCounter); + + lastOamDmaUpdate = cycleCounter; + nextOamEventTime = cycleCounter + 8; + ioamhram[0x146] = data; + oamDmaInitSetup(); + setOamDmaSrc(); + return; + case 0x47: + if (!isCgb()) { + display.dmgBgPaletteChange(data, cycleCounter); + } + + break; + case 0x48: + if (!isCgb()) { + display.dmgSpPalette1Change(data, cycleCounter); + } + + break; + case 0x49: + if (!isCgb()) { + display.dmgSpPalette2Change(data, cycleCounter); + } + + break; + case 0x4A: +// printf("%u: wyChange to %u\n", CycleCounter, data); + display.wyChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + case 0x4B: +// printf("%u: wxChange to %u\n", CycleCounter, data); + display.wxChange(data, cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + break; + + //cgb stuff: + case 0x4D: + ioamhram[0x14D] |= data & 0x01; + return; + //Select vram bank + case 0x4F: + if (isCgb()) { + vrambank = vram + (data & 0x01) * 0x2000; + + if (oamDmaArea1Lower == 0x80) + setOamDmaSrc(); + + ioamhram[0x14F] = 0xFE | data; + } + + return; + case 0x51: + dmaSource = data << 8 | (dmaSource & 0xFF); + return; + case 0x52: + dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); + return; + case 0x53: + dmaDestination = data << 8 | (dmaDestination & 0xFF); + return; + case 0x54: + dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); + return; + case 0x55: + if (!isCgb()) + return; + + ioamhram[0x155] = data & 0x7F; + + if (hdma_transfer) { + if (!(data & 0x80)) { + ioamhram[0x155] |= 0x80; + + if (next_dmatime > cycleCounter) { + hdma_transfer = 0; + next_hdmaReschedule = next_dmatime = COUNTER_DISABLED; + set_event(); + } + } + + return; + } + + if (data & 0x80) { + hdma_transfer = 1; + + if (!(ioamhram[0x140] & 0x80) || display.isHdmaPeriod(cycleCounter)) { + next_dmatime = cycleCounter; + next_hdmaReschedule = COUNTER_DISABLED; + } else { + next_dmatime = display.nextHdmaTime(cycleCounter); + next_hdmaReschedule = display.nextHdmaTimeInvalid(); + } + } else + next_dmatime = cycleCounter; + + set_event(); + return; + case 0x56: + if (isCgb()) { + ioamhram[0x156] = data | 0x3E; + } + + return; + //Set bg palette index + case 0x68: + if (isCgb()) + ioamhram[0x168] = data | 0x40; + + return; + //Write to bg palette data + case 0x69: + if (isCgb()) { + const unsigned index = ioamhram[0x168] & 0x3F; + + display.cgbBgColorChange(index, data, cycleCounter); + + ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F); + } + + return; + case 0x6A: + if (isCgb()) + ioamhram[0x16A] = data | 0x40; + + return; + //Write to obj palette data. + case 0x6B: + if (isCgb()) { + const unsigned index = ioamhram[0x16A] & 0x3F; + + display.cgbSpColorChange(index, data, cycleCounter); + + ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F); + } + + return; + case 0x6C: + if (isCgb()) + ioamhram[0x16C] = data | 0xFE; + + return; + case 0x70: + if (isCgb()) { + wramdata[1] = wramdata[0] + ((data & 0x07) ? (data & 0x07) : 1) * 0x1000; + + if (oamDmaArea1Lower == 0xC0) + setOamDmaSrc(); + else + wmem[0xD] = rmem[0xD] = wramdata[1] - 0xD000; + + ioamhram[0x170] = data | 0xF8; + } + + return; + case 0x72: + case 0x73: + case 0x74: + if (isCgb()) + break; + + return; + case 0x75: + if (isCgb()) + ioamhram[0x175] = data | 0x8F; + + return; + case 0xFF: + ioamhram[0x1FF] = data; + rescheduleIrq(cycleCounter); + return; + default: +// if (P < 0xFF80) + return; + } + + ioamhram[P - 0xFE00] = data; +} + +void Memory::mbc_write(const unsigned P, const unsigned data) { +// printf("mem[0x%X] = 0x%X\n", P, data); + + switch (P >> 12 & 0x7) { + case 0x0: + case 0x1: //Most MBCs write 0x?A to addresses lower than 0x2000 to enable ram. + if (romtype == mbc2 && (P & 0x0100)) break; + + enable_ram = (data & 0x0F) == 0xA; + + if (rtcRom) + rtc.setEnabled(enable_ram); + + setRambank(); + break; + //MBC1 writes ???n nnnn to address area 0x2000-0x3FFF, ???n nnnn makes up the lower digits to determine which rombank to load. + //MBC3 writes ?nnn nnnn to address area 0x2000-0x3FFF, ?nnn nnnn makes up the lower digits to determine which rombank to load. + //MBC5 writes nnnn nnnn to address area 0x2000-0x2FFF, nnnn nnnn makes up the lower digits to determine which rombank to load. + //MBC5 writes bit8 of the number that determines which rombank to load to address 0x3000-0x3FFF. + case 0x2: + switch (romtype) { + case plain: + return; + case mbc5: + rombank = (rombank & 0x100) | data; + rombank = rombank & (rombanks - 1); + setRombank(); + return; + default: + break; //Only supposed to break one level. + } + case 0x3: + switch (romtype) { + case mbc1: + rombank = rambank_mode ? data & 0x1F : ((rombank & 0x60) | (data & 0x1F)); + break; + case mbc2: + if (P & 0x0100) { + rombank = data & 0x0F; + break; + } + + return; + case mbc3: + rombank = data & 0x7F; + break; + case mbc5: + rombank = (data & 0x1) << 8 | (rombank & 0xFF); + break; + default: + return; + } + + rombank = rombank & (rombanks - 1); + setRombank(); + break; + //MBC1 writes ???? ??nn to area 0x4000-0x5FFF either to determine rambank to load, or upper 2 bits of the rombank number to load, depending on rom-mode. + //MBC3 writes ???? ??nn to area 0x4000-0x5FFF to determine rambank to load + //MBC5 writes ???? nnnn to area 0x4000-0x5FFF to determine rambank to load + case 0x4: + case 0x5: + switch (romtype) { + case mbc1: + if (rambank_mode) { + rambank = data & 0x03; + break; + } + + rombank = (data & 0x03) << 5 | (rombank & 0x1F); + rombank = rombank & (rombanks - 1); + setRombank(); + return; + case mbc3: + if (rtcRom) + rtc.swapActive(data); + + rambank = data & 0x03; + break; + case mbc5: + rambank = data & 0x0F; + break; + default: + return; + } + + rambank &= rambanks - 1; + setRambank(); + break; + //MBC1: If ???? ???1 is written to area 0x6000-0x7FFFF rom will be set to rambank mode. + case 0x6: + case 0x7: + switch (romtype) { + case mbc1: + rambank_mode = data & 0x01; + break; + case mbc3: + rtc.latch(data); + break; + default: + break; + } + + break; +// default: break; + } +} + +void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != COUNTER_DISABLED) { + updateOamDma(cycleCounter); + + if ((P >> 8) - oamDmaArea1Lower < oamDmaArea1Width || P >> 8 < oamDmaArea2Upper) { + ioamhram[oamDmaPos] = data; + return; + } + } + + if (P < 0xFE00) { + if (P < 0xA000) { + if (P < 0x8000) { + mbc_write(P, data); + } else if (display.vramAccessible(cycleCounter)) { + display.vramChange(cycleCounter); + vrambank[P & 0x1FFF] = data; + } + } else if (P < 0xC000) { + if (wsrambankptr) + wsrambankptr[P] = data; + else + rtc.write(data); + } else + wramdata[P >> 12 & 1][P & 0xFFF] = data; + } else if (((P + 1) & 0xFFFF) < 0xFF81) { + if (P < 0xFF00) { + if (display.oamAccessible(cycleCounter) && oamDmaPos >= 0xA0) { + display.oamChange(cycleCounter); + rescheduleIrq(cycleCounter); + rescheduleHdmaReschedule(); + ioamhram[P - 0xFE00] = data; + } + } else + nontrivial_ff_write(P, data, cycleCounter); + } else + ioamhram[P - 0xFE00] = data; +} + +static const std::string stripExtension(const std::string &str) { + const std::string::size_type lastDot = str.find_last_of('.'); + const std::string::size_type lastSlash = str.find_last_of('/'); + + if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot)) + return str.substr(0, lastDot); + + return str; +} + +static const std::string stripDir(const std::string &str) { + const std::string::size_type lastSlash = str.find_last_of('/'); + + if (lastSlash != std::string::npos) + return str.substr(lastSlash + 1); + + return str; +} + +const std::string Memory::saveBasePath() const { + return saveDir.empty() ? defaultSaveBasePath : saveDir + stripDir(defaultSaveBasePath); +} + +void Memory::set_savedir(const char *dir) { + saveDir = dir ? dir : ""; + + if (!saveDir.empty() && saveDir[saveDir.length() - 1] != '/') { + saveDir += '/'; + } +} + +static void enforce8bit(unsigned char *data, unsigned long sz) { + if (static_cast(0x100)) + while (sz--) + *data++ &= 0xFF; +} + +static unsigned pow2ceil(unsigned n) { + --n; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + ++n; + + return n; +} + +bool Memory::loadROM(const bool forceDmg) { + defaultSaveBasePath = ""; + + { + unsigned char *header = (unsigned char*)supergameboy.romdata; + + cgb = header[0x0143] >> 7 & 1; + + if (cgb & forceDmg) { + cgb = false; + defaultSaveBasePath += "_dmg"; + } + + switch (header[0x0147]) { + case 0x00: std::printf("Plain ROM loaded.\n"); + romtype = plain; + break; + case 0x01: std::printf("MBC1 ROM loaded.\n"); + romtype = mbc1; + break; + case 0x02: std::printf("MBC1 ROM+RAM loaded.\n"); + romtype = mbc1; + break; + case 0x03: std::printf("MBC1 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc1; + battery = 1; + break; + case 0x05: std::printf("MBC2 ROM loaded.\n"); + romtype = mbc2; + break; + case 0x06: std::printf("MBC2 ROM+BATTERY loaded.\n"); + romtype = mbc2; + battery = 1; + break; + case 0x08: std::printf("Plain ROM with additional RAM loaded.\n"); + break; + case 0x09: std::printf("Plain ROM with additional RAM and Battery loaded.\n"); + battery = 1; + break; + case 0x0B: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0C: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0D: /*cout << "MM01 ROM not supported.\n";*/ + return 1; + break; + case 0x0F: std::printf("MBC3 ROM+TIMER+BATTERY loaded.\n"); + romtype = mbc3; + battery = true; + rtcRom = true; + break; + case 0x10: std::printf("MBC3 ROM+TIMER+RAM+BATTERY loaded.\n"); + romtype = mbc3; + battery = true; + rtcRom = true; + break; + case 0x11: std::printf("MBC3 ROM loaded.\n"); + romtype = mbc3; + break; + case 0x12: std::printf("MBC3 ROM+RAM loaded.\n"); + romtype = mbc3; + break; + case 0x13: std::printf("MBC3 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc3; + battery = 1; + break; + case 0x15: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x16: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x17: /*cout << "MBC4 ROM not supported.\n";*/ + return 1; + break; + case 0x19: std::printf("MBC5 ROM loaded.\n"); + romtype = mbc5; + break; + case 0x1A: std::printf("MBC5 ROM+RAM loaded.\n"); + romtype = mbc5; + break; + case 0x1B: std::printf("MBC5 ROM+RAM+BATTERY loaded.\n"); + romtype = mbc5; + battery = 1; + break; + case 0x1C: std::printf("MBC5+RUMLE ROM not supported.\n"); + romtype = mbc5; + break; + case 0x1D: std::printf("MBC5+RUMLE+RAM ROM not suported.\n"); + romtype = mbc5; + break; + case 0x1E: std::printf("MBC5+RUMLE+RAM+BATTERY ROM not supported.\n"); + romtype = mbc5; + battery = 1; + break; + case 0xFC: /*cout << "Pocket Camera ROM not supported.\n";*/ + return 1; + break; + case 0xFD: /*cout << "Bandai TAMA5 ROM not supported.\n";*/ + return 1; + break; + case 0xFE: /*cout << "HuC3 ROM not supported.\n";*/ + return 1; + break; + case 0xFF: /*cout << "HuC1 ROM not supported.\n";*/ + return 1; + break; + default: /*cout << "Wrong data-format, corrupt or unsupported ROM loaded.\n";*/ + return 1; + } + + /*switch (header[0x0148]) { + case 0x00: + rombanks = 2; + break; + case 0x01: + rombanks = 4; + break; + case 0x02: + rombanks = 8; + break; + case 0x03: + rombanks = 16; + break; + case 0x04: + rombanks = 32; + break; + case 0x05: + rombanks = 64; + break; + case 0x06: + rombanks = 128; + break; + case 0x07: + rombanks = 256; + break; + case 0x08: + rombanks = 512; + break; + case 0x52: + rombanks = 72; + break; + case 0x53: + rombanks = 80; + break; + case 0x54: + rombanks = 96; + break; + default: + return 1; + } + + printf("rombanks: %u\n", rombanks);*/ + + switch (header[0x0149]) { + case 0x00: /*cout << "No RAM\n";*/ rambanks = romtype == mbc2; break; + case 0x01: /*cout << "2kB RAM\n";*/ /*rambankrom=1; break;*/ + case 0x02: /*cout << "8kB RAM\n";*/ + rambanks = 1; + break; + case 0x03: /*cout << "32kB RAM\n";*/ + rambanks = 4; + break; + case 0x04: /*cout << "128kB RAM\n";*/ + rambanks = 16; + break; + case 0x05: /*cout << "undocumented kB RAM\n";*/ + rambanks = 16; + break; + default: /*cout << "Wrong data-format, corrupt or unsupported ROM loaded.\n";*/ + rambanks = 16; + break; + } + } + + std::printf("rambanks: %u\n", rambanks); + + rombanks = pow2ceil(supergameboy.romsize / 0x4000); + std::printf("rombanks: %u\n", supergameboy.romsize / 0x4000); + + delete []memchunk; + memchunk = new unsigned char[0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + (isCgb() ? 0x8000 : 0x2000) + 0x4000]; + + romdata[0] = memchunk + 0x4000; + rambankdata = romdata[0] + rombanks * 0x4000ul; + wramdata[0] = rambankdata + rambanks * 0x2000; + rdisabled_ram = wramdata[0] + (isCgb() ? 0x8000 : 0x2000); + wdisabled_ram = rdisabled_ram + 0x2000; + + wramdata[1] = wramdata[0] + 0x1000; + std::memset(rdisabled_ram, 0xFF, 0x2000); + + memcpy((char*)romdata[0], supergameboy.romdata, (supergameboy.romsize / 0x4000) * 0x4000ul); + // In case rombanks isn't a power of 2, allocate a disabled area for invalid rombank addresses. This is only based on speculation. + std::memset(romdata[0] + (supergameboy.romsize / 0x4000) * 0x4000ul, 0xFF, (rombanks - supergameboy.romsize / 0x4000) * 0x4000ul); + enforce8bit(romdata[0], rombanks * 0x4000ul); + + sound.init(isCgb()); + display.reset(ioamhram, isCgb()); + + return 0; +} + +void Memory::loadSavedata() { + if (battery) { + if (supergameboy.ramdata) { + memcpy((char*)rambankdata, supergameboy.ramdata, std::min(supergameboy.ramsize, (unsigned int)(rambanks * 0x2000ul))); + enforce8bit(rambankdata, rambanks * 0x2000ul); + } + } + + if (rtcRom) { + if (supergameboy.rtcdata && supergameboy.rtcsize >= 4) { + unsigned long basetime = 0; + + basetime = basetime << 8 | (supergameboy.rtcdata[0]); + basetime = basetime << 8 | (supergameboy.rtcdata[1]); + basetime = basetime << 8 | (supergameboy.rtcdata[2]); + basetime = basetime << 8 | (supergameboy.rtcdata[3]); + + rtc.setBaseTime(basetime); + } + } +} + +void Memory::saveSavedata() { + if (battery) { + if (supergameboy.ramdata) { + memcpy(supergameboy.ramdata, (char*)rambankdata, std::min(supergameboy.ramsize, (unsigned int)(rambanks * 0x2000ul))); + } + } + + if (rtcRom) { + if (supergameboy.rtcdata && supergameboy.rtcsize >= 4) { + const unsigned long basetime = rtc.getBaseTime(); + + supergameboy.rtcdata[0] = basetime >> 24; + supergameboy.rtcdata[1] = basetime >> 16; + supergameboy.rtcdata[2] = basetime >> 8; + supergameboy.rtcdata[3] = basetime >> 0; + } + } +} + +unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.fillBuffer(); +} + +void Memory::setVideoBlitter(Gambatte::VideoBlitter *const vb) { + display.setVideoBlitter(vb); +} + +void Memory::videoBufferChange() { + display.videoBufferChange(); +} + +void Memory::setVideoFilter(const unsigned int n) { + display.setVideoFilter(n); +} + +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) { + display.setDmgPaletteColor(palNum, colorNum, rgb32); +} + +Memory::~Memory() { + saveSavedata(); + + delete []memchunk; +} diff --git a/supergameboy/libgambatte/src/memory.h b/supergameboy/libgambatte/src/memory.h new file mode 100644 index 00000000..eb9f3197 --- /dev/null +++ b/supergameboy/libgambatte/src/memory.h @@ -0,0 +1,238 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MEMORY_H +#define MEMORY_H + +class SaveState; + +#include "int.h" +#include "video.h" +#include "sound.h" + +#include "interrupter.h" +#include "rtc.h" +#include + +namespace Gambatte { +class InputStateGetter; +class FilterInfo; +} + +class Memory { +public: + enum { COUNTER_DISABLED = 0xFFFFFFFFu }; + +private: + enum cartridgetype { plain, mbc1, mbc2, mbc3, mbc5 }; + enum events { HDMA_RESCHEDULE, DMA, INTERRUPTS, BLIT, UNHALT, OAM, END }; + enum irqEvents { /*MODE0, MODE1, MODE2, LYC,*/ TIMA, /*M0RESC,*/ SERIAL }; + + unsigned char ioamhram[0x200]; + unsigned char vram[0x2000 * 2]; + unsigned char *rmem[0x10]; + unsigned char *wmem[0x10]; + + unsigned char *memchunk; + unsigned char *romdata[2]; + unsigned char *wramdata[2]; + unsigned char *rambankdata; + unsigned char *rdisabled_ram; + unsigned char *wdisabled_ram; + unsigned char *oamDmaSrc; + unsigned char *vrambank; + unsigned char *rsrambankptr; + unsigned char *wsrambankptr; + + Gambatte::InputStateGetter *getInput; + + unsigned long div_lastUpdate; + unsigned long tima_lastUpdate; + unsigned long next_timatime; + unsigned long next_blittime; + unsigned long nextIntTime; + unsigned long minIntTime; + unsigned long next_dmatime; + unsigned long next_hdmaReschedule; + unsigned long next_unhalttime; + unsigned long next_endtime; + unsigned long next_irqEventTime; + unsigned long tmatime; + unsigned long next_serialtime; + unsigned long next_eventtime; + unsigned long lastOamDmaUpdate; + unsigned long nextOamEventTime; + + LCD display; + PSG sound; + Interrupter interrupter; + Rtc rtc; + + events next_event; + irqEvents next_irqEvent; + cartridgetype romtype; + + std::string defaultSaveBasePath; + std::string saveDir; + + unsigned short rombanks; + unsigned short rombank; + unsigned short dmaSource; + unsigned short dmaDestination; + + unsigned char rambank; + unsigned char rambanks; + unsigned char oamDmaArea1Lower; + unsigned char oamDmaArea1Width; + unsigned char oamDmaArea2Upper; + unsigned char oamDmaPos; + + bool cgb; + bool doubleSpeed; + bool IME; + bool enable_ram; + bool rambank_mode; + bool battery, rtcRom; + bool hdma_transfer; + bool active; + + void updateInput(); + + void setRombank(); + void setRambank(); + void setBanks(); + void oamDmaInitSetup(); + void setOamDmaArea(); + void updateOamDma(unsigned long cycleCounter); + void startOamDma(unsigned long cycleCounter); + void endOamDma(unsigned long cycleCounter); + void setOamDmaSrc(); + + unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter); + unsigned nontrivial_read(unsigned P, unsigned long cycleCounter); + void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); + void mbc_write(unsigned P, unsigned data); + void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter); + + void set_event(); + void set_irqEvent(); + void update_irqEvents(unsigned long cc); + void update_tima(unsigned long cycleCounter); + + void rescheduleIrq(unsigned long cycleCounter); + void rescheduleHdmaReschedule(); + + bool isDoubleSpeed() const { return doubleSpeed; } + +public: + Memory(const Interrupter &interrupter); + ~Memory(); + + void updateVideo(unsigned cycleCounter) { display.update(cycleCounter); } + unsigned lyCounter(unsigned cycleCounter) { return display.getLyReg(cycleCounter); } + + void setStatePtrs(SaveState &state); + unsigned long saveState(SaveState &state, unsigned long cc); + void loadState(const SaveState &state, unsigned long oldCc); + void loadSavedata(); + void saveSavedata(); + const std::string saveBasePath() const; + + void setOsdElement(std::auto_ptr osdElement) { + display.setOsdElement(osdElement); + } + + void speedChange(unsigned long cycleCounter); + bool isCgb() const { return cgb; } + bool getIME() const { return IME; } + unsigned long getNextEventTime() const { return next_eventtime; } + + bool isActive() const { return active; } + + void ei(unsigned long cycleCounter); + + void di() { + IME = 0; + nextIntTime = COUNTER_DISABLED; + + if (next_event == INTERRUPTS) + set_event(); + +// next_eitime=0; +// if(next_event==EI) set_event(); + } + + unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { + return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; + } + + unsigned read(const unsigned P, const unsigned long cycleCounter) { + return rmem[P >> 12] ? rmem[P >> 12][P] : nontrivial_read(P, cycleCounter); + } + + void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (wmem[P >> 12]) + wmem[P >> 12][P] = data; + else + nontrivial_write(P, data, cycleCounter); + } + + void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (((P + 1) & 0xFF) < 0x81) + nontrivial_ff_write(P, data, cycleCounter); + else + ioamhram[P - 0xFE00] = data; + } + + unsigned long event(unsigned long cycleCounter); + unsigned long resetCounters(unsigned long cycleCounter); + + bool loadROM(bool forceDmg); + void set_savedir(const char *dir); + + void setInputStateGetter(Gambatte::InputStateGetter *getInput) { + this->getInput = getInput; + } + + void schedule_unhalt(); + void incEndtime(unsigned long inc); + void setEndtime(unsigned long cc, unsigned long inc); + + void setSoundBuffer(Gambatte::uint_least32_t *const buf) { sound.setBuffer(buf); } + unsigned fillSoundBuffer(unsigned long cc); + void setVideoBlitter(Gambatte::VideoBlitter * vb); + void setVideoFilter(unsigned int n); + + void videoBufferChange(); + + unsigned videoWidth() const { + return display.videoWidth(); + } + + unsigned videoHeight() const { + return display.videoHeight(); + } + + std::vector filterInfo() const { + return display.filterInfo(); + } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); +}; + +#endif diff --git a/supergameboy/libgambatte/src/osd_element.h b/supergameboy/libgambatte/src/osd_element.h new file mode 100644 index 00000000..2517d34f --- /dev/null +++ b/supergameboy/libgambatte/src/osd_element.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef OSD_ELEMENT_H +#define OSD_ELEMENT_H + +#include "int.h" + +class OsdElement { +public: + enum Opacity { SEVEN_EIGHTHS, THREE_FOURTHS }; + +private: + Opacity opacity_; + unsigned x_; + unsigned y_; + unsigned w_; + unsigned h_; + +protected: + OsdElement(unsigned x = 0, unsigned y = 0, unsigned w = 0, unsigned h = 0, Opacity opacity = SEVEN_EIGHTHS) { + setPos(x, y); + setSize(w, h); + setOpacity(opacity); + } + + void setPos(unsigned x, unsigned y) { + x_ = x; + y_ = y; + } + + void setSize(unsigned w, unsigned h) { + w_ = w; + h_ = h; + } + + void setOpacity(Opacity opacity) { opacity_ = opacity; } + +public: + virtual ~OsdElement() {} + unsigned x() const { return x_; } + unsigned y() const { return y_; } + unsigned w() const { return w_; } + unsigned h() const { return h_; } + Opacity opacity() const { return opacity_; } + + virtual const Gambatte::uint_least32_t* update() = 0; +}; + +#endif diff --git a/supergameboy/libgambatte/src/rtc.cpp b/supergameboy/libgambatte/src/rtc.cpp new file mode 100644 index 00000000..75164919 --- /dev/null +++ b/supergameboy/libgambatte/src/rtc.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "rtc.h" +#include "savestate.h" + +using namespace std; + +Rtc::Rtc() : +activeData(NULL), +activeSet(NULL), +baseTime(0), +haltTime(0), +index(5), +dataDh(0), +dataDl(0), +dataH(0), +dataM(0), +dataS(0), +enabled(false), +lastLatchData(false) { +} + +void Rtc::doLatch() { + time_t tmp = ((dataDh & 0x40) ? haltTime : time(NULL)) - baseTime; + + while (tmp > 0x1FF * 86400) { + baseTime += 0x1FF * 86400; + tmp -= 0x1FF * 86400; + dataDh |= 0x80; + } + + dataDl = (tmp / 86400) & 0xFF; + dataDh &= 0xFE; + dataDh |= ((tmp / 86400) & 0x100) >> 8; + tmp %= 86400; + + dataH = tmp / 3600; + tmp %= 3600; + + dataM = tmp / 60; + tmp %= 60; + + dataS = tmp; +} + +void Rtc::doSwapActive() { + if (!enabled || index > 4) { + activeData = NULL; + activeSet = NULL; + } else switch (index) { + case 0x00: + activeData = &dataS; + activeSet = &Rtc::setS; + break; + case 0x01: + activeData = &dataM; + activeSet = &Rtc::setM; + break; + case 0x02: + activeData = &dataH; + activeSet = &Rtc::setH; + break; + case 0x03: + activeData = &dataDl; + activeSet = &Rtc::setDl; + break; + case 0x04: + activeData = &dataDh; + activeSet = &Rtc::setDh; + break; + } +} + +void Rtc::saveState(SaveState &state) const { + state.rtc.baseTime = baseTime; + state.rtc.haltTime = haltTime; + state.rtc.index = index; + state.rtc.dataDh = dataDh; + state.rtc.dataDl = dataDl; + state.rtc.dataH = dataH; + state.rtc.dataM = dataM; + state.rtc.dataS = dataS; + state.rtc.lastLatchData = lastLatchData; +} + +void Rtc::loadState(const SaveState &state, const bool enabled) { + this->enabled = enabled; + + baseTime = state.rtc.baseTime; + haltTime = state.rtc.haltTime; + index = state.rtc.index; + dataDh = state.rtc.dataDh; + dataDl = state.rtc.dataDl; + dataH = state.rtc.dataH; + dataM = state.rtc.dataM; + dataS = state.rtc.dataS; + lastLatchData = state.rtc.lastLatchData; + + doSwapActive(); +} + +void Rtc::setDh(const unsigned new_dh) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; + baseTime += old_highdays * 86400; + baseTime -= ((new_dh & 0x1) << 8) * 86400; + + if ((dataDh ^ new_dh) & 0x40) { + if (new_dh & 0x40) + haltTime = time(NULL); + else + baseTime += time(NULL) - haltTime; + } +} + +void Rtc::setDl(const unsigned new_lowdays) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; + baseTime += old_lowdays * 86400; + baseTime -= new_lowdays * 86400; +} + +void Rtc::setH(const unsigned new_hours) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_hours = ((unixtime - baseTime) / 3600) % 24; + baseTime += old_hours * 3600; + baseTime -= new_hours * 3600; +} + +void Rtc::setM(const unsigned new_minutes) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + const time_t old_minutes = ((unixtime - baseTime) / 60) % 60; + baseTime += old_minutes * 60; + baseTime -= new_minutes * 60; +} + +void Rtc::setS(const unsigned new_seconds) { + const time_t unixtime = (dataDh & 0x40) ? haltTime : time(NULL); + baseTime += (unixtime - baseTime) % 60; + baseTime -= new_seconds; +} diff --git a/supergameboy/libgambatte/src/rtc.h b/supergameboy/libgambatte/src/rtc.h new file mode 100644 index 00000000..40905c18 --- /dev/null +++ b/supergameboy/libgambatte/src/rtc.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef RTC_H +#define RTC_H + +class SaveState; + +#include + +class Rtc { +private: + unsigned char *activeData; + void (Rtc::*activeSet)(unsigned); + std::time_t baseTime; + std::time_t haltTime; + unsigned char index; + unsigned char dataDh; + unsigned char dataDl; + unsigned char dataH; + unsigned char dataM; + unsigned char dataS; + bool enabled; + bool lastLatchData; + + void doLatch(); + void doSwapActive(); + void setDh(unsigned new_dh); + void setDl(unsigned new_lowdays); + void setH(unsigned new_hours); + void setM(unsigned new_minutes); + void setS(unsigned new_seconds); + +public: + Rtc(); + + const unsigned char* getActive() const { + return activeData; + } + + std::time_t getBaseTime() const { + return baseTime; + } + + void setBaseTime(const std::time_t baseTime) { + this->baseTime = baseTime; +// doLatch(); + } + + void latch(const unsigned data) { + if (!lastLatchData && data == 1) + doLatch(); + + lastLatchData = data; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state, bool enabled); + + void setEnabled(const bool enabled) { + this->enabled = enabled; + + doSwapActive(); + } + + void swapActive(unsigned index) { + index &= 0xF; + index -= 8; + + this->index = index; + + doSwapActive(); + } + + void write(const unsigned data) { +// if (activeSet) + (this->*activeSet)(data); + *activeData = data; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/savestate.h b/supergameboy/libgambatte/src/savestate.h new file mode 100644 index 00000000..c4b245fd --- /dev/null +++ b/supergameboy/libgambatte/src/savestate.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SAVESTATE_H +#define SAVESTATE_H + +#include "int.h" + +struct SaveState { + template + class Ptr { + T *ptr; + unsigned long sz; + + public: + Ptr() : ptr(0), sz(0) {} + const T* get() const { return ptr; } + unsigned long getSz() const { return sz; } + void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } + + friend class SaverList; + friend void setInitState(SaveState &, bool); + }; + + struct CPU { + unsigned long cycleCounter; + unsigned short PC; + unsigned short SP; + unsigned char A; + unsigned char B; + unsigned char C; + unsigned char D; + unsigned char E; + unsigned char F; + unsigned char H; + unsigned char L; + bool skip; + bool halted; + } cpu; + + struct Mem { + Ptr vram; + Ptr sram; + Ptr wram; + Ptr ioamhram; + unsigned long div_lastUpdate; + unsigned long tima_lastUpdate; + unsigned long tmatime; + unsigned long next_serialtime; + unsigned long lastOamDmaUpdate; + unsigned long minIntTime; + unsigned short rombank; + unsigned short dmaSource; + unsigned short dmaDestination; + unsigned char rambank; + unsigned char oamDmaPos; + bool IME; + bool enable_ram; + bool rambank_mode; + bool hdma_transfer; + } mem; + + struct PPU { + Ptr drawBuffer; + Ptr bgpData; + Ptr objpData; + //SpriteMapper::OamReader + Ptr oamReaderBuf; + Ptr oamReaderSzbuf; + + unsigned long videoCycles; + unsigned long enableDisplayM0Time; + unsigned char winYPos; + unsigned char drawStartCycle; + unsigned char scReadOffset; + unsigned char lcdc; + //ScReader + unsigned char scx[2]; + unsigned char scy[2]; + //ScxReader + unsigned char scxAnd7; + //WeMasterChecker + bool weMaster; + //WxReader + unsigned char wx; + //Wy + unsigned char wy; + bool lycIrqSkip; + } ppu; + + struct SPU { + struct Duty { + unsigned long nextPosUpdate; + unsigned char nr3; + unsigned char pos; + }; + + struct Env { + unsigned long counter; + unsigned char volume; + }; + + struct LCounter { + unsigned long counter; + unsigned short lengthCounter; + }; + + struct { + struct { + unsigned long counter; + unsigned short shadow; + unsigned char nr0; + bool negging; + } sweep; + Duty duty; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch1; + + struct { + Duty duty; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch2; + + struct { + Ptr waveRam; + LCounter lcounter; + unsigned long waveCounter; + unsigned long lastReadTime; + unsigned char nr3; + unsigned char nr4; + unsigned char wavePos; + unsigned char sampleBuf; + bool master; + } ch3; + + struct { + struct { + unsigned long counter; + unsigned short reg; + } lfsr; + Env env; + LCounter lcounter; + unsigned char nr4; + bool master; + } ch4; + + unsigned long cycleCounter; + } spu; + + struct RTC { + unsigned long baseTime; + unsigned long haltTime; + unsigned char index; + unsigned char dataDh; + unsigned char dataDl; + unsigned char dataH; + unsigned char dataM; + unsigned char dataS; + bool lastLatchData; + } rtc; +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound.cpp b/supergameboy/libgambatte/src/sound.cpp new file mode 100644 index 00000000..3ff8063f --- /dev/null +++ b/supergameboy/libgambatte/src/sound.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sound.h" + +#include "savestate.h" +#include +#include + +/* + Frame Sequencer + + Step Length Ctr Vol Env Sweep + - - - - - - - - - - - - - - - - - - - - + 0 Clock - Clock +S 1 - Clock - + 2 Clock - - + 3 - - - + 4 Clock - Clock + 5 - - - + 6 Clock - - + 7 - - - + - - - - - - - - - - - - - - - - - - - - + Rate 256 Hz 64 Hz 128 Hz + +S) start step on sound power on. +*/ + +// static const unsigned bufferSize = 35112 + 16 + 2048; //FIXME: DMA can prevent process from returning for up to 4096 cycles. + +PSG::PSG() : +buffer(NULL), +lastUpdate(0), +soVol(0), +rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later +bufferPos(0), +enabled(false) +{} + +void PSG::init(const bool cgb) { + ch1.init(cgb); + ch2.init(cgb); + ch3.init(cgb); + ch4.init(cgb); +} + +void PSG::reset() { + ch1.reset(); + ch2.reset(); + ch3.reset(); + ch4.reset(); +} + +void PSG::setStatePtrs(SaveState &state) { + ch3.setStatePtrs(state); +} + +void PSG::saveState(SaveState &state) { + ch1.saveState(state); + ch2.saveState(state); + ch3.saveState(state); + ch4.saveState(state); +} + +void PSG::loadState(const SaveState &state) { + ch1.loadState(state); + ch2.loadState(state); + ch3.loadState(state); + ch4.loadState(state); + + lastUpdate = state.cpu.cycleCounter; + set_so_volume(state.mem.ioamhram.get()[0x124]); + map_so(state.mem.ioamhram.get()[0x125]); + enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1; +} + +void PSG::accumulate_channels(const unsigned long cycles) { + Gambatte::uint_least32_t *const buf = buffer + bufferPos; + + std::memset(buf, 0, cycles * sizeof(Gambatte::uint_least32_t)); + ch1.update(buf, soVol, cycles); + ch2.update(buf, soVol, cycles); + ch3.update(buf, soVol, cycles); + ch4.update(buf, soVol, cycles); +} + +void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) { + const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed); + lastUpdate += cycles << (1 + doubleSpeed); + + if (cycles) + accumulate_channels(cycles); + + bufferPos += cycles; +} + +void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) { + generate_samples(oldCc, doubleSpeed); + lastUpdate = newCc - (oldCc - lastUpdate); +} + +unsigned PSG::fillBuffer() { + Gambatte::uint_least32_t sum = rsum; + Gambatte::uint_least32_t *b = buffer; + unsigned n = bufferPos; + + while (n--) { + sum += *b; + *b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word + } + + rsum = sum; + + return bufferPos; +} + +#ifdef WORDS_BIGENDIAN +static const unsigned long so1Mul = 0x00000001; +static const unsigned long so2Mul = 0x00010000; +#else +static const unsigned long so1Mul = 0x00010000; +static const unsigned long so2Mul = 0x00000001; +#endif + +void PSG::set_so_volume(const unsigned nr50) { + soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64; +} + +void PSG::map_so(const unsigned nr51) { + const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul; + + ch1.setSo((tmp & 0x00010001) * 0xFFFF); + ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF); + ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF); + ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF); +} + +unsigned PSG::getStatus() const { + return ch1.isActive() | (ch2.isActive() << 1) | (ch3.isActive() << 2) | (ch4.isActive() << 3); +} diff --git a/supergameboy/libgambatte/src/sound.h b/supergameboy/libgambatte/src/sound.h new file mode 100644 index 00000000..06916846 --- /dev/null +++ b/supergameboy/libgambatte/src/sound.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_H +#define SOUND_H + +class SaveState; + +#include "int.h" + +#include "sound/channel1.h" +#include "sound/channel2.h" +#include "sound/channel3.h" +#include "sound/channel4.h" + +class PSG { + Channel1 ch1; + Channel2 ch2; + Channel3 ch3; + Channel4 ch4; + + Gambatte::uint_least32_t *buffer; + + unsigned long lastUpdate; + unsigned long soVol; + + Gambatte::uint_least32_t rsum; + + unsigned bufferPos; + + bool enabled; + + void accumulate_channels(unsigned long cycles); + +public: + PSG(); + void init(bool cgb); + void reset(); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state); + void loadState(const SaveState &state); + + void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed); + void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed); + unsigned fillBuffer(); + void setBuffer(Gambatte::uint_least32_t *const buf) { buffer = buf; bufferPos = 0; } + + bool isEnabled() const { return enabled; } + void setEnabled(bool value) { enabled = value; } + + void set_nr10(unsigned data) { ch1.setNr0(data); } + void set_nr11(unsigned data) { ch1.setNr1(data); } + void set_nr12(unsigned data) { ch1.setNr2(data); } + void set_nr13(unsigned data) { ch1.setNr3(data); } + void set_nr14(unsigned data) { ch1.setNr4(data); } + + void set_nr21(unsigned data) { ch2.setNr1(data); } + void set_nr22(unsigned data) { ch2.setNr2(data); } + void set_nr23(unsigned data) { ch2.setNr3(data); } + void set_nr24(unsigned data) { ch2.setNr4(data); } + + void set_nr30(unsigned data) { ch3.setNr0(data); } + void set_nr31(unsigned data) { ch3.setNr1(data); } + void set_nr32(unsigned data) { ch3.setNr2(data); } + void set_nr33(unsigned data) { ch3.setNr3(data); } + void set_nr34(unsigned data) { ch3.setNr4(data); } + unsigned waveRamRead(unsigned index) const { return ch3.waveRamRead(index); } + void waveRamWrite(unsigned index, unsigned data) { ch3.waveRamWrite(index, data); } + + void set_nr41(unsigned data) { ch4.setNr1(data); } + void set_nr42(unsigned data) { ch4.setNr2(data); } + void set_nr43(unsigned data) { ch4.setNr3(data); } + void set_nr44(unsigned data) { ch4.setNr4(data); } + + void set_so_volume(unsigned nr50); + void map_so(unsigned nr51); + unsigned getStatus() const; +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/channel1.cpp b/supergameboy/libgambatte/src/sound/channel1.cpp new file mode 100644 index 00000000..5e112eb2 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel1.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel1.h" +#include "../savestate.h" +#include + +Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) : + disableMaster(disabler), + dutyUnit(dutyUnit), + shadow(0), + nr0(0), + negging(false) +{} + +unsigned Channel1::SweepUnit::calcFreq() { + unsigned freq = shadow >> (nr0 & 0x07); + + if (nr0 & 0x08) { + freq = shadow - freq; + negging = true; + } else + freq = shadow + freq; + + if (freq & 2048) + disableMaster(); + + return freq; +} + +void Channel1::SweepUnit::event() { + const unsigned long period = nr0 >> 4 & 0x07; + + if (period) { + const unsigned freq = calcFreq(); + + if (!(freq & 2048) && (nr0 & 0x07)) { + shadow = freq; + dutyUnit.setFreq(freq, counter); + calcFreq(); + } + + counter += period << 14; + } else + counter += 8ul << 14; +} + +void Channel1::SweepUnit::nr0Change(const unsigned newNr0) { + if (negging && !(newNr0 & 0x08)) + disableMaster(); + + nr0 = newNr0; +} + +void Channel1::SweepUnit::nr4Init(const unsigned long cc) { + negging = false; + shadow = dutyUnit.getFreq(); + + const unsigned period = nr0 >> 4 & 0x07; + const unsigned shift = nr0 & 0x07; + + if (period | shift) + counter = ((cc >> 14) + (period ? period : 8)) << 14; + else + counter = COUNTER_DISABLED; + + if (shift) + calcFreq(); +} + +void Channel1::SweepUnit::reset() { + counter = COUNTER_DISABLED; +} + +void Channel1::SweepUnit::saveState(SaveState &state) const { + state.spu.ch1.sweep.counter = counter; + state.spu.ch1.sweep.shadow = shadow; + state.spu.ch1.sweep.nr0 = nr0; + state.spu.ch1.sweep.negging = negging; +} + +void Channel1::SweepUnit::loadState(const SaveState &state) { + counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter); + shadow = state.spu.ch1.sweep.shadow; + nr0 = state.spu.ch1.sweep.nr0; + negging = state.spu.ch1.sweep.negging; +} + +Channel1::Channel1() : + staticOutputTest(*this, dutyUnit), + disableMaster(master, dutyUnit), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + sweepUnit(disableMaster, dutyUnit), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel1::setEvent() { +// nextEventUnit = &dutyUnit; +// if (sweepUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &sweepUnit; + if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel1::setNr0(const unsigned data) { + sweepUnit.nr0Change(data); + setEvent(); +} + +void Channel1::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + dutyUnit.nr1Change(data, cycleCounter); + + setEvent(); +} + +void Channel1::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel1::setNr3(const unsigned data) { + dutyUnit.nr3Change(data, cycleCounter); + setEvent(); +} + +void Channel1::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + dutyUnit.nr4Change(data, cycleCounter); + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + master = !envelopeUnit.nr4Init(cycleCounter); + sweepUnit.nr4Init(cycleCounter); + staticOutputTest(cycleCounter); + } + + setEvent(); +} + +void Channel1::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel1::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + dutyUnit.reset(); + envelopeUnit.reset(); + sweepUnit.reset(); + + setEvent(); +} + +void Channel1::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel1::saveState(SaveState &state) { + sweepUnit.saveState(state); + dutyUnit.saveState(state.spu.ch1.duty, cycleCounter); + envelopeUnit.saveState(state.spu.ch1.env); + lengthCounter.saveState(state.spu.ch1.lcounter); + + state.spu.cycleCounter = cycleCounter; + state.spu.ch1.nr4 = nr4; + state.spu.ch1.master = master; +} + +void Channel1::loadState(const SaveState &state) { + sweepUnit.loadState(state); + dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter); + envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch1.nr4; + master = state.spu.ch1.master; +} + +void Channel1::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + + while (dutyUnit.getCounter() <= nextMajorEvent) { + *buf = out - prevOut; + prevOut = out; + buf += dutyUnit.getCounter() - cycleCounter; + cycleCounter = dutyUnit.getCounter(); + + dutyUnit.event(); + out = dutyUnit.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf = out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + dutyUnit.resetCounters(cycleCounter); + lengthCounter.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + sweepUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/supergameboy/libgambatte/src/sound/channel1.h b/supergameboy/libgambatte/src/sound/channel1.h new file mode 100644 index 00000000..d790e0ec --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel1.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL1_H +#define SOUND_CHANNEL1_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel1 { + class SweepUnit : public SoundUnit { + MasterDisabler &disableMaster; + DutyUnit &dutyUnit; + unsigned short shadow; + unsigned char nr0; + bool negging; + + unsigned calcFreq(); + + public: + SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); + void event(); + void nr0Change(unsigned newNr0); + void nr4Init(unsigned long cycleCounter); + void reset(); + void saveState(SaveState &state) const; + void loadState(const SaveState &state); + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + SweepUnit sweepUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel1(); + void setNr0(unsigned data); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/channel2.cpp b/supergameboy/libgambatte/src/sound/channel2.cpp new file mode 100644 index 00000000..2db30658 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel2.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel2.h" +#include "../savestate.h" + +Channel2::Channel2() : + staticOutputTest(*this, dutyUnit), + disableMaster(master, dutyUnit), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel2::setEvent() { +// nextEventUnit = &dutyUnit; +// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel2::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + dutyUnit.nr1Change(data, cycleCounter); + + setEvent(); +} + +void Channel2::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel2::setNr3(const unsigned data) { + dutyUnit.nr3Change(data, cycleCounter); + setEvent(); +} + +void Channel2::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + master = !envelopeUnit.nr4Init(cycleCounter); + staticOutputTest(cycleCounter); + } + + dutyUnit.nr4Change(data, cycleCounter); + + setEvent(); +} + +void Channel2::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel2::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + dutyUnit.reset(); + envelopeUnit.reset(); + + setEvent(); +} + +void Channel2::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel2::saveState(SaveState &state) { + dutyUnit.saveState(state.spu.ch2.duty, cycleCounter); + envelopeUnit.saveState(state.spu.ch2.env); + lengthCounter.saveState(state.spu.ch2.lcounter); + + state.spu.ch2.nr4 = nr4; + state.spu.ch2.master = master; +} + +void Channel2::loadState(const SaveState &state) { + dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter); + envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch2.nr4; + master = state.spu.ch2.master; +} + +void Channel2::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + + while (dutyUnit.getCounter() <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += dutyUnit.getCounter() - cycleCounter; + cycleCounter = dutyUnit.getCounter(); + + dutyUnit.event(); + out = dutyUnit.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + dutyUnit.resetCounters(cycleCounter); + lengthCounter.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/supergameboy/libgambatte/src/sound/channel2.h b/supergameboy/libgambatte/src/sound/channel2.h new file mode 100644 index 00000000..24bc66a4 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel2.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL2_H +#define SOUND_CHANNEL2_H + +class SaveState; + +#include "int.h" + +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel2 { + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel2(); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + // void deactivate() { disableMaster(); setEvent(); } + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/channel3.cpp b/supergameboy/libgambatte/src/sound/channel3.cpp new file mode 100644 index 00000000..944271e3 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel3.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel3.h" +#include "../savestate.h" +#include +#include + +static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) { + return 0x800 - ((nr4 << 8 & 0x700) | nr3); +} + +Channel3::Channel3() : + disableMaster(master, waveCounter), + lengthCounter(disableMaster, 0xFF), + cycleCounter(0), + soMask(0), + prevOut(0), + waveCounter(SoundUnit::COUNTER_DISABLED), + lastReadTime(0), + nr0(0), + nr3(0), + nr4(0), + wavePos(0), + rShift(4), + sampleBuf(0), + master(false), + cgb(false) +{} + +void Channel3::setNr0(const unsigned data) { + nr0 = data & 0x80; + + if (!(data & 0x80)) + disableMaster(); +} + +void Channel3::setNr2(const unsigned data) { + rShift = (data >> 5 & 3U) - 1; + + if (rShift > 3) + rShift = 4; +} + +void Channel3::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data & 0x7F; + + if (data & nr0/* & 0x80*/) { + if (!cgb && waveCounter == cycleCounter + 1) { + const unsigned pos = ((wavePos + 1) & 0x1F) >> 1; + + if (pos < 4) + waveRam[0] = waveRam[pos]; + else + std::memcpy(waveRam, waveRam + (pos & ~3), 4); + } + + master = true; + wavePos = 0; + lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3; + } +} + +void Channel3::setSo(const unsigned long soMask) { + this->soMask = soMask; +} + +void Channel3::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + sampleBuf = 0; +} + +void Channel3::init(const bool cgb) { + this->cgb = cgb; + lengthCounter.init(cgb); +} + +void Channel3::setStatePtrs(SaveState &state) { + state.spu.ch3.waveRam.set(waveRam, sizeof waveRam); +} + +void Channel3::saveState(SaveState &state) const { + lengthCounter.saveState(state.spu.ch3.lcounter); + + state.spu.ch3.waveCounter = waveCounter; + state.spu.ch3.lastReadTime = lastReadTime; + state.spu.ch3.nr3 = nr3; + state.spu.ch3.nr4 = nr4; + state.spu.ch3.wavePos = wavePos; + state.spu.ch3.sampleBuf = sampleBuf; + state.spu.ch3.master = master; +} + +void Channel3::loadState(const SaveState &state) { + lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter); + lastReadTime = state.spu.ch3.lastReadTime; + nr3 = state.spu.ch3.nr3; + nr4 = state.spu.ch3.nr4; + wavePos = state.spu.ch3.wavePos & 0x1F; + sampleBuf = state.spu.ch3.sampleBuf; + master = state.spu.ch3.master; + + nr0 = state.mem.ioamhram.get()[0x11A] & 0x80; + setNr2(state.mem.ioamhram.get()[0x11C]); +} + +void Channel3::updateWaveCounter(const unsigned long cc) { + if (cc >= waveCounter) { + const unsigned period = toPeriod(nr3, nr4); + const unsigned long periods = (cc - waveCounter) / period; + + lastReadTime = waveCounter + periods * period; + waveCounter = lastReadTime + period; + + wavePos += periods + 1; + wavePos &= 0x1F; + + sampleBuf = waveRam[wavePos >> 1]; + } +} + +void Channel3::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0; + + if (outBase && rShift != 4) { + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles; + unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul); + + while (waveCounter <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += waveCounter - cycleCounter; + cycleCounter = waveCounter; + + lastReadTime = waveCounter; + waveCounter += toPeriod(nr3, nr4); + ++wavePos; + wavePos &= 0x1F; + sampleBuf = waveRam[wavePos >> 1]; + out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/); + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (lengthCounter.getCounter() == nextMajorEvent) { + lengthCounter.event(); + } else + break; + } + } else { + if (outBase) { + const unsigned long out = outBase * (0 - 15ul); + + *buf += out - prevOut; + prevOut = out; + } + + cycleCounter += cycles; + + while (lengthCounter.getCounter() <= cycleCounter) { + updateWaveCounter(lengthCounter.getCounter()); + lengthCounter.event(); + } + + updateWaveCounter(cycleCounter); + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + lengthCounter.resetCounters(cycleCounter); + + if (waveCounter != SoundUnit::COUNTER_DISABLED) + waveCounter -= SoundUnit::COUNTER_MAX; + + lastReadTime -= SoundUnit::COUNTER_MAX; + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/supergameboy/libgambatte/src/sound/channel3.h b/supergameboy/libgambatte/src/sound/channel3.h new file mode 100644 index 00000000..8ec8688d --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel3.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL3_H +#define SOUND_CHANNEL3_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" + +class Channel3 { + class Ch3MasterDisabler : public MasterDisabler { + unsigned long &waveCounter; + + public: + Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} + void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } + }; + + unsigned char waveRam[0x10]; + + Ch3MasterDisabler disableMaster; + LengthCounter lengthCounter; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + unsigned long waveCounter; + unsigned long lastReadTime; + + unsigned char nr0; + unsigned char nr3; + unsigned char nr4; + unsigned char wavePos; + unsigned char rShift; + unsigned char sampleBuf; + + bool master; + bool cgb; + + void updateWaveCounter(unsigned long cc); + +public: + Channel3(); + bool isActive() const { return master; } + void reset(); + void init(bool cgb); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state) const; + void loadState(const SaveState &state); + void setNr0(unsigned data); + void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); } + void setNr2(unsigned data); + void setNr3(unsigned data) { nr3 = data; } + void setNr4(unsigned data); + void setSo(unsigned long soMask); + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + unsigned waveRamRead(unsigned index) const { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return 0xFF; + + index = wavePos >> 1; + } + + return waveRam[index]; + } + + void waveRamWrite(unsigned index, unsigned data) { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return; + + index = wavePos >> 1; + } + + waveRam[index] = data; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/channel4.cpp b/supergameboy/libgambatte/src/sound/channel4.cpp new file mode 100644 index 00000000..c1efcf28 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel4.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "channel4.h" +#include "../savestate.h" +#include + +static unsigned long toPeriod(const unsigned nr3) { + unsigned s = (nr3 >> 4) + 3; + unsigned r = nr3 & 7; + + if (!r) { + r = 1; + --s; + } + + return r << s; +} + +Channel4::Lfsr::Lfsr() : +backupCounter(COUNTER_DISABLED), +reg(0xFF), +nr3(0), +master(false) +{} + +void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { + /*if (backupCounter <= cc) { + const unsigned long period = toPeriod(nr3); + backupCounter = cc - (cc - backupCounter) % period + period; + }*/ + + if (backupCounter <= cc) { + const unsigned long period = toPeriod(nr3); + unsigned long periods = (cc - backupCounter) / period + 1; + + backupCounter += periods * period; + + if (master && nr3 < 0xE0) { + if (nr3 & 8) { + while (periods > 6) { + const unsigned xored = (reg << 1 ^ reg) & 0x7E; + reg = (reg >> 6 & ~0x7E) | xored | xored << 8; + periods -= 6; + } + + const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; + reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; + } else { + while (periods > 15) { + reg = reg ^ reg >> 1; + periods -= 15; + } + + reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF); + } + } + } +} + +void Channel4::Lfsr::reviveCounter(const unsigned long cc) { + updateBackupCounter(cc); + counter = backupCounter; +} + +/*static const unsigned char nextStateDistance[0x40] = { + 6, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 4, + 4, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 5, + 5, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 4, + 4, 1, 1, 2, 2, 1, 1, 3, + 3, 1, 1, 2, 2, 1, 1, 6, +};*/ + +inline void Channel4::Lfsr::event() { + if (nr3 < 0xE0) { + const unsigned shifted = reg >> 1; + const unsigned xored = (reg ^ shifted) & 1; + + reg = shifted | xored << 14; + + if (nr3 & 8) + reg = (reg & ~0x40) | xored << 6; + } + + counter += toPeriod(nr3); + backupCounter = counter; + + + /*if (nr3 < 0xE0) { + const unsigned periods = nextStateDistance[reg & 0x3F]; + const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; + + reg = reg >> periods | xored << 8; + + if (nr3 & 8) + reg = reg & ~(0x80 - (0x80 >> periods)) | xored; + } + + const unsigned long period = toPeriod(nr3); + backupCounter = counter + period; + counter += period * nextStateDistance[reg & 0x3F];*/ +} + +void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) { + updateBackupCounter(cc); + nr3 = newNr3; + +// if (counter != COUNTER_DISABLED) +// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); +} + +void Channel4::Lfsr::nr4Init(unsigned long cc) { + disableMaster(); + updateBackupCounter(cc); + master = true; + backupCounter += 4; + counter = backupCounter; +// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); +} + +void Channel4::Lfsr::reset(const unsigned long cc) { + nr3 = 0; + disableMaster(); + backupCounter = cc + toPeriod(nr3); +} + +void Channel4::Lfsr::resetCounters(const unsigned long oldCc) { + updateBackupCounter(oldCc); + backupCounter -= COUNTER_MAX; + SoundUnit::resetCounters(oldCc); +} + +void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) { + updateBackupCounter(cc); + state.spu.ch4.lfsr.counter = backupCounter; + state.spu.ch4.lfsr.reg = reg; +} + +void Channel4::Lfsr::loadState(const SaveState &state) { + counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter); + reg = state.spu.ch4.lfsr.reg; + master = state.spu.ch4.master; + nr3 = state.mem.ioamhram.get()[0x122]; +} + +Channel4::Channel4() : + staticOutputTest(*this, lfsr), + disableMaster(master, lfsr), + lengthCounter(disableMaster, 0x3F), + envelopeUnit(staticOutputTest), + cycleCounter(0), + soMask(0), + prevOut(0), + nr4(0), + master(false) +{ + setEvent(); +} + +void Channel4::setEvent() { +// nextEventUnit = &lfsr; +// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &envelopeUnit; + if (lengthCounter.getCounter() < nextEventUnit->getCounter()) + nextEventUnit = &lengthCounter; +} + +void Channel4::setNr1(const unsigned data) { + lengthCounter.nr1Change(data, nr4, cycleCounter); + + setEvent(); +} + +void Channel4::setNr2(const unsigned data) { + if (envelopeUnit.nr2Change(data)) + disableMaster(); + else + staticOutputTest(cycleCounter); + + setEvent(); +} + +void Channel4::setNr4(const unsigned data) { + lengthCounter.nr4Change(nr4, data, cycleCounter); + + nr4 = data; + + if (data & 0x80) { //init-bit + nr4 &= 0x7F; + + master = !envelopeUnit.nr4Init(cycleCounter); + + if (master) + lfsr.nr4Init(cycleCounter); + + staticOutputTest(cycleCounter); + } + + setEvent(); +} + +void Channel4::setSo(const unsigned long soMask) { + this->soMask = soMask; + staticOutputTest(cycleCounter); + setEvent(); +} + +void Channel4::reset() { + cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + +// lengthCounter.reset(); + lfsr.reset(cycleCounter); + envelopeUnit.reset(); + + setEvent(); +} + +void Channel4::init(const bool cgb) { + lengthCounter.init(cgb); +} + +void Channel4::saveState(SaveState &state) { + lfsr.saveState(state, cycleCounter); + envelopeUnit.saveState(state.spu.ch4.env); + lengthCounter.saveState(state.spu.ch4.lcounter); + + state.spu.ch4.nr4 = nr4; + state.spu.ch4.master = master; +} + +void Channel4::loadState(const SaveState &state) { + lfsr.loadState(state); + envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter); + lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter); + + cycleCounter = state.spu.cycleCounter; + nr4 = state.spu.ch4.nr4; + master = state.spu.ch4.master; +} + +void Channel4::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { + const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; + const unsigned long outLow = outBase * (0 - 15ul); + const unsigned long endCycles = cycleCounter + cycles; + + for (;;) { + const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/; + const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; + unsigned long out = lfsr.isHighState() ? outHigh : outLow; + + while (lfsr.getCounter() <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += lfsr.getCounter() - cycleCounter; + cycleCounter = lfsr.getCounter(); + + lfsr.event(); + out = lfsr.isHighState() ? outHigh : outLow; + } + + if (cycleCounter < nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += nextMajorEvent - cycleCounter; + cycleCounter = nextMajorEvent; + } + + if (nextEventUnit->getCounter() == nextMajorEvent) { + nextEventUnit->event(); + setEvent(); + } else + break; + } + + if (cycleCounter & SoundUnit::COUNTER_MAX) { + lengthCounter.resetCounters(cycleCounter); + lfsr.resetCounters(cycleCounter); + envelopeUnit.resetCounters(cycleCounter); + + cycleCounter -= SoundUnit::COUNTER_MAX; + } +} diff --git a/supergameboy/libgambatte/src/sound/channel4.h b/supergameboy/libgambatte/src/sound/channel4.h new file mode 100644 index 00000000..7563dc2c --- /dev/null +++ b/supergameboy/libgambatte/src/sound/channel4.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SOUND_CHANNEL4_H +#define SOUND_CHANNEL4_H + +class SaveState; + +#include "int.h" + +#include "master_disabler.h" +#include "length_counter.h" +#include "envelope_unit.h" +#include "static_output_tester.h" + +class Channel4 { + class Lfsr : public SoundUnit { + unsigned long backupCounter; + unsigned short reg; + unsigned char nr3; + bool master; + + void updateBackupCounter(unsigned long cc); + + public: + Lfsr(); + void event(); + bool isHighState() const { return ~reg & 1; } + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Init(unsigned long cc); + void reset(unsigned long cc); + void saveState(SaveState &state, const unsigned long cc); + void loadState(const SaveState &state); + void resetCounters(unsigned long oldCc); + void disableMaster() { killCounter(); master = false; reg = 0xFF; } + void killCounter() { counter = COUNTER_DISABLED; } + void reviveCounter(unsigned long cc); + }; + + class Ch4MasterDisabler : public MasterDisabler { + Lfsr &lfsr; + public: + Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {} + void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); } + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + Ch4MasterDisabler disableMaster; + LengthCounter lengthCounter; + EnvelopeUnit envelopeUnit; + Lfsr lfsr; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel4(); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ } + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + bool isActive() const { return master; } + + void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void saveState(SaveState &state); + void loadState(const SaveState &state); +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/duty_unit.cpp b/supergameboy/libgambatte/src/sound/duty_unit.cpp new file mode 100644 index 00000000..d3de6abd --- /dev/null +++ b/supergameboy/libgambatte/src/sound/duty_unit.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "duty_unit.h" +#include + +static inline bool toOutState(const unsigned duty, const unsigned pos) { + static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E }; + + return duties[duty] >> pos & 1; +} + +static inline unsigned toPeriod(const unsigned freq) { + return (2048 - freq) << 1; +} + +void DutyUnit::updatePos(const unsigned long cc) { + if (cc >= nextPosUpdate) { + const unsigned long inc = (cc - nextPosUpdate) / period + 1; + nextPosUpdate += period * inc; + pos += inc; + pos &= 7; + } +} + +void DutyUnit::setDuty(const unsigned nr1) { + duty = nr1 >> 6; + high = toOutState(duty, pos); +} + +void DutyUnit::setCounter() { + static const unsigned char nextStateDistance[4 * 8] = { + 6, 5, 4, 3, 2, 1, 0, 0, + 0, 5, 4, 3, 2, 1, 0, 1, + 0, 3, 2, 1, 0, 3, 2, 1, + 0, 5, 4, 3, 2, 1, 0, 1 + }; + + if (enableEvents && nextPosUpdate != COUNTER_DISABLED) + counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos]; + else + counter = COUNTER_DISABLED; +} + +void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) { + updatePos(cc); + period = toPeriod(newFreq); + setCounter(); +} + +void DutyUnit::event() { + unsigned inc = period << duty; + + if (duty == 3) + inc -= period * 2; + + if (!(high ^= true)) + inc = period * 8 - inc; + + counter += inc; +} + +void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) { + updatePos(cc); + setDuty(newNr1); + setCounter(); +} + +void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) { + setFreq((getFreq() & 0x700) | newNr3, cc); +} + +void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) { + setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc); + + if (newNr4 & 0x80) { + nextPosUpdate = (cc & ~1) + period; + setCounter(); + } +} + +DutyUnit::DutyUnit() : +nextPosUpdate(COUNTER_DISABLED), +period(4096), +pos(0), +duty(0), +high(false), +enableEvents(true) +{} + +void DutyUnit::reset() { + pos = 0; + high = toOutState(duty, pos); + nextPosUpdate = COUNTER_DISABLED; + setCounter(); +} + +void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) { + updatePos(cc); + dstate.nextPosUpdate = nextPosUpdate; + dstate.nr3 = getFreq() & 0xFF; + dstate.pos = pos; +} + +void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) { + nextPosUpdate = std::max(dstate.nextPosUpdate, cc); + pos = dstate.pos & 7; + setDuty(nr1); + period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3); + enableEvents = true; + setCounter(); +} + +void DutyUnit::resetCounters(const unsigned long oldCc) { + if (nextPosUpdate == COUNTER_DISABLED) + return; + + updatePos(oldCc); + nextPosUpdate -= COUNTER_MAX; + SoundUnit::resetCounters(oldCc); +} + +void DutyUnit::killCounter() { + enableEvents = false; + setCounter(); +} + +void DutyUnit::reviveCounter(const unsigned long cc) { + updatePos(cc); + high = toOutState(duty, pos); + enableEvents = true; + setCounter(); +} diff --git a/supergameboy/libgambatte/src/sound/duty_unit.h b/supergameboy/libgambatte/src/sound/duty_unit.h new file mode 100644 index 00000000..e55cec59 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/duty_unit.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef DUTY_UNIT_H +#define DUTY_UNIT_H + +#include "sound_unit.h" +#include "master_disabler.h" +#include "../savestate.h" + +class DutyUnit : public SoundUnit { + unsigned long nextPosUpdate; + unsigned short period; + unsigned char pos; + unsigned char duty; + bool high; + bool enableEvents; + + void setCounter(); + void setDuty(unsigned nr1); + void updatePos(unsigned long cc); + +public: + DutyUnit(); + void event(); + bool isHighState() const { return high; } + void nr1Change(unsigned newNr1, unsigned long cc); + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Change(unsigned newNr4, unsigned long cc); + void reset(); + void saveState(SaveState::SPU::Duty &dstate, unsigned long cc); + void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc); + void resetCounters(unsigned long oldCc); + void killCounter(); + void reviveCounter(unsigned long cc); + + //intended for use by SweepUnit only. + unsigned getFreq() const { return 2048 - (period >> 1); } + void setFreq(unsigned newFreq, unsigned long cc); +}; + +class DutyMasterDisabler : public MasterDisabler { + DutyUnit &dutyUnit; +public: + DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit(dutyUnit) {} + void operator()() { MasterDisabler::operator()(); dutyUnit.killCounter(); } +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/envelope_unit.cpp b/supergameboy/libgambatte/src/sound/envelope_unit.cpp new file mode 100644 index 00000000..ed526eb5 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/envelope_unit.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "envelope_unit.h" +#include + +EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent; + +void EnvelopeUnit::event() { + const unsigned long period = nr2 & 7; + + if (period) { + unsigned newVol = volume; + + if (nr2 & 8) + ++newVol; + else + --newVol; + + if (newVol < 0x10U) { + volume = newVol; + + if (volume < 2) + volOnOffEvent(counter); + + counter += period << 15; + } else + counter = COUNTER_DISABLED; + } else + counter += 8ul << 15; +} + +bool EnvelopeUnit::nr2Change(const unsigned newNr2) { + if (!(nr2 & 7) && counter != COUNTER_DISABLED) + ++volume; + else if (!(nr2 & 8)) + volume += 2; + + if ((nr2 ^ newNr2) & 8) + volume = 0x10 - volume; + + volume &= 0xF; + + nr2 = newNr2; + + return !(newNr2 & 0xF8); +} + +bool EnvelopeUnit::nr4Init(const unsigned long cc) { + { + unsigned long period = nr2 & 7; + + if (!period) + period = 8; + + if (!(cc & 0x7000)) + ++period; + + counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; + } + + volume = nr2 >> 4; + + return !(nr2 & 0xF8); +} + +EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent) : +volOnOffEvent(volOnOffEvent), +nr2(0), +volume(0) +{} + +void EnvelopeUnit::reset() { + counter = COUNTER_DISABLED; +} + +void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const { + estate.counter = counter; + estate.volume = volume; +} + +void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) { + counter = std::max(estate.counter, cc); + volume = estate.volume; + this->nr2 = nr2; +} diff --git a/supergameboy/libgambatte/src/sound/envelope_unit.h b/supergameboy/libgambatte/src/sound/envelope_unit.h new file mode 100644 index 00000000..e9bae2f0 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/envelope_unit.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ENVELOPE_UNIT_H +#define ENVELOPE_UNIT_H + +#include "sound_unit.h" +#include "../savestate.h" + +class EnvelopeUnit : public SoundUnit { +public: + struct VolOnOffEvent { + virtual ~VolOnOffEvent() {} + virtual void operator()(unsigned long /*cc*/) {} + }; + +private: + static VolOnOffEvent nullEvent; + VolOnOffEvent &volOnOffEvent; + unsigned char nr2; + unsigned char volume; + +public: + EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent); + void event(); + bool dacIsOn() const { return nr2 & 0xF8; } + unsigned getVolume() const { return volume; } + bool nr2Change(unsigned newNr2); + bool nr4Init(unsigned long cycleCounter); + void reset(); + void saveState(SaveState::SPU::Env &estate) const; + void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc); +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/length_counter.cpp b/supergameboy/libgambatte/src/sound/length_counter.cpp new file mode 100644 index 00000000..8bbe85e1 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/length_counter.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "length_counter.h" +#include "master_disabler.h" +#include + +LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) : + disableMaster(disabler), + lengthMask(mask) +{ + init(false); + nr1Change(0, 0, 0); +} + +void LengthCounter::event() { + counter = COUNTER_DISABLED; + lengthCounter = 0; + disableMaster(); +} + +void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) { + lengthCounter = (~newNr1 & lengthMask) + 1; + counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast(COUNTER_DISABLED); +} + +void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) { + if (counter != COUNTER_DISABLED) + lengthCounter = (counter >> 13) - (cycleCounter >> 13); + + { + unsigned dec = 0; + + if (newNr4 & 0x40) { + dec = ~cycleCounter >> 12 & 1; + + if (!(oldNr4 & 0x40) && lengthCounter) { + if (!(lengthCounter -= dec)) + disableMaster(); + } + } + + if ((newNr4 & 0x80) && !lengthCounter) + lengthCounter = lengthMask + 1 - dec; + } + + if ((newNr4 & 0x40) && lengthCounter) + counter = ((cycleCounter >> 13) + lengthCounter) << 13; + else + counter = COUNTER_DISABLED; +} + +/*void LengthCounter::reset() { + counter = COUNTER_DISABLED; + + if (cgb) + lengthCounter = lengthMask + 1; +}*/ + +void LengthCounter::init(const bool cgb) { + this->cgb = cgb; +} + +void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const { + lstate.counter = counter; + lstate.lengthCounter = lengthCounter; +} + +void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) { + counter = std::max(lstate.counter, cc); + lengthCounter = lstate.lengthCounter; +} diff --git a/supergameboy/libgambatte/src/sound/length_counter.h b/supergameboy/libgambatte/src/sound/length_counter.h new file mode 100644 index 00000000..2d9451d7 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/length_counter.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef LENGTH_COUNTER_H +#define LENGTH_COUNTER_H + +#include "sound_unit.h" +#include "../savestate.h" + +class MasterDisabler; + +class LengthCounter : public SoundUnit { + MasterDisabler &disableMaster; + unsigned short lengthCounter; + const unsigned char lengthMask; + bool cgb; + +public: + LengthCounter(MasterDisabler &disabler, unsigned lengthMask); + void event(); + void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc); + void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc); +// void reset(); + void init(bool cgb); + void saveState(SaveState::SPU::LCounter &lstate) const; + void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc); +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/master_disabler.h b/supergameboy/libgambatte/src/sound/master_disabler.h new file mode 100644 index 00000000..7dd588c5 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/master_disabler.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef MASTER_DISABLER_H +#define MASTER_DISABLER_H + +class MasterDisabler { + bool &master; + +public: + MasterDisabler(bool &m) : master(m) {} + virtual ~MasterDisabler() {} + virtual void operator()() { master = false; } +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/sound_unit.h b/supergameboy/libgambatte/src/sound/sound_unit.h new file mode 100644 index 00000000..2857c0c1 --- /dev/null +++ b/supergameboy/libgambatte/src/sound/sound_unit.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef SOUND_UNIT_H +#define SOUND_UNIT_H + +class SoundUnit { +protected: + unsigned long counter; +public: + enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu }; + + SoundUnit() : counter(COUNTER_DISABLED) {} + virtual ~SoundUnit() {} + virtual void event() = 0; + unsigned long getCounter() const { return counter; } + virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; } +}; + +#endif diff --git a/supergameboy/libgambatte/src/sound/static_output_tester.h b/supergameboy/libgambatte/src/sound/static_output_tester.h new file mode 100644 index 00000000..3dbe216e --- /dev/null +++ b/supergameboy/libgambatte/src/sound/static_output_tester.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATIC_OUTPUT_TESTER_H +#define STATIC_OUTPUT_TESTER_H + +#include "envelope_unit.h" + +template +class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent { + const Channel &ch; + Unit &unit; +public: + StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {} + void operator()(unsigned long cc); +}; + +template +void StaticOutputTester::operator()(const unsigned long cc) { + if (ch.soMask && ch.master && ch.envelopeUnit.getVolume()) + unit.reviveCounter(cc); + else + unit.killCounter(); +} + +#endif diff --git a/supergameboy/libgambatte/src/state_osd_elements.cpp b/supergameboy/libgambatte/src/state_osd_elements.cpp new file mode 100644 index 00000000..44740d16 --- /dev/null +++ b/supergameboy/libgambatte/src/state_osd_elements.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "state_osd_elements.h" +#include "bitmap_font.h" +#include "statesaver.h" +#include +#include + +using namespace BitmapFont; + +static const char stateLoadedTxt[] = { S,t,a,t,e,SPC,N0,SPC,l,o,a,d,e,d,0 }; +static const char stateSavedTxt[] = { S,t,a,t,e,SPC,N0,SPC,s,a,v,e,d,0 }; +static const unsigned stateLoadedTxtWidth = getWidth(stateLoadedTxt); +static const unsigned stateSavedTxtWidth = getWidth(stateSavedTxt); + +class ShadedTextOsdElment : public OsdElement { + struct ShadeFill { + void operator()(Gambatte::uint_least32_t *dest, const unsigned pitch) { + dest[2] = dest[1] = dest[0] = 0x000000ul; + dest += pitch; + dest[2] = dest[0] = 0x000000ul; + dest += pitch; + dest[2] = dest[1] = dest[0] = 0x000000ul; + } + }; + + Gambatte::uint_least32_t *const pixels; + unsigned life; + +public: + ShadedTextOsdElment(unsigned w, const char *txt); + ~ShadedTextOsdElment(); + const Gambatte::uint_least32_t* update(); +}; + +ShadedTextOsdElment::ShadedTextOsdElment(unsigned width, const char *txt) : +OsdElement(MAX_WIDTH, 144 - HEIGHT - HEIGHT, width + 2, HEIGHT + 2, THREE_FOURTHS), +pixels(new Gambatte::uint_least32_t[w() * h()]), +life(4 * 60) { + std::memset(pixels, 0xFF, w() * h() * sizeof(Gambatte::uint_least32_t)); + + /*print(pixels + 0 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 0 * w() + 1, w(), 0x000000ul, txt); + print(pixels + 0 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 0, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 1, w(), 0x000000ul, txt); + print(pixels + 2 * w() + 2, w(), 0x000000ul, txt); + print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/ + + print(pixels, w(), ShadeFill(), txt); + print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt); +} + +ShadedTextOsdElment::~ShadedTextOsdElment() { + delete []pixels; +} + +const Gambatte::uint_least32_t* ShadedTextOsdElment::update() { + if (life--) + return pixels; + + return 0; +} + +/*class FramedTextOsdElment : public OsdElement { + Gambatte::uint_least32_t *const pixels; + unsigned life; + +public: + FramedTextOsdElment(unsigned w, const char *txt); + ~FramedTextOsdElment(); + const Gambatte::uint_least32_t* update(); +}; + +FramedTextOsdElment::FramedTextOsdElment(unsigned width, const char *txt) : +OsdElement(NUMBER_WIDTH, 144 - HEIGHT * 2 - HEIGHT / 2, width + NUMBER_WIDTH * 2, HEIGHT * 2), +pixels(new Gambatte::uint_least32_t[w() * h()]), +life(4 * 60) { + std::memset(pixels, 0x00, w() * h() * sizeof(Gambatte::uint_least32_t)); + print(pixels + (w() - width) / 2 + ((h() - HEIGHT) / 2) * w(), w(), 0xA0A0A0ul, txt); +} + +FramedTextOsdElment::~FramedTextOsdElment() { + delete []pixels; +} + +const Gambatte::uint_least32_t* FramedTextOsdElment::update() { + if (life--) + return pixels; + + return 0; +}*/ + +std::auto_ptr newStateLoadedOsdElement(unsigned stateNo) { + char txt[sizeof(stateLoadedTxt)]; + + std::memcpy(txt, stateLoadedTxt, sizeof(stateLoadedTxt)); + utoa(stateNo, txt + 6); + + return std::auto_ptr(new ShadedTextOsdElment(stateLoadedTxtWidth, txt)); +} + +std::auto_ptr newStateSavedOsdElement(unsigned stateNo) { + char txt[sizeof(stateSavedTxt)]; + + std::memcpy(txt, stateSavedTxt, sizeof(stateSavedTxt)); + utoa(stateNo, txt + 6); + + return std::auto_ptr(new ShadedTextOsdElment(stateSavedTxtWidth, txt)); +} + +class SaveStateOsdElement : public OsdElement { + Gambatte::uint_least32_t pixels[StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT]; + unsigned life; + +public: + SaveStateOsdElement(const char *fileName, unsigned stateNo); + const Gambatte::uint_least32_t* update(); +}; + +SaveStateOsdElement::SaveStateOsdElement(const char *fileName, unsigned stateNo) : +OsdElement((stateNo ? stateNo - 1 : 9) * ((160 - StateSaver::SS_WIDTH) / 10) + ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT), +life(4 * 60) { + std::ifstream file(fileName, std::ios_base::binary); + + if (file.is_open()) { + file.ignore(5); + file.read(reinterpret_cast(pixels), sizeof(pixels)); + } else { + std::memset(pixels, 0, sizeof(pixels)); + + { + using namespace BitmapFont; + + static const char txt[] = { E,m,p,t,BitmapFont::y,0 }; + + print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - BitmapFont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt); + } + } +} + +const Gambatte::uint_least32_t* SaveStateOsdElement::update() { + if (life--) + return pixels; + + return 0; +} + +std::auto_ptr newSaveStateOsdElement(const char *fileName, unsigned stateNo) { + return std::auto_ptr(new SaveStateOsdElement(fileName, stateNo)); +} diff --git a/supergameboy/libgambatte/src/state_osd_elements.h b/supergameboy/libgambatte/src/state_osd_elements.h new file mode 100644 index 00000000..c10344d2 --- /dev/null +++ b/supergameboy/libgambatte/src/state_osd_elements.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATE_OSD_ELEMENTS_H +#define STATE_OSD_ELEMENTS_H + +#include "osd_element.h" +#include + +std::auto_ptr newStateLoadedOsdElement(unsigned stateNo); +std::auto_ptr newStateSavedOsdElement(unsigned stateNo); +std::auto_ptr newSaveStateOsdElement(const char *fileName, unsigned stateNo); + +#endif diff --git a/supergameboy/libgambatte/src/statesaver.cpp b/supergameboy/libgambatte/src/statesaver.cpp new file mode 100644 index 00000000..c157129d --- /dev/null +++ b/supergameboy/libgambatte/src/statesaver.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "statesaver.h" +#include "savestate.h" +#include "array.h" +#include +#include +#include +#include + +enum AsciiChar { + NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, TAB, LF, VT, FF, CR, SO, SI, + DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US, + SP, XCL, QOT, HSH, DLR, PRC, AMP, APO, LPA, RPA, AST, PLU, COM, HYP, STP, DIV, + NO0, NO1, NO2, NO3, NO4, NO5, NO6, NO7, NO8, NO9, CLN, SCL, LT, EQL, GT, QTN, + AT, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + P, Q, R, S, T, U, V, W, X, Y, Z, LBX, BSL, RBX, CAT, UND, + ACN, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + p, q, r, s, t, u, v, w, x, y, z, LBR, BAR, RBR, TLD, DEL +}; + +struct Saver { + const char *label; + void (*save)(std::ofstream &file, const SaveState &state); + void (*load)(std::ifstream &file, SaveState &state); + unsigned char labelsize; +}; + +static inline bool operator<(const Saver &l, const Saver &r) { + return std::strcmp(l.label, r.label) < 0; +} + +static void put24(std::ofstream &file, const unsigned long data) { + file.put(data >> 16 & 0xFF); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void put32(std::ofstream &file, const unsigned long data) { + file.put(data >> 24 & 0xFF); + file.put(data >> 16 & 0xFF); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned char data) { + static const char inf[] = { 0x00, 0x00, 0x01 }; + + file.write(inf, sizeof(inf)); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned short data) { + static const char inf[] = { 0x00, 0x00, 0x02 }; + + file.write(inf, sizeof(inf)); + file.put(data >> 8 & 0xFF); + file.put(data & 0xFF); +} + +static void write(std::ofstream &file, const unsigned long data) { + static const char inf[] = { 0x00, 0x00, 0x04 }; + + file.write(inf, sizeof(inf)); + put32(file, data); +} + +static inline void write(std::ofstream &file, const bool data) { + write(file, static_cast(data)); +} + +static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) { + put24(file, sz); + file.write(reinterpret_cast(data), sz); +} + +static void write(std::ofstream &file, const bool *data, const unsigned long sz) { + put24(file, sz); + + for (unsigned long i = 0; i < sz; ++i) + file.put(data[i]); +} + +static unsigned long get24(std::ifstream &file) { + unsigned long tmp = file.get() & 0xFF; + + tmp = tmp << 8 | (file.get() & 0xFF); + + return tmp << 8 | (file.get() & 0xFF); +} + +static unsigned long read(std::ifstream &file) { + unsigned long size = get24(file); + + if (size > 4) { + file.ignore(size - 4); + size = 4; + } + + unsigned long out = 0; + + switch (size) { + case 4: out = (out | (file.get() & 0xFF)) << 8; + case 3: out = (out | (file.get() & 0xFF)) << 8; + case 2: out = (out | (file.get() & 0xFF)) << 8; + case 1: out = out | (file.get() & 0xFF); + } + + return out; +} + +static inline void read(std::ifstream &file, unsigned char &data) { + data = read(file) & 0xFF; +} + +static inline void read(std::ifstream &file, unsigned short &data) { + data = read(file) & 0xFFFF; +} + +static inline void read(std::ifstream &file, unsigned long &data) { + data = read(file); +} + +static inline void read(std::ifstream &file, bool &data) { + data = read(file); +} + +static void read(std::ifstream &file, unsigned char *data, unsigned long sz) { + const unsigned long size = get24(file); + + if (size < sz) + sz = size; + + file.read(reinterpret_cast(data), sz); + file.ignore(size - sz); + + if (static_cast(0x100)) { + for (unsigned long i = 0; i < sz; ++i) + data[i] &= 0xFF; + } +} + +static void read(std::ifstream &file, bool *data, unsigned long sz) { + const unsigned long size = get24(file); + + if (size < sz) + sz = size; + + for (unsigned long i = 0; i < sz; ++i) + data[i] = file.get(); + + file.ignore(size - sz); +} + +class SaverList { +public: + typedef std::vector list_t; + typedef list_t::const_iterator const_iterator; + +private: + list_t list; + unsigned char maxLabelsize_; + +public: + SaverList(); + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } + unsigned maxLabelsize() const { return maxLabelsize_; } +}; + +SaverList::SaverList() { +#define ADD(arg) do { \ + struct Func { \ + static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg); } \ + static void load(std::ifstream &file, SaveState &state) { read(file, state.arg); } \ + }; \ + \ + Saver saver = { label, Func::save, Func::load, sizeof label }; \ + list.push_back(saver); \ +} while (0) + +#define ADDPTR(arg) do { \ + struct Func { \ + static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \ + static void load(std::ifstream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \ + }; \ + \ + Saver saver = { label, Func::save, Func::load, sizeof label }; \ + list.push_back(saver); \ +} while (0) + + { static const char label[] = { c,c, NUL }; ADD(cpu.cycleCounter); } + { static const char label[] = { p,c, NUL }; ADD(cpu.PC); } + { static const char label[] = { s,p, NUL }; ADD(cpu.SP); } + { static const char label[] = { a, NUL }; ADD(cpu.A); } + { static const char label[] = { b, NUL }; ADD(cpu.B); } + { static const char label[] = { c, NUL }; ADD(cpu.C); } + { static const char label[] = { d, NUL }; ADD(cpu.D); } + { static const char label[] = { e, NUL }; ADD(cpu.E); } + { static const char label[] = { f, NUL }; ADD(cpu.F); } + { static const char label[] = { h, NUL }; ADD(cpu.H); } + { static const char label[] = { l, NUL }; ADD(cpu.L); } + { static const char label[] = { s,k,i,p, NUL }; ADD(cpu.skip); } + { static const char label[] = { h,a,l,t, NUL }; ADD(cpu.halted); } + { static const char label[] = { v,r,a,m, NUL }; ADDPTR(mem.vram); } + { static const char label[] = { s,r,a,m, NUL }; ADDPTR(mem.sram); } + { static const char label[] = { w,r,a,m, NUL }; ADDPTR(mem.wram); } + { static const char label[] = { h,r,a,m, NUL }; ADDPTR(mem.ioamhram); } + { static const char label[] = { l,d,i,v,u,p, NUL }; ADD(mem.div_lastUpdate); } + { static const char label[] = { l,t,i,m,a,u,p, NUL }; ADD(mem.tima_lastUpdate); } + { static const char label[] = { t,m,a,t,i,m,e, NUL }; ADD(mem.tmatime); } + { static const char label[] = { s,e,r,i,a,l,t, NUL }; ADD(mem.next_serialtime); } + { static const char label[] = { l,o,d,m,a,u,p, NUL }; ADD(mem.lastOamDmaUpdate); } + { static const char label[] = { m,i,n,i,n,t,t, NUL }; ADD(mem.minIntTime); } + { static const char label[] = { r,o,m,b,a,n,k, NUL }; ADD(mem.rombank); } + { static const char label[] = { d,m,a,s,r,c, NUL }; ADD(mem.dmaSource); } + { static const char label[] = { d,m,a,d,s,t, NUL }; ADD(mem.dmaDestination); } + { static const char label[] = { r,a,m,b,a,n,k, NUL }; ADD(mem.rambank); } + { static const char label[] = { o,d,m,a,p,o,s, NUL }; ADD(mem.oamDmaPos); } + { static const char label[] = { i,m,e, NUL }; ADD(mem.IME); } + { static const char label[] = { s,r,a,m,o,n, NUL }; ADD(mem.enable_ram); } + { static const char label[] = { r,a,m,b,m,o,d, NUL }; ADD(mem.rambank_mode); } + { static const char label[] = { h,d,m,a, NUL }; ADD(mem.hdma_transfer); } + { static const char label[] = { b,g,p, NUL }; ADDPTR(ppu.bgpData); } + { static const char label[] = { o,b,j,p, NUL }; ADDPTR(ppu.objpData); } + { static const char label[] = { s,p,o,s,b,u,f, NUL }; ADDPTR(ppu.oamReaderBuf); } + { static const char label[] = { s,p,s,z,b,u,f, NUL }; ADDPTR(ppu.oamReaderSzbuf); } + { static const char label[] = { v,c,y,c,l,e,s, NUL }; ADD(ppu.videoCycles); } + { static const char label[] = { e,d,M,NO0,t,i,m, NUL }; ADD(ppu.enableDisplayM0Time); } + { static const char label[] = { w,i,n,y,p,o,s, NUL }; ADD(ppu.winYPos); } + { static const char label[] = { d,r,a,w,c,y,c, NUL }; ADD(ppu.drawStartCycle); } + { static const char label[] = { s,c,r,d,c,y,c, NUL }; ADD(ppu.scReadOffset); } + { static const char label[] = { l,c,d,c, NUL }; ADD(ppu.lcdc); } + { static const char label[] = { s,c,x,NO0, NUL }; ADD(ppu.scx[0]); } + { static const char label[] = { s,c,x,NO1, NUL }; ADD(ppu.scx[1]); } + { static const char label[] = { s,c,y,NO0, NUL }; ADD(ppu.scy[0]); } + { static const char label[] = { s,c,y,NO1, NUL }; ADD(ppu.scy[1]); } + { static const char label[] = { s,c,x,AMP,NO7, NUL }; ADD(ppu.scxAnd7); } + { static const char label[] = { w,e,m,a,s,t,r, NUL }; ADD(ppu.weMaster); } + { static const char label[] = { w,x, NUL }; ADD(ppu.wx); } + { static const char label[] = { w,y, NUL }; ADD(ppu.wy); } + { static const char label[] = { l,y,c,s,k,i,p, NUL }; ADD(ppu.lycIrqSkip); } + { static const char label[] = { s,p,u,c,n,t,r, NUL }; ADD(spu.cycleCounter); } + { static const char label[] = { s,w,p,c,n,t,r, NUL }; ADD(spu.ch1.sweep.counter); } + { static const char label[] = { s,w,p,s,h,d,w, NUL }; ADD(spu.ch1.sweep.shadow); } + { static const char label[] = { s,w,p,n,e,g, NUL }; ADD(spu.ch1.sweep.negging); } + { static const char label[] = { d,u,t,NO1,c,t,r, NUL }; ADD(spu.ch1.duty.nextPosUpdate); } + { static const char label[] = { d,u,t,NO1,p,o,s, NUL }; ADD(spu.ch1.duty.pos); } + { static const char label[] = { e,n,v,NO1,c,t,r, NUL }; ADD(spu.ch1.env.counter); } + { static const char label[] = { e,n,v,NO1,v,o,l, NUL }; ADD(spu.ch1.env.volume); } + { static const char label[] = { l,e,n,NO1,c,t,r, NUL }; ADD(spu.ch1.lcounter.counter); } + { static const char label[] = { l,e,n,NO1,v,a,l, NUL }; ADD(spu.ch1.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO1,NO0, NUL }; ADD(spu.ch1.sweep.nr0); } + { static const char label[] = { n,r,NO1,NO3, NUL }; ADD(spu.ch1.duty.nr3); } + { static const char label[] = { n,r,NO1,NO4, NUL }; ADD(spu.ch1.nr4); } + { static const char label[] = { c,NO1,m,a,s,t,r, NUL }; ADD(spu.ch1.master); } + { static const char label[] = { d,u,t,NO2,c,t,r, NUL }; ADD(spu.ch2.duty.nextPosUpdate); } + { static const char label[] = { d,u,t,NO2,p,o,s, NUL }; ADD(spu.ch2.duty.pos); } + { static const char label[] = { e,n,v,NO2,c,t,r, NUL }; ADD(spu.ch2.env.counter); } + { static const char label[] = { e,n,v,NO2,v,o,l, NUL }; ADD(spu.ch2.env.volume); } + { static const char label[] = { l,e,n,NO2,c,t,r, NUL }; ADD(spu.ch2.lcounter.counter); } + { static const char label[] = { l,e,n,NO2,v,a,l, NUL }; ADD(spu.ch2.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO2,NO3, NUL }; ADD(spu.ch2.duty.nr3); } + { static const char label[] = { n,r,NO2,NO4, NUL }; ADD(spu.ch2.nr4); } + { static const char label[] = { c,NO2,m,a,s,t,r, NUL }; ADD(spu.ch2.master); } + { static const char label[] = { w,a,v,e,r,a,m, NUL }; ADDPTR(spu.ch3.waveRam); } + { static const char label[] = { l,e,n,NO3,c,t,r, NUL }; ADD(spu.ch3.lcounter.counter); } + { static const char label[] = { l,e,n,NO3,v,a,l, NUL }; ADD(spu.ch3.lcounter.lengthCounter); } + { static const char label[] = { w,a,v,e,c,t,r, NUL }; ADD(spu.ch3.waveCounter); } + { static const char label[] = { l,w,a,v,r,d,t, NUL }; ADD(spu.ch3.lastReadTime); } + { static const char label[] = { w,a,v,e,p,o,s, NUL }; ADD(spu.ch3.wavePos); } + { static const char label[] = { w,a,v,s,m,p,l, NUL }; ADD(spu.ch3.sampleBuf); } + { static const char label[] = { n,r,NO3,NO3, NUL }; ADD(spu.ch3.nr3); } + { static const char label[] = { n,r,NO3,NO4, NUL }; ADD(spu.ch3.nr4); } + { static const char label[] = { c,NO3,m,a,s,t,r, NUL }; ADD(spu.ch3.master); } + { static const char label[] = { l,f,s,r,c,t,r, NUL }; ADD(spu.ch4.lfsr.counter); } + { static const char label[] = { l,f,s,r,r,e,g, NUL }; ADD(spu.ch4.lfsr.reg); } + { static const char label[] = { e,n,v,NO4,c,t,r, NUL }; ADD(spu.ch4.env.counter); } + { static const char label[] = { e,n,v,NO4,v,o,l, NUL }; ADD(spu.ch4.env.volume); } + { static const char label[] = { l,e,n,NO4,c,t,r, NUL }; ADD(spu.ch4.lcounter.counter); } + { static const char label[] = { l,e,n,NO4,v,a,l, NUL }; ADD(spu.ch4.lcounter.lengthCounter); } + { static const char label[] = { n,r,NO4,NO4, NUL }; ADD(spu.ch4.nr4); } + { static const char label[] = { c,NO4,m,a,s,t,r, NUL }; ADD(spu.ch4.master); } + { static const char label[] = { r,t,c,b,a,s,e, NUL }; ADD(rtc.baseTime); } + { static const char label[] = { r,t,c,h,a,l,t, NUL }; ADD(rtc.haltTime); } + { static const char label[] = { r,t,c,i,n,d,x, NUL }; ADD(rtc.index); } + { static const char label[] = { r,t,c,d,h, NUL }; ADD(rtc.dataDh); } + { static const char label[] = { r,t,c,d,l, NUL }; ADD(rtc.dataDl); } + { static const char label[] = { r,t,c,h, NUL }; ADD(rtc.dataH); } + { static const char label[] = { r,t,c,m, NUL }; ADD(rtc.dataM); } + { static const char label[] = { r,t,c,s, NUL }; ADD(rtc.dataS); } + { static const char label[] = { r,t,c,l,l,d, NUL }; ADD(rtc.lastLatchData); } + +#undef ADD +#undef ADDPTR +#undef ADDTIME + + list.resize(list.size()); + std::sort(list.begin(), list.end()); + + maxLabelsize_ = 0; + + for (std::size_t i = 0; i < list.size(); ++i) { + if (list[i].labelsize > maxLabelsize_) + maxLabelsize_ = list[i].labelsize; + } +} + +static void writeSnapShot(std::ofstream &file, const Gambatte::uint_least32_t *pixels, const unsigned pitch) { + put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(Gambatte::uint_least32_t) : 0); + + if (pixels) { + Gambatte::uint_least32_t buf[StateSaver::SS_WIDTH]; + + for (unsigned h = StateSaver::SS_HEIGHT; h--;) { + for (unsigned x = 0; x < StateSaver::SS_WIDTH; ++x) { + unsigned long rb = 0; + unsigned long g = 0; + + static const unsigned w[StateSaver::SS_DIV] = { 3, 5, 5, 3 }; + + for (unsigned y = 0; y < StateSaver::SS_DIV; ++y) + for (unsigned xx = 0; xx < StateSaver::SS_DIV; ++xx) { + rb += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0xFF00FF) * w[y] * w[xx]; + g += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0x00FF00) * w[y] * w[xx]; + } + + buf[x] = (rb >> 8 & 0xFF00FF) | (g >> 8 & 0x00FF00); + } + + file.write(reinterpret_cast(buf), sizeof(buf)); + pixels += pitch * StateSaver::SS_DIV; + } + } +} + +static SaverList list; + +void StateSaver::saveState(const SaveState &state, const char *filename) { + std::ofstream file(filename, std::ios_base::binary); + + if (file.fail()) + return; + + { static const char ver[] = { 0, 0 }; file.write(ver, sizeof(ver)); } + + writeSnapShot(file, state.ppu.drawBuffer.get(), state.ppu.drawBuffer.getSz() / 144); + + for (SaverList::const_iterator it = list.begin(); it != list.end(); ++it) { + file.write(it->label, it->labelsize); + (*it->save)(file, state); + } +} + +bool StateSaver::loadState(SaveState &state, const char *filename) { + std::ifstream file(filename, std::ios_base::binary); + + if (file.fail() || file.get() != 0) + return false; + + file.ignore(); + file.ignore(get24(file)); + + Array labelbuf(list.maxLabelsize()); + const Saver labelbufSaver = { label: labelbuf, save: 0, load: 0, labelsize: (unsigned char)list.maxLabelsize() }; + + SaverList::const_iterator done = list.begin(); + + while (file.good() && done != list.end()) { + file.getline(labelbuf, list.maxLabelsize(), NUL); + + SaverList::const_iterator it = done; + + if (std::strcmp(labelbuf, it->label)) { + it = std::lower_bound(it + 1, list.end(), labelbufSaver); + + if (it == list.end() || std::strcmp(labelbuf, it->label)) { + file.ignore(get24(file)); + continue; + } + } else + ++done; + + (*it->load)(file, state); + } + + state.cpu.cycleCounter &= 0x7FFFFFFF; + state.spu.cycleCounter &= 0x7FFFFFFF; + + return true; +} diff --git a/supergameboy/libgambatte/src/statesaver.h b/supergameboy/libgambatte/src/statesaver.h new file mode 100644 index 00000000..ea9ce8b3 --- /dev/null +++ b/supergameboy/libgambatte/src/statesaver.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STATESAVER_H +#define STATESAVER_H + +class SaveState; + +class StateSaver { + StateSaver(); + +public: + enum { SS_SHIFT = 2 }; + enum { SS_DIV = 1 << 2 }; + enum { SS_WIDTH = 160 >> SS_SHIFT }; + enum { SS_HEIGHT = 144 >> SS_SHIFT }; + + static void saveState(const SaveState &state, const char *filename); + static bool loadState(SaveState &state, const char *filename); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video.cpp b/supergameboy/libgambatte/src/video.cpp new file mode 100644 index 00000000..875afa43 --- /dev/null +++ b/supergameboy/libgambatte/src/video.cpp @@ -0,0 +1,1474 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "video.h" +#include "videoblitter.h" +#include "video/filters/filter.h" +#include "video/filters/catrom2x.h" +#include "video/filters/catrom3x.h" +#include "video/filters/kreed2xsai.h" +#include "video/filters/maxsthq2x.h" +#include "video/filters/maxsthq3x.h" +#include "filterinfo.h" +#include "savestate.h" +#include "video/basic_add_event.h" +#include +#include + +static void addEventIfActivated(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + e->setTime(newTime); + + if (newTime != VideoEvent::DISABLED_TIME) + q.push(e); +} + +void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) { + palette[0] = dmgColors[data & 3]; + palette[1] = dmgColors[data >> 2 & 3]; + palette[2] = dmgColors[data >> 4 & 3]; + palette[3] = dmgColors[data >> 6 & 3]; +} + +unsigned long LCD::gbcToRgb32(const unsigned bgr15) { + const unsigned long r = bgr15 & 0x1F; + const unsigned long g = bgr15 >> 5 & 0x1F; + const unsigned long b = bgr15 >> 10 & 0x1F; + + return ((r * 13 + g * 2 + b) >> 1) << 16 | ((g * 3 + b) << 9) | ((r * 3 + g * 2 + b * 11) >> 1); +} + +unsigned long LCD::gbcToRgb16(const unsigned bgr15) { + const unsigned r = bgr15 & 0x1F; + const unsigned g = bgr15 >> 5 & 0x1F; + const unsigned b = bgr15 >> 10 & 0x1F; + + return (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) | ((g * 3 + b + 1) >> 1) << 5 | ((r * 3 + g * 2 + b * 11 + 8) >> 4); +} + +unsigned long LCD::gbcToUyvy(const unsigned bgr15) { + const unsigned r5 = bgr15 & 0x1F; + const unsigned g5 = bgr15 >> 5 & 0x1F; + const unsigned b5 = bgr15 >> 10 & 0x1F; + + // y = (r5 * 926151 + g5 * 1723530 + b5 * 854319) / 510000 + 16; + // u = (b5 * 397544 - r5 * 68824 - g5 * 328720) / 225930 + 128; + // v = (r5 * 491176 - g5 * 328720 - b5 * 162456) / 178755 + 128; + + const unsigned long y = (r5 * 116 + g5 * 216 + b5 * 107 + 16 * 64 + 32) >> 6; + const unsigned long u = (b5 * 225 - r5 * 39 - g5 * 186 + 128 * 128 + 64) >> 7; + const unsigned long v = (r5 * 176 - g5 * 118 - b5 * 58 + 128 * 64 + 32) >> 6; + +#ifdef WORDS_BIGENDIAN + return u << 24 | y << 16 | v << 8 | y; +#else + return y << 24 | v << 16 | y << 8 | u; +#endif +} + +LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram_in) : + vram(vram_in), + bgTileData(vram), + bgTileMap(vram + 0x1800), + wdTileMap(bgTileMap), + vBlitter(NULL), + filter(NULL), + dbuffer(NULL), + draw(NULL), + gbcToFormat(gbcToRgb32), + dmgColors(dmgColorsRgb32), + lastUpdate(0), + videoCycles(0), + dpitch(0), + winYPos(0), + m3EventQueue(11, VideoEventComparer()), + irqEventQueue(4, VideoEventComparer()), + vEventQueue(5, VideoEventComparer()), + win(m3EventQueue, lyCounter, m3ExtraCycles), + scxReader(m3EventQueue, /*wyReg.reader3(),*/ win.wxReader, win.we.enableChecker(), win.we.disableChecker(), m3ExtraCycles), + spriteMapper(m3ExtraCycles, lyCounter, oamram), + m3ExtraCycles(spriteMapper, scxReader, win), + breakEvent(drawStartCycle, scReadOffset), + mode3Event(m3EventQueue, vEventQueue, mode0Irq, irqEvent), + lycIrq(ifReg), + mode0Irq(lyCounter, lycIrq, m3ExtraCycles, ifReg), + mode1Irq(ifReg), + mode2Irq(lyCounter, lycIrq, ifReg), + irqEvent(irqEventQueue), + drawStartCycle(90), + scReadOffset(90), + ifReg(0), + tileIndexSign(0), + statReg(0), + doubleSpeed(false), + enabled(false), + cgb(false), + bgEnable(false), + spriteEnable(false) +{ + std::memset(bgpData, 0, sizeof(bgpData)); + std::memset(objpData, 0, sizeof(objpData)); + + for (unsigned i = 0; i < sizeof(dmgColorsRgb32) / sizeof(unsigned long); ++i) { + setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101); + } + + filters.push_back(NULL); + filters.push_back(new Catrom2x); + filters.push_back(new Catrom3x); + filters.push_back(new Kreed_2xSaI); + filters.push_back(new MaxSt_Hq2x); + filters.push_back(new MaxSt_Hq3x); + + reset(oamram, false); + setDoubleSpeed(false); + + setVideoFilter(0); +} + +LCD::~LCD() { +// delete []filter_buffer; + for (std::size_t i = 0; i < filters.size(); ++i) + delete filters[i]; +} + +void LCD::reset(const unsigned char *const oamram, const bool cgb_in) { + cgb = cgb_in; + spriteMapper.reset(oamram, cgb_in); + setDBuffer(); +} + +void LCD::resetVideoState(const unsigned long cycleCounter) { + vEventQueue.clear(); + m3EventQueue.clear(); + irqEventQueue.clear(); + + lyCounter.reset(videoCycles, lastUpdate); + vEventQueue.push(&lyCounter); + + spriteMapper.resetVideoState(); + m3ExtraCycles.invalidateCache(); + + addEventIfActivated(m3EventQueue, &scxReader, ScxReader::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wxReader, WxReader::schedule(scxReader.scxAnd7(), lyCounter, win.wxReader, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader1(), Wy::WyReader1::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader2(), Wy::WyReader2::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.wyReg.reader4(), Wy::WyReader4::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.we.enableChecker(), We::WeEnableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.we.disableChecker(), We::WeDisableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addEventIfActivated(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), win.we.getSource(), lyCounter, cycleCounter)); + + addEventIfActivated(irqEventQueue, &lycIrq, LycIrq::schedule(statReg, lycIrq.lycReg(), lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode0Irq, Mode0Irq::schedule(statReg, m3ExtraCycles, lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode1Irq, Mode1Irq::schedule(lyCounter, cycleCounter)); + addEventIfActivated(irqEventQueue, &mode2Irq, Mode2Irq::schedule(statReg, lyCounter, cycleCounter)); + + addEventIfActivated(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); + addEventIfActivated(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); + addEventIfActivated(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); + addEventIfActivated(vEventQueue, &breakEvent, BreakEvent::schedule(lyCounter)); +} + +void LCD::setDoubleSpeed(const bool ds) { + doubleSpeed = ds; + lyCounter.setDoubleSpeed(doubleSpeed); + scxReader.setDoubleSpeed(doubleSpeed); + win.wxReader.setDoubleSpeed(doubleSpeed); + scReader.setDoubleSpeed(doubleSpeed); + breakEvent.setDoubleSpeed(doubleSpeed); + lycIrq.setDoubleSpeed(doubleSpeed); + mode1Irq.setDoubleSpeed(doubleSpeed); +} + +void LCD::setStatePtrs(SaveState &state) { + state.ppu.drawBuffer.set(static_cast(dbuffer), dpitch * 144); + state.ppu.bgpData.set(bgpData, sizeof bgpData); + state.ppu.objpData.set(objpData, sizeof objpData); + spriteMapper.setStatePtrs(state); +} + +void LCD::saveState(SaveState &state) const { + state.ppu.videoCycles = videoCycles; + state.ppu.winYPos = winYPos; + state.ppu.drawStartCycle = drawStartCycle; + state.ppu.scReadOffset = scReadOffset; + state.ppu.lcdc = enabled << 7 | ((wdTileMap - vram - 0x1800) >> 4) | (tileIndexSign ^ 0x80) >> 3 | ((bgTileMap - vram - 0x1800) >> 7) | spriteEnable << 1 | bgEnable; + state.ppu.lycIrqSkip = lycIrq.skips(); + + spriteMapper.saveState(state); + scReader.saveState(state); + scxReader.saveState(state); + win.weMasterChecker.saveState(state); + win.wxReader.saveState(state); + win.wyReg.saveState(state); + win.we.saveState(state); +} + +void LCD::loadState(const SaveState &state, const unsigned char *oamram) { + statReg = state.mem.ioamhram.get()[0x141]; + ifReg = 0; + + setDoubleSpeed(cgb & state.mem.ioamhram.get()[0x14D] >> 7); + + lastUpdate = state.cpu.cycleCounter; + videoCycles = std::min(state.ppu.videoCycles, 70223ul); + winYPos = state.ppu.winYPos > 143 ? 0xFF : state.ppu.winYPos; + drawStartCycle = state.ppu.drawStartCycle; + scReadOffset = state.ppu.scReadOffset; + enabled = state.ppu.lcdc >> 7 & 1; + wdTileMap = vram + 0x1800 + (state.ppu.lcdc >> 6 & 1) * 0x400; + tileIndexSign = ((state.ppu.lcdc >> 4 & 1) ^ 1) * 0x80; + bgTileData = vram + ((state.ppu.lcdc >> 4 & 1) ^ 1) * 0x1000; + bgTileMap = vram + 0x1800 + (state.ppu.lcdc >> 3 & 1) * 0x400; + spriteEnable = state.ppu.lcdc >> 1 & 1; + bgEnable = state.ppu.lcdc & 1; + + lycIrq.setM2IrqEnabled(statReg >> 5 & 1); + lycIrq.setLycReg(state.mem.ioamhram.get()[0x145]); + lycIrq.setSkip(state.ppu.lycIrqSkip); + mode1Irq.setM1StatIrqEnabled(statReg >> 4 & 1); + + win.we.setSource(state.mem.ioamhram.get()[0x140] >> 5 & 1); + spriteMapper.setLargeSpritesSource(state.mem.ioamhram.get()[0x140] >> 2 & 1); + scReader.setScySource(state.mem.ioamhram.get()[0x142]); + scxReader.setSource(state.mem.ioamhram.get()[0x143]); + breakEvent.setScxSource(state.mem.ioamhram.get()[0x143]); + scReader.setScxSource(state.mem.ioamhram.get()[0x143]); + win.wyReg.setSource(state.mem.ioamhram.get()[0x14A]); + win.wxReader.setSource(state.mem.ioamhram.get()[0x14B]); + + spriteMapper.loadState(state); + scReader.loadState(state); + scxReader.loadState(state); + win.weMasterChecker.loadState(state); + win.wxReader.loadState(state); + win.wyReg.loadState(state); + win.we.loadState(state); + + resetVideoState(lastUpdate); + spriteMapper.oamChange(oamram, lastUpdate); + refreshPalettes(); +} + +void LCD::refreshPalettes() { + if (cgb) { + for (unsigned i = 0; i < 8 * 8; i += 2) { + bgPalette[i >> 1] = (*gbcToFormat)(bgpData[i] | bgpData[i + 1] << 8); + spPalette[i >> 1] = (*gbcToFormat)(objpData[i] | objpData[i + 1] << 8); + } + } else { + setDmgPalette(bgPalette, dmgColors, bgpData[0]); + setDmgPalette(spPalette, dmgColors + 4, objpData[0]); + setDmgPalette(spPalette + 4, dmgColors + 8, objpData[1]); + } +} + +void LCD::setVideoBlitter(Gambatte::VideoBlitter *vb) { + vBlitter = vb; + + if (vBlitter) { + vBlitter->setBufferDimensions(videoWidth(), videoHeight()); + pb = vBlitter->inBuffer(); + } + + setDBuffer(); +} + +void LCD::videoBufferChange() { + if (vBlitter) { + pb = vBlitter->inBuffer(); + setDBuffer(); + } +} + +void LCD::setVideoFilter(const unsigned n) { + const unsigned oldw = videoWidth(); + const unsigned oldh = videoHeight(); + + if (filter) + filter->outit(); + + filter = filters.at(n < filters.size() ? n : 0); + + if (filter) { + filter->init(); + } + + if (vBlitter && (oldw != videoWidth() || oldh != videoHeight())) { + vBlitter->setBufferDimensions(videoWidth(), videoHeight()); + pb = vBlitter->inBuffer(); + } + + setDBuffer(); +} + +std::vector LCD::filterInfo() const { + std::vector v; + + static Gambatte::FilterInfo noInfo = { "None", 160, 144 }; + v.push_back(&noInfo); + + for (std::size_t i = 1; i < filters.size(); ++i) + v.push_back(&filters[i]->info()); + + return v; +} + +unsigned int LCD::videoWidth() const { + return filter ? filter->info().outWidth : 160; +} + +unsigned int LCD::videoHeight() const { + return filter ? filter->info().outHeight : 144; +} + +template +static void blitOsdElement(Gambatte::uint_least32_t *d, const Gambatte::uint_least32_t *s, const unsigned width, unsigned h, const unsigned dpitch, Blend blend) { + while (h--) { + for (unsigned w = width; w--;) { + if (*s != 0xFFFFFFFF) + *d = blend(*s, *d); + + ++d; + ++s; + } + + d += dpitch - width; + } +} + +template +struct Blend { + enum { SW = weight - 1 }; + enum { LOWMASK = SW * 0x010101ul }; + Gambatte::uint_least32_t operator()(const Gambatte::uint_least32_t s, const Gambatte::uint_least32_t d) const { + return (s * SW + d - (((s & LOWMASK) * SW + (d & LOWMASK)) & LOWMASK)) / weight; + } +}; + +void LCD::updateScreen(const unsigned long cycleCounter) { + update(cycleCounter); + + if (pb.pixels) { + if (dbuffer && osdElement.get()) { + const Gambatte::uint_least32_t *s = osdElement->update(); + + if (s) { + Gambatte::uint_least32_t *d = static_cast(dbuffer) + osdElement->y() * dpitch + osdElement->x(); + + switch (osdElement->opacity()) { + case OsdElement::SEVEN_EIGHTHS: blitOsdElement(d, s, osdElement->w(), osdElement->h(), dpitch, Blend<8>()); break; + case OsdElement::THREE_FOURTHS: blitOsdElement(d, s, osdElement->w(), osdElement->h(), dpitch, Blend<4>()); break; + } + } else + osdElement.reset(); + } + + if (filter) { + filter->filter(static_cast(tmpbuf ? tmpbuf : pb.pixels), (tmpbuf ? videoWidth() : pb.pitch)); + } + + if (tmpbuf) { + switch (pb.format) { + case Gambatte::PixelBuffer::RGB16: + rgb32ToRgb16(tmpbuf, static_cast(pb.pixels), videoWidth(), videoHeight(), pb.pitch); + break; + case Gambatte::PixelBuffer::UYVY: + rgb32ToUyvy(tmpbuf, static_cast(pb.pixels), videoWidth(), videoHeight(), pb.pitch); + break; + default: break; + } + } + + if (vBlitter) + vBlitter->blit(); + } +} + +template +static void clear(T *buf, const unsigned long color, const unsigned dpitch) { + unsigned lines = 144; + + while (lines--) { + std::fill_n(buf, 160, color); + buf += dpitch; + } +} + +void LCD::enableChange(const unsigned long cycleCounter) { + update(cycleCounter); + enabled = !enabled; + + if (enabled) { + lycIrq.setSkip(false); + videoCycles = 0; + lastUpdate = cycleCounter; + winYPos = 0xFF; + win.weMasterChecker.unset(); + spriteMapper.enableDisplay(cycleCounter); + resetVideoState(cycleCounter); + } + + if (!enabled && dbuffer) { + const unsigned long color = cgb ? (*gbcToFormat)(0xFFFF) : dmgColors[0]; + + clear(static_cast(dbuffer), color, dpitch); + +// updateScreen(cycleCounter); + } +} + +//FIXME: needs testing +void LCD::lyWrite(const unsigned long cycleCounter) { + update(cycleCounter); + lycIrq.setSkip(false); + videoCycles = 0; + lastUpdate = cycleCounter; + winYPos = 0xFF; + win.weMasterChecker.unset(); + resetVideoState(cycleCounter); + +// if ((statReg & 0x40) && lycIrq.lycReg() == 0) +// ifReg |= 2; +} + +void LCD::preResetCounter(const unsigned long cycleCounter) { + preSpeedChange(cycleCounter); +} + +void LCD::postResetCounter(const unsigned long oldCC, const unsigned long cycleCounter) { + lastUpdate = cycleCounter - (oldCC - lastUpdate); + spriteMapper.resetCycleCounter(oldCC, cycleCounter); + resetVideoState(cycleCounter); +} + +void LCD::preSpeedChange(const unsigned long cycleCounter) { + update(cycleCounter); + spriteMapper.preCounterChange(cycleCounter); +} + +void LCD::postSpeedChange(const unsigned long cycleCounter) { + setDoubleSpeed(!doubleSpeed); + + resetVideoState(cycleCounter); +} + +bool LCD::isMode0IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 144 && timeToNextLy <= (456U - (169 + doubleSpeed * 3U + 80 + m3ExtraCycles(lyCounter.ly()) + 1 - doubleSpeed)) << doubleSpeed && timeToNextLy > 4; +} + +bool LCD::isMode2IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned nextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 143 && nextLy <= 4; +} + +bool LCD::isLycIrqPeriod(const unsigned lycReg, const unsigned endCycles, const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return (lyCounter.ly() == lycReg && timeToNextLy > endCycles) || (lycReg == 0 && lyCounter.ly() == 153 && timeToNextLy <= (456U - 8U) << doubleSpeed); +} + +bool LCD::isMode1IrqPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return lyCounter.ly() > 143 && (lyCounter.ly() < 153 || timeToNextLy > 4U - doubleSpeed * 4U); +} + +bool LCD::isHdmaPeriod(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + return /*memory.enable_display && */lyCounter.ly() < 144 && timeToNextLy <= ((456U - (169U + doubleSpeed * 3U + 80U + m3ExtraCycles(lyCounter.ly()) + 2 - doubleSpeed)) << doubleSpeed) && timeToNextLy > 4; +} + +unsigned long LCD::nextHdmaTime(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + unsigned line = lyCounter.ly(); + int next = static_cast(169 + doubleSpeed * 3U + 80 + 2 - doubleSpeed) - static_cast(lyCounter.lineCycles(cycleCounter)); + + if (line < 144 && next + static_cast(m3ExtraCycles(line)) <= 0) { + next += 456; + ++line; + } + + if (line > 143) { + next += (154 - line) * 456; + line = 0; + } + + next += m3ExtraCycles(line); + + return cycleCounter + (static_cast(next) << doubleSpeed); +} + +bool LCD::vramAccessible(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + bool accessible = true; + + if (enabled && lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles > 79 && lineCycles < 80 + 169 + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly())) + accessible = false; + } + + return accessible; +} + +bool LCD::cgbpAccessible(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + bool accessible = true; + + if (enabled && lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles > 79U + doubleSpeed && lineCycles < 80U + 169U + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly()) + 4U - doubleSpeed * 2U) + accessible = false; + } + + return accessible; +} + +bool LCD::oamAccessible(const unsigned long cycleCounter) { + bool accessible = true; + + if (enabled) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + accessible = spriteMapper.oamAccessible(cycleCounter); + } + + return accessible; +} + +void LCD::weChange(const bool newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.we.setSource(newValue); + addFixedtimeEvent(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), newValue, lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.we.disableChecker(), We::WeDisableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.we.enableChecker(), We::WeEnableChecker::schedule(scxReader.scxAnd7(), win.wxReader.wx(), lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.wxReader.setSource(newValue); + addEvent(m3EventQueue, &win.wxReader, WxReader::schedule(scxReader.scxAnd7(), lyCounter, win.wxReader, cycleCounter)); + + if (win.wyReg.reader3().time() != VideoEvent::DISABLED_TIME) + addEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + win.wyReg.setSource(newValue); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader1(), Wy::WyReader1::schedule(lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader2(), Wy::WyReader2::schedule(lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + addFixedtimeEvent(m3EventQueue, &win.wyReg.reader4(), Wy::WyReader4::schedule(lyCounter, cycleCounter)); + addEvent(m3EventQueue, &win.weMasterChecker, WeMasterChecker::schedule(win.wyReg.getSource(), win.we.getSource(), lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) { + update(cycleCounter); + + scxReader.setSource(newScx); + breakEvent.setScxSource(newScx); + scReader.setScxSource(newScx); + + addFixedtimeEvent(m3EventQueue, &scxReader, ScxReader::schedule(lyCounter, cycleCounter)); + + if (win.wyReg.reader3().time() != VideoEvent::DISABLED_TIME) + addEvent(m3EventQueue, &win.wyReg.reader3(), Wy::WyReader3::schedule(win.wxReader.getSource(), scxReader, lyCounter, cycleCounter)); + + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); + + const unsigned lineCycles = lyCounter.lineCycles(cycleCounter); + + if (lineCycles < 82U + doubleSpeed * 4U) + drawStartCycle = 90 + doubleSpeed * 4U + (newScx & 7); + else + addFixedtimeEvent(vEventQueue, &breakEvent, BreakEvent::schedule(lyCounter)); + + if (lineCycles < 86U + doubleSpeed * 2U) + scReadOffset = std::max(drawStartCycle - (newScx & 7), 90U + doubleSpeed * 4U); + + addEvent(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); +} + +void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + scReader.setScySource(newValue); + addFixedtimeEvent(vEventQueue, &scReader, ScReader::schedule(lastUpdate, videoCycles, scReadOffset, doubleSpeed)); +} + +void LCD::spriteSizeChange(const bool newLarge, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(cycleCounter); + spriteMapper.setLargeSpritesSource(newLarge); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::oamChange(const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(cycleCounter); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteMapper.oamChange(oamram, cycleCounter); + addFixedtimeEvent(m3EventQueue, &spriteMapper, SpriteMapper::schedule(lyCounter, cycleCounter)); + addUnconditionalEvent(vEventQueue, &mode3Event, Mode3Event::schedule(m3EventQueue)); +} + +void LCD::wdTileMapSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + wdTileMap = vram + 0x1800 + newValue * 0x400; +} + +void LCD::bgTileMapSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + bgTileMap = vram + 0x1800 + newValue * 0x400; +} + +void LCD::bgTileDataSelectChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + tileIndexSign = (newValue ^ 1) * 0x80; + bgTileData = vram + (newValue ^ 1) * 0x1000; +} + +void LCD::spriteEnableChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + spriteEnable = newValue; +} + +void LCD::bgEnableChange(const bool newValue, const unsigned long cycleCounter) { + update(cycleCounter); + + bgEnable = newValue; +} + +void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned old = statReg; + statReg = data; + mode1Irq.setM1StatIrqEnabled(data & 0x10); + lycIrq.setM2IrqEnabled(data & 0x20); + + if (!enabled) + return; + + const bool lycIrqPeriod = isLycIrqPeriod(lycIrq.lycReg(), lycIrq.lycReg() == 153 ? lyCounter.lineTime() - (4 << (doubleSpeed*2)) : 4 - doubleSpeed * 4U, cycleCounter); + + if (lycIrq.lycReg() < 154 && ((data ^ old) & 0x40)) { + if (data & 0x40) { + if (lycIrqPeriod) + ifReg |= 2; + } else { + if (!doubleSpeed && lycIrq.time() - cycleCounter < 5 && (!(old & 0x20) || lycIrq.lycReg() > 143 || lycIrq.lycReg() == 0)) + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &lycIrq, LycIrq::schedule(data, lycIrq.lycReg(), lyCounter, cycleCounter)); + } + + if ((((data & 0x10) && !(old & 0x10)) || !cgb) && !((old & 0x40) && lycIrqPeriod) && isMode1IrqPeriod(cycleCounter)) + ifReg |= 2; + + if ((data ^ old) & 0x08) { + if (data & 0x08) { + if (!((old & 0x40) && lycIrqPeriod) && isMode0IrqPeriod(cycleCounter)) + ifReg |= 2; + } else { + if (mode0Irq.time() - cycleCounter < 3 && (lycIrq.time() == VideoEvent::DISABLED_TIME || lyCounter.ly() != lycIrq.lycReg())) + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &mode0Irq, Mode0Irq::schedule(data, m3ExtraCycles, lyCounter, cycleCounter)); + } + + if ((data & 0x28) == 0x20 && (old & 0x28) != 0x20 && isMode2IrqPeriod(cycleCounter)) { + ifReg |= 2; + } + + addFixedtimeEvent(irqEventQueue, &mode2Irq, Mode2Irq::schedule(data, lyCounter, cycleCounter)); + + addEvent(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); +} + +void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) { + if (data == lycIrq.lycReg()) + return; + + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned old = lycIrq.lycReg(); + lycIrq.setLycReg(data); + + if (!(enabled && (statReg & 0x40))) + return; + + if (!doubleSpeed && lycIrq.time() - cycleCounter < 5 && (!(statReg & 0x20) || old > 143 || old == 0)) + ifReg |= 2; + + addEvent(irqEventQueue, &lycIrq, LycIrq::schedule(statReg, lycIrq.lycReg(), lyCounter, cycleCounter)); + + if (data < 154) { + if (isLycIrqPeriod(data, data == 153 ? lyCounter.lineTime() - doubleSpeed * 8U : 8, cycleCounter)) + ifReg |= 2; + + if (lycIrq.isSkipPeriod(cycleCounter, doubleSpeed)) + lycIrq.setSkip(true); + } + + addEvent(vEventQueue, &irqEvent, IrqEvent::schedule(irqEventQueue)); +} + +unsigned long LCD::nextIrqEvent() const { + if (!enabled) + return VideoEvent::DISABLED_TIME; + + if (mode0Irq.time() != VideoEvent::DISABLED_TIME && mode3Event.time() < irqEvent.time()) + return mode3Event.time(); + + return irqEvent.time(); +} + +unsigned LCD::getIfReg(const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + return ifReg; +} + +void LCD::setIfReg(const unsigned ifReg_in, const unsigned long cycleCounter) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + ifReg = ifReg_in; +} + +unsigned LCD::get_stat(const unsigned lycReg, const unsigned long cycleCounter) { + unsigned stat = 0; + + if (enabled) { + if (cycleCounter >= vEventQueue.top()->time()) + update(cycleCounter); + + const unsigned timeToNextLy = lyCounter.time() - cycleCounter; + + if (lyCounter.ly() > 143) { + if (lyCounter.ly() < 153 || timeToNextLy > 4 - doubleSpeed * 4U) + stat = 1; + } else { + const unsigned lineCycles = 456 - (timeToNextLy >> doubleSpeed); + + if (lineCycles < 80) { + if (!spriteMapper.inactivePeriodAfterDisplayEnable(cycleCounter)) + stat = 2; + } else if (lineCycles < 80 + 169 + doubleSpeed * 3U + m3ExtraCycles(lyCounter.ly())) + stat = 3; + } + + if ((lyCounter.ly() == lycReg && timeToNextLy > 4 - doubleSpeed * 4U) || + (lycReg == 0 && lyCounter.ly() == 153 && timeToNextLy >> doubleSpeed <= 456 - 8)) { + stat |= 4; + } + } + + return stat; +} + +void LCD::do_update(unsigned cycles) { + if (lyCounter.ly() < 144) { + const unsigned lineCycles = lyCounter.lineCycles(lastUpdate); + const unsigned xpos = lineCycles < drawStartCycle ? 0 : lineCycles - drawStartCycle; + + const unsigned endLineCycles = lineCycles + cycles; + unsigned endX = endLineCycles < drawStartCycle ? 0 : endLineCycles - drawStartCycle; + + if (endX > 160) + endX = 160; + + if (xpos < endX) + (this->*draw)(xpos, lyCounter.ly(), endX); + } else if (lyCounter.ly() == 144) { + winYPos = 0xFF; + //scy[0] = scy[1] = memory.fastread(0xFF42); + //scx[0] = scx[1] = memory.fastread(0xFF43); + win.weMasterChecker.unset(); + } + + videoCycles += cycles; + + if (videoCycles >= 70224U) + videoCycles -= 70224U; +} + +inline void LCD::event() { + vEventQueue.top()->doEvent(); + + if (vEventQueue.top()->time() == VideoEvent::DISABLED_TIME) + vEventQueue.pop(); + else + vEventQueue.modify_root(vEventQueue.top()); +} + +void LCD::update(const unsigned long cycleCounter) { + if (!enabled) + return; + + for (;;) { + const unsigned cycles = (std::max(std::min(cycleCounter, static_cast(vEventQueue.top()->time())), lastUpdate) - lastUpdate) >> doubleSpeed; + do_update(cycles); + lastUpdate += cycles << doubleSpeed; + + if (cycleCounter >= vEventQueue.top()->time()) + event(); + else + break; + } +} + +void LCD::setDBuffer() { + tmpbuf.reset(pb.format == Gambatte::PixelBuffer::RGB32 ? 0 : videoWidth() * videoHeight()); + + if (cgb) + draw = &LCD::cgb_draw; + else + draw = &LCD::dmg_draw; + + gbcToFormat = &gbcToRgb32; + dmgColors = dmgColorsRgb32; + + if (filter) { + dbuffer = filter->inBuffer(); + dpitch = filter->inPitch(); + } else if (pb.format == Gambatte::PixelBuffer::RGB32) { + dbuffer = pb.pixels; + dpitch = pb.pitch; + } else { + dbuffer = tmpbuf; + dpitch = 160; + } + + if (dbuffer == NULL) + draw = &LCD::null_draw; + + refreshPalettes(); +} + +void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) { + dmgColorsRgb32[index] = rgb32; + dmgColorsRgb16[index] = rgb32ToRgb16(rgb32); + dmgColorsUyvy[index] = ::rgb32ToUyvy(rgb32); +} + +void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) { + if (palNum > 2 || colorNum > 3) + return; + + setDmgPaletteColor((palNum * 4) | colorNum, rgb32); + refreshPalettes(); +} + +void LCD::null_draw(unsigned /*xpos*/, const unsigned ypos, const unsigned endX) { + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + if (endX == 160) { + if (enableWindow) + ++winYPos; + } +} + +template +void LCD::cgb_draw(unsigned xpos, const unsigned ypos, const unsigned endX) { + const unsigned effectiveScx = scReader.scx(); + + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + T *const bufLine = static_cast(dbuffer) + ypos * static_cast(dpitch); + + if (!(enableWindow && win.wxReader.wx() <= xpos + 7)) { + const unsigned fby = scReader.scy() + ypos /*& 0xFF*/; + const unsigned end = std::min(enableWindow ? win.wxReader.wx() - 7 : 160U, endX); + + cgb_bg_drawPixels(bufLine, xpos, end, scxReader.scxAnd7(), ((xpos + effectiveScx) & ~7) + ((xpos + drawStartCycle - scReadOffset) & 7), + bgTileMap + (fby & 0xF8) * 4, bgTileData, fby & 7); + } + + if (enableWindow && endX + 7 > win.wxReader.wx()) { + const unsigned start = std::max(win.wxReader.wx() < 7 ? 0U : (win.wxReader.wx() - 7), xpos); + + cgb_bg_drawPixels(bufLine, start, endX, 7u - win.wxReader.wx(), start + (7u - win.wxReader.wx()), + wdTileMap + (winYPos & 0xF8) * 4, bgTileData, winYPos & 7); + } + + if (endX == 160) { + if (spriteEnable) + cgb_drawSprites(bufLine, ypos); + + if (enableWindow) + ++winYPos; + } +} + +template +void LCD::dmg_draw(unsigned xpos, const unsigned ypos, const unsigned endX) { + const unsigned effectiveScx = scReader.scx(); + + const bool enableWindow = win.enabled(ypos); + + if (enableWindow && winYPos == 0xFF) + winYPos = /*ypos - wyReg.value()*/ 0; + + T *const bufLine = static_cast(dbuffer) + ypos * static_cast(dpitch); + + if (bgEnable) { + if (!(enableWindow && win.wxReader.wx() <= xpos + 7)) { + const unsigned fby = scReader.scy() + ypos /*& 0xFF*/; + const unsigned end = std::min(enableWindow ? win.wxReader.wx() - 7 : 160U, endX); + + bg_drawPixels(bufLine, xpos, end, scxReader.scxAnd7(), ((xpos + effectiveScx) & ~7) + ((xpos + drawStartCycle - scReadOffset) & 7), + bgTileMap + (fby & 0xF8) * 4, bgTileData + (fby & 7) * 2); + } + + if (enableWindow && endX + 7 > win.wxReader.wx()) { + const unsigned start = std::max(win.wxReader.wx() < 7 ? 0U : (win.wxReader.wx() - 7), xpos); + + bg_drawPixels(bufLine, start, endX, 7u - win.wxReader.wx(), start + (7u - win.wxReader.wx()), + wdTileMap + (winYPos & 0xF8) * 4, bgTileData + (winYPos & 7) * 2); + } + } else + std::fill_n(bufLine + xpos, endX - xpos, bgPalette[0]); + + if (endX == 160) { + if (spriteEnable) + drawSprites(bufLine, ypos); + + if (enableWindow) + ++winYPos; + } +} + +#define FLIP(u8) ( (((u8) & 0x01) << 7) | (((u8) & 0x02) << 5) | (((u8) & 0x04) << 3) | (((u8) & 0x08) << 1) | \ +(((u8) & 0x10) >> 1) | (((u8) & 0x20) >> 3) | (((u8) & 0x40) >> 5) | (((u8) & 0x80) >> 7) ) + +#define FLIP_ROW(n) FLIP((n)|0x0), FLIP((n)|0x1), FLIP((n)|0x2), FLIP((n)|0x3), FLIP((n)|0x4), FLIP((n)|0x5), FLIP((n)|0x6), FLIP((n)|0x7), \ +FLIP((n)|0x8), FLIP((n)|0x9), FLIP((n)|0xA), FLIP((n)|0xB), FLIP((n)|0xC), FLIP((n)|0xD), FLIP((n)|0xE), FLIP((n)|0xF) + +static const unsigned char xflipt[0x100] = { + FLIP_ROW(0x00), FLIP_ROW(0x10), FLIP_ROW(0x20), FLIP_ROW(0x30), + FLIP_ROW(0x40), FLIP_ROW(0x50), FLIP_ROW(0x60), FLIP_ROW(0x70), + FLIP_ROW(0x80), FLIP_ROW(0x90), FLIP_ROW(0xA0), FLIP_ROW(0xB0), + FLIP_ROW(0xC0), FLIP_ROW(0xD0), FLIP_ROW(0xE0), FLIP_ROW(0xF0) +}; + +#undef FLIP_ROW +#undef FLIP + +#define PREP(u8) (u8) + +#define EXPAND(u8) ((PREP(u8) << 7 & 0x4000) | (PREP(u8) << 6 & 0x1000) | (PREP(u8) << 5 & 0x0400) | (PREP(u8) << 4 & 0x0100) | \ + (PREP(u8) << 3 & 0x0040) | (PREP(u8) << 2 & 0x0010) | (PREP(u8) << 1 & 0x0004) | (PREP(u8) & 0x0001)) + +#define EXPAND_ROW(n) EXPAND((n)|0x0), EXPAND((n)|0x1), EXPAND((n)|0x2), EXPAND((n)|0x3), \ + EXPAND((n)|0x4), EXPAND((n)|0x5), EXPAND((n)|0x6), EXPAND((n)|0x7), \ + EXPAND((n)|0x8), EXPAND((n)|0x9), EXPAND((n)|0xA), EXPAND((n)|0xB), \ + EXPAND((n)|0xC), EXPAND((n)|0xD), EXPAND((n)|0xE), EXPAND((n)|0xF) + +#define EXPAND_TABLE EXPAND_ROW(0x00), EXPAND_ROW(0x10), EXPAND_ROW(0x20), EXPAND_ROW(0x30), \ + EXPAND_ROW(0x40), EXPAND_ROW(0x50), EXPAND_ROW(0x60), EXPAND_ROW(0x70), \ + EXPAND_ROW(0x80), EXPAND_ROW(0x90), EXPAND_ROW(0xA0), EXPAND_ROW(0xB0), \ + EXPAND_ROW(0xC0), EXPAND_ROW(0xD0), EXPAND_ROW(0xE0), EXPAND_ROW(0xF0) + +static const unsigned short expand_lut[0x200] = { + EXPAND_TABLE, + +#undef PREP +#define PREP(u8) (((u8) << 7 & 0x80) | ((u8) << 5 & 0x40) | ((u8) << 3 & 0x20) | ((u8) << 1 & 0x10) | \ + ((u8) >> 1 & 0x08) | ((u8) >> 3 & 0x04) | ((u8) >> 5 & 0x02) | ((u8) >> 7 & 0x01)) + + EXPAND_TABLE +}; + +#undef EXPAND_TABLE +#undef EXPAND_ROW +#undef EXPAND +#undef PREP + +//shoud work for the window too, if -wx is passed as scx. +//tilemap and tiledata must point to the areas in the first vram bank +//the second vram bank has to be placed immediately after the first one in memory (0x4000 continous bytes that cover both). +//tilemap needs to be offset to the right line +template +void LCD::cgb_bg_drawPixels(T * const buffer_line, unsigned xpos, const unsigned end, const unsigned scx, unsigned tilemappos, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline) +{ + const unsigned sign = tileIndexSign; + unsigned shift = (7 - ((scx + xpos) & 7)) * 2; + T *buf = buffer_line + xpos; + T *const bufend = buffer_line + end; + + while (buf < bufend) { + if ((tilemappos & 7) || bufend - buf < 8) { + const unsigned char *const maptmp = tilemap + (tilemappos >> 3 & 0x1F); + const unsigned attributes = maptmp[0x2000]; + const unsigned char *const dataptr = tiledata + (attributes << 10 & 0x2000) + + maptmp[0] * 16 - (maptmp[0] & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + const unsigned short *const exp_lut = expand_lut + (attributes << 3 & 0x100); + + const unsigned data = exp_lut[dataptr[0]] + exp_lut[dataptr[1]] * 2; + const unsigned long *const palette = bgPalette + (attributes & 7) * 4; + + do { + *buf++ = palette[data >> shift & 3]; + shift = (shift - 2) & 15; + } while ((++tilemappos & 7) && buf < bufend); + } + + while (bufend - buf > 7) { + const unsigned char *const maptmp = tilemap + (tilemappos >> 3 & 0x1F); + const unsigned attributes = maptmp[0x2000]; + const unsigned char *const dataptr = tiledata + (attributes << 10 & 0x2000) + + maptmp[0] * 16 - (maptmp[0] & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + const unsigned short *const exp_lut = expand_lut + (attributes << 3 & 0x100); + + const unsigned data = exp_lut[dataptr[0]] + exp_lut[dataptr[1]] * 2; + const unsigned long *const palette = bgPalette + (attributes & 7) * 4; + + buf[0] = palette[data >> shift & 3]; + buf[1] = palette[data >> ((shift - 2) & 15) & 3]; + buf[2] = palette[data >> ((shift - 4) & 15) & 3]; + buf[3] = palette[data >> ((shift - 6) & 15) & 3]; + buf[4] = palette[data >> ((shift - 8) & 15) & 3]; + buf[5] = palette[data >> ((shift - 10) & 15) & 3]; + buf[6] = palette[data >> ((shift - 12) & 15) & 3]; + buf[7] = palette[data >> ((shift - 14) & 15) & 3]; + + buf += 8; + tilemappos += 8; + } + } +} + +static unsigned cgb_prioritizedBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte; + + { + const unsigned pos = scx + spx - 8 + spStart; + const unsigned char *maptmp = tilemap + (pos >> 3 & 0x1F); + unsigned tile = maptmp[0]; + unsigned attributes = maptmp[0x2000]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte = (attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1]); + + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + maptmp = tilemap + (((pos >> 3) + 1) & 0x1F); + tile = maptmp[0]; + attributes = maptmp[0x2000]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte |= ((attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1])) >> (8 - offset); + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +static unsigned cgb_toplayerBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned tileline, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte = 0; + + { + const unsigned pos = scx + spx - 8 + spStart; + const unsigned char *maptmp = tilemap + (pos >> 3 & 0x1F); + unsigned attributes = maptmp[0x2000]; + + if (attributes & 0x80) { + const unsigned tile = maptmp[0]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte = (attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1]); + } + + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + maptmp = tilemap + (((pos >> 3) + 1) & 0x1F); + attributes = maptmp[0x2000]; + + if (attributes & 0x80) { + const unsigned tile = maptmp[0]; + + const unsigned char *const data = tiledata + (attributes << 10 & 0x2000) + + tile * 16 - (tile & sign) * 32 + ((attributes & 0x40) ? 7 - tileline : tileline) * 2; + + bgbyte |= ((attributes & 0x20) ? xflipt[data[0] | data[1]] : (data[0] | data[1])) >> (8 - offset); + } + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +template +void LCD::cgb_drawSprites(T * const buffer_line, const unsigned ypos) { + const unsigned scy = scReader.scy() + ypos /*& 0xFF*/; + const unsigned wx = win.wxReader.wx() < 7 ? 0 : win.wxReader.wx() - 7; + const bool enableWindow = win.enabled(ypos); + const unsigned char *const spriteMapLine = spriteMapper.sprites(ypos); + + for (int i = spriteMapper.numSprites(ypos) - 1; i >= 0; --i) { + const unsigned spNrX2 = spriteMapLine[i]; + const unsigned spx = spriteMapper.posbuf()[spNrX2 + 1]; + + if (spx < 168 && spx) { + unsigned spLine = ypos + 16 - spriteMapper.posbuf()[spNrX2]; + unsigned spTile = spriteMapper.oamram()[spNrX2 * 2 + 2]; + const unsigned attributes = spriteMapper.oamram()[spNrX2 * 2 + 3]; + + if (spriteMapper.largeSprites(spNrX2 >> 1)) { + if (attributes & 0x40) //yflip + spLine = 15 - spLine; + + if (spLine < 8) + spTile &= 0xFE; + else { + spLine -= 8; + spTile |= 0x01; + } + } else { + if (attributes & 0x40) //yflip + spLine = 7 - spLine; + } + + const unsigned char *const data = vram + ((attributes * 0x400) & 0x2000) + spTile * 16 + spLine * 2; + + unsigned byte1 = data[0]; + unsigned byte2 = data[1]; + + if (attributes & 0x20) { + byte1 = xflipt[byte1]; + byte2 = xflipt[byte2]; + } + + //(Sprites with priority-bit are still allowed to cover other sprites according to GBdev-faq.) + if (bgEnable) { + unsigned mask = 0xFF; + + if (attributes & 0x80) { + if (!(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = cgb_prioritizedBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData, scy & 7, tileIndexSign); + if (enableWindow && spx > wx) + mask &= cgb_prioritizedBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData, winYPos & 7, tileIndexSign); + } else { + if (!(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = cgb_toplayerBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData, scy & 7, tileIndexSign); + if (enableWindow && spx > wx) + mask &= cgb_toplayerBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData, winYPos & 7, tileIndexSign); + } + + byte1 &= mask; + byte2 &= mask; + } + + const unsigned bytes = expand_lut[byte1] + expand_lut[byte2] * 2; + const unsigned long *const palette = spPalette + (attributes & 7) * 4; + + if (spx > 7 && spx < 161) { + T * const buf = buffer_line + spx - 8; + unsigned color; + + if ((color = bytes >> 14 )) + buf[0] = palette[color]; + if ((color = bytes >> 12 & 3)) + buf[1] = palette[color]; + if ((color = bytes >> 10 & 3)) + buf[2] = palette[color]; + if ((color = bytes >> 8 & 3)) + buf[3] = palette[color]; + if ((color = bytes >> 6 & 3)) + buf[4] = palette[color]; + if ((color = bytes >> 4 & 3)) + buf[5] = palette[color]; + if ((color = bytes >> 2 & 3)) + buf[6] = palette[color]; + if ((color = bytes & 3)) + buf[7] = palette[color]; + } else { + const unsigned end = spx >= 160 ? 160 : spx; + unsigned xpos = spx <= 8 ? 0 : (spx - 8); + unsigned shift = (7 - (xpos + 8 - spx)) * 2; + + while (xpos < end) { + if (const unsigned color = bytes >> shift & 3) + buffer_line[xpos] = palette[color]; + + shift -= 2; + ++xpos; + } + } + } + } +} + + +//shoud work for the window too, if -wx is passed as scx. +//tilemap and tiledata need to be offset to the right line +template +void LCD::bg_drawPixels(T * const buffer_line, unsigned xpos, const unsigned end, const unsigned scx, unsigned tilemappos, + const unsigned char *const tilemap, const unsigned char *const tiledata) +{ + const unsigned sign = tileIndexSign; + unsigned shift = (7 - ((scx + xpos) & 7)) * 2; + T *buf = buffer_line + xpos; + T *const bufend = buffer_line + end; + + while (buf < bufend) { + if ((tilemappos & 7) || bufend - buf < 8) { + const unsigned tile = tilemap[tilemappos >> 3 & 0x1F]; + const unsigned char *const dataptr = tiledata + tile * 16 - (tile & sign) * 32; + const unsigned data = expand_lut[dataptr[0]] + expand_lut[dataptr[1]] * 2; + + do { + *buf++ = bgPalette[data >> shift & 3]; + shift = (shift - 2) & 15; + } while ((++tilemappos & 7) && buf < bufend); + } + + while (bufend - buf > 7) { + const unsigned tile = tilemap[tilemappos >> 3 & 0x1F]; + const unsigned char *const dataptr = tiledata + tile * 16 - (tile & sign) * 32; + const unsigned data = expand_lut[dataptr[0]] + expand_lut[dataptr[1]] * 2; + buf[0] = bgPalette[data >> shift & 3]; + buf[1] = bgPalette[data >> ((shift - 2) & 15) & 3]; + buf[2] = bgPalette[data >> ((shift - 4) & 15) & 3]; + buf[3] = bgPalette[data >> ((shift - 6) & 15) & 3]; + buf[4] = bgPalette[data >> ((shift - 8) & 15) & 3]; + buf[5] = bgPalette[data >> ((shift - 10) & 15) & 3]; + buf[6] = bgPalette[data >> ((shift - 12) & 15) & 3]; + buf[7] = bgPalette[data >> ((shift - 14) & 15) & 3]; + buf += 8; + tilemappos += 8; + } + } +} + +static unsigned prioritizedBG_mask(const unsigned spx, const unsigned bgStart, const unsigned bgEnd, const unsigned scx, + const unsigned char *const tilemap, const unsigned char *const tiledata, const unsigned sign) { + const unsigned spStart = spx < bgStart + 8 ? bgStart + 8 - spx : 0; + + unsigned bgbyte; + + { + const unsigned pos = scx + spx - 8 + spStart; + unsigned tile = tilemap[pos >> 3 & 0x1F]; + const unsigned char *data = tiledata + tile * 16 - (tile & sign) * 32; + bgbyte = data[0] | data[1]; + const unsigned offset = pos & 7; + + if (offset) { + bgbyte <<= offset; + tile = tilemap[((pos >> 3) + 1) & 0x1F]; + data = tiledata + tile * 16 - (tile & sign) * 32; + bgbyte |= (data[0] | data[1]) >> (8 - offset); + } + } + + bgbyte >>= spStart; + const unsigned spEnd = spx > bgEnd ? bgEnd + 8 - spx : 8; + const unsigned mask = ~bgbyte | 0xFF >> spEnd; + + return mask; +} + +template +void LCD::drawSprites(T * const buffer_line, const unsigned ypos) { + const unsigned scy = scReader.scy() + ypos /*& 0xFF*/; + const unsigned wx = win.wxReader.wx() < 7 ? 0 : win.wxReader.wx() - 7; + const bool enableWindow = win.enabled(ypos); + const unsigned char *const spriteMapLine = spriteMapper.sprites(ypos); + + for (int i = spriteMapper.numSprites(ypos) - 1; i >= 0; --i) { + const unsigned spNrX2 = spriteMapLine[i]; + const unsigned spx = spriteMapper.posbuf()[spNrX2 + 1]; + + if (spx < 168 && spx) { + unsigned spLine = ypos + 16 - spriteMapper.posbuf()[spNrX2]; + unsigned spTile = spriteMapper.oamram()[spNrX2 * 2 + 2]; + const unsigned attributes = spriteMapper.oamram()[spNrX2 * 2 + 3]; + + if (spriteMapper.largeSprites(spNrX2 >> 1)) { + if (attributes & 0x40) //yflip + spLine = 15 - spLine; + + if (spLine < 8) + spTile &= 0xFE; + else { + spLine -= 8; + spTile |= 0x01; + } + } else { + if (attributes & 0x40) //yflip + spLine = 7 - spLine; + } + + const unsigned char *const data = vram + spTile * 16 + spLine * 2; + + unsigned byte1 = data[0]; + unsigned byte2 = data[1]; + + if (attributes & 0x20) { + byte1 = xflipt[byte1]; + byte2 = xflipt[byte2]; + } + + //(Sprites with priority-bit are still allowed to cover other sprites according to GBdev-faq.) + if (attributes & 0x80) { + unsigned mask = 0xFF; + + if (bgEnable && !(enableWindow && (wx == 0 || spx >= wx + 8u))) + mask = prioritizedBG_mask(spx, 0, enableWindow ? wx : 160, scReader.scx(), + bgTileMap + ((scy & 0xF8) << 2), bgTileData + ((scy & 7) << 1), tileIndexSign); + if (enableWindow && spx > wx) + mask &= prioritizedBG_mask(spx, wx, 160, 0u - wx, wdTileMap + ((winYPos & 0xF8) << 2), bgTileData + ((winYPos & 7) << 1), tileIndexSign); + + byte1 &= mask; + byte2 &= mask; + } + + const unsigned bytes = expand_lut[byte1] + expand_lut[byte2] * 2; + const unsigned long *const palette = spPalette + ((attributes >> 2) & 4); + + if (spx > 7 && spx < 161) { + T * const buf = buffer_line + spx - 8; + unsigned color; + + if ((color = bytes >> 14 )) + buf[0] = palette[color]; + if ((color = bytes >> 12 & 3)) + buf[1] = palette[color]; + if ((color = bytes >> 10 & 3)) + buf[2] = palette[color]; + if ((color = bytes >> 8 & 3)) + buf[3] = palette[color]; + if ((color = bytes >> 6 & 3)) + buf[4] = palette[color]; + if ((color = bytes >> 4 & 3)) + buf[5] = palette[color]; + if ((color = bytes >> 2 & 3)) + buf[6] = palette[color]; + if ((color = bytes & 3)) + buf[7] = palette[color]; + } else { + const unsigned end = spx >= 160 ? 160 : spx; + unsigned xpos = spx <= 8 ? 0 : (spx - 8); + unsigned shift = (7 - (xpos + 8 - spx)) * 2; + + while (xpos < end) { + if (const unsigned color = bytes >> shift & 3) + buffer_line[xpos] = palette[color]; + + shift -= 2; + ++xpos; + } + } + } + } +} diff --git a/supergameboy/libgambatte/src/video.h b/supergameboy/libgambatte/src/video.h new file mode 100644 index 00000000..7271d1b1 --- /dev/null +++ b/supergameboy/libgambatte/src/video.h @@ -0,0 +1,293 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_H +#define VIDEO_H + +namespace Gambatte { +class VideoBlitter; +struct FilterInfo; +} + +class Filter; +class SaveState; + +#include +#include +#include "event_queue.h" +#include "videoblitter.h" +#include "array.h" +#include "int.h" +#include "colorconversion.h" +#include "osd_element.h" + +#include "video/video_event_comparer.h" +#include "video/ly_counter.h" +#include "video/window.h" +#include "video/scx_reader.h" +#include "video/sprite_mapper.h" +#include "video/sc_reader.h" +#include "video/break_event.h" +#include "video/mode3_event.h" + +#include "video/lyc_irq.h" +#include "video/mode0_irq.h" +#include "video/mode1_irq.h" +#include "video/mode2_irq.h" +#include "video/irq_event.h" +#include "video/m3_extra_cycles.h" + +class LCD { + //static const uint8_t xflipt[0x100]; + unsigned long dmgColorsRgb32[3 * 4]; + unsigned long dmgColorsRgb16[3 * 4]; + unsigned long dmgColorsUyvy[3 * 4]; + + unsigned long bgPalette[8 * 4]; + unsigned long spPalette[8 * 4]; + + unsigned char bgpData[8 * 8]; + unsigned char objpData[8 * 8]; + + const unsigned char *const vram; + const unsigned char *bgTileData; + const unsigned char *bgTileMap; + const unsigned char *wdTileMap; + + Gambatte::VideoBlitter *vBlitter; + Filter *filter; + + void *dbuffer; + void (LCD::*draw)(unsigned xpos, unsigned ypos, unsigned endX); + unsigned long (*gbcToFormat)(unsigned bgr15); + const unsigned long *dmgColors; + + unsigned long lastUpdate; + unsigned long videoCycles; + + unsigned dpitch; + unsigned winYPos; + + event_queue m3EventQueue; + event_queue irqEventQueue; + event_queue vEventQueue; + + LyCounter lyCounter; + Window win; + ScxReader scxReader; + SpriteMapper spriteMapper; + M3ExtraCycles m3ExtraCycles; + ScReader scReader; + BreakEvent breakEvent; + Mode3Event mode3Event; + + LycIrq lycIrq; + Mode0Irq mode0Irq; + Mode1Irq mode1Irq; + Mode2Irq mode2Irq; + IrqEvent irqEvent; + + Gambatte::PixelBuffer pb; + Array tmpbuf; + Rgb32ToUyvy rgb32ToUyvy; + std::auto_ptr osdElement; + + std::vector filters; + + unsigned char drawStartCycle; + unsigned char scReadOffset; + unsigned char ifReg; + unsigned char tileIndexSign; + unsigned char statReg; + + bool doubleSpeed; + bool enabled; + bool cgb; + bool bgEnable; + bool spriteEnable; + + static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data); + void setDmgPaletteColor(unsigned index, unsigned long rgb32); + static unsigned long gbcToRgb32(unsigned bgr15); + static unsigned long gbcToRgb16(unsigned bgr15); + static unsigned long gbcToUyvy(unsigned bgr15); + + void refreshPalettes(); + void setDBuffer(); + void resetVideoState(unsigned long cycleCounter); + + void setDoubleSpeed(bool enabled); + + void event(); + + bool cgbpAccessible(unsigned long cycleCounter); + bool isMode0IrqPeriod(unsigned long cycleCounter); + bool isMode2IrqPeriod(unsigned long cycleCounter); + bool isLycIrqPeriod(unsigned lycReg, unsigned endCycles, unsigned long cycleCounter); + bool isMode1IrqPeriod(unsigned long cycleCounter); + + template void bg_drawPixels(T *buffer_line, unsigned xpos, unsigned end, unsigned scx, unsigned tilemappos, + const unsigned char *tilemap, const unsigned char *tiledata); + template void drawSprites(T *buffer_line, unsigned ypos); + + template void cgb_bg_drawPixels(T *buffer_line, unsigned xpos, unsigned end, unsigned scx, unsigned tilemappos, + const unsigned char *tilemap, const unsigned char *tiledata, unsigned tileline); + template void cgb_drawSprites(T *buffer_line, unsigned ypos); + + void null_draw(unsigned xpos, unsigned ypos, unsigned endX); + template void dmg_draw(unsigned xpos, unsigned ypos, unsigned endX); + template void cgb_draw(unsigned xpos, unsigned ypos, unsigned endX); + + void do_update(unsigned cycles); + +public: + void update(unsigned long cycleCounter); + + LCD(const unsigned char *oamram, const unsigned char *vram_in); + ~LCD(); + void reset(const unsigned char *oamram, bool cgb); + void setStatePtrs(SaveState &state); + void saveState(SaveState &state) const; + void loadState(const SaveState &state, const unsigned char *oamram); + void setVideoBlitter(Gambatte::VideoBlitter *vb); + void videoBufferChange(); + void setVideoFilter(unsigned n); + std::vector filterInfo() const; + unsigned videoWidth() const; + unsigned videoHeight() const; + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); + + void setOsdElement(std::auto_ptr osdElement) { + this->osdElement = osdElement; + } + + void wdTileMapSelectChange(bool newValue, unsigned long cycleCounter); + void bgTileMapSelectChange(bool newValue, unsigned long cycleCounter); + void bgTileDataSelectChange(bool newValue, unsigned long cycleCounter); + void bgEnableChange(bool newValue, unsigned long cycleCounter); + void spriteEnableChange(bool newValue, unsigned long cycleCounter); + + void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + bgpData[0] = data; + setDmgPalette(bgPalette, dmgColors, data); + } + + void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + objpData[0] = data; + setDmgPalette(spPalette, dmgColors + 4, data); + } + + void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) { + update(cycleCounter); + objpData[1] = data; + setDmgPalette(spPalette + 4, dmgColors + 8, data); + } + + void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { + if (bgpData[index] != data && cgbpAccessible(cycleCounter)) { + update(cycleCounter); + bgpData[index] = data; + index >>= 1; + bgPalette[index] = (*gbcToFormat)(bgpData[index << 1] | bgpData[(index << 1) + 1] << 8); + } + } + + void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { + if (objpData[index] != data && cgbpAccessible(cycleCounter)) { + update(cycleCounter); + objpData[index] = data; + index >>= 1; + spPalette[index] = (*gbcToFormat)(objpData[index << 1] | objpData[(index << 1) + 1] << 8); + } + } + + unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) { + return cgb & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF; + } + + unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) { + return cgb & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF; + } + + void updateScreen(unsigned long cc); + void enableChange(unsigned long cycleCounter); + void preResetCounter(unsigned long cycleCounter); + void postResetCounter(unsigned long oldCC, unsigned long cycleCounter); + void preSpeedChange(unsigned long cycleCounter); + void postSpeedChange(unsigned long cycleCounter); +// unsigned get_mode(unsigned cycleCounter) /*const*/; + bool vramAccessible(unsigned long cycleCounter); + bool oamAccessible(unsigned long cycleCounter); + void weChange(bool newValue, unsigned long cycleCounter); + void wxChange(unsigned newValue, unsigned long cycleCounter); + void wyChange(unsigned newValue, unsigned long cycleCounter); + void oamChange(unsigned long cycleCounter); + void oamChange(const unsigned char *oamram, unsigned long cycleCounter); + void scxChange(unsigned newScx, unsigned long cycleCounter); + void scyChange(unsigned newValue, unsigned long cycleCounter); + void spriteSizeChange(bool newLarge, unsigned long cycleCounter); + + void vramChange(const unsigned long cycleCounter) { + update(cycleCounter); + } + + unsigned get_stat(unsigned lycReg, unsigned long cycleCounter); + + unsigned getLyReg(const unsigned long cycleCounter) { + unsigned lyReg = 0; + + if (enabled) { + if (cycleCounter >= lyCounter.time()) + update(cycleCounter); + + lyReg = lyCounter.ly(); + + if (lyCounter.time() - cycleCounter <= 4) { + if (lyReg == 153) + lyReg = 0; + else + ++lyReg; + } else if (lyReg == 153) + lyReg = 0; + } + + return lyReg; + } + + unsigned long nextMode1IrqTime() const { + return mode1Irq.time(); + } + + void lyWrite(unsigned long cycleCounter); + void lcdstatChange(unsigned data, unsigned long cycleCounter); + void lycRegChange(unsigned data, unsigned long cycleCounter); + unsigned long nextIrqEvent() const; + unsigned getIfReg(unsigned long cycleCounter); + void setIfReg(unsigned ifReg_in, unsigned long cycleCounter); + + unsigned long nextHdmaTime(unsigned long cycleCounter); + bool isHdmaPeriod(unsigned long cycleCounter); + + unsigned long nextHdmaTimeInvalid() const { + return mode3Event.time(); + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/basic_add_event.cpp b/supergameboy/libgambatte/src/video/basic_add_event.cpp new file mode 100644 index 00000000..4bc57a09 --- /dev/null +++ b/supergameboy/libgambatte/src/video/basic_add_event.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "basic_add_event.h" +#include "../event_queue.h" + +void addEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + if (oldTime != newTime) { + e->setTime(newTime); + + if (newTime < oldTime) { + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.dec(e, e); + } else { + if (newTime == VideoEvent::DISABLED_TIME) + q.remove(e); + else + q.inc(e, e); + } + } +} + +void addUnconditionalEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + e->setTime(newTime); + + if (newTime < oldTime) { + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.dec(e, e); + } else if (oldTime != newTime) { + q.inc(e, e); + } +} + +void addFixedtimeEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + const unsigned long oldTime = e->time(); + + if (oldTime != newTime) { + e->setTime(newTime); + + if (oldTime == VideoEvent::DISABLED_TIME) + q.push(e); + else + q.remove(e); + } +} + +void addUnconditionalFixedtimeEvent(event_queue &q, VideoEvent *const e, const unsigned long newTime) { + if (e->time() == VideoEvent::DISABLED_TIME) { + e->setTime(newTime); + q.push(e); + } +} diff --git a/supergameboy/libgambatte/src/video/basic_add_event.h b/supergameboy/libgambatte/src/video/basic_add_event.h new file mode 100644 index 00000000..780d7191 --- /dev/null +++ b/supergameboy/libgambatte/src/video/basic_add_event.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BASIC_ADD_EVENT_H +#define BASIC_ADD_EVENT_H + +template class event_queue; + +#include "video_event.h" +#include "video_event_comparer.h" + +/*template +static inline void addEvent(T &event, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(lyCounter, cycleCounter); + queue.push(&event); + } +} + +template +static inline void addEvent(T &event, const unsigned data, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(data, lyCounter, cycleCounter); + queue.push(&event); + } +} + +template +static inline void addEvent(T &event, const unsigned data1, const unsigned data2, const LyCounter &lyCounter, const unsigned long cycleCounter, event_queue &queue) { + if (event.time() == VideoEvent::DISABLED_TIME) { + event.schedule(data1, data2, lyCounter, cycleCounter); + queue.push(&event); + } +}*/ + +void addEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addUnconditionalEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addFixedtimeEvent(event_queue &q, VideoEvent *e, unsigned long newTime); +void addUnconditionalFixedtimeEvent(event_queue &q, VideoEvent *e, unsigned long newTime); + +#endif diff --git a/supergameboy/libgambatte/src/video/break_event.cpp b/supergameboy/libgambatte/src/video/break_event.cpp new file mode 100644 index 00000000..e6e7ffbf --- /dev/null +++ b/supergameboy/libgambatte/src/video/break_event.cpp @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "break_event.h" + +BreakEvent::BreakEvent(unsigned char &drawStartCycle_in, unsigned char &scReadOffset_in) : + VideoEvent(3), + drawStartCycle(drawStartCycle_in), + scReadOffset(scReadOffset_in) +{ + setDoubleSpeed(false); + setScxSource(0); +} + +void BreakEvent::doEvent() { + scReadOffset = baseCycle; + drawStartCycle = baseCycle + (scxSrc & 7); + + setTime(DISABLED_TIME); +} diff --git a/supergameboy/libgambatte/src/video/break_event.h b/supergameboy/libgambatte/src/video/break_event.h new file mode 100644 index 00000000..9e7dcb82 --- /dev/null +++ b/supergameboy/libgambatte/src/video/break_event.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BREAK_EVENT_H +#define BREAK_EVENT_H + +#include "video_event.h" +#include "ly_counter.h" +#include "basic_add_event.h" + +class BreakEvent : public VideoEvent { + unsigned char &drawStartCycle; + unsigned char &scReadOffset; + + unsigned char scxSrc; + unsigned char baseCycle; + +public: + BreakEvent(unsigned char &drawStartCycle_in, unsigned char &scReadOffset_in); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter) { + return lyCounter.time(); + } + + void setDoubleSpeed(const bool dS) { + baseCycle = 90 + dS * 4; + } + + void setScxSource(const unsigned scxSrc_in) { + scxSrc = scxSrc_in; + } +}; + +static inline void addEvent(event_queue &q, BreakEvent *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, BreakEvent *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/catrom2x.cpp b/supergameboy/libgambatte/src/video/filters/catrom2x.cpp new file mode 100644 index 00000000..53a4c931 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/catrom2x.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "catrom2x.h" +#include "filterinfo.h" +#include + +struct Colorsum { + Gambatte::uint_least32_t r, g, b; +}; + +static void merge_columns(Gambatte::uint_least32_t *dest, const Colorsum *sums) { + unsigned w = 160; + + while (w--) { + { + Gambatte::uint_least32_t rsum = sums[1].r; + Gambatte::uint_least32_t gsum = sums[1].g; + Gambatte::uint_least32_t bsum = sums[1].b; + + if (rsum & 0x80000000) rsum = 0; + if (gsum & 0x80000000) gsum = 0; + if (bsum & 0x80000000) bsum = 0; + + rsum <<= 12; + rsum += 0x008000; + gsum >>= 4; + gsum += 0x0080; + bsum += 0x0008; + bsum >>= 4; + + if (rsum > 0xFF0000) rsum = 0xFF0000; + if (gsum > 0x00FF00) gsum = 0x00FF00; + if (bsum > 0x0000FF) bsum = 0x0000FF; + + *dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum; + } + + { + Gambatte::uint_least32_t rsum = sums[1].r * 9; + Gambatte::uint_least32_t gsum = sums[1].g * 9; + Gambatte::uint_least32_t bsum = sums[1].b * 9; + + rsum -= sums[0].r; + gsum -= sums[0].g; + bsum -= sums[0].b; + + rsum += sums[2].r * 9; + gsum += sums[2].g * 9; + bsum += sums[2].b * 9; + + rsum -= sums[3].r; + gsum -= sums[3].g; + bsum -= sums[3].b; + + if (rsum & 0x80000000) rsum = 0; + if (gsum & 0x80000000) gsum = 0; + if (bsum & 0x80000000) bsum = 0; + + rsum <<= 8; + rsum += 0x008000; + gsum >>= 8; + gsum += 0x000080; + bsum += 0x000080; + bsum >>= 8; + + if (rsum > 0xFF0000) rsum = 0xFF0000; + if (gsum > 0x00FF00) gsum = 0x00FF00; + if (bsum > 0x0000FF) bsum = 0x0000FF; + + *dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum; + } + + ++sums; + } +} + +static void filter(Gambatte::uint_least32_t *dline, const unsigned pitch, const Gambatte::uint_least32_t *sline) { + Colorsum sums[163]; + + for (unsigned h = 144; h--;) { + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + sum->r = pixel >> 12 & 0x000FF0 ; + pixel <<= 4; + sum->g = pixel & 0x0FF000; + sum->b = pixel & 0x000FF0; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 9; + unsigned long gsum = (pixel & 0x00FF00) * 9; + unsigned long bsum = (pixel & 0x0000FF) * 9; + + pixel = s[-1*163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + pixel = s[1*163]; + rsum += (pixel >> 16) * 9; + gsum += (pixel & 0x00FF00) * 9; + bsum += (pixel & 0x0000FF) * 9; + + pixel = s[2*163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + sline += 163; + } +} + +Catrom2x::Catrom2x() { + buffer = NULL; +} + +Catrom2x::~Catrom2x() { + delete []buffer; +} + +void Catrom2x::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[147 * 163]; + std::memset(buffer, 0, 147ul * 163 * sizeof(Gambatte::uint_least32_t)); +} + +void Catrom2x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Catrom2x::info() { + static Gambatte::FilterInfo fInfo = { "Bicubic Catmull-Rom Spline 2x", 160 * 2, 144 * 2 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Catrom2x::inBuffer() { + return buffer + 164; +} + +unsigned Catrom2x::inPitch() { + return 163; +} + +void Catrom2x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer + 163); +} diff --git a/supergameboy/libgambatte/src/video/filters/catrom2x.h b/supergameboy/libgambatte/src/video/filters/catrom2x.h new file mode 100644 index 00000000..df657f04 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/catrom2x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CATROM2X_H +#define CATROM2X_H + +#include "filter.h" + +struct FilterInfo; + +class Catrom2x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Catrom2x(); + ~Catrom2x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/catrom3x.cpp b/supergameboy/libgambatte/src/video/filters/catrom3x.cpp new file mode 100644 index 00000000..09a03f6a --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/catrom3x.cpp @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "catrom3x.h" +#include "filterinfo.h" +#include + +struct Colorsum { + Gambatte::uint_least32_t r, g, b; +}; + +static void merge_columns(Gambatte::uint_least32_t *dest, const Colorsum *sums) { + unsigned w = 160; + + while (w--) { + { + Gambatte::uint_least32_t rsum = sums[1].r; + Gambatte::uint_least32_t gsum = sums[1].g; + Gambatte::uint_least32_t bsum = sums[1].b; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 6869) + rsum = 0xFF0000; + else { + rsum *= 607; + rsum <<= 2; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 1758567) + gsum = 0xFF00; + else { + gsum *= 607; + gsum >>= 14; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 6869) + bsum = 0xFF; + else { + bsum *= 607; + bsum += 8192; + bsum >>= 14; + } + + /*rsum/=27; + rsum<<=8; + gsum/=27; + gsum<<=5; + bsum<<=4; + bsum+=27; + bsum/=54; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + { + Gambatte::uint_least32_t rsum = sums[1].r * 21; + Gambatte::uint_least32_t gsum = sums[1].g * 21; + Gambatte::uint_least32_t bsum = sums[1].b * 21; + + rsum -= sums[0].r << 1; + gsum -= sums[0].g << 1; + bsum -= sums[0].b << 1; + + rsum += sums[2].r * 9; + gsum += sums[2].g * 9; + bsum += sums[2].b * 9; + + rsum -= sums[3].r; + gsum -= sums[3].g; + bsum -= sums[3].b; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 185578) + rsum = 0xFF0000; + else { + rsum *= 719; + rsum >>= 3; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 47508223) + gsum = 0x00FF00; + else { + gsum >>= 8; + gsum *= 719; + gsum >>= 11; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 185578) + bsum = 0x0000FF; + else { + bsum *= 719; + bsum += 0x040000; + bsum >>= 19; + } + + /*rsum/=729; + rsum<<=8; + gsum/=729; + gsum<<=5; + bsum<<=4; + bsum+=729; + bsum/=1458; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + { + Gambatte::uint_least32_t rsum = sums[1].r * 9; + Gambatte::uint_least32_t gsum = sums[1].g * 9; + Gambatte::uint_least32_t bsum = sums[1].b * 9; + + rsum -= sums[0].r; + gsum -= sums[0].g; + bsum -= sums[0].b; + + rsum += sums[2].r * 21; + gsum += sums[2].g * 21; + bsum += sums[2].b * 21; + + rsum -= sums[3].r << 1; + gsum -= sums[3].g << 1; + bsum -= sums[3].b << 1; + + if (rsum & 0x80000000) + rsum = 0; + else if (rsum > 185578) + rsum = 0xFF0000; + else { + rsum *= 719; + rsum >>= 3; + rsum += 0x008000; + rsum &= 0xFF0000; + } + + if (gsum & 0x80000000) + gsum = 0; + else if (gsum > 47508223) + gsum = 0xFF00; + else { + gsum >>= 8; + gsum *= 719; + gsum >>= 11; + gsum += 0x000080; + gsum &= 0x00FF00; + } + + if (bsum & 0x80000000) + bsum = 0; + else if (bsum > 185578) + bsum = 0x0000FF; + else { + bsum *= 719; + bsum += 0x040000; + bsum >>= 19; + } + + /*rsum/=729; + rsum<<=8; + gsum/=729; + gsum<<=5; + bsum<<=4; + bsum+=729; + bsum/=1458; + rsum+=0x008000; + gsum+=0x000080; + + if(rsum>0xFF0000) rsum=0xFF0000; + if(gsum>0x00FF00) gsum=0x00FF00; + if(bsum>0x0000FF) bsum=0x0000FF;*/ + + *dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum; + } + ++sums; + } +} + +static void filter(Gambatte::uint_least32_t *dline, const unsigned pitch, const Gambatte::uint_least32_t *sline) { + Colorsum sums[163]; + + for (unsigned h = 144; h--;) { + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + const unsigned long pixel = *s; + sum->r = (pixel >> 16) * 27; + sum->g = (pixel & 0x00FF00) * 27; + sum->b = (pixel & 0x0000FF) * 27; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 21; + unsigned long gsum = (pixel & 0x00FF00) * 21; + unsigned long bsum = (pixel & 0x0000FF) * 21; + + pixel = s[-1 * 163]; + rsum -= (pixel >> 16) << 1; + pixel <<= 1; + gsum -= pixel & 0x01FE00; + bsum -= pixel & 0x0001FE; + + pixel = s[1 * 163]; + rsum += (pixel >> 16) * 9; + gsum += (pixel & 0x00FF00) * 9; + bsum += (pixel & 0x0000FF) * 9; + + pixel = s[2 * 163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + + { + const Gambatte::uint_least32_t *s = sline; + Colorsum *sum = sums; + unsigned n = 163; + + while (n--) { + unsigned long pixel = *s; + unsigned long rsum = (pixel >> 16) * 9; + unsigned long gsum = (pixel & 0x00FF00) * 9; + unsigned long bsum = (pixel & 0x0000FF) * 9; + + pixel = s[-1 * 163]; + rsum -= pixel >> 16; + gsum -= pixel & 0x00FF00; + bsum -= pixel & 0x0000FF; + + pixel = s[1 * 163]; + rsum += (pixel >> 16) * 21; + gsum += (pixel & 0x00FF00) * 21; + bsum += (pixel & 0x0000FF) * 21; + + pixel = s[2 * 163]; + rsum -= (pixel >> 16) << 1; + pixel <<= 1; + gsum -= pixel & 0x01FE00; + bsum -= pixel & 0x0001FE; + + sum->r = rsum; + sum->g = gsum; + sum->b = bsum; + + ++s; + ++sum; + } + } + + merge_columns(dline, sums); + dline += pitch; + sline += 163; + } +} + +Catrom3x::Catrom3x() { + buffer = NULL; +} + +Catrom3x::~Catrom3x() { + delete []buffer; +} + +void Catrom3x::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[147 * 163]; + std::memset(buffer, 0, 147ul * 163 * sizeof(Gambatte::uint_least32_t)); +} + +void Catrom3x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Catrom3x::info() { + static Gambatte::FilterInfo fInfo = { "Bicubic Catmull-Rom Spline 3x", 160 * 3, 144 * 3 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Catrom3x::inBuffer() { + return buffer + 164; +} + +unsigned Catrom3x::inPitch() { + return 163; +} + +void Catrom3x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer + 163); +} diff --git a/supergameboy/libgambatte/src/video/filters/catrom3x.h b/supergameboy/libgambatte/src/video/filters/catrom3x.h new file mode 100644 index 00000000..64f47827 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/catrom3x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CATROM3X_H +#define CATROM3X_H + +#include "filter.h" + +struct FilterInfo; + +class Catrom3x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Catrom3x(); + ~Catrom3x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/filter.h b/supergameboy/libgambatte/src/video/filters/filter.h new file mode 100644 index 00000000..72e3bf7d --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/filter.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef FILTER_H +#define FILTER_H + +#include "int.h" + +namespace Gambatte { +struct FilterInfo; +} + +class Filter { +public: + virtual ~Filter() {} + virtual void init() {}; + virtual void outit() {}; + virtual const Gambatte::FilterInfo& info() = 0; + virtual void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch) = 0; + virtual Gambatte::uint_least32_t* inBuffer() = 0; + virtual unsigned inPitch() = 0; +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/kreed2xsai.cpp b/supergameboy/libgambatte/src/video/filters/kreed2xsai.cpp new file mode 100644 index 00000000..70c261b3 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/kreed2xsai.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 1999 Derek Liauw Kie Fa (Kreed) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "kreed2xsai.h" +#include "filterinfo.h" +#include + +static inline int getResult1(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + int x = 0; + int y = 0; + int r = 0; + + if (a == c) ++x; + else if (b == c) ++y; + + if (a == d) ++x; + else if (b == d) ++y; + + if (x <= 1) ++r; + + if (y <= 1) --r; + + return r; +} + +static inline int getResult2(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + int x = 0; + int y = 0; + int r = 0; + + if (a == c) ++x; + else if (b == c) ++y; + + if (a == d) ++x; + else if (b == d) ++y; + + if (x <= 1) --r; + + if (y <= 1) ++r; + + return r; +} + +static inline unsigned long interpolate(const unsigned long a, const unsigned long b) { + return (a + b - ((a ^ b) & 0x010101)) >> 1; +} + +static inline unsigned long qInterpolate(const unsigned long a, const unsigned long b, const unsigned long c, const unsigned long d) { + const unsigned long lowBits = ((a & 0x030303) + (b & 0x030303) + (c & 0x030303) + (d & 0x030303)) & 0x030303; + + return (a + b + c + d - lowBits) >> 2; +} + +static void filter(Gambatte::uint_least32_t *dstPtr, const unsigned dstPitch, + const Gambatte::uint_least32_t *srcPtr, const unsigned srcPitch, const unsigned width, unsigned height) +{ + while (height--) { + const Gambatte::uint_least32_t *bP = srcPtr; + Gambatte::uint_least32_t *dP = dstPtr; + + for (unsigned finish = width; finish--;) { + register unsigned long colorA, colorB; + unsigned long colorC, colorD, + colorE, colorF, colorG, colorH, + colorI, colorJ, colorK, colorL, + + colorM, colorN, colorO, colorP; + unsigned long product, product1, product2; + + //--------------------------------------- + // Map of the pixels: I|E F|J + // G|A B|K + // H|C D|L + // M|N O|P + colorI = *(bP - srcPitch - 1); + colorE = *(bP - srcPitch); + colorF = *(bP - srcPitch + 1); + colorJ = *(bP - srcPitch + 2); + + colorG = *(bP - 1); + colorA = *(bP); + colorB = *(bP + 1); + colorK = *(bP + 2); + + colorH = *(bP + srcPitch - 1); + colorC = *(bP + srcPitch); + colorD = *(bP + srcPitch + 1); + colorL = *(bP + srcPitch + 2); + + colorM = *(bP + srcPitch * 2 - 1); + colorN = *(bP + srcPitch * 2); + colorO = *(bP + srcPitch * 2 + 1); + colorP = *(bP + srcPitch * 2 + 2); + + if (colorA == colorD && colorB != colorC) { + if ((colorA == colorE && colorB == colorL) || + (colorA == colorC && colorA == colorF + && colorB != colorE && colorB == colorJ)) { + product = colorA; + } else { + product = interpolate(colorA, colorB); + } + + if ((colorA == colorG && colorC == colorO) || + (colorA == colorB && colorA == colorH + && colorG != colorC && colorC == colorM)) { + product1 = colorA; + } else { + product1 = interpolate(colorA, colorC); + } + product2 = colorA; + } else if (colorB == colorC && colorA != colorD) { + if ((colorB == colorF && colorA == colorH) || + (colorB == colorE && colorB == colorD + && colorA != colorF && colorA == colorI)) { + product = colorB; + } else { + product = interpolate(colorA, colorB); + } + + if ((colorC == colorH && colorA == colorF) || + (colorC == colorG && colorC == colorD + && colorA != colorH && colorA == colorI)) { + product1 = colorC; + } else { + product1 = interpolate(colorA, colorC); + } + product2 = colorB; + } else if (colorA == colorD && colorB == colorC) { + if (colorA == colorB) { + product = colorA; + product1 = colorA; + product2 = colorA; + } else { + register int r = 0; + + product1 = interpolate(colorA, colorC); + product = interpolate(colorA, colorB); + + r += getResult1(colorA, colorB, colorG, colorE); + r += getResult2(colorB, colorA, colorK, colorF); + r += getResult2(colorB, colorA, colorH, colorN); + r += getResult1(colorA, colorB, colorL, colorO); + + if (r > 0) + product2 = colorA; + else if (r < 0) + product2 = colorB; + else { + product2 = qInterpolate(colorA, colorB, colorC, colorD); + } + } + } else { + product2 = qInterpolate(colorA, colorB, colorC, colorD); + + if (colorA == colorC && colorA == colorF + && colorB != colorE && colorB == colorJ) { + product = colorA; + } else if (colorB == colorE && colorB == colorD + && colorA != colorF && colorA == colorI) { + product = colorB; + } else { + product = interpolate(colorA, colorB); + } + + if (colorA == colorB && colorA == colorH + && colorG != colorC && colorC == colorM) { + product1 = colorA; + } else if (colorC == colorG && colorC == colorD + && colorA != colorH && colorA == colorI) { + product1 = colorC; + } else { + product1 = interpolate(colorA, colorC); + } + } + *dP = colorA; + *(dP + 1) = product; + *(dP + dstPitch) = product1; + *(dP + dstPitch + 1) = product2; + + ++bP; + dP += 2; + } + + srcPtr += srcPitch; + dstPtr += dstPitch * 2; + } +} + +Kreed_2xSaI::Kreed_2xSaI() { + buffer = NULL; +} + +Kreed_2xSaI::~Kreed_2xSaI() { + delete []buffer; +} + +void Kreed_2xSaI::init() { + delete []buffer; + + buffer = new Gambatte::uint_least32_t[145 * 161]; + std::memset(buffer, 0, 145ul * 161 * sizeof(Gambatte::uint_least32_t)); +} + +void Kreed_2xSaI::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& Kreed_2xSaI::info() { + static Gambatte::FilterInfo fInfo = { "Kreed's 2xSaI", 160 * 2, 144 * 2 }; + + return fInfo; +} + +Gambatte::uint_least32_t* Kreed_2xSaI::inBuffer() { + return buffer; +} + +unsigned Kreed_2xSaI::inPitch() { + return 161; +} + +void Kreed_2xSaI::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 161, 160, 144); +} diff --git a/supergameboy/libgambatte/src/video/filters/kreed2xsai.h b/supergameboy/libgambatte/src/video/filters/kreed2xsai.h new file mode 100644 index 00000000..f2feffc0 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/kreed2xsai.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef KREED2XSAI_H +#define KREED2XSAI_H + +#include "filter.h" + +struct FilterInfo; + +class Kreed_2xSaI : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + Kreed_2xSaI(); + ~Kreed_2xSaI(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/maxsthq2x.cpp b/supergameboy/libgambatte/src/video/filters/maxsthq2x.cpp new file mode 100644 index 00000000..a818d62a --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/maxsthq2x.cpp @@ -0,0 +1,2875 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 2003 MaxSt * + * maxst@hiend3d.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "maxsthq2x.h" +#include "filterinfo.h" +#include + +static /*inline*/ unsigned long Interp1(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x030303) * 3 + (c2 & 0x030303)) & 0x030303; + + return (c1 * 3 + c2 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp2(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x020202) + (c2 & 0x030303) + (c3 & 0x030303)) & 0x030303; + + return (c1 * 2 + c2 + c3 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp6(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 & 0x070707) * 5 + (c2 * 2 & 0x060606) + (c3 & 0x070707)) & 0x070707; + + return ((c1 * 5 + c2 * 2 + c3) - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp7(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 & 0x070707) * 6 + (c2 & 0x070707) + (c3 & 0x070707)) & 0x070707; + + return ((c1 * 6 + c2 + c3) - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp9(unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned lowbits = ((c1 * 2 & 0x070707) + ((c2 & 0x070707) + (c3 & 0x070707)) * 3) & 0x070707; + + return (c1 * 2 + (c2 + c3) * 3 - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp10(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned lowbits = ((c1 & 0x0F0F0F) * 14 + (c2 & 0x0F0F0F) + (c3 & 0x0F0F0F)) & 0x0F0F0F; + + return (c1 * 14 + c2 + c3 - lowbits) >> 4; +} + +#define PIXEL00_0 *pOut = w[5]; +#define PIXEL00_10 *pOut = Interp1(w[5], w[1]); +#define PIXEL00_11 *pOut = Interp1(w[5], w[4]); +#define PIXEL00_12 *pOut = Interp1(w[5], w[2]); +#define PIXEL00_20 *pOut = Interp2(w[5], w[4], w[2]); +#define PIXEL00_21 *pOut = Interp2(w[5], w[1], w[2]); +#define PIXEL00_22 *pOut = Interp2(w[5], w[1], w[4]); +#define PIXEL00_60 *pOut = Interp6(w[5], w[2], w[4]); +#define PIXEL00_61 *pOut = Interp6(w[5], w[4], w[2]); +#define PIXEL00_70 *pOut = Interp7(w[5], w[4], w[2]); +#define PIXEL00_90 *pOut = Interp9(w[5], w[4], w[2]); +#define PIXEL00_100 *pOut = Interp10(w[5], w[4], w[2]); +#define PIXEL01_0 *(pOut+1) = w[5]; +#define PIXEL01_10 *(pOut+1) = Interp1(w[5], w[3]); +#define PIXEL01_11 *(pOut+1) = Interp1(w[5], w[2]); +#define PIXEL01_12 *(pOut+1) = Interp1(w[5], w[6]); +#define PIXEL01_20 *(pOut+1) = Interp2(w[5], w[2], w[6]); +#define PIXEL01_21 *(pOut+1) = Interp2(w[5], w[3], w[6]); +#define PIXEL01_22 *(pOut+1) = Interp2(w[5], w[3], w[2]); +#define PIXEL01_60 *(pOut+1) = Interp6(w[5], w[6], w[2]); +#define PIXEL01_61 *(pOut+1) = Interp6(w[5], w[2], w[6]); +#define PIXEL01_70 *(pOut+1) = Interp7(w[5], w[2], w[6]); +#define PIXEL01_90 *(pOut+1) = Interp9(w[5], w[2], w[6]); +#define PIXEL01_100 *(pOut+1) = Interp10(w[5], w[2], w[6]); +#define PIXEL10_0 *(pOut+dstPitch) = w[5]; +#define PIXEL10_10 *(pOut+dstPitch) = Interp1(w[5], w[7]); +#define PIXEL10_11 *(pOut+dstPitch) = Interp1(w[5], w[8]); +#define PIXEL10_12 *(pOut+dstPitch) = Interp1(w[5], w[4]); +#define PIXEL10_20 *(pOut+dstPitch) = Interp2(w[5], w[8], w[4]); +#define PIXEL10_21 *(pOut+dstPitch) = Interp2(w[5], w[7], w[4]); +#define PIXEL10_22 *(pOut+dstPitch) = Interp2(w[5], w[7], w[8]); +#define PIXEL10_60 *(pOut+dstPitch) = Interp6(w[5], w[4], w[8]); +#define PIXEL10_61 *(pOut+dstPitch) = Interp6(w[5], w[8], w[4]); +#define PIXEL10_70 *(pOut+dstPitch) = Interp7(w[5], w[8], w[4]); +#define PIXEL10_90 *(pOut+dstPitch) = Interp9(w[5], w[8], w[4]); +#define PIXEL10_100 *(pOut+dstPitch) = Interp10(w[5], w[8], w[4]); +#define PIXEL11_0 *(pOut+dstPitch+1) = w[5]; +#define PIXEL11_10 *(pOut+dstPitch+1) = Interp1(w[5], w[9]); +#define PIXEL11_11 *(pOut+dstPitch+1) = Interp1(w[5], w[6]); +#define PIXEL11_12 *(pOut+dstPitch+1) = Interp1(w[5], w[8]); +#define PIXEL11_20 *(pOut+dstPitch+1) = Interp2(w[5], w[6], w[8]); +#define PIXEL11_21 *(pOut+dstPitch+1) = Interp2(w[5], w[9], w[8]); +#define PIXEL11_22 *(pOut+dstPitch+1) = Interp2(w[5], w[9], w[6]); +#define PIXEL11_60 *(pOut+dstPitch+1) = Interp6(w[5], w[8], w[6]); +#define PIXEL11_61 *(pOut+dstPitch+1) = Interp6(w[5], w[6], w[8]); +#define PIXEL11_70 *(pOut+dstPitch+1) = Interp7(w[5], w[6], w[8]); +#define PIXEL11_90 *(pOut+dstPitch+1) = Interp9(w[5], w[6], w[8]); +#define PIXEL11_100 *(pOut+dstPitch+1) = Interp10(w[5], w[6], w[8]); + +static /*inline*/ bool Diff(const unsigned long w1, const unsigned long w2) { + const unsigned rdiff = (w1 >> 16) - (w2 >> 16); + const unsigned gdiff = (w1 >> 8 & 0xFF) - (w2 >> 8 & 0xFF); + const unsigned bdiff = (w1 & 0xFF) - (w2 & 0xFF); + + return rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2; +} + +static void filter(Gambatte::uint_least32_t *pOut, const unsigned dstPitch, + const Gambatte::uint_least32_t *pIn, const unsigned Xres, const unsigned Yres) +{ + unsigned long w[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (unsigned j = 0; j < Yres; j++) { + const unsigned prevline = j > 0 ? Xres : 0; + const unsigned nextline = j < Yres - 1 ? Xres : 0; + + for (unsigned i = 0; i < Xres; i++) { + w[2] = *(pIn - prevline); + w[5] = *(pIn); + w[8] = *(pIn + nextline); + + if (i > 0) { + w[1] = *(pIn - prevline - 1); + w[4] = *(pIn - 1); + w[7] = *(pIn + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < Xres - 1) { + w[3] = *(pIn - prevline + 1); + w[6] = *(pIn + 1); + w[9] = *(pIn + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + unsigned pattern = 0; + + { + unsigned flag = 1; + + const unsigned r1 = w[5] >> 16; + const unsigned g1 = w[5] >> 8 & 0xFF; + const unsigned b1 = w[5] & 0xFF; + + for (unsigned k = 1; k < 10; ++k) { + if (k == 5) continue; + + if (w[k] != w[5]) { + const unsigned rdiff = r1 - (w[k] >> 16); + const unsigned gdiff = g1 - (w[k] >> 8 & 0xFF); + const unsigned bdiff = b1 - (w[k] & 0xFF); + + if (rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2) + pattern |= flag; + } + + flag <<= 1; + } + } + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_20 + PIXEL11_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_21 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_22 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_21 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_12 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_11 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_22 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 18: + case 50: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_20 + } + break; + } + case 72: + case 76: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 10: + case 138: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 66: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 24: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 22: + case 54: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 104: + case 108: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 11: + case 139: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 19: + case 51: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_10 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 146: + case 178: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 84: + case 85: + { + PIXEL00_20 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_10 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_22 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_10 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 200: + case 204: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 73: + case 77: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_10 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 42: + case 170: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 14: + case 142: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 67: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 70: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 28: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 152: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 194: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 98: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 56: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 25: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 26: + case 31: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 82: + case 214: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 88: + case 248: + { + PIXEL00_21 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 74: + case 107: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 27: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_21 + break; + } + case 86: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + PIXEL11_10 + break; + } + case 216: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 106: + { + PIXEL00_10 + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 30: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 210: + { + PIXEL00_22 + PIXEL01_10 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 120: + { + PIXEL00_21 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 75: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_22 + break; + } + case 29: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 198: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 184: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 99: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 57: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 71: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 156: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 226: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 60: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 195: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 102: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 153: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 58: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 83: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 92: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 202: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 78: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 154: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 114: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 89: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 90: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 55: + case 23: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 182: + case 150: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 213: + case 212: + { + PIXEL00_20 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_22 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 236: + case 232: + { + PIXEL00_21 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 109: + case 105: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 171: + case 43: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 143: + case 15: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 124: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 203: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_11 + break; + } + case 62: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 211: + { + PIXEL00_11 + PIXEL01_10 + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 118: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 217: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 110: + { + PIXEL00_10 + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 155: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_12 + break; + } + case 188: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 185: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 61: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 157: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 103: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 227: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 230: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 199: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 220: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 158: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 234: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_11 + break; + } + case 242: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 59: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 121: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 87: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 79: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 122: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 94: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 218: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 91: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 167: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 173: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 186: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 115: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 93: + { + PIXEL00_12 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 206: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 205: + case 201: + { + PIXEL00_12 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 174: + case 46: + { + if (Diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 179: + case 147: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 189: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 231: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 126: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 219: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 125: + { + if (Diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_11 + PIXEL11_10 + break; + } + case 221: + { + PIXEL00_12 + if (Diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_10 + break; + } + case 207: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_10 + PIXEL11_11 + break; + } + case 238: + { + PIXEL00_10 + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 190: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_11 + break; + } + case 187: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_10 + PIXEL11_12 + break; + } + case 243: + { + PIXEL00_11 + PIXEL01_10 + if (Diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 119: + { + if (Diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 237: + case 233: + { + PIXEL00_12 + PIXEL01_20 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 175: + case 47: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 183: + case 151: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 250: + { + PIXEL00_10 + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 123: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 95: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + PIXEL11_10 + break; + } + case 222: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 252: + { + PIXEL00_21 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 249: + { + PIXEL00_12 + PIXEL01_22 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 235: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 111: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 63: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 159: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 215: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_21 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 246: + { + PIXEL00_22 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 254: + { + PIXEL00_10 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 253: + { + PIXEL00_12 + PIXEL01_11 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 251: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 239: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 127: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 191: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 223: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_10 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 247: + { + PIXEL00_11 + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_12 + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 255: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (Diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + if (Diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (Diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + } + ++pIn; + pOut += 2; + } + pOut += dstPitch * 2 - Xres * 2; + } +} + +MaxSt_Hq2x::MaxSt_Hq2x() { + buffer = NULL; +} + +MaxSt_Hq2x::~MaxSt_Hq2x() { + outit(); +} + +void MaxSt_Hq2x::init() { + delete []buffer; + buffer = new Gambatte::uint_least32_t[144 * 160]; +} + +void MaxSt_Hq2x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& MaxSt_Hq2x::info() { + static const Gambatte::FilterInfo fInfo = { "MaxSt's Hq2x", 160 * 2, 144 * 2 }; + return fInfo; +} + +Gambatte::uint_least32_t* MaxSt_Hq2x::inBuffer() { + return buffer; +} + +unsigned MaxSt_Hq2x::inPitch() { + return 160; +} + +void MaxSt_Hq2x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 160, 144); +} diff --git a/supergameboy/libgambatte/src/video/filters/maxsthq2x.h b/supergameboy/libgambatte/src/video/filters/maxsthq2x.h new file mode 100644 index 00000000..ca2cf411 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/maxsthq2x.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAXSTHQ2X_H +#define MAXSTHQ2X_H + +#include "filter.h" + +struct FilterInfo; + +class MaxSt_Hq2x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + MaxSt_Hq2x(); + ~MaxSt_Hq2x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + + +#endif diff --git a/supergameboy/libgambatte/src/video/filters/maxsthq3x.cpp b/supergameboy/libgambatte/src/video/filters/maxsthq3x.cpp new file mode 100644 index 00000000..996a221e --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/maxsthq3x.cpp @@ -0,0 +1,3845 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * Copyright (C) 2003 MaxSt * + * maxst@hiend3d.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "maxsthq3x.h" +#include "filterinfo.h" +#include + +static /*inline*/ unsigned long Interp1(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x030303) * 3 + (c2 & 0x030303)) & 0x030303; + + return (c1 * 3 + c2 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp2(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x020202) + (c2 & 0x030303) + (c3 & 0x030303)) & 0x030303; + + return (c1 * 2 + c2 + c3 - lowbits) >> 2; +} + +static /*inline*/ unsigned long Interp3(const unsigned long c1, const unsigned long c2) { + const unsigned long lowbits = ((c1 & 0x070707) * 7 + (c2 & 0x070707)) & 0x070707; + + return (c1 * 7 + c2 - lowbits) >> 3; +} + +static /*inline*/ unsigned long Interp4(const unsigned long c1, const unsigned long c2, const unsigned long c3) { + const unsigned long lowbits = ((c1 * 2 & 0x0E0E0E) + ((c2 & 0x0F0F0F) + (c3 & 0x0F0F0F)) * 7) & 0x0F0F0F; + + return (c1 * 2 + (c2 + c3) * 7 - lowbits) >> 4; +} + +static /*inline*/ unsigned long Interp5(const unsigned long c1, const unsigned long c2) { + return (c1 + c2 - ((c1 ^ c2) & 0x010101)) >> 1; +} + +#define PIXEL00_1M *pOut = Interp1(w[5], w[1]); +#define PIXEL00_1U *pOut = Interp1(w[5], w[2]); +#define PIXEL00_1L *pOut = Interp1(w[5], w[4]); +#define PIXEL00_2 *pOut = Interp2(w[5], w[4], w[2]); +#define PIXEL00_4 *pOut = Interp4(w[5], w[4], w[2]); +#define PIXEL00_5 *pOut = Interp5(w[4], w[2]); +#define PIXEL00_C *pOut = w[5]; + +#define PIXEL01_1 *(pOut+1) = Interp1(w[5], w[2]); +#define PIXEL01_3 *(pOut+1) = Interp3(w[5], w[2]); +#define PIXEL01_6 *(pOut+1) = Interp1(w[2], w[5]); +#define PIXEL01_C *(pOut+1) = w[5]; + +#define PIXEL02_1M *(pOut+2) = Interp1(w[5], w[3]); +#define PIXEL02_1U *(pOut+2) = Interp1(w[5], w[2]); +#define PIXEL02_1R *(pOut+2) = Interp1(w[5], w[6]); +#define PIXEL02_2 *(pOut+2) = Interp2(w[5], w[2], w[6]); +#define PIXEL02_4 *(pOut+2) = Interp4(w[5], w[2], w[6]); +#define PIXEL02_5 *(pOut+2) = Interp5(w[2], w[6]); +#define PIXEL02_C *(pOut+2) = w[5]; + +#define PIXEL10_1 *(pOut+dstPitch) = Interp1(w[5], w[4]); +#define PIXEL10_3 *(pOut+dstPitch) = Interp3(w[5], w[4]); +#define PIXEL10_6 *(pOut+dstPitch) = Interp1(w[4], w[5]); +#define PIXEL10_C *(pOut+dstPitch) = w[5]; + +#define PIXEL11 *(pOut+dstPitch+1) = w[5]; + +#define PIXEL12_1 *(pOut+dstPitch+2) = Interp1(w[5], w[6]); +#define PIXEL12_3 *(pOut+dstPitch+2) = Interp3(w[5], w[6]); +#define PIXEL12_6 *(pOut+dstPitch+2) = Interp1(w[6], w[5]); +#define PIXEL12_C *(pOut+dstPitch+2) = w[5]; + +#define PIXEL20_1M *(pOut+dstPitch*2) = Interp1(w[5], w[7]); +#define PIXEL20_1D *(pOut+dstPitch*2) = Interp1(w[5], w[8]); +#define PIXEL20_1L *(pOut+dstPitch*2) = Interp1(w[5], w[4]); +#define PIXEL20_2 *(pOut+dstPitch*2) = Interp2(w[5], w[8], w[4]); +#define PIXEL20_4 *(pOut+dstPitch*2) = Interp4(w[5], w[8], w[4]); +#define PIXEL20_5 *(pOut+dstPitch*2) = Interp5(w[8], w[4]); +#define PIXEL20_C *(pOut+dstPitch*2) = w[5]; + +#define PIXEL21_1 *(pOut+dstPitch*2+1) = Interp1(w[5], w[8]); +#define PIXEL21_3 *(pOut+dstPitch*2+1) = Interp3(w[5], w[8]); +#define PIXEL21_6 *(pOut+dstPitch*2+1) = Interp1(w[8], w[5]); +#define PIXEL21_C *(pOut+dstPitch*2+1) = w[5]; + +#define PIXEL22_1M *(pOut+dstPitch*2+2) = Interp1(w[5], w[9]); +#define PIXEL22_1D *(pOut+dstPitch*2+2) = Interp1(w[5], w[8]); +#define PIXEL22_1R *(pOut+dstPitch*2+2) = Interp1(w[5], w[6]); +#define PIXEL22_2 *(pOut+dstPitch*2+2) = Interp2(w[5], w[6], w[8]); +#define PIXEL22_4 *(pOut+dstPitch*2+2) = Interp4(w[5], w[6], w[8]); +#define PIXEL22_5 *(pOut+dstPitch*2+2) = Interp5(w[6], w[8]); +#define PIXEL22_C *(pOut+dstPitch*2+2) = w[5]; + +static /*inline*/ bool Diff(const unsigned long w1, const unsigned long w2) { + const unsigned rdiff = (w1 >> 16) - (w2 >> 16); + const unsigned gdiff = (w1 >> 8 & 0xFF) - (w2 >> 8 & 0xFF); + const unsigned bdiff = (w1 & 0xFF) - (w2 & 0xFF); + + return rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2; +} + +static void filter(Gambatte::uint_least32_t *pOut, const unsigned dstPitch, + const Gambatte::uint_least32_t *pIn, const unsigned Xres, const unsigned Yres) +{ + unsigned long w[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (unsigned j = 0; j < Yres; j++) { + const unsigned prevline = j > 0 ? Xres : 0; + const unsigned nextline = j < Yres - 1 ? Xres : 0; + + for (unsigned i = 0; i < Xres; i++) { + w[2] = *(pIn - prevline); + w[5] = *(pIn); + w[8] = *(pIn + nextline); + + if (i > 0) { + w[1] = *(pIn - prevline - 1); + w[4] = *(pIn - 1); + w[7] = *(pIn + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < Xres - 1) { + w[3] = *(pIn - prevline + 1); + w[6] = *(pIn + 1); + w[9] = *(pIn + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + unsigned pattern = 0; + + { + unsigned flag = 1; + + const unsigned r1 = w[5] >> 16; + const unsigned g1 = w[5] >> 8 & 0xFF; + const unsigned b1 = w[5] & 0xFF; + + for (unsigned k = 1; k < 10; ++k) { + if (k == 5) continue; + + if (w[k] != w[5]) { + const unsigned rdiff = r1 - (w[k] >> 16); + const unsigned gdiff = g1 - (w[k] >> 8 & 0xFF); + const unsigned bdiff = b1 - (w[k] & 0xFF); + + if (rdiff + gdiff + bdiff + 0xC0U > 0xC0U * 2 || + rdiff - bdiff + 0x1CU > 0x1CU * 2 || + gdiff * 2 - rdiff - bdiff + 0x30U > 0x30U * 2) + pattern |= flag; + } + + flag <<= 1; + } + } + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 18: + case 50: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 80: + case 81: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 72: + case 76: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 10: + case 138: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 66: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 24: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 22: + case 54: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 208: + case 209: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 104: + case 108: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 11: + case 139: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 19: + case 51: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 146: + case 178: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 84: + case 85: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 112: + case 113: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 200: + case 204: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 73: + case 77: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 42: + case 170: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 14: + case 142: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 67: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 70: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 28: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 152: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 194: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 98: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 56: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 25: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 26: + case 31: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 82: + case 214: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 88: + case 248: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 74: + case 107: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 27: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 86: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 216: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 106: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 30: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 210: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 120: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 75: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 29: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 198: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 184: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 99: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 57: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 71: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 156: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 226: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 60: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 195: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 102: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 153: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 58: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 83: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 92: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 202: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 78: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 154: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 114: + { + PIXEL00_1M + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 89: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 90: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 55: + case 23: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 182: + case 150: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 213: + case 212: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 241: + case 240: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 236: + case 232: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 109: + case 105: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 171: + case 43: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 143: + case 15: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 124: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 203: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 62: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 211: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 118: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 217: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 110: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 155: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 188: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 185: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 61: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 157: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 103: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 227: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 230: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 199: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 220: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 158: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 234: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1R + break; + } + case 242: + { + PIXEL00_1M + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 59: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 121: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 87: + { + PIXEL00_1L + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 79: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 122: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 94: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 218: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 91: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 229: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 167: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 173: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 181: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 186: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 115: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 93: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 206: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 205: + case 201: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 174: + case 46: + { + if (Diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 179: + case 147: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 117: + case 116: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 189: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 231: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 126: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 219: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 125: + { + if (Diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + break; + } + case 221: + { + if (Diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + break; + } + case 207: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 238: + { + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + break; + } + case 190: + { + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + break; + } + case 187: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + break; + } + case 243: + { + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 119: + { + if (Diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 237: + case 233: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 175: + case 47: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 183: + case 151: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 245: + case 244: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 250: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 123: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 95: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 222: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 252: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 249: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 235: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 111: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 63: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 159: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 215: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 246: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 254: + { + PIXEL00_1M + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_2 + } + break; + } + case 253: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 251: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_2 + PIXEL21_3 + } + if (Diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 239: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 127: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_2 + PIXEL01_3 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (Diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 191: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 223: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + if (Diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_2 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + if (Diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 247: + { + PIXEL00_1L + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 255: + { + if (Diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (Diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (Diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (Diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + } + ++pIn; + pOut += 3; + } + pOut += dstPitch * 3 - Xres * 3; + } +} + +MaxSt_Hq3x::MaxSt_Hq3x() { + buffer = NULL; +} + +MaxSt_Hq3x::~MaxSt_Hq3x() { + outit(); +} + +void MaxSt_Hq3x::init() { + delete []buffer; + buffer = new Gambatte::uint_least32_t[144 * 160]; +} + +void MaxSt_Hq3x::outit() { + delete []buffer; + buffer = NULL; +} + +const Gambatte::FilterInfo& MaxSt_Hq3x::info() { + static const Gambatte::FilterInfo fInfo = { "MaxSt's Hq3x", 160 * 3, 144 * 3 }; + return fInfo; +} + +Gambatte::uint_least32_t* MaxSt_Hq3x::inBuffer() { + return buffer; +} + +unsigned MaxSt_Hq3x::inPitch() { + return 160; +} + +void MaxSt_Hq3x::filter(Gambatte::uint_least32_t *const dbuffer, const unsigned pitch) { + ::filter(dbuffer, pitch, buffer, 160, 144); +} diff --git a/supergameboy/libgambatte/src/video/filters/maxsthq3x.h b/supergameboy/libgambatte/src/video/filters/maxsthq3x.h new file mode 100644 index 00000000..9e1f51d6 --- /dev/null +++ b/supergameboy/libgambatte/src/video/filters/maxsthq3x.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MAXSTHQ3X_H +#define MAXSTHQ3X_H + +#include "filter.h" + +struct FilterInfo; + +class MaxSt_Hq3x : public Filter { + Gambatte::uint_least32_t *buffer; + +public: + MaxSt_Hq3x(); + ~MaxSt_Hq3x(); + void init(); + void outit(); + const Gambatte::FilterInfo& info(); + void filter(Gambatte::uint_least32_t *dbuffer, unsigned pitch); + Gambatte::uint_least32_t* inBuffer(); + unsigned inPitch(); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/irq_event.cpp b/supergameboy/libgambatte/src/video/irq_event.cpp new file mode 100644 index 00000000..358f1daf --- /dev/null +++ b/supergameboy/libgambatte/src/video/irq_event.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "irq_event.h" + +IrqEvent::IrqEvent(event_queue &irqEventQueue_in) : + VideoEvent(11), + irqEventQueue(irqEventQueue_in) +{ +} + +void IrqEvent::doEvent() { + irqEventQueue.top()->doEvent(); + + if (irqEventQueue.top()->time() == DISABLED_TIME) + irqEventQueue.pop(); + else + irqEventQueue.modify_root(irqEventQueue.top()); + + setTime(schedule(irqEventQueue)); +} diff --git a/supergameboy/libgambatte/src/video/irq_event.h b/supergameboy/libgambatte/src/video/irq_event.h new file mode 100644 index 00000000..c8a5b991 --- /dev/null +++ b/supergameboy/libgambatte/src/video/irq_event.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef VIDEO_IRQ_EVENT_H +#define VIDEO_IRQ_EVENT_H + +#include "../event_queue.h" +#include "video_event.h" +#include "video_event_comparer.h" +#include "basic_add_event.h" + +class IrqEvent : public VideoEvent { + event_queue &irqEventQueue; + +public: + IrqEvent(event_queue &irqEventQueue_in); + + void doEvent(); + + static unsigned long schedule(const event_queue &irqEventQueue) { + return irqEventQueue.top()->time(); + } + + void schedule() { + setTime(irqEventQueue.top()->time()); + } +}; + +static inline void addEvent(event_queue &q, IrqEvent *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, IrqEvent *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/ly_counter.cpp b/supergameboy/libgambatte/src/video/ly_counter.cpp new file mode 100644 index 00000000..5d5b6d98 --- /dev/null +++ b/supergameboy/libgambatte/src/video/ly_counter.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ly_counter.h" +#include "../savestate.h" + +LyCounter::LyCounter() : VideoEvent(0) { + setDoubleSpeed(false); + reset(0, 0); +} + +void LyCounter::doEvent() { + ++ly_; + + if (ly_ == 154) + ly_ = 0; + + setTime(time() + lineTime_); +} + +unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const { + unsigned long tmp = time() + (lineCycle << ds); + + if (tmp - cycleCounter > lineTime_) + tmp -= lineTime_; + + return tmp; +} + +unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const { + unsigned long tmp = time() + (((153U - ly()) * 456U + frameCycle) << ds); + + if (tmp - cycleCounter > 70224U << ds) + tmp -= 70224U << ds; + + return tmp; +} + +void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) { + ly_ = videoCycles / 456; + setTime(lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed())); +} + +void LyCounter::setDoubleSpeed(const bool ds_in) { + ds = ds_in; + lineTime_ = 456U << ds_in; +} diff --git a/supergameboy/libgambatte/src/video/ly_counter.h b/supergameboy/libgambatte/src/video/ly_counter.h new file mode 100644 index 00000000..2b795fb8 --- /dev/null +++ b/supergameboy/libgambatte/src/video/ly_counter.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LY_COUNTER_H +#define LY_COUNTER_H + +class SaveState; + +#include "video_event.h" +#include "basic_add_event.h" + +class LyCounter : public VideoEvent { + unsigned short lineTime_; + unsigned char ly_; + bool ds; + +public: + LyCounter(); + + void doEvent(); + + bool isDoubleSpeed() const { + return ds; + } + + unsigned lineCycles(const unsigned long cc) const { + return 456u - ((time() - cc) >> isDoubleSpeed()); + } + + unsigned lineTime() const { + return lineTime_; + } + + unsigned ly() const { + return ly_; + } + + unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const; + unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const; + + void reset(unsigned long videoCycles, unsigned long lastUpdate); + + void setDoubleSpeed(bool ds_in); +}; + +static inline void addEvent(event_queue &q, LyCounter *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, LyCounter *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/lyc_irq.cpp b/supergameboy/libgambatte/src/video/lyc_irq.cpp new file mode 100644 index 00000000..eb81d41b --- /dev/null +++ b/supergameboy/libgambatte/src/video/lyc_irq.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "lyc_irq.h" + +LycIrq::LycIrq(unsigned char &ifReg_in) : + VideoEvent(1), + ifReg(ifReg_in) +{ + setDoubleSpeed(false); + setM2IrqEnabled(false); + setLycReg(0); + setSkip(false); +} + +void LycIrq::doEvent() { + if (!skip && (!m2IrqEnabled || lycReg_ > 143 || lycReg_ == 0)) + ifReg |= 0x2; + + skip = false; + + setTime(time() + frameTime); +} + +unsigned long LycIrq::schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return ((statReg & 0x40) && lycReg < 154) ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cycleCounter) : static_cast(DISABLED_TIME); +} diff --git a/supergameboy/libgambatte/src/video/lyc_irq.h b/supergameboy/libgambatte/src/video/lyc_irq.h new file mode 100644 index 00000000..ed93fdda --- /dev/null +++ b/supergameboy/libgambatte/src/video/lyc_irq.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_LYC_IRQ_H +#define VIDEO_LYC_IRQ_H + +#include "ly_counter.h" + +class LycIrq : public VideoEvent { + unsigned char &ifReg; + unsigned long frameTime; + unsigned char lycReg_; + bool m2IrqEnabled; + bool skip; + +public: + LycIrq(unsigned char &ifReg_in); + + void doEvent(); + + unsigned lycReg() const { + return lycReg_; + } + + static unsigned long schedule(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cycleCounter); + + void setDoubleSpeed(const bool ds) { + frameTime = 70224 << ds; + } + + void setLycReg(const unsigned lycReg_in) { + lycReg_ = lycReg_in; + } + + void setM2IrqEnabled(const bool enabled) { + m2IrqEnabled = enabled; + } + + void setSkip(const bool skip) { + this->skip = skip; + } + + bool skips() const { + return skip; + } + + bool isSkipPeriod(const unsigned long cycleCounter, const bool doubleSpeed) const { + return lycReg_ > 0 && time() - cycleCounter > 4U >> doubleSpeed && time() - cycleCounter < 9; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/m3_extra_cycles.cpp b/supergameboy/libgambatte/src/video/m3_extra_cycles.cpp new file mode 100644 index 00000000..de4eadb7 --- /dev/null +++ b/supergameboy/libgambatte/src/video/m3_extra_cycles.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "m3_extra_cycles.h" +#include "scx_reader.h" +#include "window.h" +#include "sprite_mapper.h" +#include "../insertion_sort.h" + +M3ExtraCycles::M3ExtraCycles(const SpriteMapper &spriteMapper, + const ScxReader &scxReader, + const Window &win) : + spriteMapper(spriteMapper), + scxReader(scxReader), + win(win) +{ + invalidateCache(); +} + +static const unsigned char* addLineCycles(const unsigned char *const start, const unsigned char *const end, + const unsigned maxSpx, const unsigned scwxAnd7, const unsigned char *const posbuf_plus1, unsigned char *cycles_out) { + unsigned sum = 0; + + const unsigned char *a = start; + + for (; a < end; ++a) { + const unsigned spx = posbuf_plus1[*a]; + + if (spx > maxSpx) + break; + + unsigned cycles = 6; + const unsigned posAnd7 = (scwxAnd7 + spx) & 7; + + if (posAnd7 < 5) { + cycles = 11 - posAnd7; + + for (const unsigned char *b = a; b > start;) { + const unsigned bSpx = posbuf_plus1[*--b]; + + if (spx - bSpx > 4U) + break; + + if (((scwxAnd7 + bSpx) & 7) < 4 || spx == bSpx) { + cycles = 6; + break; + } + } + } + + sum += cycles; + } + + *cycles_out += sum; + + return a; +} + +void M3ExtraCycles::updateLine(const unsigned ly) const { + const bool windowEnabled = win.enabled(ly); + + cycles[ly] = windowEnabled ? scxReader.scxAnd7() + 6 : scxReader.scxAnd7(); + + const unsigned numSprites = spriteMapper.numSprites(ly); + + if (numSprites == 0) + return; + + unsigned char sortBuf[10]; + const unsigned char *tmp = spriteMapper.sprites(ly); + + if (spriteMapper.isCgb()) { + std::memcpy(sortBuf, tmp, sizeof(sortBuf)); + insertionSort(sortBuf, sortBuf + numSprites, SpriteMapper::SpxLess(spriteMapper.posbuf())); + tmp = sortBuf; + } + + const unsigned char *const tmpend = tmp + numSprites; + const unsigned char *const posbuf_plus1 = spriteMapper.posbuf() + 1; + + if (windowEnabled) { + addLineCycles(addLineCycles(tmp, tmpend, win.wxReader.wx(), scxReader.scxAnd7(), posbuf_plus1, cycles + ly), + tmpend, 167, 7 - win.wxReader.wx(), posbuf_plus1, cycles + ly); + } else + addLineCycles(tmp, tmpend, 167, scxReader.scxAnd7(), posbuf_plus1, cycles + ly); +} diff --git a/supergameboy/libgambatte/src/video/m3_extra_cycles.h b/supergameboy/libgambatte/src/video/m3_extra_cycles.h new file mode 100644 index 00000000..8a7f1470 --- /dev/null +++ b/supergameboy/libgambatte/src/video/m3_extra_cycles.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_M3_EXTRA_CYCLES_H +#define VIDEO_M3_EXTRA_CYCLES_H + +class ScxReader; +class Window; +class SpriteMapper; + +#include + +class M3ExtraCycles { + enum { CYCLES_INVALID = 0xFF }; + + mutable unsigned char cycles[144]; + + const SpriteMapper &spriteMapper; + const ScxReader &scxReader; + const Window &win; + + void updateLine(unsigned ly) const; + +public: + M3ExtraCycles(const SpriteMapper &spriteMapper, + const ScxReader &scxReader_in, + const Window &win); + + void invalidateCache() { + std::memset(cycles, CYCLES_INVALID, sizeof(cycles)); + } + + unsigned operator()(const unsigned ly) const { + if (cycles[ly] == CYCLES_INVALID) + updateLine(ly); + + return cycles[ly]; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/mode0_irq.cpp b/supergameboy/libgambatte/src/video/mode0_irq.cpp new file mode 100644 index 00000000..041d3db1 --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode0_irq.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "mode0_irq.h" + +#include "ly_counter.h" +#include "lyc_irq.h" +#include "m3_extra_cycles.h" + +Mode0Irq::Mode0Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + const M3ExtraCycles &m3ExtraCycles_in, unsigned char &ifReg_in) : + VideoEvent(0), + lyCounter(lyCounter_in), + lycIrq(lycIrq_in), + m3ExtraCycles(m3ExtraCycles_in), + ifReg(ifReg_in) +{ +} + +static unsigned baseCycle(const bool ds) { + return 80 + 169 + ds * 3 + 1 - ds; +} + +void Mode0Irq::doEvent() { + if (lycIrq.time() == DISABLED_TIME || lyCounter.ly() != lycIrq.lycReg()) + ifReg |= 2; + + unsigned long nextTime = lyCounter.time(); + unsigned nextLy = lyCounter.ly() + 1; + + if (nextLy == 144) { + nextLy = 0; + nextTime += lyCounter.lineTime() * 10; + } + + nextTime += (baseCycle(lyCounter.isDoubleSpeed()) + m3ExtraCycles(nextLy)) << lyCounter.isDoubleSpeed(); + + setTime(nextTime); +} + +void Mode0Irq::mode3CyclesChange() { + unsigned long nextTime = lyCounter.time() - lyCounter.lineTime(); + unsigned nextLy = lyCounter.ly(); + + if (time() > lyCounter.time()) { + nextTime += lyCounter.lineTime(); + ++nextLy; + + if (nextLy > 143) { + nextTime += lyCounter.lineTime() * (154 - nextLy); + nextLy = 0; + } + } + + nextTime += (baseCycle(lyCounter.isDoubleSpeed()) + m3ExtraCycles(nextLy)) << lyCounter.isDoubleSpeed(); + + setTime(nextTime); +} + +unsigned long Mode0Irq::schedule(const unsigned statReg, const M3ExtraCycles &m3ExtraCycles, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if (!(statReg & 0x08)) + return DISABLED_TIME; + + unsigned line = lyCounter.ly(); + int next = static_cast(baseCycle(lyCounter.isDoubleSpeed())) - static_cast(lyCounter.lineCycles(cycleCounter)); + + if (line < 144 && next + static_cast(m3ExtraCycles(line)) <= 0) { + next += 456; + ++line; + } + + if (line > 143) { + next += (154 - line) * 456; + line = 0; + } + + next += m3ExtraCycles(line); + + return cycleCounter + (static_cast(next) << lyCounter.isDoubleSpeed()); +} diff --git a/supergameboy/libgambatte/src/video/mode0_irq.h b/supergameboy/libgambatte/src/video/mode0_irq.h new file mode 100644 index 00000000..bc5f1540 --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode0_irq.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE0_IRQ_H +#define VIDEO_MODE0_IRQ_H + +class LycIrq; +class M3ExtraCycles; + +#include "ly_counter.h" + +class Mode0Irq : public VideoEvent { + const LyCounter &lyCounter; + const LycIrq &lycIrq; + const M3ExtraCycles &m3ExtraCycles; + unsigned char &ifReg; + +public: + Mode0Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + const M3ExtraCycles &m3ExtraCycles_in, unsigned char &ifReg_in); + + void doEvent(); + void mode3CyclesChange(); + static unsigned long schedule(unsigned statReg, const M3ExtraCycles &m3ExtraCycles, const LyCounter &lyCounter, unsigned long cycleCounter); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/mode1_irq.cpp b/supergameboy/libgambatte/src/video/mode1_irq.cpp new file mode 100644 index 00000000..ddafe25c --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode1_irq.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode1_irq.h" + +Mode1Irq::Mode1Irq(unsigned char &ifReg_in) : + VideoEvent(0), + ifReg(ifReg_in) +{ + setDoubleSpeed(false); + setM1StatIrqEnabled(false); +} + +void Mode1Irq::doEvent() { + ifReg |= flags; + + setTime(time() + frameTime); +} diff --git a/supergameboy/libgambatte/src/video/mode1_irq.h b/supergameboy/libgambatte/src/video/mode1_irq.h new file mode 100644 index 00000000..f4e6270f --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode1_irq.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE1_IRQ_H +#define VIDEO_MODE1_IRQ_H + +#include "ly_counter.h" +#include "basic_add_event.h" + +class Mode1Irq : public VideoEvent { + unsigned char &ifReg; + unsigned long frameTime; + unsigned char flags; + +public: + Mode1Irq(unsigned char &ifReg_in); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, unsigned long cycleCounter) { + return lyCounter.nextFrameCycle(144 * 456, cycleCounter); + } + + void setDoubleSpeed(const bool ds) { + frameTime = 70224 << ds; + } + + void setM1StatIrqEnabled(const bool enabled) { + flags = (enabled * 2) | 1; + } +}; + +static inline void addEvent(event_queue &q, Mode1Irq *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Mode1Irq *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/mode2_irq.cpp b/supergameboy/libgambatte/src/video/mode2_irq.cpp new file mode 100644 index 00000000..b1a419ea --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode2_irq.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode2_irq.h" + +#include "ly_counter.h" +#include "lyc_irq.h" + +Mode2Irq::Mode2Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + unsigned char &ifReg_in) : + VideoEvent(0), + lyCounter(lyCounter_in), + lycIrq(lycIrq_in), + ifReg(ifReg_in) +{ +} + +void Mode2Irq::doEvent() { + const unsigned ly = lyCounter.time() - time() < 8 ? (lyCounter.ly() == 153 ? 0 : lyCounter.ly() + 1) : lyCounter.ly(); + + if (lycIrq.time() == DISABLED_TIME || (lycIrq.lycReg() != 0 && ly != (lycIrq.lycReg() + 1U)) || (lycIrq.lycReg() == 0 && ly > 1)) + ifReg |= 2; + + setTime(time() + lyCounter.lineTime()); + + if (ly == 0) + setTime(time() - 4); + else if (ly == 143) + setTime(time() + lyCounter.lineTime() * 10 + 4); +} + +unsigned long Mode2Irq::schedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if ((statReg & 0x28) != 0x20) + return DISABLED_TIME; + + unsigned next = lyCounter.time() - cycleCounter; + + if (lyCounter.ly() >= 143 || (lyCounter.ly() == 142 && next <= 4)) { + next += (153u - lyCounter.ly()) * lyCounter.lineTime(); + } else { + if (next <= 4) + next += lyCounter.lineTime(); + + next -= 4; + } + + return cycleCounter + next; +} diff --git a/supergameboy/libgambatte/src/video/mode2_irq.h b/supergameboy/libgambatte/src/video/mode2_irq.h new file mode 100644 index 00000000..2ea86055 --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode2_irq.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_MODE2_IRQ_H +#define VIDEO_MODE2_IRQ_H + +class LycIrq; + +#include "ly_counter.h" +#include "basic_add_event.h" + +class Mode2Irq : public VideoEvent { + const LyCounter &lyCounter; + const LycIrq &lycIrq; + unsigned char &ifReg; + +public: + Mode2Irq(const LyCounter &lyCounter_in, const LycIrq &lycIrq_in, + unsigned char &ifReg_in); + + void doEvent(); + static unsigned long schedule(unsigned statReg, const LyCounter &lyCounter, unsigned long cycleCounter); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/mode3_event.cpp b/supergameboy/libgambatte/src/video/mode3_event.cpp new file mode 100644 index 00000000..84502315 --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode3_event.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "mode3_event.h" +#include "mode0_irq.h" +#include "irq_event.h" + +Mode3Event::Mode3Event(event_queue &m3EventQueue_in, + event_queue &vEventQueue_in, + Mode0Irq &mode0Irq_in, IrqEvent &irqEvent_in) : + VideoEvent(1), + m3EventQueue(m3EventQueue_in), + vEventQueue(vEventQueue_in), + mode0Irq(mode0Irq_in), + irqEvent(irqEvent_in) +{ +} + +void Mode3Event::doEvent() { + m3EventQueue.top()->doEvent(); + + if (m3EventQueue.top()->time() == DISABLED_TIME) + m3EventQueue.pop(); + else + m3EventQueue.modify_root(m3EventQueue.top()); + + if (mode0Irq.time() != DISABLED_TIME) { + const unsigned long oldTime = mode0Irq.time(); + mode0Irq.mode3CyclesChange(); + + if (mode0Irq.time() != oldTime) { + // position in irqEventQueue should remain the same. + // The same may be possible for vEventQueue, with some precautions. + if (irqEvent.time() == oldTime) { + irqEvent.schedule(); + + if (mode0Irq.time() > oldTime) + vEventQueue.inc(&irqEvent, &irqEvent); + else + vEventQueue.dec(&irqEvent, &irqEvent); + } + + } + } + + setTime(schedule(m3EventQueue)); +} diff --git a/supergameboy/libgambatte/src/video/mode3_event.h b/supergameboy/libgambatte/src/video/mode3_event.h new file mode 100644 index 00000000..7f9aedc6 --- /dev/null +++ b/supergameboy/libgambatte/src/video/mode3_event.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MODE3_EVENT_H +#define MODE3_EVENT_H + +class Mode0Irq; +class IrqEvent; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "../event_queue.h" + +class Mode3Event : public VideoEvent { + event_queue &m3EventQueue; + event_queue &vEventQueue; + Mode0Irq &mode0Irq; + IrqEvent &irqEvent; + +public: + Mode3Event(event_queue &m3EventQueue_in, + event_queue &vEventQueue_in, + Mode0Irq &mode0Irq_in, IrqEvent &irqEvent_in); + + void doEvent(); + + static unsigned long schedule(const event_queue &m3EventQueue) { + return m3EventQueue.empty() ? static_cast(DISABLED_TIME) : m3EventQueue.top()->time(); + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/sc_reader.cpp b/supergameboy/libgambatte/src/video/sc_reader.cpp new file mode 100644 index 00000000..fff2f66c --- /dev/null +++ b/supergameboy/libgambatte/src/video/sc_reader.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sc_reader.h" + +#include "../event_queue.h" +#include "../savestate.h" + +ScReader::ScReader() : VideoEvent(2) { + setDoubleSpeed(false); + setScxSource(0); + setScySource(0); + scx_[1] = scx_[0] = scxSrc; + scy_[1] = scy_[0] = scySrc; +} + +void ScReader::doEvent() { + scy_[0] = scy_[1]; + scy_[1] = scySrc; + scx_[0] = scx_[1]; + scx_[1] = scxSrc; + + if ((scy_[0] ^ scy_[1]) | (scx_[0] ^ scx_[1])) + setTime(time() + incCycles); + else + setTime(DISABLED_TIME); + +} + +void ScReader::saveState(SaveState &state) const { + state.ppu.scx[0] = scx_[0]; + state.ppu.scx[1] = scx_[1]; + state.ppu.scy[0] = scy_[0]; + state.ppu.scy[1] = scy_[1]; +} + +void ScReader::loadState(const SaveState &state) { + scx_[0] = state.ppu.scx[0]; + scx_[1] = state.ppu.scx[1]; + scy_[0] = state.ppu.scy[0]; + scy_[1] = state.ppu.scy[1]; +} + +void ScReader::setDoubleSpeed(const bool dS_in) { + dS = dS_in; + incCycles = 8u << dS_in; +} diff --git a/supergameboy/libgambatte/src/video/sc_reader.h b/supergameboy/libgambatte/src/video/sc_reader.h new file mode 100644 index 00000000..0d7ef7d1 --- /dev/null +++ b/supergameboy/libgambatte/src/video/sc_reader.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SC_READER_H +#define SC_READER_H + +class SaveState; +template class event_queue; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "basic_add_event.h" + +class ScReader : public VideoEvent { + unsigned char scx_[2]; + unsigned char scy_[2]; + + unsigned char scxSrc; + unsigned char scySrc; + unsigned char incCycles; + bool dS; + +public: + ScReader(); + + void doEvent(); + + unsigned scx() const { + return /*(*/scx_[0]/* & ~0x7) | (scxSrc & 0x7)*/; + } + + unsigned scy() const { + return scy_[0]; + } + + static unsigned long schedule(const unsigned long lastUpdate, const unsigned long videoCycles, const unsigned scReadOffset, const bool dS) { + return lastUpdate + ((8u - ((videoCycles - scReadOffset) & 7)) << dS); + } + + void setDoubleSpeed(bool dS_in); + + void setScxSource(const unsigned scxSrc_in) { + scxSrc = scxSrc_in; + } + + void setScySource(const unsigned scySrc_in) { + scySrc = scySrc_in; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, ScReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, ScReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/scx_reader.cpp b/supergameboy/libgambatte/src/video/scx_reader.cpp new file mode 100644 index 00000000..6baa97f9 --- /dev/null +++ b/supergameboy/libgambatte/src/video/scx_reader.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "scx_reader.h" + +#include "../event_queue.h" +#include "m3_extra_cycles.h" +#include "../savestate.h" + +ScxReader::ScxReader(event_queue &m3EventQueue_in, +// VideoEvent &wyReader3_in, + VideoEvent &wxReader_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles) : + VideoEvent(1), + m3EventQueue(m3EventQueue_in), +// wyReader3(wyReader3_in), + wxReader(wxReader_in), + weEnableChecker(weEnableChecker_in), + weDisableChecker(weDisableChecker_in), + m3ExtraCycles(m3ExtraCycles) +{ + setDoubleSpeed(false); + setSource(0); + scxAnd7_ = src; +} + +static void rescheduleEvent(event_queue &m3EventQueue, VideoEvent& event, const unsigned long diff) { + if (event.time() != VideoEvent::DISABLED_TIME) { + event.setTime(event.time() + diff); + (diff & 0x10) ? m3EventQueue.dec(&event, &event) : m3EventQueue.inc(&event, &event); + } +} + +void ScxReader::doEvent() { + const unsigned long diff = (static_cast(src) - static_cast(scxAnd7_)) << dS; + scxAnd7_ = src; + +// rescheduleEvent(m3EventQueue, wyReader3, diff); + rescheduleEvent(m3EventQueue, wxReader, diff); + rescheduleEvent(m3EventQueue, weEnableChecker, diff); + rescheduleEvent(m3EventQueue, weDisableChecker, diff); + + m3ExtraCycles.invalidateCache(); + + setTime(DISABLED_TIME); +} + +void ScxReader::saveState(SaveState &state) const { + state.ppu.scxAnd7 = scxAnd7_; +} + +void ScxReader::loadState(const SaveState &state) { + scxAnd7_ = state.ppu.scxAnd7; +} diff --git a/supergameboy/libgambatte/src/video/scx_reader.h b/supergameboy/libgambatte/src/video/scx_reader.h new file mode 100644 index 00000000..f92f8b2b --- /dev/null +++ b/supergameboy/libgambatte/src/video/scx_reader.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SCX_READER_H +#define SCX_READER_H + +template class event_queue; +class M3ExtraCycles; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" + +class ScxReader : public VideoEvent { + event_queue &m3EventQueue; +// VideoEvent &wyReader3; + VideoEvent &wxReader; + VideoEvent &weEnableChecker; + VideoEvent &weDisableChecker; + M3ExtraCycles &m3ExtraCycles; + + unsigned char scxAnd7_; + unsigned char src; + bool dS; + +public: + ScxReader(event_queue &m3EventQueue_in, +// VideoEvent &wyReader3_in, + VideoEvent &wxReader_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + unsigned getSource() const { + return src; + } + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(82 + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + + unsigned scxAnd7() const { + return scxAnd7_; + } + + void setDoubleSpeed(const bool dS_in) { + dS = dS_in; + } + + void setSource(const unsigned scxSrc) { + src = scxSrc & 7; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, ScxReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, ScxReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/sprite_mapper.cpp b/supergameboy/libgambatte/src/video/sprite_mapper.cpp new file mode 100644 index 00000000..f1e9cd97 --- /dev/null +++ b/supergameboy/libgambatte/src/video/sprite_mapper.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre AamÃ¥s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "sprite_mapper.h" +#include "m3_extra_cycles.h" +#include "../insertion_sort.h" +#include + +#include + +SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram) +: lyCounter(lyCounter), oamram(oamram) { + reset(oamram); +} + +void SpriteMapper::OamReader::reset(const unsigned char *const oamram) { + this->oamram = oamram; + setLargeSpritesSrc(false); + lu = 0; + lastChange = 0xFF; + std::fill_n(szbuf, 40, largeSpritesSrc); + + unsigned pos = 0; + unsigned distance = 80; + + while (distance--) { + buf[pos] = oamram[((pos * 2) & ~3) | (pos & 1)]; + ++pos; + } +} + +static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) { + unsigned lc = lyCounter.lineCycles(cc) + 4 - lyCounter.isDoubleSpeed() * 3u; + + if (lc >= 456) + lc -= 456; + + return lc >> 1; +} + +void SpriteMapper::OamReader::update(const unsigned long cc) { + if (cc > lu) { + if (changed()) { + const unsigned lulc = toPosCycles(lu, lyCounter); + + unsigned pos = std::min(lulc, 40u); + unsigned distance = 40; + + if ((cc - lu) >> lyCounter.isDoubleSpeed() < 456) { + const unsigned cclc = toPosCycles(cc, lyCounter); + + distance = std::min(cclc, 40u) - pos + (cclc < lulc ? 40 : 0); + } + + { + const unsigned targetDistance = lastChange - pos + (lastChange <= pos ? 40 : 0); + + if (targetDistance <= distance) { + distance = targetDistance; + lastChange = 0xFF; + } + } + + while (distance--) { + if (pos >= 40) + pos = 0; + + szbuf[pos] = largeSpritesSrc; + buf[pos * 2] = oamram[pos * 4]; + buf[pos * 2 + 1] = oamram[pos * 4 + 1]; + + ++pos; + } + } + + lu = cc; + } +} + +void SpriteMapper::OamReader::change(const unsigned long cc) { + update(cc); + lastChange = std::min(toPosCycles(lu, lyCounter), 40u); +} + +void SpriteMapper::OamReader::setStatePtrs(SaveState &state) { + state.ppu.oamReaderBuf.set(buf, sizeof buf); + state.ppu.oamReaderSzbuf.set(szbuf, sizeof(szbuf) / sizeof(bool)); +} + +void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) { + std::memset(buf, 0x00, sizeof(buf)); + std::fill(szbuf, szbuf + 40, false); + lu = cc + 160; + lastChange = 40; +} + +bool SpriteMapper::OamReader::oamAccessible(const unsigned long cycleCounter, const M3ExtraCycles &m3ExtraCycles) const { + unsigned ly = lyCounter.ly(); + unsigned lc = lyCounter.lineCycles(cycleCounter) + 4 - lyCounter.isDoubleSpeed() * 3u; + + if (lc >= 456) { + lc -= 456; + ++ly; + } + + return cycleCounter < lu || ly >= 144 || lc >= 80 + 173 + m3ExtraCycles(ly); +} + +SpriteMapper::SpriteMapper(M3ExtraCycles &m3ExtraCycles, + const LyCounter &lyCounter, + const unsigned char *const oamram) : + VideoEvent(2), + m3ExtraCycles(m3ExtraCycles), + oamReader(lyCounter, oamram), + cgb(false) +{ + clearMap(); +} + +void SpriteMapper::reset(const unsigned char *const oamram, const bool cgb_in) { + oamReader.reset(oamram); + cgb = cgb_in; + clearMap(); +} + +void SpriteMapper::clearMap() { + std::memset(num, cgb ? 0 : NEED_SORTING_MASK, sizeof(num)); +} + +void SpriteMapper::mapSprites() { + clearMap(); + + for (unsigned i = 0x00; i < 0x50; i += 2) { + const unsigned spriteHeight = 8u << largeSprites(i >> 1); + const unsigned bottom_pos = posbuf()[i] - (17u - spriteHeight); + + if (bottom_pos >= 143 + spriteHeight) + continue; + + unsigned char *map = spritemap; + unsigned char *n = num; + + if (bottom_pos >= spriteHeight) { + const unsigned startly = bottom_pos + 1 - spriteHeight; + n += startly; + map += startly * 10; + } + + unsigned char *const end = num + (bottom_pos >= 143 ? 143 : bottom_pos); + + do { + if ((*n & ~NEED_SORTING_MASK) < 10) + map[(*n)++ & ~NEED_SORTING_MASK] = i; + + map += 10; + ++n; + } while (n <= end); + } + + m3ExtraCycles.invalidateCache(); +} + +void SpriteMapper::sortLine(const unsigned ly) const { + num[ly] &= ~NEED_SORTING_MASK; + insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf())); +} + +void SpriteMapper::doEvent() { + oamReader.update(time()); + mapSprites(); + setTime(oamReader.changed() ? time() + oamReader.lyCounter.lineTime() : static_cast(DISABLED_TIME)); +} diff --git a/supergameboy/libgambatte/src/video/sprite_mapper.h b/supergameboy/libgambatte/src/video/sprite_mapper.h new file mode 100644 index 00000000..25b8090b --- /dev/null +++ b/supergameboy/libgambatte/src/video/sprite_mapper.h @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SPRITE_MAPPER_H +#define SPRITE_MAPPER_H + +#include "video_event.h" +//#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" +#include "../savestate.h" + +class M3ExtraCycles; +class SaveState; + +class SpriteMapper : public VideoEvent { + class OamReader { + unsigned char buf[80]; + bool szbuf[40]; + + public: + const LyCounter &lyCounter; + + private: + const unsigned char *oamram; + unsigned long lu; + unsigned char lastChange; + bool largeSpritesSrc; + + public: + OamReader(const LyCounter &lyCounter, const unsigned char *oamram); + void reset(const unsigned char *oamram); + void change(unsigned long cc); + void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; } + bool changed() const { return lastChange != 0xFF; } + bool largeSprites(unsigned spNr) const { return szbuf[spNr]; } + const unsigned char *oam() const { return oamram; } + void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu = lu + newCc - oldCc; } + void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; } + void update(unsigned long cc); + const unsigned char *spritePosBuf() const { return buf; } + void setStatePtrs(SaveState &state); + void enableDisplay(unsigned long cc); + void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; } + void loadState(const SaveState &state) { lu = state.ppu.enableDisplayM0Time; } + void resetVideoState() { change(lu); } + bool oamAccessible(unsigned long cycleCounter, const M3ExtraCycles &m3ExtraCycles) const; + bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; } + }; + + enum { NEED_SORTING_MASK = 0x80 }; + +public: + class SpxLess { + const unsigned char *const posbuf_plus1; + + public: + SpxLess(const unsigned char *const posbuf) : posbuf_plus1(posbuf + 1) {} + + bool operator()(const unsigned char l, const unsigned char r) const { + return posbuf_plus1[l] < posbuf_plus1[r]; + } + }; + +private: + mutable unsigned char spritemap[144*10]; + mutable unsigned char num[144]; + + M3ExtraCycles &m3ExtraCycles; + OamReader oamReader; + + bool cgb; + + void clearMap(); + void mapSprites(); + void sortLine(unsigned ly) const; + +public: + SpriteMapper(M3ExtraCycles &m3ExtraCycles, + const LyCounter &lyCounter, + const unsigned char *oamram_in); + void reset(const unsigned char *oamram, bool cgb_in); + void doEvent(); + bool isCgb() const { return cgb; } + bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); } + unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; } + void oamChange(const unsigned long cc) { oamReader.change(cc); } + void oamChange(const unsigned char *oamram, const unsigned long cc) { oamReader.change(oamram, cc); } + const unsigned char *oamram() const { return oamReader.oam(); } + const unsigned char *posbuf() const { return oamReader.spritePosBuf(); } + void preCounterChange(const unsigned long cc) { oamReader.update(cc); } + + void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { + oamReader.resetCycleCounter(oldCc, newCc); + } + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(80, cycleCounter); + } + + void setLargeSpritesSource(const bool src) { oamReader.setLargeSpritesSrc(src); } + + const unsigned char* sprites(const unsigned ly) const { + if (num[ly] & NEED_SORTING_MASK) + sortLine(ly); + + return spritemap + ly * 10; + } + + void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); } + void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); } + void saveState(SaveState &state) const { oamReader.saveState(state); } + void loadState(const SaveState &state) { oamReader.loadState(state); } + void resetVideoState() { oamReader.resetVideoState(); } + + bool oamAccessible(unsigned long cycleCounter) const { + return oamReader.oamAccessible(cycleCounter, m3ExtraCycles); + } + + bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { + return oamReader.inactivePeriodAfterDisplayEnable(cc); + } +}; + +static inline void addEvent(event_queue &q, SpriteMapper *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, SpriteMapper *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/video_event.h b/supergameboy/libgambatte/src/video/video_event.h new file mode 100644 index 00000000..fb64d5b2 --- /dev/null +++ b/supergameboy/libgambatte/src/video/video_event.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_EVENT_H +#define VIDEO_EVENT_H + +class VideoEvent { + unsigned long time_; + const unsigned char priority_; + +public: + enum { DISABLED_TIME = 0xFFFFFFFFu }; + + VideoEvent(const unsigned priority_in) : + time_(DISABLED_TIME), + priority_(priority_in) + {} + + virtual ~VideoEvent() {} + virtual void doEvent() = 0; + + unsigned priority() const { + return priority_; + } + + unsigned long time() const { + return time_; + } + + void setTime(const unsigned long time_in) { + time_ = time_in; + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/video_event_comparer.h b/supergameboy/libgambatte/src/video/video_event_comparer.h new file mode 100644 index 00000000..4eb25969 --- /dev/null +++ b/supergameboy/libgambatte/src/video/video_event_comparer.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef VIDEO_EVENT_COMPARER_H +#define VIDEO_EVENT_COMPARER_H + +#include "video_event.h" + +class VideoEventComparer { +public: + bool less(const VideoEvent *const a, const VideoEvent *const b) const { + return a->time() < b->time() || (a->time() == b->time() && a->priority() < b->priority()); + } +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/we.cpp b/supergameboy/libgambatte/src/video/we.cpp new file mode 100644 index 00000000..d5e66c47 --- /dev/null +++ b/supergameboy/libgambatte/src/video/we.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "we.h" +#include "../savestate.h" + +We::WeEnableChecker::WeEnableChecker(We &we) : + VideoEvent(8), + we(we) +{} + +void We::WeEnableChecker::doEvent() { + we.set(we.src_); + + setTime(DISABLED_TIME); +} + +We::WeDisableChecker::WeDisableChecker(We &we) : + VideoEvent(9), + we(we) +{} + +void We::WeDisableChecker::doEvent() { + we.set(we.we_ & we.src_); + + setTime(DISABLED_TIME); +} + +We::We(M3ExtraCycles &m3ExtraCycles) : + m3ExtraCycles_(m3ExtraCycles), + enableChecker_(*this), + disableChecker_(*this) +{ + setSource(false); + we_ = src_; +} + +void We::saveState(SaveState &state) const { + state.ppu.lcdc = (state.ppu.lcdc & ~0x20) | we_ << 5; +} + +void We::loadState(const SaveState &state) { + we_ = state.ppu.lcdc >> 5 & 1; +} diff --git a/supergameboy/libgambatte/src/video/we.h b/supergameboy/libgambatte/src/video/we.h new file mode 100644 index 00000000..d800ca11 --- /dev/null +++ b/supergameboy/libgambatte/src/video/we.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WE_H +#define WE_H + +class SaveState; + +#include "video_event.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" +#include "basic_add_event.h" + +class We { +public: + class WeEnableChecker : public VideoEvent { + We &we; + + public: + WeEnableChecker(We &we); + + void doEvent(); + + static unsigned long schedule(const unsigned scxAnd7, const unsigned wx, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 82 + wx + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + }; + + class WeDisableChecker : public VideoEvent { + We &we; + + public: + WeDisableChecker(We &we); + + void doEvent(); + + static unsigned long schedule(const unsigned scxAnd7, const unsigned wx, const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 88 + wx + lyCounter.isDoubleSpeed() * 3, cycleCounter); + } + }; + + friend class WeEnableChecker; + friend class WeDisableChecker; + +private: + M3ExtraCycles &m3ExtraCycles_; + WeEnableChecker enableChecker_; + WeDisableChecker disableChecker_; + + bool we_; + bool src_; + + void set(const bool value) { + if (we_ != value) + m3ExtraCycles_.invalidateCache(); + + we_ = value; + } + +public: + We(M3ExtraCycles &m3ExtraCycles); + + WeDisableChecker& disableChecker() { + return disableChecker_; + } + + WeEnableChecker& enableChecker() { + return enableChecker_; + } + + bool getSource() const { + return src_; + } + + void setSource(const bool src) { + src_ = src; + } + + bool value() const { + return we_; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, We::WeEnableChecker *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, We::WeEnableChecker *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, We::WeDisableChecker *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, We::WeDisableChecker *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/we_master_checker.cpp b/supergameboy/libgambatte/src/video/we_master_checker.cpp new file mode 100644 index 00000000..bff81585 --- /dev/null +++ b/supergameboy/libgambatte/src/video/we_master_checker.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "we_master_checker.h" + +#include "event_queue.h" +#include "wy.h" +#include "basic_add_event.h" +#include "../savestate.h" + +WeMasterChecker::WeMasterChecker(event_queue &m3EventQueue_in, + Wy &wy_in, + const LyCounter &lyCounter_in, + M3ExtraCycles &m3ExtraCycles) : + VideoEvent(10), + m3EventQueue(m3EventQueue_in), + wy(wy_in), + lyCounter(lyCounter_in), + m3ExtraCycles(m3ExtraCycles) +{ + weMaster_ = false; +} + +void WeMasterChecker::doEvent() { +// if (wy.value() >= lyCounter.ly()) { + if (!weMaster_ /*&& src */&& wy.value() == lyCounter.ly()) { + wy.weirdAssWeMasterEnableOnWyLineCase(); + addEvent(m3EventQueue, &wy.reader4(), Wy::WyReader4::schedule(lyCounter, time())); + } + + set(true); +// } + + setTime(time() + (70224U << lyCounter.isDoubleSpeed())); +} + +void WeMasterChecker::saveState(SaveState &state) const { + state.ppu.weMaster = weMaster_; +} + +void WeMasterChecker::loadState(const SaveState &state) { + weMaster_ = state.ppu.weMaster; +} diff --git a/supergameboy/libgambatte/src/video/we_master_checker.h b/supergameboy/libgambatte/src/video/we_master_checker.h new file mode 100644 index 00000000..cf1f1209 --- /dev/null +++ b/supergameboy/libgambatte/src/video/we_master_checker.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WE_MASTER_CHECKER_H +#define WE_MASTER_CHECKER_H + +template class event_queue; +class Wy; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" + +class WeMasterChecker : public VideoEvent { + event_queue &m3EventQueue; + Wy &wy; + const LyCounter &lyCounter; + M3ExtraCycles &m3ExtraCycles; + + bool weMaster_; + + void set(const bool value) { + if (weMaster_ != value) + m3ExtraCycles.invalidateCache(); + + weMaster_ = value; + } + +public: + WeMasterChecker(event_queue &m3EventQueue_in, + Wy &wy_in, + const LyCounter &lyCounter_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + static unsigned long schedule(const unsigned wySrc, const bool weSrc, const LyCounter &lyCounter, const unsigned long cycleCounter) { + if (weSrc && wySrc < 143) + return lyCounter.nextFrameCycle(wySrc * 456ul + 448 + lyCounter.isDoubleSpeed() * 4, cycleCounter); + else + return DISABLED_TIME; + } + + void unset() { + set(false); + } + + bool weMaster() const { + return weMaster_; + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +#endif diff --git a/supergameboy/libgambatte/src/video/window.h b/supergameboy/libgambatte/src/video/window.h new file mode 100644 index 00000000..790d612c --- /dev/null +++ b/supergameboy/libgambatte/src/video/window.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WINDOW_H +#define WINDOW_H + +#include "we.h" +#include "we_master_checker.h" +#include "wy.h" +#include "wx_reader.h" + +struct Window { + We we; + WeMasterChecker weMasterChecker; + Wy wyReg; + WxReader wxReader; + + Window(event_queue &m3EventQueue, + const LyCounter &lyCounter, + M3ExtraCycles &m3ExtraCycles) : + we(m3ExtraCycles), + weMasterChecker(m3EventQueue, wyReg, lyCounter, m3ExtraCycles), + wyReg(lyCounter, weMasterChecker, m3ExtraCycles), + wxReader(m3EventQueue, we.enableChecker(), we.disableChecker(), m3ExtraCycles) + {} + + bool enabled(const unsigned ly) const { + return we.value() && wxReader.wx() < 0xA7 && ly >= wyReg.value() && (weMasterChecker.weMaster() || ly == wyReg.value()); + } +}; + +#endif /*WINDOW_H*/ diff --git a/supergameboy/libgambatte/src/video/wx_reader.cpp b/supergameboy/libgambatte/src/video/wx_reader.cpp new file mode 100644 index 00000000..80a6b640 --- /dev/null +++ b/supergameboy/libgambatte/src/video/wx_reader.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aam�s * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "wx_reader.h" + +#include "../event_queue.h" +#include "m3_extra_cycles.h" +#include "../savestate.h" + +WxReader::WxReader(event_queue &m3EventQueue, + VideoEvent &weEnableChecker, + VideoEvent &weDisableChecker, + M3ExtraCycles &m3ExtraCycles) : +VideoEvent(7), +m3EventQueue(m3EventQueue), +weEnableChecker(weEnableChecker), +weDisableChecker(weDisableChecker), +m3ExtraCycles(m3ExtraCycles) +{ + setDoubleSpeed(false); + setSource(0); + wx_ = src_; +} + +static void rescheduleEvent(event_queue &m3EventQueue, VideoEvent& event, const unsigned long diff) { + if (event.time() != VideoEvent::DISABLED_TIME) { + event.setTime(event.time() + diff); + (diff & 0x200) ? m3EventQueue.dec(&event, &event) : m3EventQueue.inc(&event, &event); + } +} + +void WxReader::doEvent() { + const unsigned long diff = (static_cast(src_) - static_cast(wx_)) << dS; + wx_ = src_; + + rescheduleEvent(m3EventQueue, weEnableChecker, diff); + rescheduleEvent(m3EventQueue, weDisableChecker, diff); + + m3ExtraCycles.invalidateCache(); + + setTime(DISABLED_TIME); +} + +void WxReader::saveState(SaveState &state) const { + state.ppu.wx = wx_; +} + +void WxReader::loadState(const SaveState &state) { + wx_ = state.ppu.wx; +} diff --git a/supergameboy/libgambatte/src/video/wx_reader.h b/supergameboy/libgambatte/src/video/wx_reader.h new file mode 100644 index 00000000..1681f8a4 --- /dev/null +++ b/supergameboy/libgambatte/src/video/wx_reader.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WX_READER_H +#define WX_READER_H + +template class event_queue; +class M3ExtraCycles; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "basic_add_event.h" +#include + +class WxReader : public VideoEvent { + event_queue &m3EventQueue; + VideoEvent &weEnableChecker; + VideoEvent &weDisableChecker; + M3ExtraCycles &m3ExtraCycles; + + unsigned char wx_; + unsigned char src_; + bool dS; + +public: + WxReader(event_queue &m3EventQueue_in, + VideoEvent &weEnableChecker_in, + VideoEvent &weDisableChecker_in, + M3ExtraCycles &m3ExtraCycles); + + void doEvent(); + + unsigned getSource() const { + return src_; + } + + unsigned wx() const { + return wx_; + } + + void setDoubleSpeed(const bool dS_in) { + dS = dS_in; + } + + void setSource(const unsigned src) { + src_ = src; + } + + static unsigned long schedule(const unsigned scxAnd7, const LyCounter &lyCounter, const WxReader &wxReader, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(scxAnd7 + 82 + lyCounter.isDoubleSpeed() * 3 + std::min(wxReader.getSource(), wxReader.wx()), cycleCounter); + //setTime(lyCounter.nextLineCycle(scxAnd7 + 89 + lyCounter.isDoubleSpeed() * 3, cycleCounter)); + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, WxReader *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, WxReader *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/libgambatte/src/video/wy.cpp b/supergameboy/libgambatte/src/video/wy.cpp new file mode 100644 index 00000000..64a5f725 --- /dev/null +++ b/supergameboy/libgambatte/src/video/wy.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "wy.h" + +#include "we_master_checker.h" +#include "scx_reader.h" +#include "../event_queue.h" +#include "../savestate.h" + +Wy::WyReader1::WyReader1(Wy &wy, const WeMasterChecker &weMasterChecker) : + VideoEvent(3), + wy(wy), + weMasterChecker(weMasterChecker) +{} + +void Wy::WyReader1::doEvent() { + if (wy.src_ >= wy.lyCounter.ly() && /*wy >= lyCounter.ly()*/ !weMasterChecker.weMaster()) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::WyReader2::WyReader2(Wy &wy) : + VideoEvent(4), + wy(wy) +{} + +void Wy::WyReader2::doEvent() { + if (wy.wy_ == wy.lyCounter.ly() + 1 - wy.lyCounter.isDoubleSpeed() && wy.src_ > wy.wy_) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::WyReader3::WyReader3(Wy &wy) : + VideoEvent(5), + wy(wy) +{} + +void Wy::WyReader3::doEvent() { + if (wy.src_ == wy.lyCounter.ly() && wy.wy_ > wy.lyCounter.ly()) + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +unsigned long Wy::WyReader3::schedule(const unsigned wxSrc, const ScxReader &scxReader, const LyCounter &lyCounter, const unsigned long cycleCounter) { + const unsigned curLineCycle = 456 - ((lyCounter.time() - cycleCounter) >> lyCounter.isDoubleSpeed()); + const unsigned baseTime = 78 + lyCounter.isDoubleSpeed() * 6 + wxSrc; + + if (curLineCycle >= 82U + lyCounter.isDoubleSpeed() * 3) { + if (baseTime + scxReader.scxAnd7() > curLineCycle) + return lyCounter.time() + ((baseTime + scxReader.scxAnd7()) << lyCounter.isDoubleSpeed()) - lyCounter.lineTime(); + else + return lyCounter.time() + ((baseTime + scxReader.getSource()) << lyCounter.isDoubleSpeed()); + } else + return lyCounter.nextLineCycle(baseTime + scxReader.getSource(), cycleCounter); +} + +Wy::WyReader4::WyReader4(Wy &wy) : + VideoEvent(6), + wy(wy) +{} + +void Wy::WyReader4::doEvent() { + wy.set(wy.src_); + + setTime(DISABLED_TIME); +} + +Wy::Wy(const LyCounter &lyCounter, const WeMasterChecker &weMasterChecker, M3ExtraCycles &m3ExtraCycles) : + lyCounter(lyCounter), + m3ExtraCycles(m3ExtraCycles), + reader1_(*this, weMasterChecker), + reader2_(*this), + reader3_(*this), + reader4_(*this) +{ + setSource(0); + wy_ = src_; +} + +void Wy::saveState(SaveState &state) const { + state.ppu.wy = wy_; +} + +void Wy::loadState(const SaveState &state) { + wy_ = state.ppu.wy; +} diff --git a/supergameboy/libgambatte/src/video/wy.h b/supergameboy/libgambatte/src/video/wy.h new file mode 100644 index 00000000..2a1033f9 --- /dev/null +++ b/supergameboy/libgambatte/src/video/wy.h @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2007 by Sindre Aamås * + * aamas@stud.ntnu.no * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + * 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 version 2 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * version 2 along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef WY_H +#define WY_H + +class WeMasterChecker; +class ScxReader; +template class event_queue; +class SaveState; + +#include "video_event.h" +#include "video_event_comparer.h" +#include "ly_counter.h" +#include "m3_extra_cycles.h" +#include "basic_add_event.h" + +class Wy { +public: + class WyReader1 : public VideoEvent { + Wy &wy; + const WeMasterChecker &weMasterChecker; + + public: + WyReader1(Wy &wy, const WeMasterChecker &weMasterChecker); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextLineCycle(448 + lyCounter.isDoubleSpeed() * 4, cycleCounter); + } + }; + + class WyReader2 : public VideoEvent { + Wy &wy; + + public: + WyReader2(Wy &wy); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.isDoubleSpeed() ? lyCounter.time() : lyCounter.nextLineCycle(452, cycleCounter); + } + }; + + class WyReader3 : public VideoEvent { + Wy &wy; + + public: + WyReader3(Wy &wy); + + void doEvent(); + static unsigned long schedule(unsigned wxSrc, const ScxReader &scxReader, const LyCounter &lyCounter, unsigned long cycleCounter); + + //void schedule(const unsigned scxAnd7, const LyCounter &lyCounter, const unsigned cycleCounter) { + // setTime(lyCounter.nextLineCycle(scxAnd7 + 85 + lyCounter.isDoubleSpeed() * 6, cycleCounter)); + //} + }; + + class WyReader4 : public VideoEvent { + Wy &wy; + + public: + WyReader4(Wy &wy); + + void doEvent(); + + static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) { + return lyCounter.nextFrameCycle(lyCounter.isDoubleSpeed() * 4, cycleCounter); + } + }; + + friend class WyReader1; + friend class WyReader2; + friend class WyReader3; + friend class WyReader4; + +private: + const LyCounter &lyCounter; + M3ExtraCycles &m3ExtraCycles; + WyReader1 reader1_; + WyReader2 reader2_; + WyReader3 reader3_; + WyReader4 reader4_; + + unsigned char wy_; + unsigned char src_; + + void set(const unsigned char value) { + if (wy_ != value) + m3ExtraCycles.invalidateCache(); + + wy_ = value; + } + +public: + Wy(const LyCounter &lyCounter, const WeMasterChecker &weMasterChecker, M3ExtraCycles &m3ExtraCycles); + + WyReader1& reader1() { + return reader1_; + } + + WyReader2& reader2() { + return reader2_; + } + + WyReader3& reader3() { + return reader3_; + } + + WyReader4& reader4() { + return reader4_; + } + + unsigned getSource() const { + return src_; + } + + void setSource(const unsigned src) { + src_ = src; + } + + //void setValue(const unsigned val) { + // wy_ = val; + //} + + unsigned value() const { + return wy_; + } + + void weirdAssWeMasterEnableOnWyLineCase() { + set(wy_ + 1); + } + + void saveState(SaveState &state) const; + void loadState(const SaveState &state); +}; + +static inline void addEvent(event_queue &q, Wy::WyReader1 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader1 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader2 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader2 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader3 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader3 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +static inline void addEvent(event_queue &q, Wy::WyReader4 *const e, const unsigned long newTime) { + addUnconditionalEvent(q, e, newTime); +} + +static inline void addFixedtimeEvent(event_queue &q, Wy::WyReader4 *const e, const unsigned long newTime) { + addUnconditionalFixedtimeEvent(q, e, newTime); +} + +#endif diff --git a/supergameboy/nall/Makefile b/supergameboy/nall/Makefile new file mode 100644 index 00000000..8149bf15 --- /dev/null +++ b/supergameboy/nall/Makefile @@ -0,0 +1,107 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + ifeq ($(platform),osx) + compiler := gcc-4.2 + else + compiler := gcc + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/supergameboy/nall/algorithm.hpp b/supergameboy/nall/algorithm.hpp new file mode 100644 index 00000000..cdc48dcf --- /dev/null +++ b/supergameboy/nall/algorithm.hpp @@ -0,0 +1,23 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } +} + +#endif diff --git a/supergameboy/nall/any.hpp b/supergameboy/nall/any.hpp new file mode 100644 index 00000000..b31cff3c --- /dev/null +++ b/supergameboy/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + std::is_array::value, + typename std::remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename std::remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/supergameboy/nall/array.hpp b/supergameboy/nall/array.hpp new file mode 100644 index 00000000..c1d33fd1 --- /dev/null +++ b/supergameboy/nall/array.hpp @@ -0,0 +1,120 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void add(const T data) { + operator[](buffersize) = data; + } + + signed find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i; + return -1; //not found + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //move + array& operator=(array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + array(array &&source) : pool(0), poolsize(0), buffersize(0) { + operator=(std::move(source)); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/supergameboy/nall/base64.hpp b/supergameboy/nall/base64.hpp new file mode 100644 index 00000000..e41c87b7 --- /dev/null +++ b/supergameboy/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/supergameboy/nall/bit.hpp b/supergameboy/nall/bit.hpp new file mode 100644 index 00000000..169fc144 --- /dev/null +++ b/supergameboy/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/supergameboy/nall/concept.hpp b/supergameboy/nall/concept.hpp new file mode 100644 index 00000000..2949cd5e --- /dev/null +++ b/supergameboy/nall/concept.hpp @@ -0,0 +1,15 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +namespace nall { + //unsigned count() const; + template struct has_count { enum { value = false }; }; + + //unsigned length() const; + template struct has_length { enum { value = false }; }; + + //unsigned size() const; + template struct has_size { enum { value = false }; }; +} + +#endif diff --git a/supergameboy/nall/config.hpp b/supergameboy/nall/config.hpp new file mode 100644 index 00000000..31ae4e00 --- /dev/null +++ b/supergameboy/nall/config.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + part.qsplit(" = ", line[i]); + trim(part[0]); + trim(part[1]); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/supergameboy/nall/crc32.hpp b/supergameboy/nall/crc32.hpp new file mode 100644 index 00000000..ad36fbf6 --- /dev/null +++ b/supergameboy/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[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 + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/supergameboy/nall/detect.hpp b/supergameboy/nall/detect.hpp new file mode 100644 index 00000000..b4991aaf --- /dev/null +++ b/supergameboy/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/supergameboy/nall/dictionary.hpp b/supergameboy/nall/dictionary.hpp new file mode 100644 index 00000000..f14e2095 --- /dev/null +++ b/supergameboy/nall/dictionary.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + trim(part[0]); + trim(part[1]); + + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + dictionary& operator=(const dictionary&) = delete; + dictionary(const dictionary&) = delete; + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/supergameboy/nall/dl.hpp b/supergameboy/nall/dl.hpp new file mode 100644 index 00000000..22acf51f --- /dev/null +++ b/supergameboy/nall/dl.hpp @@ -0,0 +1,119 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&) = delete; + library(const library&) = delete; + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 8]; + strcpy(t, name); + strcat(t, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(t)); + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/supergameboy/nall/endian.hpp b/supergameboy/nall/endian.hpp new file mode 100644 index 00000000..40d15633 --- /dev/null +++ b/supergameboy/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/supergameboy/nall/file.hpp b/supergameboy/nall/file.hpp new file mode 100644 index 00000000..4c8ca8ee --- /dev/null +++ b/supergameboy/nall/file.hpp @@ -0,0 +1,259 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + file& operator=(const file&) = delete; + file(const file&) = delete; + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/supergameboy/nall/filemap.hpp b/supergameboy/nall/filemap.hpp new file mode 100644 index 00000000..a05f0eb7 --- /dev/null +++ b/supergameboy/nall/filemap.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode) { + default: return false; + case mode_read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode_write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode mode) { + int open_flags, mmap_flags; + + switch(mode) { + default: return false; + case mode_read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode_write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode_readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode_writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/supergameboy/nall/foreach.hpp b/supergameboy/nall/foreach.hpp new file mode 100644 index 00000000..ea975b84 --- /dev/null +++ b/supergameboy/nall/foreach.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#undef foreach +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = foreach_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) + +#include +#include +#include + +namespace nall { + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.count(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.length(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return object.size(); + } + + template unsigned foreach_size(const T& object, typename mp_enable_if>::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/supergameboy/nall/function.hpp b/supergameboy/nall/function.hpp new file mode 100644 index 00000000..3f0f704e --- /dev/null +++ b/supergameboy/nall/function.hpp @@ -0,0 +1,102 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include +#include + +namespace nall { + template class function; + + template + class function { + private: + struct base1 { virtual void func1(P...) {} }; + struct base2 { virtual void func2(P...) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*callback)(const data_t&, P...); + union { + R (*callback_global)(P...); + struct { + R (derived::*callback_member)(P...); + void *object; + }; + }; + } data; + + static R callback_global(const data_t &data, P... p) { + return data.callback_global(p...); + } + + template + static R callback_member(const data_t &data, P... p) { + return (((C*)data.object)->*((R (C::*&)(P...))data.callback_member))(p...); + } + + public: + R operator()(P... p) const { return data.callback(data, p...); } + operator bool() const { return data.callback; } + void reset() { data.callback = 0; } + + function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { operator=(source); } + + //no pointer + function() { + data.callback = 0; + } + + //symbolic link pointer (nall/dl.hpp::sym, etc) + function(void *callback) { + data.callback = callback ? &callback_global : 0; + data.callback_global = (R (*)(P...))callback; + } + + //global function pointer + function(R (*callback)(P...)) { + data.callback = &callback_global; + data.callback_global = callback; + } + + //member function pointer + template + function(R (C::*callback)(P...), C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = callback; + data.object = object; + } + + //const member function pointer + template + function(R (C::*callback)(P...) const, C *object) { + static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); + data.callback = &callback_member; + (R (C::*&)(P...))data.callback_member = (R (C::*&)(P...))callback; + data.object = object; + } + + //lambda function pointer + template + function(T callback) { + static_assert(std::is_same::type>::value, "lambda mismatch"); + data.callback = &callback_global; + data.callback_global = (R (*)(P...))callback; + } + }; + + //bind functions to ease construction and assignment of function() with more than one argument + + template + function bind(R (C::*callback)(P...), C *object) { + return function(callback, object); + } + + template + function bind(R (C::*callback)(P...) const, C *object) { + return function(callback, object); + } +} + +#endif diff --git a/supergameboy/nall/input.hpp b/supergameboy/nall/input.hpp new file mode 100644 index 00000000..b3ce9ebf --- /dev/null +++ b/supergameboy/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + ltrim(s, "KB"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + ltrim(s, "MS"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + ltrim(s, "JP"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/supergameboy/nall/lzss.hpp b/supergameboy/nall/lzss.hpp new file mode 100644 index 00000000..202bc814 --- /dev/null +++ b/supergameboy/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/supergameboy/nall/moduloarray.hpp b/supergameboy/nall/moduloarray.hpp new file mode 100644 index 00000000..be549ae9 --- /dev/null +++ b/supergameboy/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/supergameboy/nall/platform.hpp b/supergameboy/nall/platform.hpp new file mode 100644 index 00000000..68ed37ce --- /dev/null +++ b/supergameboy/nall/platform.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface +#else + #include + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +#endif + diff --git a/supergameboy/nall/priorityqueue.hpp b/supergameboy/nall/priorityqueue.hpp new file mode 100644 index 00000000..7104e791 --- /dev/null +++ b/supergameboy/nall/priorityqueue.hpp @@ -0,0 +1,109 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue&) = delete; + priority_queue(const priority_queue&) = delete; + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/supergameboy/nall/property.hpp b/supergameboy/nall/property.hpp new file mode 100644 index 00000000..6fd33acd --- /dev/null +++ b/supergameboy/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/supergameboy/nall/qt/Makefile b/supergameboy/nall/qt/Makefile new file mode 100644 index 00000000..69e84960 --- /dev/null +++ b/supergameboy/nall/qt/Makefile @@ -0,0 +1,55 @@ +# requires nall/Makefile + +# imports: +# $(qtlibs) -- list of Qt components to link against + +# exports the following symbols: +# $(moc) -- meta-object compiler +# $(rcc) -- resource compiler +# $(qtinc) -- includes for compiling +# $(qtlib) -- libraries for linking + +ifeq ($(moc),) +moc := moc +endif + +ifeq ($(rcc),) +rcc := rcc +endif + +ifeq ($(platform),x) + qtinc := `pkg-config --cflags $(qtlibs)` + qtlib := `pkg-config --libs $(qtlibs)` +else ifeq ($(platform),osx) + qtinc := $(foreach lib,$(qtlibs),-I/Library/Frameworks/$(lib).framework/Versions/4/Headers) + + qtlib := -L/Library/Frameworks + qtlib += $(foreach lib,$(qtlibs),-framework $(lib)) + qtlib += -framework Carbon + qtlib += -framework Cocoa + qtlib += -framework OpenGL + qtlib += -framework AppKit + qtlib += -framework ApplicationServices +else ifeq ($(platform),win) + ifeq ($(qtpath),) + # find Qt install directory from PATH environment variable + qtpath := $(foreach path,$(subst ;, ,$(PATH)),$(if $(wildcard $(path)/$(moc).exe),$(path))) + qtpath := $(strip $(qtpath)) + qtpath := $(subst \,/,$(qtpath)) + qtpath := $(patsubst %/bin,%,$(qtpath)) + endif + + qtinc := -I$(qtpath)/include + qtinc += $(foreach lib,$(qtlibs),-I$(qtpath)/include/$(lib)) + + qtlib := -L$(qtpath)/lib + qtlib += -L$(qtpath)/plugins/imageformats + + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + qtlib += -lmingw32 -lqtmain -lcomdlg32 -loleaut32 -limm32 -lwinmm + qtlib += -lwinspool -lmsimg32 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32 + qtlib += $(foreach lib,$(qtlibs),-l$(lib)4) + + # optional image-file support: + # qtlib += -lqjpeg -lqmng +endif diff --git a/supergameboy/nall/qt/check-action.moc.hpp b/supergameboy/nall/qt/check-action.moc.hpp new file mode 100644 index 00000000..db378fe9 --- /dev/null +++ b/supergameboy/nall/qt/check-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_CHECKACTION_HPP +#define NALL_QT_CHECKACTION_HPP + +namespace nall { + +class CheckAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + CheckAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool CheckAction::isChecked() const { + return checked; +} + +inline void CheckAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-check-on.png")); + else setIcon(QIcon(":/16x16/item-check-off.png")); +} + +inline void CheckAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline CheckAction::CheckAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/supergameboy/nall/qt/concept.hpp b/supergameboy/nall/qt/concept.hpp new file mode 100644 index 00000000..51cacef4 --- /dev/null +++ b/supergameboy/nall/qt/concept.hpp @@ -0,0 +1,10 @@ +#ifndef NALL_QT_CONCEPT_HPP +#define NALL_QT_CONCEPT_HPP + +#include + +namespace nall { + template struct has_count> { enum { value = true }; }; +} + +#endif diff --git a/supergameboy/nall/qt/file-dialog.moc.hpp b/supergameboy/nall/qt/file-dialog.moc.hpp new file mode 100644 index 00000000..bcccfaf5 --- /dev/null +++ b/supergameboy/nall/qt/file-dialog.moc.hpp @@ -0,0 +1,392 @@ +#ifndef NALL_QT_FILEDIALOG_HPP +#define NALL_QT_FILEDIALOG_HPP + +#include +#include +#include + +namespace nall { + +class FileDialog; + +class NewFolderDialog : public Window { + Q_OBJECT + +public: + void show(); + NewFolderDialog(FileDialog*); + +protected slots: + void createFolderAction(); + +protected: + FileDialog *parent; + QVBoxLayout *layout; + QLineEdit *folderNameEdit; + QHBoxLayout *controlLayout; + QPushButton *okButton; + QPushButton *cancelButton; +}; + +class FileView : public QListView { + Q_OBJECT + +protected: + void keyPressEvent(QKeyEvent*); + +signals: + void changed(const QModelIndex&); + void browseUp(); + +protected slots: + void currentChanged(const QModelIndex&, const QModelIndex&); +}; + +class FileDialog : public Window { + Q_OBJECT + +public: + void showLoad(); + void showSave(); + void showFolder(); + + void setPath(string path); + void setNameFilters(const string &filters); + FileDialog(); + +signals: + void changed(const string&); + void activated(const string&); + void accepted(const string&); + void rejected(); + +protected slots: + void fileViewChange(const QModelIndex&); + void fileViewActivate(const QModelIndex&); + void pathBoxChanged(); + void filterBoxChanged(); + void createNewFolder(); + void browseUp(); + void acceptAction(); + void rejectAction(); + +protected: + NewFolderDialog *newFolderDialog; + QVBoxLayout *layout; + QHBoxLayout *navigationLayout; + QComboBox *pathBox; + QPushButton *newFolderButton; + QPushButton *upFolderButton; + QHBoxLayout *browseLayout; + QFileSystemModel *fileSystemModel; + FileView *fileView; + QGroupBox *previewFrame; + QLineEdit *fileNameEdit; + QHBoxLayout *controlLayout; + QComboBox *filterBox; + QPushButton *optionsButton; + QPushButton *acceptButton; + QPushButton *rejectButton; + bool lock; + void createFolderAction(const string &name); + void closeEvent(QCloseEvent*); + + friend class NewFolderDialog; +}; + +inline void NewFolderDialog::show() { + folderNameEdit->setText(""); + Window::show(); + folderNameEdit->setFocus(); +} + +inline void NewFolderDialog::createFolderAction() { + string name = folderNameEdit->text().toUtf8().constData(); + if(name == "") { + folderNameEdit->setFocus(); + } else { + parent->createFolderAction(name); + close(); + } +} + +inline NewFolderDialog::NewFolderDialog(FileDialog *fileDialog) : parent(fileDialog) { + setMinimumWidth(240); + setWindowTitle("Create New Folder"); + + layout = new QVBoxLayout; + layout->setAlignment(Qt::AlignTop); + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + folderNameEdit = new QLineEdit; + layout->addWidget(folderNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + okButton = new QPushButton("Ok"); + controlLayout->addWidget(okButton); + + cancelButton = new QPushButton("Cancel"); + controlLayout->addWidget(cancelButton); + + connect(folderNameEdit, SIGNAL(returnPressed()), this, SLOT(createFolderAction())); + connect(okButton, SIGNAL(released()), this, SLOT(createFolderAction())); + connect(cancelButton, SIGNAL(released()), this, SLOT(close())); +} + +inline void FileView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QAbstractItemView::currentChanged(current, previous); + emit changed(current); +} + +inline void FileView::keyPressEvent(QKeyEvent *event) { + //enhance consistency: force OS X to act like Windows and Linux; enter = activate item + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + emit activated(currentIndex()); + return; + } + + //simulate popular file manager behavior; backspace = go up one directory + if(event->key() == Qt::Key_Backspace) { + emit browseUp(); + return; + } + + //fallback: unrecognized keypresses get handled by the widget itself + QListView::keyPressEvent(event); +} + +inline void FileDialog::showLoad() { + acceptButton->setText("Load"); + fileNameEdit->hide(); + filterBox->show(); + show(); +} + +inline void FileDialog::showSave() { + acceptButton->setText("Save"); + fileNameEdit->show(); + filterBox->show(); + show(); +} + +inline void FileDialog::showFolder() { + acceptButton->setText("Choose"); + fileNameEdit->hide(); + filterBox->hide(); + setNameFilters("Folders ()"); + show(); +} + +inline void FileDialog::fileViewChange(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(path == fileSystemModel->rootPath().toUtf8().constData()) path = ""; + fileNameEdit->setText(notdir(path)); + emit changed(path); +} + +inline void FileDialog::fileViewActivate(const QModelIndex &index) { + string path = fileSystemModel->filePath(index).toUtf8().constData(); + if(fileSystemModel->isDir(index)) { + emit activated(path); + setPath(path); + } else { + emit activated(path); + close(); + } +} + +inline void FileDialog::pathBoxChanged() { + if(lock) return; + setPath(pathBox->currentText().toUtf8().constData()); +} + +inline void FileDialog::filterBoxChanged() { + if(lock) return; + string filters = filterBox->currentText().toUtf8().constData(); + if(filters.length() == 0) { + fileSystemModel->setNameFilters(QStringList() << "*"); + } else { + filters = substr(filters, strpos(filters, "(")); + ltrim(filters, "("); + rtrim(filters, ")"); + lstring part; + part.split(" ", filters); + QStringList list; + for(unsigned i = 0; i < part.size(); i++) list << part[i]; + fileSystemModel->setNameFilters(list); + } +} + +inline void FileDialog::createNewFolder() { + newFolderDialog->show(); +} + +inline void FileDialog::browseUp() { + if(pathBox->count() > 1) pathBox->setCurrentIndex(1); +} + +inline void FileDialog::setPath(string path) { + lock = true; + newFolderDialog->close(); + + if(QDir(path).exists()) { + newFolderButton->setEnabled(true); + } else { + newFolderButton->setEnabled(false); + path = ""; + } + + fileSystemModel->setRootPath(path); + fileView->setRootIndex(fileSystemModel->index(path)); + fileView->setCurrentIndex(fileView->rootIndex()); + fileView->setFocus(); + + pathBox->clear(); + if(path.length() > 0) { + QDir directory(path); + while(true) { + pathBox->addItem(directory.absolutePath()); + if(directory.isRoot()) break; + directory.cdUp(); + } + } + pathBox->addItem(""); + fileNameEdit->setText(""); + + lock = false; +} + +inline void FileDialog::setNameFilters(const string &filters) { + lock = true; + + lstring list; + list.split("\n", filters); + + filterBox->clear(); + for(unsigned i = 0; i < list.size(); i++) { + filterBox->addItem(list[i]); + } + + lock = false; + filterBoxChanged(); +} + +inline void FileDialog::acceptAction() { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(fileNameEdit->text().toUtf8().constData()); + rtrim(path, "/"); + if(QDir(path).exists()) { + emit accepted(path); + setPath(path); + } else { + emit accepted(path); + close(); + } +} + +inline void FileDialog::rejectAction() { + emit rejected(); + close(); +} + +inline void FileDialog::createFolderAction(const string &name) { + string path = fileSystemModel->rootPath().toUtf8().constData(); + path << "/" << notdir(name); + mkdir(path, 0755); +} + +inline void FileDialog::closeEvent(QCloseEvent *event) { + newFolderDialog->close(); + Window::closeEvent(event); +} + +inline FileDialog::FileDialog() { + newFolderDialog = new NewFolderDialog(this); + resize(640, 360); + + layout = new QVBoxLayout; + layout->setMargin(5); + layout->setSpacing(5); + setLayout(layout); + + navigationLayout = new QHBoxLayout; + layout->addLayout(navigationLayout); + + pathBox = new QComboBox; + pathBox->setEditable(true); + pathBox->setMinimumContentsLength(16); + pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + pathBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + navigationLayout->addWidget(pathBox); + + newFolderButton = new QPushButton; + newFolderButton->setIconSize(QSize(16, 16)); + newFolderButton->setIcon(QIcon(":/16x16/folder-new.png")); + navigationLayout->addWidget(newFolderButton); + + upFolderButton = new QPushButton; + upFolderButton->setIconSize(QSize(16, 16)); + upFolderButton->setIcon(QIcon(":/16x16/go-up.png")); + navigationLayout->addWidget(upFolderButton); + + browseLayout = new QHBoxLayout; + layout->addLayout(browseLayout); + + fileSystemModel = new QFileSystemModel; + fileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + fileSystemModel->setNameFilterDisables(false); + + fileView = new FileView; + fileView->setMinimumWidth(320); + fileView->setModel(fileSystemModel); + fileView->setIconSize(QSize(16, 16)); + browseLayout->addWidget(fileView); + + previewFrame = new QGroupBox; + previewFrame->hide(); + browseLayout->addWidget(previewFrame); + + fileNameEdit = new QLineEdit; + layout->addWidget(fileNameEdit); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + filterBox = new QComboBox; + filterBox->setMinimumContentsLength(16); + filterBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + filterBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + controlLayout->addWidget(filterBox); + + optionsButton = new QPushButton("Options"); + optionsButton->hide(); + controlLayout->addWidget(optionsButton); + + acceptButton = new QPushButton("Ok"); + controlLayout->addWidget(acceptButton); + + rejectButton = new QPushButton("Cancel"); + controlLayout->addWidget(rejectButton); + + lock = false; + connect(pathBox, SIGNAL(currentIndexChanged(int)), this, SLOT(pathBoxChanged())); + connect(newFolderButton, SIGNAL(released()), this, SLOT(createNewFolder())); + connect(upFolderButton, SIGNAL(released()), this, SLOT(browseUp())); + connect(fileView, SIGNAL(changed(const QModelIndex&)), this, SLOT(fileViewChange(const QModelIndex&))); + connect(fileView, SIGNAL(activated(const QModelIndex&)), this, SLOT(fileViewActivate(const QModelIndex&))); + connect(fileView, SIGNAL(browseUp()), this, SLOT(browseUp())); + connect(fileNameEdit, SIGNAL(returnPressed()), this, SLOT(acceptAction())); + connect(filterBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterBoxChanged())); + connect(acceptButton, SIGNAL(released()), this, SLOT(acceptAction())); + connect(rejectButton, SIGNAL(released()), this, SLOT(rejectAction())); +} + +} + +#endif diff --git a/supergameboy/nall/qt/hex-editor.moc.hpp b/supergameboy/nall/qt/hex-editor.moc.hpp new file mode 100644 index 00000000..d59f4be9 --- /dev/null +++ b/supergameboy/nall/qt/hex-editor.moc.hpp @@ -0,0 +1,173 @@ +#ifndef NALL_QT_HEXEDITOR_HPP +#define NALL_QT_HEXEDITOR_HPP + +#include +#include +#include + +namespace nall { + +class HexEditor : public QTextEdit { + Q_OBJECT + +public: + function reader; + function writer; + + void setColumns(unsigned columns); + void setRows(unsigned rows); + void setOffset(unsigned offset); + void setSize(unsigned size); + unsigned lineWidth() const; + void refresh(); + + HexEditor(); + +protected slots: + void scrolled(); + +protected: + QHBoxLayout *layout; + QScrollBar *scrollBar; + unsigned editorColumns; + unsigned editorRows; + unsigned editorOffset; + unsigned editorSize; + bool lock; + + void keyPressEvent(QKeyEvent*); +}; + +inline void HexEditor::keyPressEvent(QKeyEvent *event) { + QTextCursor cursor = textCursor(); + unsigned x = cursor.position() % lineWidth(); + unsigned y = cursor.position() / lineWidth(); + + int hexCode = -1; + switch(event->key()) { + case Qt::Key_0: hexCode = 0; break; + case Qt::Key_1: hexCode = 1; break; + case Qt::Key_2: hexCode = 2; break; + case Qt::Key_3: hexCode = 3; break; + case Qt::Key_4: hexCode = 4; break; + case Qt::Key_5: hexCode = 5; break; + case Qt::Key_6: hexCode = 6; break; + case Qt::Key_7: hexCode = 7; break; + case Qt::Key_8: hexCode = 8; break; + case Qt::Key_9: hexCode = 9; break; + case Qt::Key_A: hexCode = 10; break; + case Qt::Key_B: hexCode = 11; break; + case Qt::Key_C: hexCode = 12; break; + case Qt::Key_D: hexCode = 13; break; + case Qt::Key_E: hexCode = 14; break; + case Qt::Key_F: hexCode = 15; break; + } + + if(cursor.hasSelection() == false && hexCode != -1) { + bool cursorOffsetValid = (x >= 11 && ((x - 11) % 3) != 2); + if(cursorOffsetValid) { + bool nibble = (x - 11) % 3; //0 = top nibble, 1 = bottom nibble + unsigned cursorOffset = y * editorColumns + ((x - 11) / 3); + unsigned effectiveOffset = editorOffset + cursorOffset; + if(effectiveOffset >= editorSize) effectiveOffset %= editorSize; + + uint8_t data = reader ? reader(effectiveOffset) : 0x00; + data &= (nibble == 0 ? 0x0f : 0xf0); + data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0)); + if(writer) writer(effectiveOffset, data); + refresh(); + + cursor.setPosition(y * lineWidth() + x + 1); //advance cursor + setTextCursor(cursor); + } + } else { + //allow navigation keys to move cursor, but block text input + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + QTextEdit::keyPressEvent(event); + setTextInteractionFlags(Qt::TextEditorInteraction); + } +} + +inline void HexEditor::setColumns(unsigned columns) { + editorColumns = columns; +} + +inline void HexEditor::setRows(unsigned rows) { + editorRows = rows; + scrollBar->setPageStep(editorRows); +} + +inline void HexEditor::setOffset(unsigned offset) { + lock = true; + editorOffset = offset; + scrollBar->setSliderPosition(editorOffset / editorColumns); + lock = false; +} + +inline void HexEditor::setSize(unsigned size) { + editorSize = size; + bool indivisible = (editorSize % editorColumns) != 0; //add one for incomplete row + scrollBar->setRange(0, editorSize / editorColumns + indivisible - editorRows); +} + +inline unsigned HexEditor::lineWidth() const { + return 11 + 3 * editorColumns; +} + +inline void HexEditor::refresh() { + string output; + char temp[256]; + unsigned offset = editorOffset; + + for(unsigned y = 0; y < editorRows; y++) { + if(offset >= editorSize) break; + sprintf(temp, "%.4x:%.4x", (offset >> 16) & 0xffff, (offset >> 0) & 0xffff); + output << "" << temp << "  "; + + for(unsigned x = 0; x < editorColumns; x++) { + if(offset >= editorSize) break; + sprintf(temp, "%.2x", reader ? reader(offset) : 0x00); + offset++; + output << "" << temp << ""; + if(x != (editorColumns - 1)) output << " "; + } + + if(y != (editorRows - 1)) output << "
"; + } + + setHtml(output); +} + +inline void HexEditor::scrolled() { + if(lock) return; + unsigned offset = scrollBar->sliderPosition(); + editorOffset = offset * editorColumns; + refresh(); +} + +inline HexEditor::HexEditor() { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignRight); + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + scrollBar = new QScrollBar(Qt::Vertical); + scrollBar->setSingleStep(1); + layout->addWidget(scrollBar); + + lock = false; + connect(scrollBar, SIGNAL(actionTriggered(int)), this, SLOT(scrolled())); + + setColumns(16); + setRows(16); + setSize(0); + setOffset(0); +} + +} + +#endif diff --git a/supergameboy/nall/qt/radio-action.moc.hpp b/supergameboy/nall/qt/radio-action.moc.hpp new file mode 100644 index 00000000..a2bbca48 --- /dev/null +++ b/supergameboy/nall/qt/radio-action.moc.hpp @@ -0,0 +1,41 @@ +#ifndef NALL_QT_RADIOACTION_HPP +#define NALL_QT_RADIOACTION_HPP + +namespace nall { + +class RadioAction : public QAction { + Q_OBJECT + +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + RadioAction(const QString&, QObject*); + +protected slots: + +protected: + bool checked; +}; + +inline bool RadioAction::isChecked() const { + return checked; +} + +inline void RadioAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-radio-on.png")); + else setIcon(QIcon(":/16x16/item-radio-off.png")); +} + +inline void RadioAction::toggleChecked() { + setChecked(!isChecked()); +} + +inline RadioAction::RadioAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +} + +#endif diff --git a/supergameboy/nall/qt/window.moc.hpp b/supergameboy/nall/qt/window.moc.hpp new file mode 100644 index 00000000..0d3bf390 --- /dev/null +++ b/supergameboy/nall/qt/window.moc.hpp @@ -0,0 +1,105 @@ +#ifndef NALL_QT_WINDOW_HPP +#define NALL_QT_WINDOW_HPP + +#include +#include + +namespace nall { + +class Window : public QWidget { + Q_OBJECT + +public: + void setGeometryString(string *geometryString); + void setCloseOnEscape(bool); + void show(); + void hide(); + void shrink(); + + Window(); + +protected slots: + +protected: + string *geometryString; + bool closeOnEscape; + void keyReleaseEvent(QKeyEvent *event); + void closeEvent(QCloseEvent *event); +}; + +inline void Window::setGeometryString(string *geometryString_) { + geometryString = geometryString_; + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } +} + +inline void Window::setCloseOnEscape(bool value) { + closeOnEscape = value; +} + +inline void Window::show() { + if(geometryString && isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, *geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + } + QWidget::show(); + QApplication::processEvents(); + activateWindow(); + raise(); +} + +inline void Window::hide() { + if(geometryString && isVisible() == true) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::hide(); +} + +inline void Window::shrink() { + if(isFullScreen()) return; + + for(unsigned i = 0; i < 2; i++) { + resize(0, 0); + usleep(2000); + QApplication::processEvents(); + } +} + +inline void Window::keyReleaseEvent(QKeyEvent *event) { + if(closeOnEscape && (event->key() == Qt::Key_Escape)) close(); + QWidget::keyReleaseEvent(event); +} + +inline void Window::closeEvent(QCloseEvent *event) { + if(geometryString) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + *geometryString = data; + delete[] data; + } + QWidget::closeEvent(event); +} + +inline Window::Window() { + geometryString = 0; + closeOnEscape = true; +} + +} + +#endif diff --git a/supergameboy/nall/serial.hpp b/supergameboy/nall/serial.hpp new file mode 100644 index 00000000..6f5cf6d6 --- /dev/null +++ b/supergameboy/nall/serial.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/supergameboy/nall/serializer.hpp b/supergameboy/nall/serializer.hpp new file mode 100644 index 00000000..9f816dfe --- /dev/null +++ b/supergameboy/nall/serializer.hpp @@ -0,0 +1,145 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //move + serializer& operator=(serializer &&s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = s.idata; + isize = s.isize; + icapacity = s.icapacity; + + s.idata = 0; + return *this; + } + + serializer(serializer &&s) { + operator=(std::move(s)); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/supergameboy/nall/sha256.hpp b/supergameboy/nall/sha256.hpp new file mode 100644 index 00000000..7f41f04e --- /dev/null +++ b/supergameboy/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/supergameboy/nall/sort.hpp b/supergameboy/nall/sort.hpp new file mode 100644 index 00000000..23c317a5 --- /dev/null +++ b/supergameboy/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/supergameboy/nall/static.hpp b/supergameboy/nall/static.hpp new file mode 100644 index 00000000..4acb9fd0 --- /dev/null +++ b/supergameboy/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/supergameboy/nall/stdint.hpp b/supergameboy/nall/stdint.hpp new file mode 100644 index 00000000..d8b6c788 --- /dev/null +++ b/supergameboy/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size"); + + static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" ); + static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size"); + static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); + static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); +} + +#endif diff --git a/supergameboy/nall/string.hpp b/supergameboy/nall/string.hpp new file mode 100644 index 00000000..65a4a4b8 --- /dev/null +++ b/supergameboy/nall/string.hpp @@ -0,0 +1,26 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/supergameboy/nall/string/base.hpp b/supergameboy/nall/string/base.hpp new file mode 100644 index 00000000..179a7dd4 --- /dev/null +++ b/supergameboy/nall/string/base.hpp @@ -0,0 +1,137 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + inline void reserve(unsigned); + inline unsigned length() const; + + inline string& assign(const char*); + inline string& append(const char*); + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string(); + inline string(const char*); + inline string(const string&); + inline string(string&&); + inline string& operator=(const string&); + inline string& operator=(string&&); + inline ~string(); + + inline bool readfile(const char*); + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + protected: + char *data; + unsigned size; + + #if defined(QT_CORE_LIB) + public: + inline operator QString() const; + #endif + }; + + class lstring : public linear_vector { + public: + template inline lstring& operator<<(T value); + + inline int find(const char*); + inline void split (const char*, const char*, unsigned = 0); + inline void qsplit(const char*, const char*, unsigned = 0); + + lstring(); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *dest, const char *src); + inline int strpos (const char *str, const char *key); + inline int qstrpos(const char *str, const char *key); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t strhex (const char *str); + inline intmax_t strsigned (const char *str); + inline uintmax_t strunsigned(const char *str); + inline uintmax_t strbin (const char *str); + inline double strdouble (const char *str); + + //match.hpp + inline bool match(const char *pattern, const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //trim.hpp + inline char* ltrim(char *str, const char *key = " "); + inline char* rtrim(char *str, const char *key = " "); + inline char* trim (char *str, const char *key = " "); + inline char* ltrim_once(char *str, const char *key = " "); + inline char* rtrim_once(char *str, const char *key = " "); + inline char* trim_once (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + inline string& strlower(string &str); + inline string& strupper(string &str); + inline string& strtr(string &dest, const char *before, const char *after); + inline string& ltrim(string &str, const char *key = " "); + inline string& rtrim(string &str, const char *key = " "); + inline string& trim (string &str, const char *key = " "); + inline string& ltrim_once(string &str, const char *key = " "); + inline string& rtrim_once(string &str, const char *key = " "); + inline string& trim_once (string &str, const char *key = " "); + template inline string strhex(uintmax_t value); + template inline string strsigned(intmax_t value); + template inline string strunsigned(uintmax_t value); + template inline string strbin(uintmax_t value); + inline unsigned strdouble(char *str, double value); + inline string strdouble(double value); + + //variadic.hpp + template inline string sprint(Args... args); + template inline void print(Args... args); +}; + +#endif diff --git a/supergameboy/nall/string/cast.hpp b/supergameboy/nall/string/cast.hpp new file mode 100644 index 00000000..7b48eda0 --- /dev/null +++ b/supergameboy/nall/string/cast.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (bool v) { return v ? "true" : "false"; } +template<> inline string to_string (signed int v) { return strsigned(v); } +template<> inline string to_string (unsigned int v) { return strunsigned(v); } +template<> inline string to_string (double v) { return strdouble(v); } +template<> inline string to_string (char *v) { return v; } +template<> inline string to_string (const char *v) { return v; } +template<> inline string to_string (string v) { return v; } +template<> inline string to_string(const string &v) { return v; } + +template string& string::operator= (T value) { return assign(to_string(value)); } +template string& string::operator<<(T value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QT_CORE_LIB) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/supergameboy/nall/string/compare.hpp b/supergameboy/nall/string/compare.hpp new file mode 100644 index 00000000..e1173de4 --- /dev/null +++ b/supergameboy/nall/string/compare.hpp @@ -0,0 +1,104 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +int strpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + return i; + } + } + return -1; +} + +int qstrpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) { + return i; + } else { + i++; + } + } + return -1; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + 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 false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +} + +#endif diff --git a/supergameboy/nall/string/convert.hpp b/supergameboy/nall/string/convert.hpp new file mode 100644 index 00000000..3ff134be --- /dev/null +++ b/supergameboy/nall/string/convert.hpp @@ -0,0 +1,153 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +} + +#endif diff --git a/supergameboy/nall/string/core.hpp b/supergameboy/nall/string/core.hpp new file mode 100644 index 00000000..d13bfc38 --- /dev/null +++ b/supergameboy/nall/string/core.hpp @@ -0,0 +1,133 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string::string() { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +string::string(const char *value) { + size = strlen(value); + data = strdup(value); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::string(string &&source) { + size = source.size; + data = source.data; + source.data = 0; +} + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string& string::operator=(string &&source) { + if(data) free(data); + size = source.size; + data = source.data; + source.data = 0; + source.size = 0; + return *this; +} + +string::~string() { + free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +int lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return i; + } + return -1; +} + +inline lstring::lstring() { +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/supergameboy/nall/string/filename.hpp b/supergameboy/nall/string/filename.hpp new file mode 100644 index 00000000..f3750760 --- /dev/null +++ b/supergameboy/nall/string/filename.hpp @@ -0,0 +1,61 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/", "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/supergameboy/nall/string/match.hpp b/supergameboy/nall/string/match.hpp new file mode 100644 index 00000000..d8cf702d --- /dev/null +++ b/supergameboy/nall/string/match.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_STRING_MATCH_HPP +#define NALL_STRING_MATCH_HPP + +namespace nall { + +bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++, *s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +} + +#endif diff --git a/supergameboy/nall/string/math.hpp b/supergameboy/nall/string/math.hpp new file mode 100644 index 00000000..ea8b99c8 --- /dev/null +++ b/supergameboy/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/supergameboy/nall/string/replace.hpp b/supergameboy/nall/string/replace.hpp new file mode 100644 index 00000000..db405a9b --- /dev/null +++ b/supergameboy/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/supergameboy/nall/string/split.hpp b/supergameboy/nall/string/split.hpp new file mode 100644 index 00000000..bb77dfcd --- /dev/null +++ b/supergameboy/nall/string/split.hpp @@ -0,0 +1,56 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/supergameboy/nall/string/strl.hpp b/supergameboy/nall/string/strl.hpp new file mode 100644 index 00000000..84c841fa --- /dev/null +++ b/supergameboy/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/supergameboy/nall/string/trim.hpp b/supergameboy/nall/string/trim.hpp new file mode 100644 index 00000000..b13ab9ba --- /dev/null +++ b/supergameboy/nall/string/trim.hpp @@ -0,0 +1,54 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +} + +#endif diff --git a/supergameboy/nall/string/utility.hpp b/supergameboy/nall/string/utility.hpp new file mode 100644 index 00000000..2da2762b --- /dev/null +++ b/supergameboy/nall/string/utility.hpp @@ -0,0 +1,169 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +namespace nall { + +unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* very simplistic wrappers to return string& instead of char* type */ + +string& strlower(string &str) { strlower(str()); return str; } +string& strupper(string &str) { strupper(str()); return str; } +string& strtr(string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; } +string& ltrim(string &str, const char *key) { ltrim(str(), key); return str; } +string& rtrim(string &str, const char *key) { rtrim(str(), key); return str; } +string& trim (string &str, const char *key) { trim (str(), key); return str; } +string& ltrim_once(string &str, const char *key) { ltrim_once(str(), key); return str; } +string& rtrim_once(string &str, const char *key) { rtrim_once(str(), key); return str; } +string& trim_once (string &str, const char *key) { trim_once (str(), key); return str; } + +/* arithmetic <> string */ + +template string strhex(uintmax_t value) { + string output; + unsigned offset = 0; + + //render string backwards, as we do not know its length yet + do { + unsigned n = value & 15; + output[offset++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + //reverse the string in-place + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strsigned(intmax_t value) { + string output; + unsigned offset = 0; + + bool negative = value < 0; + if(negative) value = abs(value); + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + if(negative) output[offset++] = '-'; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strunsigned(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value % 10; + output[offset++] = '0' + n; + value /= 10; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +template string strbin(uintmax_t value) { + string output; + unsigned offset = 0; + + do { + unsigned n = value & 1; + output[offset++] = '0' + n; + value >>= 1; + } while(value); + + while(offset < length) output[offset++] = padding; + output[offset--] = 0; + + for(unsigned i = 0; i < (offset + 1) >> 1; i++) { + char temp = output[i]; + output[i] = output[offset - i]; + output[offset - i] = temp; + } + + return output; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +unsigned strdouble(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +string strdouble(double value) { + string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +} + +#endif diff --git a/supergameboy/nall/string/variadic.hpp b/supergameboy/nall/string/variadic.hpp new file mode 100644 index 00000000..13c477a8 --- /dev/null +++ b/supergameboy/nall/string/variadic.hpp @@ -0,0 +1,27 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +static void isprint(string &output) { +} + +template +static void isprint(string &output, T value, Args... args) { + output << to_string(value); + isprint(output, args...); +} + +template inline string sprint(Args... args) { + string output; + isprint(output, args...); + return output; +} + +template inline void print(Args... args) { + printf("%s", (const char*)sprint(args...)); +} + +} + +#endif diff --git a/supergameboy/nall/string/xml.hpp b/supergameboy/nall/string/xml.hpp new file mode 100644 index 00000000..d423f87f --- /dev/null +++ b/supergameboy/nall/string/xml.hpp @@ -0,0 +1,257 @@ +#ifndef NALL_STRING_XML_HPP +#define NALL_STRING_XML_HPP + +//XML subset parser +//version 0.05 + +namespace nall { + +struct xml_attribute { + string name; + string content; + virtual string parse() const; +}; + +struct xml_element : xml_attribute { + string parse() const; + linear_vector attribute; + linear_vector element; + +protected: + void parse_doctype(const char *&data); + bool parse_head(string data); + bool parse_body(const char *&data); + friend xml_element xml_parse(const char *data); +}; + +inline string xml_attribute::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline string xml_element::parse() const { + string data; + unsigned offset = 0; + + const char *source = content; + while(*source) { + if(*source == '&') { + if(strbegin(source, "<")) { data[offset++] = '<'; source += 4; continue; } + if(strbegin(source, ">")) { data[offset++] = '>'; source += 4; continue; } + if(strbegin(source, "&")) { data[offset++] = '&'; source += 5; continue; } + if(strbegin(source, "'")) { data[offset++] = '\''; source += 6; continue; } + if(strbegin(source, """)) { data[offset++] = '"'; source += 6; continue; } + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + source += pos + 3; + continue; + } + + if(strbegin(source, ""); + if(pos == -1) return ""; + string cdata = substr(source, 9, pos - 9); + data << cdata; + offset += strlen(cdata); + + source += offset + 3; + continue; + } + + //reject illegal characters + if(*source == '&') return ""; + if(*source == '<') return ""; + if(*source == '>') return ""; + + data[offset++] = *source++; + } + + data[offset] = 0; + return data; +} + +inline void xml_element::parse_doctype(const char *&data) { + name = "!DOCTYPE"; + const char *content_begin = data; + + signed counter = 0; + while(*data) { + char value = *data++; + if(value == '<') counter++; + if(value == '>') counter--; + if(counter < 0) { + content = substr(content_begin, 0, data - content_begin - 1); + return; + } + } + throw "..."; +} + +inline bool xml_element::parse_head(string data) { + data.qreplace("\t", " "); + data.qreplace("\r", " "); + data.qreplace("\n", " "); + while(qstrpos(data, " ") >= 0) data.qreplace(" ", " "); + data.qreplace(" =", "="); + data.qreplace("= ", "="); + rtrim(data); + + lstring part; + part.qsplit(" ", data); + + name = part[0]; + if(name == "") throw "..."; + + for(unsigned i = 1; i < part.size(); i++) { + lstring side; + side.qsplit("=", part[i]); + if(side.size() != 2) throw "..."; + + xml_attribute attr; + attr.name = side[0]; + attr.content = side[1]; + if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) trim_once(attr.content, "\""); + else if(strbegin(attr.content, "'") && strend(attr.content, "'")) trim_once(attr.content, "'"); + else throw "..."; + attribute.add(attr); + } +} + +inline bool xml_element::parse_body(const char *&data) { + while(true) { + if(!*data) return false; + if(*data++ != '<') continue; + if(*data == '/') return false; + + if(strbegin(data, "!DOCTYPE") == true) { + parse_doctype(data); + return true; + } + + if(strbegin(data, "!--")) { + signed offset = strpos(data, "-->"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + if(strbegin(data, "![CDATA[")) { + signed offset = strpos(data, "]]>"); + if(offset == -1) throw "..."; + data += offset + 3; + continue; + } + + signed offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + string tag = substr(data, 0, offset); + data += offset + 1; + const char *content_begin = data; + + bool self_terminating = false; + + if(strend(tag, "?") == true) { + self_terminating = true; + rtrim_once(tag, "?"); + } else if(strend(tag, "/") == true) { + self_terminating = true; + rtrim_once(tag, "/"); + } + + parse_head(tag); + if(self_terminating) return true; + + while(*data) { + unsigned index = element.size(); + xml_element node; + if(node.parse_body(data) == false) { + if(*data == '/') { + signed length = data - content_begin - 1; + if(length > 0) content = substr(content_begin, 0, length); + + data++; + offset = strpos(data, ">"); + if(offset == -1) throw "..."; + + tag = substr(data, 0, offset); + data += offset + 1; + + tag.replace("\t", " "); + tag.replace("\r", " "); + tag.replace("\n", " "); + while(strpos(tag, " ") >= 0) tag.replace(" ", " "); + rtrim(tag); + + if(name != tag) throw "..."; + return true; + } + } else { + element.add(node); + } + } + } +} + +//ensure there is only one root element +inline bool xml_validate(xml_element &document) { + unsigned root_counter = 0; + + for(unsigned i = 0; i < document.element.size(); i++) { + string &name = document.element[i].name; + if(strbegin(name, "?")) continue; + if(strbegin(name, "!")) continue; + if(++root_counter > 1) return false; + } + + return true; +} + +inline xml_element xml_parse(const char *data) { + xml_element self; + + try { + while(*data) { + xml_element node; + if(node.parse_body(data) == false) { + break; + } else { + self.element.add(node); + } + } + + if(xml_validate(self) == false) throw "..."; + return self; + } catch(const char*) { + xml_element empty; + return empty; + } +} + +} + +#endif diff --git a/supergameboy/nall/ups.hpp b/supergameboy/nall/ups.hpp new file mode 100644 index 00000000..f255ecb3 --- /dev/null +++ b/supergameboy/nall/ups.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new uint8_t[y_size](); + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/supergameboy/nall/utf8.hpp b/supergameboy/nall/utf8.hpp new file mode 100644 index 00000000..c66c341a --- /dev/null +++ b/supergameboy/nall/utf8.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#undef NOMINMAX +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + private: + char *buffer; + }; +} + +#endif //if defined(_WIN32) + +#endif diff --git a/supergameboy/nall/utility.hpp b/supergameboy/nall/utility.hpp new file mode 100644 index 00000000..2a63f515 --- /dev/null +++ b/supergameboy/nall/utility.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T &x, T &y) { + T temp(std::move(x)); + x = std::move(y); + y = std::move(temp); + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template inline T* allocate(size_t size, const T &value) { + T *array = new T[size]; + for(size_t i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/supergameboy/nall/varint.hpp b/supergameboy/nall/varint.hpp new file mode 100644 index 00000000..cc3bb17c --- /dev/null +++ b/supergameboy/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert::value> uint_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert::value> int_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/supergameboy/nall/vector.hpp b/supergameboy/nall/vector.hpp new file mode 100644 index 00000000..3d69d4d5 --- /dev/null +++ b/supergameboy/nall/vector.hpp @@ -0,0 +1,240 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline linear_vector& operator=(linear_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + linear_vector(linear_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~linear_vector() { + reset(); + } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //move + inline pointer_vector& operator=(pointer_vector &&source) { + reset(); + pool = source.pool; + poolsize = source.poolsize; + objectsize = source.objectsize; + source.pool = 0; + source.reset(); + return *this; + } + + pointer_vector(pointer_vector &&source) : pool(0), poolsize(0), objectsize(0) { + operator=(std::move(source)); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) add(*p); + } + + ~pointer_vector() { + reset(); + } + }; + + template struct has_size> { enum { value = true }; }; + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/supergameboy/supergameboy.cpp b/supergameboy/supergameboy.cpp new file mode 100644 index 00000000..0c011af7 --- /dev/null +++ b/supergameboy/supergameboy.cpp @@ -0,0 +1,68 @@ +#include "supergameboy.hpp" + +#ifdef _WIN32 + #define dllexport __declspec(dllexport) +#else + #define dllexport +#endif + +#include +#include +#include + +#include + +dllexport void sgb_rom(uint8_t *data, unsigned size) { + supergameboy.romdata = data; + supergameboy.romsize = size; +} + +dllexport void sgb_ram(uint8_t *data, unsigned size) { + supergameboy.ramdata = data; + supergameboy.ramsize = size; +} + +dllexport void sgb_rtc(uint8_t *data, unsigned size) { + supergameboy.rtcdata = data; + supergameboy.rtcsize = size; +} + +dllexport bool sgb_init(bool version) { + return supergameboy.init(version); +} + +dllexport void sgb_term() { + supergameboy.term(); +} + +dllexport void sgb_power() { + supergameboy.power(); +} + +dllexport void sgb_reset() { + supergameboy.reset(); +} + +dllexport void sgb_row(unsigned row) { + supergameboy.row(row); +} + +dllexport uint8_t sgb_read(uint16_t addr) { + return supergameboy.read(addr); +} + +dllexport void sgb_write(uint16_t addr, uint8_t data) { + supergameboy.write(addr, data); +} + +dllexport unsigned sgb_run(uint32_t *samplebuffer, unsigned clocks) { + return supergameboy.run(samplebuffer, clocks); +} + +dllexport void sgb_save() { + supergameboy.save(); +} + +dllexport void sgb_serialize(nall::serializer &s) { + supergameboy.serialize(s); +} diff --git a/supergameboy/supergameboy.hpp b/supergameboy/supergameboy.hpp new file mode 100644 index 00000000..715e4893 --- /dev/null +++ b/supergameboy/supergameboy.hpp @@ -0,0 +1,32 @@ +#ifndef SUPERGAMEBOY_HPP +#define SUPERGAMEBOY_HPP + +#include +#include +#include +#include + +#include +#include +using namespace nall; + +#include +#include + +extern "C" { + void sgb_rom(uint8_t *data, unsigned size); + void sgb_ram(uint8_t *data, unsigned size); + void sgb_rtc(uint8_t *data, unsigned size); + bool sgb_init(bool version); + void sgb_term(); + void sgb_power(); + void sgb_reset(); + void sgb_row(unsigned row); + uint8_t sgb_read(uint16_t addr); + void sgb_write(uint16_t addr, uint8_t data); + unsigned sgb_run(uint32_t *samplebuffer, unsigned clocks); + void sgb_save(); + void sgb_serialize(nall::serializer &s); +} + +#endif diff --git a/supergameboy/sync.sh b/supergameboy/sync.sh new file mode 100644 index 00000000..4bbaf34f --- /dev/null +++ b/supergameboy/sync.sh @@ -0,0 +1,2 @@ +rm -r nall +cp -r ../nall ./nall