144 Commits
Author SHA1 Message Date
Vladimir Panteleev 8ed623a322 Merge pull request #58 from Aaron1011/dlang-update
Get RABCDAsm compiling with latest dlang
2023-03-11 11:10:34 -06:00
Vladimir Panteleev ed130fe1b0 zlibx: Free memory on error
This is closer to the original behavior.
2023-03-11 17:09:38 +00:00
Aaron Hill a2d18a1a72 Get RABCDAsm compiling with latest dlang
Both `opCatAssign` and `destroy` produce deprecation errors
as of the latest dlang (dmd 2.100.0). This commit changes them
both to use the suggested replacements, making RABCDAsm compile
with the latest dlang.
2022-06-15 20:14:47 -05:00
Vladimir Panteleev f455e29deb abcfile: Add missing argument to getouterscope 2021-10-18 08:05:48 +00:00
Vladimir Panteleev 03b13d0c87 Replace getpropertylate with getouterscope
Fixes #55.
2021-10-18 07:18:20 +00:00
Vladimir Panteleev cdbac56b21 abcfile: Change main name of ASType 0x00 from Void to Undefined
Fixes issue #54.
2021-02-08 19:11:39 +00:00
Vladimir Panteleev a5153c7d22 CHANGELOG: Update 2021-02-08 19:11:39 +00:00
Vladimir Panteleev 7818a79009 Merge pull request #52 from Graham-G/patch-1
Updating dead links
2019-11-16 07:24:01 -06:00
Graham-G 656c2e01f4 Updating dead links 2019-11-16 03:42:05 +00:00
Vladimir Panteleev 0d2c02af46 zlibx: Improve robustness of zlib decompression 2016-12-24 00:29:57 +00:00
Vladimir Panteleev 086ee07659 zlibx: Improve "Wrong uncompressed file length" error message 2016-12-24 00:13:25 +00:00
Vladimir Panteleev efdde9271c zlibx: Make over-estimation of destlen non-fatal 2016-11-26 22:01:57 +00:00
Vladimir Panteleev f7c28ac340 assembler: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 17a70c6c9b asprogram: Ignore duplicate class names during dependency registration 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 8454abf95e .gitignore 2016-11-26 21:53:06 +00:00
Vladimir Panteleev d68e7a1f7f .gitignore: dos2unix 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 62f13d4bab swf7zcompress: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev c1adeddbea build_rabcdasm: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 37131650e5 build_rabcdasm: Detect DMD bug 15861 2016-04-01 16:33:49 +00:00
Vladimir Panteleev 68f6fa241c Update Changelog 2016-01-16 09:10:28 +00:00
Vladimir Panteleev 77b5366ada lzma: Decompress piecewise, ignore decompressed size from header 2016-01-13 11:09:57 +00:00
Vladimir Panteleev 59a0b2c167 swffile: Ignore mismatched decompressed file size 2016-01-13 11:09:44 +00:00
Vladimir Panteleev 9bd1c698b5 Improve size mismatch error messages 2016-01-13 10:52:00 +00:00
Vladimir Panteleev e01cff3f24 assembler: Close main file when done 2015-11-30 15:33:30 +00:00
Vladimir Panteleev 988b17a401 assembler: Rename File to SourceFile to remove std.stdio.File ambiguity 2015-11-30 15:24:29 +00:00
Vladimir Panteleev 3ed3fb1a27 diassembler: Make raw byte dumps optional 2015-11-24 19:18:42 +00:00
Vladimir Panteleev 1e7cbaca9b disassembler: Don't write script index comment
Complicates merging
2015-11-24 08:45:37 +00:00
Vladimir Panteleev 9b6dfe13b3 disassembler: Dump doubles in hex notation as necessary; verify precision 2015-01-18 02:59:56 +00:00
Vladimir Panteleev dc01be0604 asprogram: Work around DMD issue 13998 2015-01-04 18:58:44 +00:00
Vladimir Panteleev 5e7b2ea4e7 build_rabcdasm: Detect DMD 2.064 x64 isWordChar bug 2015-01-04 18:54:46 +00:00
Vladimir Panteleev 8ab69fc6b7 disassembler: Fix usage of deprecated .sort property 2014-12-20 21:04:56 +00:00
Vladimir Panteleev 576294f949 Add .gitignore 2014-12-20 21:04:35 +00:00
Vladimir Panteleev 21650d2056 lzma.index: Fix C-style array syntax 2014-12-20 21:03:13 +00:00
Vladimir Panteleev 0983145a0d Fix handling of pushbyte argument 2014-10-12 22:25:42 +00:00
Vladimir Panteleev 62c8fce63b assembler: Fix buffer reuse bugs with macros 2014-09-22 03:17:47 +00:00
Vladimir Panteleev 7097774834 assembler: Minor refactoring 2014-09-22 03:17:36 +00:00
Vladimir Panteleev fe93f5e764 Update Changelog 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 0fbfba1186 README: Update 2.066 note 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 6bdf6dae39 abcfile: 64-bit fix 2014-09-10 11:41:26 +00:00
Vladimir Panteleev 62ef183d95 Do not attempt to disassemble unreachable code
Also, improve handling of disassembly errors -
methods will be partially disassembled as far as possible.
2014-09-03 15:12:26 +00:00
Vladimir Panteleev f6eb080be7 disassembler: Print new lines after OP_throw 2014-09-03 15:12:26 +00:00
Vladimir Panteleev a17a03e6f6 asprogram: Work around issue 13420 in DMD 2.066 2014-09-03 15:11:26 +00:00
Vladimir Panteleev 0486c1a54a lzma: Provide 10% slack space for uncompressable data 2014-07-19 23:28:40 +00:00
Vladimir Panteleev a2d22e7606 README: Add link to liblzma 2014-06-10 03:05:07 +00:00
Vladimir Panteleev 6568793aaf Update Changelog 2014-04-21 07:52:43 +00:00
Vladimir Panteleev b0bdb250c5 README: Document build requirement for long path support 2014-04-21 07:51:27 +00:00
Vladimir Panteleev 99e49a9385 Fix handling very long paths on Windows again 2014-03-17 11:09:30 +00:00
Vladimir Panteleev 6313739b5e asprogram: Ignore null parameters in TypeName multinames 2014-02-16 21:03:11 +00:00
Vladimir Panteleev 8ed3295199 autodata: Fix stringizing null references 2014-02-16 20:52:23 +00:00
Vladimir Panteleev 31ad8dcef7 disassembler: Fix compilation with 2.063.2
This change may also improve performance.
2014-01-12 22:14:18 +00:00
Vladimir Panteleev ef8e86388a Update Changelog 2014-01-11 14:18:24 +00:00
Vladimir Panteleev d4f7a070b6 assembler: Memory/performance optimizations 2013-12-29 09:01:59 +00:00
Vladimir Panteleev e5e01ea90f assembler: Memory/performance optimizations 2013-12-29 07:57:27 +00:00
Vladimir Panteleev fbebd86f47 assembler: Memory/performance optimizations 2013-12-29 07:45:18 +00:00
Vladimir Panteleev e9fbbef9b7 assembler: Replace some casts with std.conv.to 2013-12-29 01:51:54 +00:00
Vladimir Panteleev 9c28d20e99 disassembler: Further optimize an optimization 2013-12-21 03:48:46 +00:00
Vladimir Panteleev 3449822efd disassembler: Use one buffer for all files 2013-12-21 03:38:29 +00:00
Vladimir Panteleev 3ded602166 disassembler: Avoid more memory allocations by using formattedWrite 2013-12-21 03:07:51 +00:00
Vladimir Panteleev 08008d5643 disassembler: Remove memory allocation from dumpDouble 2013-12-21 03:00:08 +00:00
Vladimir Panteleev 98e9198c36 disassembler: Flush output periodically to reduce memory usage 2013-12-21 02:45:36 +00:00
Vladimir Panteleev f6dbbf08ee diasassembler: Remove a redundant .dup 2013-12-21 02:09:34 +00:00
Vladimir Panteleev 4251e409a2 asprogram: Track TypeName names as dependencies as well 2013-11-12 18:38:08 +00:00
Vladimir Panteleev 7ad6949b45 asprogram: 64-bit fix 2013-11-12 16:58:59 +00:00
Vladimir Panteleev 9bd472e4f8 asprogram: Remove dead code 2013-11-12 05:43:15 +00:00
Vladimir Panteleev efada3d5c1 asprogram: Don't emit forward typename references (sort multinames topographically) 2013-11-12 05:19:27 +00:00
Vladimir Panteleev 05d9624f68 asprogram: Unify constant pool logic; sort doubles bitwise 2013-11-12 05:18:30 +00:00
Vladimir Panteleev a2ea734202 build_rabcdasm: Bypass /tmp/ noexec problems by using -od. 2013-11-09 02:58:16 +00:00
Vladimir Panteleev 32909f3893 Update Changelog 2013-08-21 21:11:03 +00:00
Vladimir Panteleev 472a9cec57 Add finddef instruction 2013-08-18 08:16:11 +00:00
Vladimir Panteleev 6cbb185114 disassembler: Use std.digest.md
Silences compiler deprecation notice.
2013-08-18 07:58:46 +00:00
Vladimir Panteleev f32f6fb128 abcfile: Add unimplemented {get|set|delete}propertylate instructions
Needed to handle code obfuscated by DoSWF.
Parameter list (empty) guessed judging by {as|is}typelate instructions.

Fixes issue #18.
2013-06-23 06:38:25 +00:00
Vladimir Panteleev d6d7ecf47e swflzmacompress: Add an option to update the SWF version number 2013-06-02 15:27:55 +00:00
Vladimir Panteleev 0cefc587e6 Fix DMD 2.063 slice warnings 2013-03-23 18:53:22 +00:00
Vladimir Panteleev a45b4e2bdc lzma: Work around liblzma dictionary size limitation 2013-03-18 11:25:04 +00:00
Vladimir Panteleev e383172765 lzma: Remove debugging code 2013-03-18 09:35:00 +00:00
Vladimir Panteleev c62b5438d7 Documentation: Update default DCFLAGS 2013-03-18 09:28:27 +00:00
Vladimir Panteleev a1a214ae72 build_rabcdasm: Allow overriding program list / build flags on command line 2013-03-18 09:28:09 +00:00
Vladimir Panteleev 222fa1109c Documentation: Update download link 2012-12-26 13:48:53 +02:00
Vladimir Panteleev 9fa2d122fa disassembler: Further improve refname generation by adding context priority 2012-12-02 12:06:06 +02:00
Vladimir Panteleev 93adc953c2 disassembler: Separate class and instance ref context 2012-12-02 12:05:34 +02:00
Vladimir Panteleev dc222f9e43 disassembler: Relax trait multiname kind restrictions 2012-12-01 14:05:33 +02:00
Vladimir Panteleev fb2f96b81a disassembler: Visit instance protected namespace 2012-12-01 14:01:19 +02:00
Vladimir Panteleev 14f5e669ee disassembler: Visit slot types 2012-11-27 15:23:24 +02:00
Vladimir Panteleev 4e3d43df5f 64-bit fixes 2012-09-29 14:06:39 +02:00
Vladimir Panteleev c1d6b29d94 Work around apparent liblzma bugs 2012-09-29 14:06:06 +02:00
Vladimir Panteleev 8fc7253bba Fix some warnings and -property compliance 2012-09-29 14:18:56 +03:00
Vladimir Panteleev ac2bde2550 Fix handling of truncated tags 2012-09-29 14:08:33 +03:00
Vladimir Panteleev 99dfd797ba Update Changelog 2012-09-08 20:57:34 +03:00
Vladimir Panteleev f4824cd3b4 assembler: Distinguish unlabeled and first-label homonym namespace 2012-09-07 00:52:25 +03:00
Vladimir Panteleev 87047b920c disassembler: Fix absolute UNIX paths 2012-09-07 00:50:22 +03:00
Vladimir Panteleev f17e97848b DMD 2.060 (work around issue 8526) 2012-09-07 00:01:47 +03:00
Vladimir Panteleev 336665a213 Documentation updates 2012-09-04 20:14:04 +03:00
Vladimir Panteleev fdb8489382 Method ID debug code 2012-09-04 20:13:59 +03:00
Vladimir Panteleev afe42b5d1f disassembler: improve ContextItem combining 2012-09-04 20:13:54 +03:00
Vladimir Panteleev 3a9829467f disassembler: dead code cleanup 2012-09-04 20:13:50 +03:00
Vladimir Panteleev 8e969d9e76 disassembler: Reduce memory allocations 2012-09-04 20:13:45 +03:00
Vladimir Panteleev d755065785 refid generation overhaul (support non-private homonym namespaces) 2012-09-04 20:13:40 +03:00
Vladimir Panteleev ea3dfd30b1 Documentation: rewrap Markdown to 79 columns 2012-09-03 18:25:40 +03:00
Vladimir Panteleev 86a4a2e0b8 Documentation: dos2unix 2012-09-03 18:05:49 +03:00
Vladimir Panteleev 704045594a Update Changelog 2012-09-03 16:01:00 +03:00
Vladimir Panteleev 5b4c31d2a0 Documentation: Explain #version a bit more 2012-09-03 16:00:04 +03:00
Vladimir Panteleev a56d074dad disassembler: Fix handling of recursively-nested functions 2012-09-03 14:38:11 +03:00
Vladimir Panteleev d722bdcb3c abcfile: Add coerce_b, coerce_i and coerce_d opcodes 2012-09-03 14:37:51 +03:00
Vladimir Panteleev ef9e768b07 DMD 2.060 2012-08-20 15:15:59 +03:00
Vladimir Panteleev d321be2b25 build_rabcdasm: Force rebuild of test programs 2012-08-10 06:57:23 +03:00
Vladimir Panteleev 6543d17fc4 Attempted fix for DMD 2.060 2012-08-09 08:42:18 +03:00
Vladimir Panteleev e20332aeba autodata: Fix AutoToString for strings 2012-08-09 06:36:58 +03:00
Vladimir Panteleev d77e2b679e @property fixes 2012-08-09 06:36:55 +03:00
Vladimir Panteleev 9d0d0355e5 autodata: Use .toString when present when dumping arrays 2012-08-09 06:36:38 +03:00
Vladimir Panteleev 5795d5f374 assembler: Don't accept out-of-range ubyte literals 2012-05-01 01:17:16 +03:00
Vladimir Panteleev 8e8d204269 disassembler: Relax filename sanitization for names like "controller" 2012-04-10 01:50:44 +03:00
Vladimir Panteleev 20f416d9d8 DMD 2.059 fixes 2012-04-09 10:22:33 +03:00
Vladimir Panteleev e3634e59cd Update Changelog 2012-03-15 22:44:02 +02:00
Vladimir Panteleev 3c5101f6c0 Update Changelog 2012-03-15 02:55:03 +02:00
Vladimir Panteleev c2042660f2 Fix metadata handling (caused by error in ABC format specification) 2012-03-15 02:47:48 +02:00
Vladimir Panteleev 57d05c6c35 disassembler: Use Windows filename logic on all platforms
This is to allow files disassembled on one platform to
work on other platforms.
2012-03-15 02:23:44 +02:00
Vladimir Panteleev 532700df03 disassembler: Use more Windows-friendly filenames 2012-03-15 02:21:26 +02:00
Vladimir Panteleev fe9b49bfd3 Use UNC paths on Windows to avoid the MAX_PATH limitation 2012-03-15 01:35:26 +02:00
Vladimir Panteleev e7b65d6d50 Handle empty contexts 2012-03-15 01:12:27 +02:00
Vladimir Panteleev a304843b5b Improve handling of out-of-range signed integers 2012-03-15 01:00:32 +02:00
Vladimir Panteleev ebe6639e8a Update asasm.hrc 2012-03-15 00:39:15 +02:00
Vladimir Panteleev 09be72ea9c Improve refid generation for protected namespaces 2012-03-15 00:39:08 +02:00
Vladimir Panteleev 151bf87038 Fix unintentional hierarchy flattening 2012-03-07 05:09:27 +02:00
Vladimir Panteleev 950f16fd23 Update Changelog 2012-02-29 14:01:22 +02:00
Vladimir Panteleev 0dd9dcdb2f Escape empty path segments 2012-02-29 13:56:37 +02:00
Vladimir Panteleev 5e69e44c17 Fix compilation with latest DMD 2012-02-27 21:54:51 +02:00
Vladimir Panteleev 15d20019d5 Update Changelog 2012-02-07 04:40:36 +02:00
Vladimir Panteleev b0795bfcde Fix handling of overflown integers 2012-02-06 08:26:04 +02:00
Vladimir Panteleev bd93ae35d7 disassembler: Discover private namespaces through class ancestry 2012-02-06 08:01:28 +02:00
Vladimir Panteleev e9bf251777 Documentation updates 2012-02-06 07:33:45 +02:00
Vladimir Panteleev de68fea1f1 swffile: 64-bit fix 2012-02-06 06:36:13 +02:00
Vladimir Panteleev 03c02cbc4a build_rabcdasm: Minor visual tweak 2012-02-06 06:29:30 +02:00
Vladimir Panteleev fa08188324 Documentation: mention liblzma 2012-02-06 06:24:01 +02:00
Vladimir Panteleev 9278ae9910 Update build_rabcdasm, allow building without LZMA 2012-02-06 06:21:46 +02:00
Vladimir Panteleev e479fbc25a Add swflzmacompress 2012-02-06 05:15:07 +02:00
Vladimir Panteleev 10464de2a2 Add LZMA compression 2012-02-06 04:50:22 +02:00
Vladimir Panteleev b371fb5bec Add LZMA decompression 2012-02-06 04:14:08 +02:00
Vladimir Panteleev 216beb2edd Fix build instructions for gdmd 2012-01-02 17:53:27 +02:00
Vladimir Panteleev b67c0f8751 AStoABC refactoring 2011-10-03 15:23:46 +03:00
Vladimir Panteleev 8fe2227446 Fix DMD 2.055 deprecation warnings 2011-10-02 18:48:48 +03:00
Vladimir Panteleev 1f0ef64b83 64-bit fixes 2011-07-25 02:12:22 +03:00
Vladimir Panteleev 701e5b6f80 build_rabcdasm: build with warnings 2011-07-25 02:11:15 +03:00
Vladimir Panteleev fc46ad02e9 Documentation: fix abcreplace example 2011-07-13 19:22:45 +03:00
Vladimir Panteleev e7a5dd92c6 Fix DMD 2.054 deprecation warnings 2011-07-11 19:47:32 +03:00
40 changed files with 7166 additions and 1589 deletions
+18
View File
@@ -0,0 +1,18 @@
*.exe
*.dll
*.pdb
*.ilk
*.exp
*.lib
*.suo
/build_rabcdasm
/rabcasm
/rabcdasm
/abcexport
/abcreplace
/swfbinexport
/swfbinreplace
/swfdecompress
/swf7zcompress
/swflzmacompress
+119 -13
View File
@@ -1,6 +1,112 @@
RABCDAsm Changelog
==================
RABCDAsm v1.19 (????.??.??)
---------------------------
* Ignore duplicate class names during dependency registration
* Make over-estimation of uncompressed SWF size non-fatal
* Improve robustness of zlib decompression
* Disassemble type `0x00` as `Undefined` instead of `Void`
- This matches the terminology used in the AVM specification.
- `Void` continues to be recognized as a synonym for `Undefined`.
RABCDAsm v1.18 (2016.01.16)
---------------------------
* Fix disassembly of `pushbyte` instructions (the AVM specification
incorrectly lists the argument as unsigned).
* Bump `#version` directive of new disassemblies to 4:
- Versions below 4 treat `pushbyte` as unsigned, and throw an exception if
the argument is outside the range `0` ... `255`.
- Versions 4 and above treat `pushbyte` as signed, and throw an exception
if the argument is outside the range `-128` ... `127`.
* Fix buffer reuse bugs when using macros
* Dump floating-point numbers in hex notation if necessary to ensure precision
* Cease emitting a number in a comment after opening `script` tags
* Detect a known DMD bug in `build_rabcdasm`
* Ignore invalid file size in header
RABCDAsm v1.17 (2014.09.10)
---------------------------
* Do not attempt to disassemble unreachable code
* Improve handling of disassembly errors:
methods will be partially disassembled as far as possible.
* Fix LZMA errors with uncompressable data.
RABCDAsm v1.16 (2014.04.21)
---------------------------
* Fix handling of TypeName-kind Multinames with null parameters
* Fix v1.15 regression in handling very long paths on Windows
(DMD 2.066 is required when building from source for this to work)
RABCDAsm v1.15 (2014.01.11)
---------------------------
* Fix building on systems with a noexec `/tmp/`
* Improve compatibility with 3rd-party players
* Don't emit forward references in TypeName-kind Multinames
* Improve performance and memory usage
RABCDAsm v1.14 (2013.08.21)
---------------------------
* Improved refid generation
* Worked around liblzma dictionary size limitation, which prevented
decompression of some LZMA-compressed files
* Added an option to `swflzmacompress` to update the SWF version number
* Added `finddef` instruction
* Added unimplemented `{get|set|delete}propertylate` instructions
* Documentation updates
RABCDAsm v1.13 (2012.09.29)
---------------------------
* Fixed handling of truncated SWF tags
RABCDAsm v1.12 (2012.09.08)
---------------------------
* Relaxed filename sanitization for names like "controller"
* Added range validation for byte literals
* Fixed LZMA support misdetection caused by stale rdmd cache
* Added coerce_b, coerce_i and coerce_d opcodes
* Fixed handling of recursively-nested function literals
* Fixed handling of homonym non-private namespaces
* #privatens directives are now ignored, and no longer generated
* Improved refid generation
* Bumped #version to 3 (v1.12 disassemblies are not backwards-compatible)
* Documentation updates
RABCDAsm v1.11 (2012.03.15)
---------------------------
* Fixed v1.10 hierarchy flattening regression
* Improved refid generation for protected namespaces
* Improved handling of overflown signed integers
* Fixed metadata handling (caused by error in ABC format specification)
* Added #version directive (v1.11 disassemblies are not backwards-compatible)
* Removed path length limitation on Windows
* Improved filename sanitization
* Updated asasm.hrc
* Documentation updates
RABCDAsm v1.10 (2012.02.29)
---------------------------
* Fixed escaping of paths with empty segments
RABCDAsm v1.9 (2012.02.07)
--------------------------
* Added support for LZMA-compressed SWF files
* Added swflzmacompress
* Fixed undiscovered private namespaces in class interfaces
* Fixed handling of overflown integers
* Documentation updates
RABCDAsm v1.8 (2011.07.06)
--------------------------
@@ -26,27 +132,27 @@ RABCDAsm v1.6 (2011.05.13)
--------------------------
* Overhaul of the structure of disassembler-generated files
* Private namespace names and `refid`s for nested classes are now generated
* Private namespace names and `refid`s for nested classes are now generated
from their referenced contexts
* This also fixes problems caused by private namespaces being referenced
* This also fixes problems caused by private namespaces being referenced
in multiple distinct contexts
* One `#include`d file per script instead of per class
* Nested classes, script initializers and other script traits are now in
* Nested classes, script initializers and other script traits are now in
the same file as their main class
* Inline methods are now in separate files, placed according to their usage
* Inline methods are now in separate files, placed according to their usage
context
* Private namespaces are sorted by their autogenerated names in the
* Private namespaces are sorted by their autogenerated names in the
`.privatens.asasm` file
* No syntax changes, output is backwards-compatible
* Improved escaping of file names
* Output correct opcode error locations
* Disassembler will now refuse to overwrite files
* `rabcdasm` will now always generate output in the same directory as the
* `rabcdasm` will now always generate output in the same directory as the
input file
* Failure to decode a method body is now not a fatal error
* Aggressively try to shorten double literals to their shortest representation
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
contents of `DefineBinaryData` tags
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in
manipulating contents of `DefineBinaryData` tags
* The package can now be built using a simple, dedicated build tool
* Documentation updates
@@ -63,7 +169,7 @@ RABCDAsm v1.4 (2011.03.07)
* Source code ported to D2
* Add support for forward-references for TypeName-kind Multinames
* Correctly order classes by dependencies (extends/implements) and reference
* Correctly order classes by dependencies (extends/implements) and reference
count
* Finish Metadata support
* Documentation updates
@@ -72,7 +178,7 @@ RABCDAsm v1.3 (2010.11.11)
--------------------------
* Fixed double precision problem
* This also fixes problems with illegal default values for function
* This also fixes problems with illegal default values for function
parameters (default values for integer parameters are stored as doubles,
which might become out-of-range due to inadequate double precision)
* Added Changelog
@@ -81,8 +187,8 @@ RABCDAsm v1.3 (2010.11.11)
RABCDAsm v1.2 (2010.11.06)
--------------------------
* Fixed ref generation for orphan objects which were only referenced
by other orphans
* Fixed ref generation for orphan objects which were only referenced by other
orphans
* Better error handling in `abcexport`; warn when no DoABC tags found
* Documentation updates
@@ -92,7 +198,7 @@ RABCDAsm v1.1 (2010.06.30)
* Private namespaces are now referenced by auto-generated names
* Use `:` to delimit namespace and name in QNames for consistency
* Fixed relative include paths
* Add optional byte offsets to labels, which allows lossless representation
* Add optional byte offsets to labels, which allows lossless representation
of jumps inside instructions and outside the function bounds
* Documentation updates
+459 -440
View File
@@ -1,440 +1,459 @@
Robust ABC (ActionScript Bytecode) [Dis-]Assembler
==================================================
[RABCDAsm][] is a collection of utilities including an ActionScript 3
assembler/disassembler, and a few tools to manipulate SWF files.
These are:
* `rabcdasm` - ABC disassembler
* `rabcasm` - ABC assembler
* `abcexport` - extracts ABC from SWF files
* `abcreplace` - replaces ABC in SWF files
* `swfdecompress` - decompresses zlib-compressed SWF files
* `swf7zcompress` - (re-)compress the contents of a SWF using 7-Zip
* `swfbinexport` / `swfbinreplace` - extract/replace contents of binary
data tags from SWF files
`abcexport` and `abcreplace` are reimplementations of similar utilities from
my [swfutilsex][] Java package, however these work faster as they do not parse
the SWF files as deeply.
`swfdecompress` is ancillary and is only useful for debugging and studying of
the SWF file format, and not required for ABC manipulation. It is functionally
equivalent to [flasm][]'s `-x` option.
`swf7zcompress` is an utility to further reduce the size of SWF files. It uses
[7-Zip][] to compress the data better than the standard zlib library would. It
requires that the `7z` command-line program be installed and in `PATH`.
`swfbinexport` and `swfbinreplace` aid in the manipulation of
`DefineBinaryData` tags in SWF files (some files may contain nested SWF files
stored in these tags).
[RABCDAsm]: http://github.com/CyberShadow/RABCDAsm
[swfutilsex]: http://github.com/CyberShadow/swfutilsex
[flasm]: http://flasm.sourceforge.net/
[7-Zip]: http://www.7-zip.org/
Motivation and goals
--------------------
This package was created due to lack of similar software out there.
Particularly, I needed an utility which would allow me to edit ActionScript 3
bytecode with the following properties:
1. Speed. Less waiting means more productivity. `rabcasm` can assemble large
projects (>200000 LOC) in under a second on modern machines.
2. Comfortably-editable output. Each class is decompiled to its own file, with
files arranged in subdirectories representing the package hierarchy. Class
files are `#include`d from the main file.
3. Most importantly - robustness! If the Adobe AVM can load and run the file,
then it must be editable - no matter if the file is obfuscated or otherwise
mutilated to prevent reverse-engineering. RABCDAsm achieves this by using
a textual representation closer to the ABC file format, rather than to what
an ActionScript compiler would generate.
Compiling from source
---------------------
RABCDAsm is written in the [D programming language, version 2][d2].
Assuming you have [git][] and a D2 compiler, such as [dmd][] or [gdc][]
installed, compiling should be as straight-forward as:
git clone git://github.com/CyberShadow/RABCDAsm.git
cd RABCDAsm
dmd -run build_rabcdasm
Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
`DCFLAGS` environment variables to override the detected compiler and default
compilation flags (`-O -inline`).
[d2]: http://www.digitalmars.com/d/2.0/
[dmd]: http://www.digitalmars.com/d/download.html
[gdc]: http://bitbucket.org/goshawk/gdc/
[git]: http://git-scm.com/
Pre-compiled binaries
---------------------
You can find pre-compiled Windows binaries in the [Downloads section on
GitHub][downloads]. However, please don't expect them to be up-to-date
with the latest source versions.
[downloads]: http://github.com/CyberShadow/RABCDAsm/downloads
Usage
-----
To begin hacking on a SWF file:
abcexport file.swf
This will create `file-0.abc` ... `file-N.abc` (often just `file-0.abc`). Each
file corresponds to an ABC block inside the SWF file.
To disassemble one of the `.abc` files:
rabcdasm file-0.abc
This will create a `file-0` directory, which will contain `file-0.main.asasm`
(the main program file), `file-0.privatens.asasm` (private namespace alias
definitions), and files for ActionScript scripts, classes, and orphan and
script-level methods.
To assemble the `.asasm` files back, and update the SWF file:
rabcasm file-0/file-0.main.asasm
abcreplace file-0.swf 0 file-0/file-0.main.abc
The second `abcreplace` argument represents the index of the ABC block in the
SWF file, and corresponds to the number in the filename created by `abcexport`.
`swfbinexport` and `swfbinreplace` are used in the same manner as `abcexport`
and `abcreplace`.
Syntax
======
The syntax of the disassembly was designed to be very simple and allow fast
and easy parsing. It is a close representation of the `.abc` file format, and
thus it is somewhat verbose. All constant pool elements (signed/unsigned
integers, doubles, strings, namespaces, namespace sets, multinames) are always
*expanded inline*, for ease of editing. Similarly, classes, instances, methods
and method bodies are also defined inline, in the context of their "parent"
object. By-index references of classes and methods (used in the `newclass`,
`newfunction` and `callstatic` instructions) are represented via
automatically-generated unique "reference strings", declared as `refid` fields.
If you haven't yet, I strongly recommend that you look through Adobe's
[ActionScript Virtual Machine 2 (AVM2) Overview][avm2]. You will most likely
need to consult it for the instruction reference anyway (although you can also
use [this handy list][avm2i] as well). You will find it difficult to understand
the disassembly without good understanding of concepts such as namespaces and
multinames.
[avm2]: http://www.adobe.com/devnet-archive/actionscript/articles/avm2overview.pdf
[avm2i]: http://www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
Overview
--------
In order to guarantee unambiguity and data preservation, all strings read from
the input file - including identifiers (variable/function/class names) - are
represented as string literals. Thus, the syntax does not have any "reserved
words" or such - an unrecognized word is treated as an error, not as an
identifier.
Whitespace (outside string literals, of course) is completely ignored, except
where required to separate words. Comments are Intel-assembler-style: a single
`;` demarks a comment until the next end-of-line. Control directives (such as
`#include`) are allowed anywhere where whitespace is allowed.
The syntax is comprised of hierarchical blocks. Each block contains a number
of fields - starting with a keyword specifying the field type. A block is
terminated with the `end` keyword. Some fields contain a limited number of
parameters, and others are, or contain blocks.
Hierarchy
---------
The topmost block in the hierarchy is the `program` block. This must be the
first block in the file (thus, `program` must be the first word in the file as
well). The `program` block contains `script` fields, and `class` / `method`
fields for "orphan" classes and methods (not owned by other objects in the
hierarchy). Orphan methods are usually anonymous functions. The file version is
also specified in the `program` block, using the `minorversion` and
`majorversion` fields (both unsigned integers).
`script` blocks have one mandatory `sinit` field (the script initialization
method) and `trait` fields.
A "trait" can be one of several kinds. The kind is specified right after the
`trait` keyword, followed by the trait name (a multiname). Following the name
are the trait fields, varying by trait kind:
* `slot` / `const` : `slotid` (unsigned integer), `type` (multiname), `value`
* `class` : `slotid`, `class` (the actual class block)
* `function` : `slotid`, `method` (the actual method block)
* `method` / `getter` / `setter` : `dispid` (unsigned integer), `method`
Additionally, all traits may have `flag` fields, describing the trait's
attributes (`FINAL` / `OVERRIDE` / `METADATA`), and `metadata` blocks.
`metadata` blocks (which are ignored by the AVM) consist of a name string, and
a series of `item` fields - each item having a key and value string.
`class` blocks have mandatory `instance` and `cinit` fields, defining the class
instance and the class initializer method respectively. They may also have
`trait` fields and a `refid` field (the `refid` field is not part of the file
format - it's an unique string to allow referencing the class, see above).
`instance` blocks - always declared inline of their `class` block - must
contain one `iinit` field (the instance initializer method), and may contain
one `extends` field (multiname), `implements` fields (multinames), `flag`
fields (`SEALED` / `FINAL` / `INTERFACE` / `PROTECTEDNS`), one `protectedns`
field (namespace), and `trait` fields.
`method` blocks may contain one `name` field (multiname), a `refid` field,
`param` fields (multinames - this represents the parameter types), one
`returns` field (multiname), `flag` fields (`NEED_ARGUMENTS` /
`NEED_ACTIVATION` / `NEED_REST` / `HAS_OPTIONAL` / `SET_DXNS` /
`HAS_PARAM_NAMES`), `optional` fields (values), `paramname` fields (strings),
and a `body` field (method body).
`body` blocks - always declared inline of their `method` block - must contain
the `maxstack`, `localcount`, `initscopedepth` and `maxscopedepth` fields
(unsigned integers), and a `code` field. It may also contain `try` and `trait`
fields.
`code` blocks - always declared inline of their `body` block - are somewhat
different in syntax from other blocks - mostly in that they may contain labels.
Labels follow the most common syntax - a word followed by a `:` character,
optionally followed by a relative byte offset (in case of pointers inside
instructions). Multiple instruction arguments are comma-separated. Instruction
arguments' types depend on the instruction - see the `OpcodeInfo` array in
`abcfile.d` for a reference.
`try` blocks - always declared inline of their `body` block - represent an
"exception" (try/catch) block. They contain five mandatory fields: `from`,
`to` and `target` (names of labels representing start and end of the "try"
block, and start of the "catch" block respectively), and `type` and `name`
(multinames), representing the type and name of the exception variable.
Values have the syntax *type* `(` *value* `)` . *type* can be one of `Integer`,
`UInteger`, `Double`, `Utf8`, `Namespace`, `PackageNamespace`,
`PackageInternalNs`, `ProtectedNamespace`, `ExplicitNamespace`,
`StaticProtectedNs`, `PrivateNamespace`, `True`, `False`, `Null` or
`Undefined`. The type of the value depends on *type*. Types `True`, `False`,
`Null` and `Undefined` have no value.
Constants
---------
Multinames have the syntax *type* `(` *parameters* `)` . *type* can be one of
`QName` / `QNameA`, `RTQName` / `RTQNameA`, `RTQNameL` / `RTQNameLA`,
`Multiname` / `MultinameA`, `MultinameL` / `MultinameLA`, or `TypeName`.
*parameters* depends on *type*:
* `QName` / `QNameA` `(` *namespace* `,` *string* `)`
* `RTQName` / `RTQNameA` `(` *string* `)`
* `RTQNameL` / `RTQNameLA` `(` `)`
* `Multiname` / `MultinameA` `(` *string* `,` *namespace-set* `)`
* `MultinameL` / `MultinameLA` `(` *namespace-set* `)`
* `TypeName` `(` *multiname* `<` *multiname [* `,` *multiname ... ]* `>` `)`
Namespace sets have the syntax `[` *[ namespace [* `,` *namespace ... ] ]* `]`
(that is, a comma-separated list of namespaces in square brackets). Empty
namespace sets can be specified using `[]`.
Namespaces have the syntax *type* `(` *parameters* `)` . For types other than
`PrivateNamespace` there is only one parameter - a string. `PrivateNamespace`
namespaces have a second parameter, a named alias for a particular private
namespace. Internally (the ABC file format), private namespaces are
distinguished by a numerical index - `rabcdasm` will attempt to give them
descriptive names based on their context. Aliases can be defined using the
`#privatens` directive. `rabcdasm` will create a separate file containing the
aliases (`file-0.privatens.asasm`).
Strings have a syntax similar to C string literals. Strings start and end with
a `"`. Supported escape sequences (a backslash followed by a letter) are `\n`
(generates ASCII 0x0A), `\r` (ASCII 0x0D), and `\x` followed by two hexadecimal
digits, which inserts the ASCII character with that code. Any other characters
following a backslash generate that character - thus, you can escape
backslashes using `\\` and double quotes using `\"`. When decompiling,
high-ASCII characters (usually UTF-8) are not escaped - if you see gibberish
instead of international text, configure your editor to open the files in UTF-8
encoding.
Additionally, constant pool types (signed/unsigned integers, doubles, strings,
namespaces, namespace sets and multinames) may also have the value `null`
(which represents the index 0 in the ABC file). Note that `null` is
conceptually different from zero, an empty string or empty namespace set.
Macros
------
RABCDAsm has some basic macro-like capabilities, controlled by directives and
variables. These bear some similarity to the C preprocessor, however these
are processed in-loop rather than as a separate pre-processing step.
### Directives
Directives start with a `#`, followed by a word identifying the directive:
* `#include` *string* - inserts the contents of the file by the specified
filename inline. Functionally equivalent to `#mixin #get` *string* , but
faster.
* `#mixin` *string* - inserts the contents of the specified string inline.
Not very useful on its own.
* `#call` *string* `(` *[ string [* `,` *string ... ] ]* `)` - same as
`#mixin`, however it additionally sets the special variables `$1`, `$2`
etc. to the contents of the specified arguments. When the end of the
inserted string is reached, the old values of `$1`, `$2` etc. are restored.
* `#get` *string* - inserts **a string containing** the contents of the
file by the specified filename inline. Similar to #include, but it inserts
a string (surrounded by `"` etc.) instead.
* `#set` *word* *string* - assigns the contents of the string to the
variable *word*.
* `#unset` *word* - deletes the variable *word*.
* `#privatens` defines a private namespace alias, as described above.
### Variables
Variables are manipulated with the `#set` and `#unset` directives, and can
be instantiated in two ways:
1. `$`*name* - this inserts the contents of the variable inline. Note that
although variables are defined using a string syntax, they are not
inserted as a string using this syntax. Thus, the code:
#set str "Hello, world!"
...
pushstring $str
will expand to `pushstring Hello, world!`, which will result in an error.
To correct the problem, add escaped quotes around the variable contents
( `#set str "\"Hello, world!\""` ), or use the second syntax:
2. `$"`*name*`"` - this inserts a string containing the contents of the
variable inline. This syntax also works for `#call` arguments (e.g.
`$"1"`).
### Example
Here's an example of how to use the above features to create a macro which
logs a string literal and the contents of a register:
#set log "
findpropstrict QName(PackageNamespace(\"\"), \"log\")
pushstring $\"1\"
getlocal $2
callpropvoid QName(PackageNamespace(\"\"), \"log\"), 2
"
; ...
pushbyte 2
pushbyte 2
add_i
setlocal1
#call $"log"("two plus two equals", "1")
Highlighting
------------
Included with the project is the file `asasm.hrc`, a simple syntax definition
for the [Colorer take5][] syntax highlighting library. It should be
straight-forward to adapt it to other syntax highlighting systems.
[Colorer take5]: http://colorer.sourceforge.net/
Hacking
=======
ABC is internally represented in two forms. The `ABCFile` class stores the raw
data structures, as they appear in the binary file. `ASProgram` uses pointers
instead of indexes, allowing easy manipulation without having to worry about
record order or constant pools. Conversion between various states is done as
follows:
file.abc
| ^
------ ABCReader | | ABCWriter ----
/ v | \
/ ABCFile \
/ | ^ \
rabcdasm---------- ABCtoAS | | AStoABC --------rabcasm
\ v | /
\ ASProgram /
\ | ^ /
--- Disassembler | | Assembler ----
v |
file.asasm
`AStoABC` will rebuild the constant pools, in a manner similar to Adobe's
compilers (reverse-sorted by reference count). The exact order will almost
surely be different, however.
Should you need to write an utility to manipulate ABC, you can use the existing
code to load the file to either an `ABCFile` or `ASProgram` instance, and
perform the necessary manipulations using those classes.
Tips
====
The following tips come from the author's experience and may be useful for
RABCDAsm users.
1. Once you have disassembled a SWF file you intend to modify, you should
immediately add the directory to a distributed source control system, such as
[Git][] or [Mercurial][]. This will allow you to easily track and undo your
changes, and easily merge your changes with new versions of SWF files.
[Git]: http://git-scm.com/
[Mercurial]: http://mercurial.selenic.com/
2. If you plan on making non-trivial changes to SWF files, you should install
the [debug Flash Player][]. This will allow you to see validation and
run-time error messages, instead of simply getting an empty window.
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
3. The [Fiddler][] Web Debugging Proxy can be very useful for analyzing
websites with SWF content. The following script fragment (which is to be
placed in the `OnBeforeResponse` function) will automatically save all SWF
files while preserving the directory structure.
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"application/x-shockwave-flash")) {
// Set desired path here
var path:String = "C:\\Temp\\FiddlerCapture\\" +
oSession.host + oSession.PathAndQuery;
if (path.Contains('?'))
path = path.Substring(0, path.IndexOf('?'));
var dir:String = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
oSession.utilDecodeResponse();
oSession.SaveResponseBody(path);
}
A more robust version of the above snippet is available as a Fiddler plugin
[here][FiddlerAutoCapture].
Once you have edited a SWF file, you can use Fiddler's [AutoResponder][] to
replace the original file with your modified version.
[Fiddler]: http://www.fiddler2.com/fiddler2/
[AutoResponder]: http://www.fiddler2.com/fiddler2/help/AutoResponder.asp
[FiddlerAutoCapture]: https://github.com/CyberShadow/FiddlerAutoCapture
Limitations
===========
* None known.
License
=======
RABCDAsm is distributed under the terms of the GPL v3 or later, with the
exception of `murmurhash2a.d` and `zlibx.d`, which are in the public domain,
and `asasm.hrc`, which is tri-licensed under the MPL 1.1/GPL 2.0/LGPL 2.1. The
full text of the GNU General Public License can be found in the file `COPYING`.
Robust ABC (ActionScript Bytecode) [Dis-]Assembler
==================================================
[RABCDAsm][] is a collection of utilities including an ActionScript 3
assembler/disassembler, and a few tools to manipulate SWF files. These are:
* `rabcdasm` - ABC disassembler
* `rabcasm` - ABC assembler
* `abcexport` - extracts ABC from SWF files
* `abcreplace` - replaces ABC in SWF files
* `swfdecompress` - decompresses zlib-compressed SWF files
* `swf7zcompress` - (re-)compress the contents of a SWF using 7-Zip
* `swflzmacompress` - compress the contents of a SWF using LZMA
* `swfbinexport` / `swfbinreplace` - extract/replace contents of binary data
tags from SWF files
`abcexport` and `abcreplace` are reimplementations of similar utilities from
my [swfutilsex][] Java package, however these work faster as they do not parse
the SWF files as deeply.
`swfdecompress` is ancillary and is only useful for debugging and studying of
the SWF file format, and not required for ABC manipulation. It is functionally
equivalent to [flasm][]'s `-x` option. If you frequently work on compressed
SWF files, you may want to decompress them to speed processing up.
`swf7zcompress` is an utility to further reduce the size of SWF files. It uses
[7-Zip][] to compress the data better than the standard zlib library would. It
requires that the `7z` command-line program be installed and in `PATH`.
`swflzmacompress` compresses SWF files using the [LZMA][] algorithm, support
for which was introduced in Flash 11. It will only work with SWF files with
version 13 or higher.
`swfbinexport` and `swfbinreplace` aid in the manipulation of
`DefineBinaryData` tags in SWF files (some files may contain nested SWF files
stored in these tags).
[RABCDAsm]: http://github.com/CyberShadow/RABCDAsm
[swfutilsex]: http://github.com/CyberShadow/swfutilsex
[flasm]: http://flasm.sourceforge.net/
[7-Zip]: http://www.7-zip.org/
[LZMA]: http://en.wikipedia.org/wiki/Lempel-Ziv-Markov_chain_algorithm
Motivation and goals
--------------------
This package was created due to lack of similar software out there.
Particularly, I needed an utility which would allow me to edit ActionScript 3
bytecode with the following properties:
1. Speed. Less waiting means more productivity. `rabcasm` can assemble large
projects (>200000 LOC) in under a second on modern machines.
2. Comfortably-editable output. Each class is decompiled to its own file,
with files arranged in subdirectories representing the package hierarchy.
Class files are `#include`d from the main file.
3. Most importantly - robustness! If the Adobe AVM can load and run the file,
then it must be editable - no matter if the file is obfuscated or
otherwise mutilated to prevent reverse-engineering. RABCDAsm achieves this
by using a textual representation closer to the ABC file format, rather
than to what an ActionScript compiler would generate.
Compiling from source
---------------------
RABCDAsm is written in the [D programming language, version 2][d2].
Assuming you have [git][] and a D2 compiler, such as [dmd][] or [gdc][]
installed, compiling should be as straight-forward as:
git clone git://github.com/CyberShadow/RABCDAsm.git
cd RABCDAsm
dmd -run build_rabcdasm.d
Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
`DCFLAGS` environment variables to override the detected compiler and default
compilation flags (`-O -inline`).
To be able to manipulate SWF files packed with LZMA compression, you'll need
to have the [liblzma library][] and development files installed on your system.
Note: DMD 2.066 is required for long path support on Windows since RABCDAsm
1.16.
[d2]: http://dlang.org/
[dmd]: http://www.digitalmars.com/d/download.html
[gdc]: http://bitbucket.org/goshawk/gdc/
[git]: http://git-scm.com/
[liblzma library]: http://tukaani.org/xz/
Pre-compiled binaries
---------------------
You can find pre-compiled Windows binaries on [my website][downloads].
However, please don't expect them to be up-to-date with the latest source
versions.
[downloads]: http://files.thecybershadow.net/RABCDAsm/
Usage
-----
To begin hacking on a SWF file:
abcexport file.swf
This will create `file-0.abc` ... `file-N.abc` (often just `file-0.abc`). Each
file corresponds to an ABC block inside the SWF file.
To disassemble one of the `.abc` files:
rabcdasm file-0.abc
This will create a `file-0` directory, which will contain `file-0.main.asasm`
(the main program file) and files for ActionScript scripts, classes, and
orphan and script-level methods.
To assemble the `.asasm` files back, and update the SWF file:
rabcasm file-0/file-0.main.asasm
abcreplace file.swf 0 file-0/file-0.main.abc
The second `abcreplace` argument represents the index of the ABC block in the
SWF file, and corresponds to the number in the filename created by `abcexport`.
`swfbinexport` and `swfbinreplace` are used in the same manner as `abcexport`
and `abcreplace`.
Syntax
======
The syntax of the disassembly was designed to be very simple and allow fast
and easy parsing. It is a close representation of the `.abc` file format, and
thus it is somewhat verbose. All constant pool elements (signed/unsigned
integers, doubles, strings, namespaces, namespace sets, multinames) are always
*expanded inline*, for ease of editing. Similarly, classes, instances, methods
and method bodies are also defined inline, in the context of their "parent"
object. By-index references of classes and methods (used in the `newclass`,
`newfunction` and `callstatic` instructions) are represented via
automatically-generated unique "reference strings", declared as `refid` fields.
If you haven't yet, I strongly recommend that you look through Adobe's
[ActionScript Virtual Machine 2 (AVM2) Overview][avm2]. You will most likely
need to consult it for the instruction reference anyway (although you can also
use [this handy list][avm2i] as well). You will find it difficult to
understand the disassembly without good understanding of concepts such as
namespaces and multinames.
[avm2]: https://www.adobe.com/content/dam/acom/en/devnet/pdf/avm2overview.pdf
[avm2i]: https://web.archive.org/web/20160215185222/www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
Overview
--------
In order to guarantee unambiguity and data preservation, all strings read from
the input file - including identifiers (variable/function/class names) - are
represented as string literals. Thus, the syntax does not have any "reserved
words" or such - an unrecognized word is treated as an error, not as an
identifier.
Whitespace (outside string literals, of course) is completely ignored, except
where required to separate words. Comments are Intel-assembler-style: a single
`;` demarks a comment until the next end-of-line. Control directives (such as
`#include`) are allowed anywhere where whitespace is allowed.
The syntax is comprised of hierarchical blocks. Each block contains a number
of fields - starting with a keyword specifying the field type. A block is
terminated with the `end` keyword. Some fields contain a limited number of
parameters, and others are, or contain blocks.
Hierarchy
---------
The topmost block in the hierarchy is the `program` block. This must be the
first block in the file (thus, `program` must be the first word in the file as
well). The `program` block contains `script` fields, and `class` / `method`
fields for "orphan" classes and methods (not owned by other objects in the
hierarchy). Orphan methods are usually anonymous functions. The file version
is also specified in the `program` block, using the `minorversion` and
`majorversion` fields (both unsigned integers).
`script` blocks have one mandatory `sinit` field (the script initialization
method) and `trait` fields.
A "trait" can be one of several kinds. The kind is specified right after the
`trait` keyword, followed by the trait name (a multiname). Following the name
are the trait fields, varying by trait kind:
* `slot` / `const` : `slotid` (unsigned integer), `type` (multiname), `value`
* `class` : `slotid`, `class` (the actual class block)
* `function` : `slotid`, `method` (the actual method block)
* `method` / `getter` / `setter` : `dispid` (unsigned integer), `method`
Additionally, all traits may have `flag` fields, describing the trait's
attributes (`FINAL` / `OVERRIDE` / `METADATA`), and `metadata` blocks.
`metadata` blocks (which are ignored by the AVM) consist of a name string, and
a series of `item` fields - each item having a key and value string.
`class` blocks have mandatory `instance` and `cinit` fields, defining the
class instance and the class initializer method respectively. They may also
have `trait` fields and a `refid` field (the `refid` field is not part of the
file format - it's an unique string to allow referencing the class, see above).
`instance` blocks - always declared inline of their `class` block - must
contain one `iinit` field (the instance initializer method), and may contain
one `extends` field (multiname), `implements` fields (multinames), `flag`
fields (`SEALED` / `FINAL` / `INTERFACE` / `PROTECTEDNS`), one `protectedns`
field (namespace), and `trait` fields.
`method` blocks may contain one `name` field (multiname), a `refid` field,
`param` fields (multinames - this represents the parameter types), one
`returns` field (multiname), `flag` fields (`NEED_ARGUMENTS` /
`NEED_ACTIVATION` / `NEED_REST` / `HAS_OPTIONAL` / `SET_DXNS` /
`HAS_PARAM_NAMES`), `optional` fields (values), `paramname` fields (strings),
and a `body` field (method body).
`body` blocks - always declared inline of their `method` block - must contain
the `maxstack`, `localcount`, `initscopedepth` and `maxscopedepth` fields
(unsigned integers), and a `code` field. It may also contain `try` and `trait`
fields.
`code` blocks - always declared inline of their `body` block - are somewhat
different in syntax from other blocks - mostly in that they may contain
labels. Labels follow the most common syntax - a word followed by a `:`
character, optionally followed by a relative byte offset (in case of pointers
inside instructions). Multiple instruction arguments are comma-separated.
Instruction arguments' types depend on the instruction - see the `OpcodeInfo`
array in `abcfile.d` for a reference.
`try` blocks - always declared inline of their `body` block - represent an
"exception" (try/catch) block. They contain five mandatory fields: `from`,
`to` and `target` (names of labels representing start and end of the "try"
block, and start of the "catch" block respectively), and `type` and `name`
(multinames), representing the type and name of the exception variable.
Values have the syntax *type* `(` *value* `)` . *type* can be one of
`Integer`, `UInteger`, `Double`, `Utf8`, `Namespace`, `PackageNamespace`,
`PackageInternalNs`, `ProtectedNamespace`, `ExplicitNamespace`,
`StaticProtectedNs`, `PrivateNamespace`, `True`, `False`, `Null` or
`Undefined`. The type of the value depends on *type*. Types `True`, `False`,
`Null` and `Undefined` have no value.
Constants
---------
Multinames have the syntax *type* `(` *parameters* `)` . *type* can be one of
`QName` / `QNameA`, `RTQName` / `RTQNameA`, `RTQNameL` / `RTQNameLA`,
`Multiname` / `MultinameA`, `MultinameL` / `MultinameLA`, or `TypeName`.
*parameters* depends on *type*:
* `QName` / `QNameA` `(` *namespace* `,` *string* `)`
* `RTQName` / `RTQNameA` `(` *string* `)`
* `RTQNameL` / `RTQNameLA` `(` `)`
* `Multiname` / `MultinameA` `(` *string* `,` *namespace-set* `)`
* `MultinameL` / `MultinameLA` `(` *namespace-set* `)`
* `TypeName` `(` *multiname* `<` *multiname [* `,` *multiname ... ]* `>` `)`
Namespace sets have the syntax `[` *[ namespace [* `,` *namespace ... ] ]* `]`
(that is, a comma-separated list of namespaces in square brackets). Empty
namespace sets can be specified using `[]`.
Namespaces have the syntax *type* `(` *string [* `,` *string ]* `)` . The
first string indicates the namespace name. In the case that there are multiple
distinct namespaces with the same type and name (as `PrivateNamespace`
namespaces usually are), a second parameter may be present to uniquely
distinguish them. Internally (the ABC file format), namespaces are
distinguished by their numerical index. When disassembling, `rabcdasm` will
attempt to assign descriptive labels to homonym namespaces based on their
context.
Strings have a syntax similar to C string literals. Strings start and end with
a `"`. Supported escape sequences (a backslash followed by a letter) are `\n`
(generates ASCII 0x0A), `\r` (ASCII 0x0D), and `\x` followed by two
hexadecimal digits, which inserts the ASCII character with that code. Any
other characters following a backslash generate that character - thus, you can
escape backslashes using `\\` and double quotes using `\"`. When decompiling,
high-ASCII characters (usually UTF-8) are not escaped - if you see gibberish
instead of international text, configure your editor to open the files in
UTF-8 encoding.
Additionally, constant pool types (signed/unsigned integers, doubles, strings,
namespaces, namespace sets and multinames) may also have the value `null`
(which represents the index 0 in the ABC file). Note that `null` is
conceptually different from zero, an empty string or empty namespace set.
Macros
------
RABCDAsm has some basic macro-like capabilities, controlled by directives and
variables. These bear some similarity to the C preprocessor, however these are
processed in-loop rather than as a separate pre-processing step.
### Directives
Directives start with a `#`, followed by a word identifying the directive:
* `#include` *string* - inserts the contents of the file by the specified
filename inline. Functionally equivalent to `#mixin #get` *string* , but
faster.
* `#mixin` *string* - inserts the contents of the specified string inline.
Not very useful on its own.
* `#call` *string* `(` *[ string [* `,` *string ... ] ]* `)` - same as
`#mixin`, however it additionally sets the special variables `$1`, `$2`
etc. to the contents of the specified arguments. When the end of the
inserted string is reached, the old values of `$1`, `$2` etc. are restored.
* `#get` *string* - inserts **a string containing** the contents of the file
by the specified filename inline. Similar to #include, but it inserts a
string (surrounded by `"` etc.) instead.
* `#set` *word* *string* - assigns the contents of the string to the
variable *word*.
* `#unset` *word* - deletes the variable *word*.
* `#privatens` *number* *string* - deprecated, currently ignored.
* `#version` specifies the syntax version of the disassembly. Newer RABCDAsm
versions may emit disassembly output that is not backwards-compatible, but
should still understand older disassemblies. The versions are:
1. The first version.
2. Introduced in v1.11 to work around error in ABC format specification.
3. Introduced in v1.12 to support multiple non-private namespaces with
the same name. This is the current version.
### Variables
Variables are manipulated with the `#set` and `#unset` directives, and can be
instantiated in two ways:
1. `$`*name* - this inserts the contents of the variable inline. Note that
although variables are defined using a string syntax, they are not
inserted as a string using this syntax. Thus, the code:
#set str "Hello, world!"
...
pushstring $str
will expand to `pushstring Hello, world!`, which will result in an error.
To correct the problem, add escaped quotes around the variable contents
( `#set str "\"Hello, world!\""` ), or use the second syntax:
2. `$"`*name*`"` - this inserts a string containing the contents of the
variable inline. This syntax also works for `#call` arguments (e.g.
`$"1"`).
### Example
Here's an example of how to use the above features to create a macro which
logs a string literal and the contents of a register:
#set log "
findpropstrict QName(PackageNamespace(\"\"), \"log\")
pushstring $\"1\"
getlocal $2
callpropvoid QName(PackageNamespace(\"\"), \"log\"), 2
"
; ...
pushbyte 2
pushbyte 2
add_i
setlocal1
#call $"log"("two plus two equals", "1")
Highlighting
------------
Included with the project is the file `asasm.hrc`, a simple syntax definition
for the [Colorer take5][] syntax highlighting library. It should be
straight-forward to adapt it to other syntax highlighting systems.
[Colorer take5]: http://colorer.sourceforge.net/
Hacking
=======
ABC is internally represented in two forms. The `ABCFile` class stores the raw
data structures, as they appear in the binary file. `ASProgram` uses pointers
instead of indexes, allowing easy manipulation without having to worry about
record order or constant pools. Conversion between various states is done as
follows:
file.abc
| ^
------ ABCReader | | ABCWriter ----
/ v | \
/ ABCFile \
/ | ^ \
rabcdasm---------- ABCtoAS | | AStoABC --------rabcasm
\ v | /
\ ASProgram /
\ | ^ /
--- Disassembler | | Assembler ----
v |
file.asasm
`AStoABC` will rebuild the constant pools, in a manner similar to Adobe's
compilers (reverse-sorted by reference count). The exact order will almost
surely be different, however.
Should you need to write an utility to manipulate ABC, you can use the
existing code to load the file to either an `ABCFile` or `ASProgram` instance,
and perform the necessary manipulations using those classes.
Tips
====
The following tips come from the author's experience and may be useful for
RABCDAsm users.
1. Once you have disassembled a SWF file you intend to modify, you should
immediately add the directory to a distributed source control system, such
as [Git][] or [Mercurial][]. This will allow you to easily track and undo
your changes, and easily merge your changes with new versions of SWF files.
[Git]: http://git-scm.com/
[Mercurial]: http://mercurial.selenic.com/
2. If you plan on making non-trivial changes to SWF files, you should install
the [debug Flash Player][]. This will allow you to see validation and
run-time error messages, instead of simply getting an empty window.
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
3. The [Fiddler][] Web Debugging Proxy can be very useful for analyzing
websites with SWF content. The following script fragment (which is to be
placed in the `OnBeforeResponse` function) will automatically save all SWF
files while preserving the directory structure.
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"application/x-shockwave-flash")) {
// Set desired path here
var path:String = "C:\\Temp\\FiddlerCapture\\" +
oSession.host + oSession.PathAndQuery;
if (path.Contains('?'))
path = path.Substring(0, path.IndexOf('?'));
var dir:String = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
oSession.utilDecodeResponse();
oSession.SaveResponseBody(path);
}
A more robust version of the above snippet is available as a Fiddler plugin
[here][FiddlerAutoCapture].
Once you have edited a SWF file, you can use Fiddler's [AutoResponder][] to
replace the original file with your modified version.
[Fiddler]: http://www.fiddler2.com/fiddler2/
[AutoResponder]: http://www.fiddler2.com/fiddler2/help/AutoResponder.asp
[FiddlerAutoCapture]: https://github.com/CyberShadow/FiddlerAutoCapture
Limitations
===========
* None known.
License
=======
RABCDAsm is distributed under the terms of the GPL v3 or later, with the
exception of `murmurhash2a.d`, `zlibx.d` and LZMA components, which are in the
public domain, and `asasm.hrc`, which is tri-licensed under the MPL 1.1/GPL
2.0/LGPL 2.1. The full text of the GNU General Public License can be found in
the file `COPYING`.
+1 -1
View File
@@ -45,7 +45,7 @@ void main(string[] args)
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
}
std.file.write(getName(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
std.file.write(stripExtension(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
}
if (count == 0)
throw new Exception("No DoABC tags found");
+312 -157
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013, 2014, 2021 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
module abcfile;
import std.string : format; // exception formatting
import std.conv;
import std.exception;
/**
@@ -46,9 +47,13 @@ class ABCFile
Script[] scripts;
MethodBody[] bodies;
static const long NULL_INT = long.max;
static const ulong NULL_UINT = ulong.max;
static const double NULL_DOUBLE = double.init; // NaN
enum long NULL_INT = long.max;
enum ulong NULL_UINT = ulong.max;
enum double NULL_DOUBLE = double.init; // NaN
enum ulong MAX_UINT = (1L << 36) - 1;
enum long MAX_INT = MAX_UINT / 2;
enum long MIN_INT = -MAX_INT - 1;
this()
{
@@ -126,13 +131,8 @@ class ABCFile
struct Metadata
{
struct Item
{
uint key, value;
}
uint name;
Item[] items;
uint[] keys, values;
}
struct Instance
@@ -177,12 +177,12 @@ class ABCFile
}
uint[] metadata;
TraitKind kind() { return cast(TraitKind)(kindAttr&0xF); }
void kind(TraitKind value) { kindAttr = (kindAttr&0xF0) | value; }
@property TraitKind kind() { return cast(TraitKind)(kindAttr&0xF); }
@property void kind(TraitKind value) { kindAttr = (kindAttr&0xF0) | value; }
// TraitAttributes bitmask
ubyte attr() { return cast(ubyte)(kindAttr >> 4); }
void attr(ubyte value) { kindAttr = cast(ubyte)((kindAttr&0xF) | (value<<4)); }
@property ubyte attr() { return cast(ubyte)(kindAttr >> 4); }
@property void attr(ubyte value) { kindAttr = cast(ubyte)((kindAttr&0xF) | (value<<4)); }
}
struct Class
@@ -208,8 +208,7 @@ class ABCFile
ExceptionInfo[] exceptions;
TraitsInfo[] traits;
string error;
ubyte[] rawBytes;
Error[] errors;
}
/// Destination for a jump or exception block boundary
@@ -219,18 +218,26 @@ class ABCFile
{
struct
{
uint index; /// instruction index
int offset; /// signed offset relative to said instruction
uint index = uint.max; /// instruction index
int offset = int.max; /// signed offset relative to said instruction
}
private int absoluteOffset; /// internal temporary value used during reading and writing
private ptrdiff_t absoluteOffset; /// internal temporary value used during reading and writing
}
}
/// Disassembly/decoding error
struct Error
{
Label loc;
string msg;
}
struct Instruction
{
Opcode opcode;
union Argument
{
byte bytev;
ubyte ubytev;
long intv;
ulong uintv;
@@ -262,8 +269,8 @@ class ABCFile
enum ASType : ubyte
{
Void = 0x00, // not actually interned
Undefined = Void,
Undefined = 0x00,
Void = Undefined, // not actually interned
Utf8 = 0x01,
Decimal = 0x02,
Integer = 0x03,
@@ -295,7 +302,7 @@ enum ASType : ubyte
}
string[ASType.Max] ASTypeNames = [
"Void",
"Undefined",
"Utf8",
"Decimal",
"Integer",
@@ -333,6 +340,7 @@ static this()
{
foreach (t, n; ASTypeNames)
ASTypeByName[n] = cast(ASType)t;
ASTypeByName["Void"] = ASType.Void;
ASTypeByName = ASTypeByName.rehash;
}
@@ -439,6 +447,7 @@ const string[4] TraitAttributeNames = ["FINAL", "OVERRIDE", "METADATA", "0x08"];
enum Opcode : ubyte
{
OP_raw = 0x00, /// Used internally by RABCDAsm
OP_bkpt = 0x01,
OP_nop = 0x02,
OP_throw = 0x03,
@@ -536,7 +545,7 @@ enum Opcode : ubyte
OP_getglobalscope = 0x64,
OP_getscopeobject = 0x65,
OP_getproperty = 0x66,
OP_getpropertylate = 0x67,
OP_getouterscope = 0x67,
OP_initproperty = 0x68,
OP_setpropertylate = 0x69,
OP_deleteproperty = 0x6A,
@@ -634,6 +643,7 @@ enum OpcodeArgumentType
{
Unknown,
ByteLiteral,
UByteLiteral,
IntLiteral,
UIntLiteral,
@@ -659,7 +669,7 @@ struct OpcodeInfo
}
const OpcodeInfo[256] opcodeInfo = [
/* 0x00 */ {"0x00", [OpcodeArgumentType.Unknown]},
/* 0x00 */ {"db", [OpcodeArgumentType.UByteLiteral]},
/* 0x01 */ {"bkpt", [OpcodeArgumentType.Unknown]},
/* 0x02 */ {"nop", []},
/* 0x03 */ {"throw", []},
@@ -695,7 +705,7 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x21 */ {"pushundefined", []},
/* 0x22 */ {"pushuninitialized", [OpcodeArgumentType.Unknown]},
/* 0x23 */ {"nextvalue", []},
/* 0x24 */ {"pushbyte", [OpcodeArgumentType.UByteLiteral]},
/* 0x24 */ {"pushbyte", [OpcodeArgumentType.ByteLiteral]},
/* 0x25 */ {"pushshort", [OpcodeArgumentType.IntLiteral]},
/* 0x26 */ {"pushtrue", []},
/* 0x27 */ {"pushfalse", []},
@@ -754,7 +764,7 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x5C */ {"0x5C", [OpcodeArgumentType.Unknown]},
/* 0x5D */ {"findpropstrict", [OpcodeArgumentType.Multiname]},
/* 0x5E */ {"findproperty", [OpcodeArgumentType.Multiname]},
/* 0x5F */ {"finddef", [OpcodeArgumentType.Unknown]},
/* 0x5F */ {"finddef", [OpcodeArgumentType.Multiname]},
/* 0x60 */ {"getlex", [OpcodeArgumentType.Multiname]},
/* 0x61 */ {"setproperty", [OpcodeArgumentType.Multiname]},
/* 0x62 */ {"getlocal", [OpcodeArgumentType.UIntLiteral]},
@@ -762,11 +772,11 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x64 */ {"getglobalscope", []},
/* 0x65 */ {"getscopeobject", [OpcodeArgumentType.UByteLiteral]},
/* 0x66 */ {"getproperty", [OpcodeArgumentType.Multiname]},
/* 0x67 */ {"getpropertylate", [OpcodeArgumentType.Unknown]},
/* 0x67 */ {"getouterscope", [OpcodeArgumentType.UIntLiteral]},
/* 0x68 */ {"initproperty", [OpcodeArgumentType.Multiname]},
/* 0x69 */ {"setpropertylate", [OpcodeArgumentType.Unknown]},
/* 0x69 */ {"setpropertylate", []},
/* 0x6A */ {"deleteproperty", [OpcodeArgumentType.Multiname]},
/* 0x6B */ {"deletepropertylate", [OpcodeArgumentType.Unknown]},
/* 0x6B */ {"deletepropertylate", []},
/* 0x6C */ {"getslot", [OpcodeArgumentType.UIntLiteral]},
/* 0x6D */ {"setslot", [OpcodeArgumentType.UIntLiteral]},
/* 0x6E */ {"getglobalslot", [OpcodeArgumentType.UIntLiteral]},
@@ -788,10 +798,10 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x7E */ {"0x7E", [OpcodeArgumentType.Unknown]},
/* 0x7F */ {"0x7F", [OpcodeArgumentType.Unknown]},
/* 0x80 */ {"coerce", [OpcodeArgumentType.Multiname]},
/* 0x81 */ {"coerce_b", [OpcodeArgumentType.Unknown]},
/* 0x81 */ {"coerce_b", []},
/* 0x82 */ {"coerce_a", []},
/* 0x83 */ {"coerce_i", [OpcodeArgumentType.Unknown]},
/* 0x84 */ {"coerce_d", [OpcodeArgumentType.Unknown]},
/* 0x83 */ {"coerce_i", []},
/* 0x84 */ {"coerce_d", []},
/* 0x85 */ {"coerce_s", []},
/* 0x86 */ {"astype", [OpcodeArgumentType.Multiname]},
/* 0x87 */ {"astypelate", []},
@@ -926,6 +936,16 @@ static this()
OpcodeByName = OpcodeByName.rehash;
}
bool[256] genLookup(Opcode[] opcodes)
{
bool[256] result;
foreach (op; opcodes)
result[op] = true;
return result;
}
const bool[256] stopsExecution = genLookup([Opcode.OP_returnvalue, Opcode.OP_returnvoid, Opcode.OP_throw, Opcode.OP_jump, Opcode.OP_lookupswitch]);
private final class ABCReader
{
ubyte[] buf;
@@ -1018,27 +1038,39 @@ private final class ABCReader
/// Note: may return values larger than 0xFFFFFFFF.
ulong readU32()
out(result)
{
ulong result = readU8();
assert(result <= ABCFile.MAX_UINT);
}
body
{
ulong next() { return readU8(); } // force ulong
ulong result = next();
if (0==(result & 0x00000080))
return result;
result = result & 0x0000007f | readU8()<<7;
result = result & 0x0000007f | next()<<7;
if (0==(result & 0x00004000))
return result;
result = result & 0x00003fff | readU8()<<14;
result = result & 0x00003fff | next()<<14;
if (0==(result & 0x00200000))
return result;
result = result & 0x001fffff | readU8()<<21;
result = result & 0x001fffff | next()<<21;
if (0==(result & 0x10000000))
return result;
return result & 0x0fffffff | readU8()<<28;
return result & 0x0fffffff | next()<<28;
}
long readS32()
out(result)
{
ulong l = readU32();
if (l & 0xFFFFFFFF00000000) // preserve unused bits
return cast(long)l;
assert(result >= ABCFile.MIN_INT && result <= ABCFile.MAX_INT);
}
body
{
auto l = readU32();
if (l & 0xFFFFFFFF_00000000) // preserve unused bits
return l | 0xFFFFFFF0_00000000;
else
return cast(int)l;
}
@@ -1178,12 +1210,11 @@ private final class ABCReader
{
ABCFile.Metadata r;
r.name = readU30();
r.items.length = readU30();
foreach (ref value; r.items)
{
value.key = readU30();
value.value = readU30();
}
r.keys.length = r.values.length = readU30();
foreach (ref key; r.keys)
key = readU30();
foreach (ref value; r.values)
value = readU30();
return r;
}
@@ -1280,18 +1311,207 @@ private final class ABCReader
r.instructions = null;
size_t len = readU30();
uint[] instructionAtOffset = new uint[len];
r.rawBytes = buf[pos..pos+len];
size_t start = pos;
size_t end = pos + len;
pos = end;
r.exceptions.length = readU30();
foreach (ref value; r.exceptions)
value = readExceptionInfo();
size_t postExceptions = pos;
enum TraceState : ubyte
{
unexplored,
pending,
instruction,
instructionBody,
error,
}
auto traceState = new TraceState[len];
auto instructions = new ABCFile.Instruction[len];
@property size_t offset() { return pos - start; }
bool havePending;
void queue(size_t traceOffset)
{
if (traceOffset < len && traceState[traceOffset] == TraceState.unexplored)
{
traceState[traceOffset] = TraceState.pending;
havePending = true;
}
}
queue(0);
foreach (ref value; r.exceptions)
queue(value.target.absoluteOffset);
while (havePending)
{
havePending = false;
pos = start;
while (pos < end)
{
if (traceState[offset] == TraceState.pending)
{
size_t instructionOffset;
try
{
while (pos < end)
{
instructionOffset = offset;
enforce(traceState[instructionOffset] != TraceState.instructionBody, "Overlapping instruction");
if (traceState[instructionOffset] == TraceState.instruction)
break; // already decoded
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
enforce(instruction.opcode != Opcode.OP_raw, "Null opcode");
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.ByteLiteral:
instruction.arguments[i].bytev = readU8();
break;
case OpcodeArgumentType.UByteLiteral:
instruction.arguments[i].ubytev = readU8();
break;
case OpcodeArgumentType.IntLiteral:
instruction.arguments[i].intv = readS32();
break;
case OpcodeArgumentType.UIntLiteral:
instruction.arguments[i].uintv = readU32();
break;
case OpcodeArgumentType.Int:
case OpcodeArgumentType.UInt:
case OpcodeArgumentType.Double:
case OpcodeArgumentType.String:
case OpcodeArgumentType.Namespace:
case OpcodeArgumentType.Multiname:
case OpcodeArgumentType.Class:
case OpcodeArgumentType.Method:
{
auto index = readU30();
size_t length;
switch (type)
{
case OpcodeArgumentType.Int: length = abc.ints .length; break;
case OpcodeArgumentType.UInt: length = abc.uints .length; break;
case OpcodeArgumentType.Double: length = abc.doubles .length; break;
case OpcodeArgumentType.String: length = abc.strings .length; break;
case OpcodeArgumentType.Namespace: length = abc.namespaces.length; break;
case OpcodeArgumentType.Multiname: length = abc.multinames.length; break;
case OpcodeArgumentType.Class: length = abc.classes .length; break;
case OpcodeArgumentType.Method: length = abc.methods .length; break;
default: assert(false);
}
enforce(index < length, "Out-of-bounds constant index");
instruction.arguments[i].index = index;
break;
}
case OpcodeArgumentType.JumpTarget:
{
auto delta = readS24();
auto target = offset + delta;
instruction.arguments[i].jumpTarget.absoluteOffset = target;
queue(target);
break;
}
case OpcodeArgumentType.SwitchDefaultTarget:
{
auto target = instructionOffset + readS24();
instruction.arguments[i].jumpTarget.absoluteOffset = target;
queue(target);
break;
}
case OpcodeArgumentType.SwitchTargets:
instruction.arguments[i].switchTargets.length = readU30()+1;
foreach (ref label; instruction.arguments[i].switchTargets)
{
label.absoluteOffset = instructionOffset + readS24();
queue(label.absoluteOffset);
}
break;
}
enforce(offset <= len, "Out-of-bounds code read error");
instructions[instructionOffset] = instruction;
traceState[instructionOffset] = TraceState.instruction;
traceState[instructionOffset+1..offset] = TraceState.instructionBody;
if (stopsExecution[instruction.opcode])
break;
}
}
catch (Exception e)
{
traceState[instructionOffset] = TraceState.error;
ABCFile.Label loc;
loc.absoluteOffset = instructionOffset;
r.errors ~= ABCFile.Error(loc, e.msg);
pos = start + instructionOffset + 1;
}
}
else
pos++;
}
}
size_t[] instructionOffsets;
auto instructionAtOffset = new uint[len];
instructionAtOffset[] = uint.max;
void addInstruction(ref ABCFile.Instruction i, size_t offset)
{
instructionAtOffset[offset] = to!uint(r.instructions.length);
r.instructions ~= i;
instructionOffsets ~= offset;
}
foreach (o, state; traceState)
{
assert(state != TraceState.pending);
if (state == TraceState.instruction)
addInstruction(instructions[o], o);
else
if (state == TraceState.unexplored || state == TraceState.error)
{
ABCFile.Instruction instruction;
instruction.opcode = Opcode.OP_raw;
instruction.arguments.length = 1;
instruction.arguments[0].ubytev = buf[start + o];
addInstruction(instruction, o);
}
else
assert(state == TraceState.instructionBody);
}
void translateLabel(ref ABCFile.Label label)
{
int absoluteOffset = label.absoluteOffset;
int instructionOffset = absoluteOffset;
auto absoluteOffset = label.absoluteOffset;
auto instructionOffset = absoluteOffset;
while (true)
{
if (instructionOffset >= cast(int)len)
if (instructionOffset >= len)
{
label.index = r.instructions.length;
label.index = to!uint(r.instructions.length);
instructionOffset = len;
break;
}
@@ -1308,108 +1528,40 @@ private final class ABCReader
}
instructionOffset--;
}
label.offset = absoluteOffset-instructionOffset;
label.offset = to!int(absoluteOffset-instructionOffset);
}
size_t start = pos;
size_t end = pos + len;
// convert jump target offsets to instruction indices
foreach (ii, ref instruction; r.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
switch (type)
{
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
translateLabel(instruction.arguments[i].jumpTarget);
break;
case OpcodeArgumentType.SwitchTargets:
foreach (ref x; instruction.arguments[i].switchTargets)
translateLabel(x);
break;
default:
break;
}
uint offset() { return pos - start; }
// convert error offsets to instruction indices
foreach (ref e; r.errors)
translateLabel(e.loc);
try
{
instructionAtOffset[] = uint.max;
uint[] instructionOffsets;
while (pos < end)
{
uint instructionOffset = offset;
scope(failure) pos = start + instructionOffset;
instructionAtOffset[instructionOffset] = r.instructions.length;
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
instruction.arguments[i].ubytev = readU8();
break;
case OpcodeArgumentType.IntLiteral:
instruction.arguments[i].intv = readS32();
break;
case OpcodeArgumentType.UIntLiteral:
instruction.arguments[i].uintv = readU32();
break;
case OpcodeArgumentType.Int:
case OpcodeArgumentType.UInt:
case OpcodeArgumentType.Double:
case OpcodeArgumentType.String:
case OpcodeArgumentType.Namespace:
case OpcodeArgumentType.Multiname:
case OpcodeArgumentType.Class:
case OpcodeArgumentType.Method:
instruction.arguments[i].index = readU30();
break;
case OpcodeArgumentType.JumpTarget:
int delta = readS24();
instruction.arguments[i].jumpTarget.absoluteOffset = offset + delta;
break;
case OpcodeArgumentType.SwitchDefaultTarget:
instruction.arguments[i].jumpTarget.absoluteOffset = instructionOffset + readS24();
break;
case OpcodeArgumentType.SwitchTargets:
instruction.arguments[i].switchTargets.length = readU30()+1;
foreach (ref label; instruction.arguments[i].switchTargets)
label.absoluteOffset = instructionOffset + readS24();
break;
}
r.instructions ~= instruction;
instructionOffsets ~= instructionOffset;
}
if (pos > end)
throw new Exception("Out-of-bounds code read error");
// convert jump target offsets to instruction indices
foreach (ii, ref instruction; r.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
switch (type)
{
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
translateLabel(instruction.arguments[i].jumpTarget);
break;
case OpcodeArgumentType.SwitchTargets:
foreach (ref x; instruction.arguments[i].switchTargets)
translateLabel(x);
break;
default:
break;
}
}
catch (Exception e)
{
r.instructions = null;
r.error = e.msg;
instructionAtOffset[] = 0;
}
pos = end;
r.exceptions.length = readU30();
// convert exception offsets to instruction indices
foreach (ref value; r.exceptions)
{
value = readExceptionInfo();
translateLabel(value.from);
translateLabel(value.to);
translateLabel(value.target);
}
pos = postExceptions;
r.traits.length = readU30();
foreach (ref value; r.traits)
value = readTrait();
@@ -1517,7 +1669,6 @@ private final class ABCWriter
writeU8(cast(ubyte)(v>>16));
}
/// Note: may return values larger than 0xFFFFFFFF.
void writeU32(ulong v)
{
if ( v < 128)
@@ -1557,8 +1708,9 @@ private final class ABCWriter
writeU32(cast(ulong)v);
}
void writeU30(uint v)
void writeU30(ulong v)
{
enforce(v < (1<<30));
writeU32(v);
}
@@ -1669,12 +1821,12 @@ private final class ABCWriter
void writeMetadata(ref ABCFile.Metadata v)
{
writeU30(v.name);
writeU30(v.items.length);
foreach (ref value; v.items)
{
writeU30(value.key);
writeU30(value.value);
}
assert(v.keys.length == v.values.length);
writeU30(v.keys.length);
foreach (key; v.keys)
writeU30(key);
foreach (value; v.values)
writeU30(value);
}
void writeInstance(ref ABCFile.Instance v)
@@ -1756,9 +1908,9 @@ private final class ABCWriter
writeU30(v.initScopeDepth);
writeU30(v.maxScopeDepth);
uint[] instructionOffsets = new uint[v.instructions.length+1];
auto instructionOffsets = new size_t[v.instructions.length+1];
uint resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
ptrdiff_t resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
{
// we don't know the length before writing all the instructions - swap buffer with a temporary one
@@ -1768,12 +1920,12 @@ private final class ABCWriter
buf = methodBuf[];
pos = 0;
struct Fixup { ABCFile.Label target; uint pos, base; }
struct Fixup { ABCFile.Label target; size_t pos, base; }
Fixup[] fixups;
foreach (ii, ref instruction; v.instructions)
{
uint instructionOffset = pos;
auto instructionOffset = pos;
instructionOffsets[ii] = instructionOffset;
writeU8(instruction.opcode);
@@ -1787,6 +1939,9 @@ private final class ABCWriter
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to encode OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.ByteLiteral:
writeU8(instruction.arguments[i].bytev);
break;
case OpcodeArgumentType.UByteLiteral:
writeU8(instruction.arguments[i].ubytev);
break;
@@ -1837,7 +1992,7 @@ private final class ABCWriter
foreach (ref fixup; fixups)
{
pos = fixup.pos;
writeS24(resolveLabel(fixup.target)-fixup.base);
writeS24(to!int(cast(ptrdiff_t)(resolveLabel(fixup.target)-fixup.base)));
}
auto code = buf;
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -41,6 +41,7 @@ void main(string[] args)
while (*p++) {} // skip name
tag.data = tag.data[0..p-tag.data.ptr] ~ abc;
}
tag.length = cast(uint)tag.data.length;
write(args[1], swf.write());
return;
}
+11 -5
View File
@@ -4,7 +4,7 @@
<hrc version="take5" xmlns="http://colorer.sf.net/2003/hrc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://colorer.sf.net/2003/hrc http://colorer.sf.net/2003/hrc.xsd">
<type name="asasm">
<annotation><documentation>
ActionScript Assembler Syntax (RABCDAsm variant)
@@ -31,20 +31,24 @@
<!-- Decimal numbers -->
<regexp match="/\b[0-9]+\b/" region0="def:NumberDec"/>
<!-- Paired brackets -->
<block start="/(\()/" end="/(\))/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<block start="/(\[)/" end="/(\])/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<block start="/(\<)/" end="/(\>)/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<!-- Outliner for refids -->
<regexp match="/(\brefid\b)\s*((?{def:StringEdge}&#34;))((?{def:String}[^&#34;]*))((?{def:StringEdge}&#34;))/" region1="def:Keyword" region2="def:PairStart" region3="def:Outlined" region4="def:PairEnd"/> <!-- BUG: does not support escape sequences -->
<!--regexp match="/(\brefid\b)\s*((?{def:StringEdge}&#34;))((?{def:String}[^&#34;]*))((?{def:StringEdge}&#34;))/" region1="def:Keyword" region2="def:PairStart" region3="def:Outlined" region4="def:PairEnd"/--> <!-- BUG: does not support escape sequences -->
<regexp
match="/(\brefid\b)\M\s*&#34;(.*)&#34;$/"
region1="def:Keyword"
region2="def:Outlined"/>
<!--block start="/(\brefid\b)\s*(?{def:StringEdge}&#34;)/" end="/(?{def:StringEdge}&#34;)/" scheme="StringContent" region="def:Outlined" inner-region="yes" region00="def:PairStart" region10="def:PairEnd"/-->
<!-- Symbol/keyword highlighting below -->
<keywords region="def:Symbol">
<symb name=","/>
<symb name=","/>
</keywords>
<keywords ignorecase="no" region="def:Directive">
@@ -55,6 +59,7 @@
<word name="#privatens"/>
<word name="#set"/>
<word name="#unset"/>
<word name="#version"/>
</keywords>
<keywords ignorecase="no" region="def:Keyword">
@@ -105,6 +110,7 @@
</keywords>
<keywords ignorecase="no" region="ASType">
<word name="Undefined"/>
<word name="Void"/>
<word name="Utf8"/>
<word name="Decimal"/>
@@ -235,7 +241,7 @@
<word name="getglobalscope"/>
<word name="getscopeobject"/>
<word name="getproperty"/>
<word name="getpropertylate"/>
<word name="getouterscope"/>
<word name="initproperty"/>
<word name="setpropertylate"/>
<word name="deleteproperty"/>
+407 -356
View File
File diff suppressed because it is too large Load Diff
+494 -235
View File
File diff suppressed because it is too large Load Diff
+45 -7
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -34,7 +34,7 @@ template AutoCompare()
alias typeof(this) _AutoDataTypeReference;
alias Object _AutoDataOtherTypeReference;
override hash_t toHash() const { return _AutoDataHash(); }
override hash_t toHash() const { try { return _AutoDataHash(); } catch(object.Exception e) { assert(0, e.msg); } }
override bool opEquals(Object o) const { return _AutoDataEquals(o); }
override int opCmp(Object o) const { return _AutoDataCmp(o); }
}
@@ -48,7 +48,7 @@ template AutoCompare()
int opCmp(ref const typeof(this) s) const { return _AutoDataCmp(&s); }
}
private hash_t _AutoDataHash() const
@trusted private hash_t _AutoDataHash() const
{
HashDataHandler handler;
handler.hasher.Begin();
@@ -78,7 +78,7 @@ template AutoToString()
static if (is(typeof(this)==class))
override string toString() const { return _AutoDataToString(); }
else // struct
string toString() { return _AutoDataToString(); }
string toString() const { return _AutoDataToString(); }
string _AutoDataToString() const
{
@@ -138,7 +138,7 @@ struct HashDataHandler
template getRawMixin(string ptr, string len)
{
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", to!int(" ~ len ~ "));";
}
}
@@ -202,7 +202,7 @@ struct CmpDataHandler(O)
else
static if (is(typeof(T.opCmp)))
enum dataCode = nullCheck!(T, name, reverseStr)
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(cast()_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
@@ -212,8 +212,46 @@ struct CmpDataHandler(O)
struct ToStringDataHandler
{
template getMixinSingle(T, string name)
{
/*
enum getMixinSingle = "
static if (is(typeof(_AutoDataResult ~= " ~ name ~ ".toString())))
_AutoDataResult ~= " ~ name ~ ".toString();
else
_AutoDataResult ~= to!string(" ~ name ~ ");
";
*/
static if (is(typeof(T.init is null)))
enum getMixinSingle = "_AutoDataResult ~= " ~ name ~ " ? to!string(" ~ name ~ ") : `null`;";
else
enum getMixinSingle = "_AutoDataResult ~= to!string(" ~ name ~ ");";
}
template getMixinBody(T, string name)
{
// TODO: arrays of arrays
static if (is(T U : U[]) && !is(T : const(char)[]))
{
enum getMixinBody = "
_AutoDataResult ~= ` [ `;
foreach (_AutoDataArrayIndex, _AutoDataArrayItem; " ~ name ~ ")
{
if (_AutoDataArrayIndex) _AutoDataResult ~= ` , `;
" ~ getMixinSingle!(U, "_AutoDataArrayItem") ~ "
}
_AutoDataResult ~= ` ] `;
";
}
else
enum getMixinBody = getMixinSingle!(T, name);
}
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = "_AutoDataResult ~= `" ~ name ~ " = ` ~ to!string(this." ~ name ~ ") ~ ` `;";
enum getMixin =
"_AutoDataResult ~= `" ~ name ~ " = `;" ~
getMixinBody!(T, name) ~
"_AutoDataResult ~= ` `;";
}
}
+101 -26
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -18,13 +18,16 @@
/// A simple tool to build RABCDAsm in one command.
/// You can use the DC and DCFLAGS environment variables to override the detected compiler and compilation flags.
/// You can also pass program names or compilation options on the command-line to override the default ones.
module build_rabcdasm;
version(D_Version2)
{ /* All OK */ }
else
static assert(false, "Unsupported D version.\nThis software requires a D2 ( http://www.digitalmars.com/d/2.0/ ) compiler to build.");
static assert(false, "Unsupported D version.\nThis software requires a D2 ( http://dlang.org/ ) compiler to build.");
version(D_Version2):
version(DigitalMars)
const DEFAULT_COMPILER = "dmd";
@@ -32,40 +35,112 @@ else
const DEFAULT_COMPILER = "gdmd";
const DEFAULT_FLAGS = "-O -inline";
const LZMA_FLAGS = ["-version=HAVE_LZMA"];
import std.exception;
import std.file;
import std.process;
import std.stdio;
import std.string;
string[][string] programs;
string compiler;
string[] flags;
static this()
void compile(string program)
{
programs["rabcasm" ] = ["abcfile", "asprogram", "assembler", "autodata", "murmurhash2a"];
programs["rabcdasm" ] = ["abcfile", "asprogram", "disassembler", "autodata", "murmurhash2a"];
programs["abcexport" ] = ["swffile", "zlibx"];
programs["abcreplace" ] = ["swffile", "zlibx"];
programs["swfbinexport" ] = ["swffile", "zlibx"];
programs["swfbinreplace"] = ["swffile", "zlibx"];
programs["swfdecompress"] = ["swffile", "zlibx"];
programs["swf7zcompress"] = ["swffile", "zlibx"];
stderr.writeln("* Building ", program);
enforce(spawnProcess(["rdmd", "--build-only", "--compiler=" ~ compiler] ~ flags ~ program).wait() == 0, "Compilation of " ~ program ~ " failed");
}
int main()
void test(string code, in string[] extraFlags=null)
{
string compiler = getenv("DC");
if (compiler is null)
compiler = DEFAULT_COMPILER;
const BASE = "build_rabcdasm_buildtest";
const FN = BASE ~ ".d";
std.file.write(FN, code);
scope(exit) foreach (de; dirEntries(".", BASE ~ "*", SpanMode.shallow)) remove(de.name);
enforce(spawnProcess(["rdmd", "--force", "--compiler=" ~ compiler, "-od."] ~ flags ~ extraFlags ~ FN).wait() == 0, "Test failed");
stderr.writeln(" >>> OK");
}
string flags = getenv("DCFLAGS");
if (flags is null)
flags = DEFAULT_FLAGS;
foreach (program, modules; programs)
void testBug(string description, int bugId, string code)
{
stderr.writefln("* Checking for compiler bug %d...", bugId);
scope(failure)
{
int ret = system(format("%s %s %s %s", compiler, flags, program, join(modules, " ")));
if (ret)
return ret;
stderr.writefln("Compiler bug detected: %s ( https://issues.dlang.org/show_bug.cgi?id=%d ).", description, bugId);
stderr.writeln("Try again with a different D compiler, compiler version, or build flags (DCFLAGS environment variable)");
}
test(code);
}
int main(string[] args)
{
try
{
auto programs = ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"];
compiler = environment.get("DC", DEFAULT_COMPILER);
flags = environment.get("DCFLAGS", DEFAULT_FLAGS).split(" ");
string[] optionArgs, programArgs;
foreach (arg; args[1..$])
(arg.startsWith("-") ? optionArgs : programArgs) ~= arg;
if (optionArgs.length)
flags = optionArgs;
if (programArgs.length)
programs = programArgs;
stderr.writeln("* Checking for working compiler...");
test(`
void main() {}
`);
testBug("[REG 2.064] Wrong code with -O on x86_64 for char comparisons", 11508, `
import assembler; int main() { foreach (c; "_") if (!Assembler.isWordChar(c)) return 1; return 0; }
`);
testBug("[REG 2.069] Wrong double-to-string conversion with -O", 15861, `
import std.format; int main() { return format("%.18g", 4286853117.0) == "4286853117" ? 0 : 1; }
`);
bool haveLZMA;
stderr.writeln("* Checking for LZMA...");
try
{
test(`
import lzma, std.exception;
void main()
{
LZMAHeader header;
auto data = cast(immutable(ubyte)[])"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
auto cdata = lzmaCompress(data, &header);
header.decompressedSize = data.length;
auto ddata = lzmaDecompress(header, cdata);
enforce(data == ddata);
}
`, LZMA_FLAGS);
// Test succeeded
haveLZMA = true;
}
catch (Exception e)
stderr.writeln(" >>> LZMA not found, building without LZMA support.");
if (haveLZMA)
flags ~= LZMA_FLAGS;
foreach (program; programs)
compile(program);
if (haveLZMA)
compile("swflzmacompress");
return 0;
}
catch (Exception e)
{
stderr.writeln("Error: ", e.msg);
return 1;
}
return 0;
}
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright 2012, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm 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 RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module common;
import std.array;
import std.path;
import std.stdio;
import std.string;
string longPath(string s)
{
version(Windows)
{
if (s.startsWith(`\\`))
return s;
else
return `\\?\` ~ s.absolutePath().buildNormalizedPath().replace(`/`, `\`);
}
else
return s;
}
File openFile(string fn, string mode)
{
File f;
static if (is(typeof(&f.windowsHandleOpen)))
{
import core.sys.windows.windows;
import std.exception;
import std.utf;
import std.windows.syserror;
string winMode;
foreach (c; mode)
switch (c)
{
case 'r':
case 'w':
case 'a':
case '+':
winMode ~= c;
break;
case 'b':
case 't':
break;
default:
assert(false, "Unknown character in mode");
}
DWORD access, creation;
bool append;
switch (winMode)
{
case "r" : access = GENERIC_READ ; creation = OPEN_EXISTING; break;
case "r+": access = GENERIC_READ | GENERIC_WRITE; creation = OPEN_EXISTING; break;
case "w" : access = GENERIC_WRITE; creation = OPEN_ALWAYS ; break;
case "w+": access = GENERIC_READ | GENERIC_WRITE; creation = OPEN_ALWAYS ; break;
case "a" : access = GENERIC_WRITE; creation = OPEN_ALWAYS ; append = true; break;
case "a+": assert(false, "Not implemented"); // requires two file pointers
default: assert(false, "Bad file mode: " ~ mode);
}
auto pathW = toUTF16z(longPath(fn));
auto h = CreateFileW(pathW, access, FILE_SHARE_READ, null, creation, 0, HANDLE.init);
enforce(h != INVALID_HANDLE_VALUE, "Failed to open file \"" ~ fn ~ "\": " ~ sysErrorString(GetLastError()));
assert(!append, "'a' mode not implemented");
f.windowsHandleOpen(h, mode);
}
else
f.open(fn, mode);
return f;
}
+83
View File
@@ -0,0 +1,83 @@
/**
* \file api/lzma.h
* \brief The public API of liblzma data compression library
*
* liblzma is a public domain general-purpose data compression library with
* a zlib-like API. The native file format is .xz, but also the old .lzma
* format and raw (no headers) streams are supported. Multiple compression
* algorithms (filters) are supported. Currently LZMA2 is the primary filter.
*
* liblzma is part of XZ Utils <http://tukaani.org/xz/>. XZ Utils includes
* a gzip-like command line tool named xz and some other tools. XZ Utils
* is developed and maintained by Lasse Collin.
*
* Major parts of liblzma are based on Igor Pavlov's public domain LZMA SDK
* <http://7-zip.org/sdk.html>.
*
* The SHA-256 implementation is based on the public domain code found from
* 7-Zip <http://7-zip.org/>, which has a modified version of the public
* domain SHA-256 code found from Crypto++ <http://www.cryptopp.com/>.
* The SHA-256 code in Crypto++ was written by Kevin Springle and Wei Dai.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
module deimos.lzma;
/***********
* nothrow *
***********/
/*
* None of the functions in liblzma may throw an exception. Even
* the functions that use callback functions won't throw exceptions,
* because liblzma would break if a callback function threw an exception.
*/
//lzma_nothrow --> nothrow
/********************
* GNU C extensions *
********************/
/*
* GNU C extensions are used conditionally in the public API. It doesn't
* break anything if these are sometimes enabled and sometimes not, only
* affects warnings and optimizations.
*/
//lzma_attr_pure --> pure
//lzma_attr_const --> const
//lzma_attr_warn_unused_result --> n/a
/**************
* Subheaders *
**************/
public import deimos.lzma_.version_;
public import deimos.lzma_.base;
public import deimos.lzma_.vli;
public import deimos.lzma_.check;
/* Filters */
public import deimos.lzma_.filter;
public import deimos.lzma_.bcj;
public import deimos.lzma_.delta;
public import deimos.lzma_.lzma;
/* Container formats */
public import deimos.lzma_.container;
/* Advanced features */
public import deimos.lzma_.stream_flags;
public import deimos.lzma_.block;
public import deimos.lzma_.index;
public import deimos.lzma_.index_hash;
/* Hardware information */
public import deimos.lzma_.hardware;
+582
View File
@@ -0,0 +1,582 @@
/**
* \file lzma/base.h
* \brief Data types and functions used in many places in liblzma API
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.base;
import deimos.lzma;
extern(C):
/**
* \brief Boolean
*
* This is here because C89 doesn't have stdbool.h. To set a value for
* variables having type lzma_bool, you can use
* - C99's `true' and `false' from stdbool.h;
* - C++'s internal `true' and `false'; or
* - integers one (true) and zero (false).
*/
alias bool lzma_bool;
/**
* \brief Type of reserved enumeration variable in structures
*
* To avoid breaking library ABI when new features are added, several
* structures contain extra variables that may be used in future. Since
* sizeof(enum) can be different than sizeof(int), and sizeof(enum) may
* even vary depending on the range of enumeration constants, we specify
* a separate type to be used for reserved enumeration variables. All
* enumeration constants in liblzma API will be non-negative and less
* than 128, which should guarantee that the ABI won't break even when
* new constants are added to existing enumerations.
*/
enum lzma_reserved_enum
{
LZMA_RESERVED_ENUM = 0
}
/**
* \brief Return values used by several functions in liblzma
*
* Check the descriptions of specific functions to find out which return
* values they can return. With some functions the return values may have
* more specific meanings than described here; those differences are
* described per-function basis.
*/
enum lzma_ret
{
LZMA_OK = 0,
/**<
* \brief Operation completed successfully
*/
LZMA_STREAM_END = 1,
/**<
* \brief End of stream was reached
*
* In encoder, LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, or
* LZMA_FINISH was finished. In decoder, this indicates
* that all the data was successfully decoded.
*
* In all cases, when LZMA_STREAM_END is returned, the last
* output bytes should be picked from strm->next_out.
*/
LZMA_NO_CHECK = 2,
/**<
* \brief Input stream has no integrity check
*
* This return value can be returned only if the
* LZMA_TELL_NO_CHECK flag was used when initializing
* the decoder. LZMA_NO_CHECK is just a warning, and
* the decoding can be continued normally.
*
* It is possible to call lzma_get_check() immediately after
* lzma_code has returned LZMA_NO_CHECK. The result will
* naturally be LZMA_CHECK_NONE, but the possibility to call
* lzma_get_check() may be convenient in some applications.
*/
LZMA_UNSUPPORTED_CHECK = 3,
/**<
* \brief Cannot calculate the integrity check
*
* The usage of this return value is different in encoders
* and decoders.
*
* Encoders can return this value only from the initialization
* function. If initialization fails with this value, the
* encoding cannot be done, because there's no way to produce
* output with the correct integrity check.
*
* Decoders can return this value only from lzma_code() and
* only if the LZMA_TELL_UNSUPPORTED_CHECK flag was used when
* initializing the decoder. The decoding can still be
* continued normally even if the check type is unsupported,
* but naturally the check will not be validated, and possible
* errors may go undetected.
*
* With decoder, it is possible to call lzma_get_check()
* immediately after lzma_code() has returned
* LZMA_UNSUPPORTED_CHECK. This way it is possible to find
* out what the unsupported Check ID was.
*/
LZMA_GET_CHECK = 4,
/**<
* \brief Integrity check type is now available
*
* This value can be returned only by the lzma_code() function
* and only if the decoder was initialized with the
* LZMA_TELL_ANY_CHECK flag. LZMA_GET_CHECK tells the
* application that it may now call lzma_get_check() to find
* out the Check ID. This can be used, for example, to
* implement a decoder that accepts only files that have
* strong enough integrity check.
*/
LZMA_MEM_ERROR = 5,
/**<
* \brief Cannot allocate memory
*
* Memory allocation failed, or the size of the allocation
* would be greater than SIZE_MAX.
*
* Due to internal implementation reasons, the coding cannot
* be continued even if more memory were made available after
* LZMA_MEM_ERROR.
*/
LZMA_MEMLIMIT_ERROR = 6,
/**
* \brief Memory usage limit was reached
*
* Decoder would need more memory than allowed by the
* specified memory usage limit. To continue decoding,
* the memory usage limit has to be increased with
* lzma_memlimit_set().
*/
LZMA_FORMAT_ERROR = 7,
/**<
* \brief File format not recognized
*
* The decoder did not recognize the input as supported file
* format. This error can occur, for example, when trying to
* decode .lzma format file with lzma_stream_decoder,
* because lzma_stream_decoder accepts only the .xz format.
*/
LZMA_OPTIONS_ERROR = 8,
/**<
* \brief Invalid or unsupported options
*
* Invalid or unsupported options, for example
* - unsupported filter(s) or filter options; or
* - reserved bits set in headers (decoder only).
*
* Rebuilding liblzma with more features enabled, or
* upgrading to a newer version of liblzma may help.
*/
LZMA_DATA_ERROR = 9,
/**<
* \brief Data is corrupt
*
* The usage of this return value is different in encoders
* and decoders. In both encoder and decoder, the coding
* cannot continue after this error.
*
* Encoders return this if size limits of the target file
* format would be exceeded. These limits are huge, thus
* getting this error from an encoder is mostly theoretical.
* For example, the maximum compressed and uncompressed
* size of a .xz Stream is roughly 8 EiB (2^63 bytes).
*
* Decoders return this error if the input data is corrupt.
* This can mean, for example, invalid CRC32 in headers
* or invalid check of uncompressed data.
*/
LZMA_BUF_ERROR = 10,
/**<
* \brief No progress is possible
*
* This error code is returned when the coder cannot consume
* any new input and produce any new output. The most common
* reason for this error is that the input stream being
* decoded is truncated or corrupt.
*
* This error is not fatal. Coding can be continued normally
* by providing more input and/or more output space, if
* possible.
*
* Typically the first call to lzma_code() that can do no
* progress returns LZMA_OK instead of LZMA_BUF_ERROR. Only
* the second consecutive call doing no progress will return
* LZMA_BUF_ERROR. This is intentional.
*
* With zlib, Z_BUF_ERROR may be returned even if the
* application is doing nothing wrong, so apps will need
* to handle Z_BUF_ERROR specially. The above hack
* guarantees that liblzma never returns LZMA_BUF_ERROR
* to properly written applications unless the input file
* is truncated or corrupt. This should simplify the
* applications a little.
*/
LZMA_PROG_ERROR = 11,
/**<
* \brief Programming error
*
* This indicates that the arguments given to the function are
* invalid or the internal state of the decoder is corrupt.
* - Function arguments are invalid or the structures
* pointed by the argument pointers are invalid
* e.g. if strm->next_out has been set to NULL and
* strm->avail_out > 0 when calling lzma_code().
* - lzma_* functions have been called in wrong order
* e.g. lzma_code() was called right after lzma_end().
* - If errors occur randomly, the reason might be flaky
* hardware.
*
* If you think that your code is correct, this error code
* can be a sign of a bug in liblzma. See the documentation
* how to report bugs.
*/
}
/**
* \brief The `action' argument for lzma_code()
*
* After the first use of LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, or LZMA_FINISH,
* the same `action' must is used until lzma_code() returns LZMA_STREAM_END.
* Also, the amount of input (that is, strm->avail_in) must not be modified
* by the application until lzma_code() returns LZMA_STREAM_END. Changing the
* `action' or modifying the amount of input will make lzma_code() return
* LZMA_PROG_ERROR.
*/
enum lzma_action
{
LZMA_RUN = 0,
/**<
* \brief Continue coding
*
* Encoder: Encode as much input as possible. Some internal
* buffering will probably be done (depends on the filter
* chain in use), which causes latency: the input used won't
* usually be decodeable from the output of the same
* lzma_code() call.
*
* Decoder: Decode as much input as possible and produce as
* much output as possible.
*/
LZMA_SYNC_FLUSH = 1,
/**<
* \brief Make all the input available at output
*
* Normally the encoder introduces some latency.
* LZMA_SYNC_FLUSH forces all the buffered data to be
* available at output without resetting the internal
* state of the encoder. This way it is possible to use
* compressed stream for example for communication over
* network.
*
* Only some filters support LZMA_SYNC_FLUSH. Trying to use
* LZMA_SYNC_FLUSH with filters that don't support it will
* make lzma_code() return LZMA_OPTIONS_ERROR. For example,
* LZMA1 doesn't support LZMA_SYNC_FLUSH but LZMA2 does.
*
* Using LZMA_SYNC_FLUSH very often can dramatically reduce
* the compression ratio. With some filters (for example,
* LZMA2), fine-tuning the compression options may help
* mitigate this problem significantly (for example,
* match finder with LZMA2).
*
* Decoders don't support LZMA_SYNC_FLUSH.
*/
LZMA_FULL_FLUSH = 2,
/**<
* \brief Finish encoding of the current Block
*
* All the input data going to the current Block must have
* been given to the encoder (the last bytes can still be
* pending in* next_in). Call lzma_code() with LZMA_FULL_FLUSH
* until it returns LZMA_STREAM_END. Then continue normally
* with LZMA_RUN or finish the Stream with LZMA_FINISH.
*
* This action is currently supported only by Stream encoder
* and easy encoder (which uses Stream encoder). If there is
* no unfinished Block, no empty Block is created.
*/
LZMA_FINISH = 3
/**<
* \brief Finish the coding operation
*
* All the input data must have been given to the encoder
* (the last bytes can still be pending in next_in).
* Call lzma_code() with LZMA_FINISH until it returns
* LZMA_STREAM_END. Once LZMA_FINISH has been used,
* the amount of input must no longer be changed by
* the application.
*
* When decoding, using LZMA_FINISH is optional unless the
* LZMA_CONCATENATED flag was used when the decoder was
* initialized. When LZMA_CONCATENATED was not used, the only
* effect of LZMA_FINISH is that the amount of input must not
* be changed just like in the encoder.
*/
}
/**
* \brief Custom functions for memory handling
*
* A pointer to lzma_allocator may be passed via lzma_stream structure
* to liblzma, and some advanced functions take a pointer to lzma_allocator
* as a separate function argument. The library will use the functions
* specified in lzma_allocator for memory handling instead of the default
* malloc() and free(). C++ users should note that the custom memory
* handling functions must not throw exceptions.
*
* liblzma doesn't make an internal copy of lzma_allocator. Thus, it is
* OK to change these function pointers in the middle of the coding
* process, but obviously it must be done carefully to make sure that the
* replacement `free' can deallocate memory allocated by the earlier
* `alloc' function(s).
*/
struct lzma_allocator
{
/**
* \brief Pointer to a custom memory allocation function
*
* If you don't want a custom allocator, but still want
* custom free(), set this to NULL and liblzma will use
* the standard malloc().
*
* \param opaque lzma_allocator.opaque (see below)
* \param nmemb Number of elements like in calloc(). liblzma
* will always set nmemb to 1, so it is safe to
* ignore nmemb in a custom allocator if you like.
* The nmemb argument exists only for
* compatibility with zlib and libbzip2.
* \param size Size of an element in bytes.
* liblzma never sets this to zero.
*
* \return Pointer to the beginning of a memory block of
* `size' bytes, or NULL if allocation fails
* for some reason. When allocation fails, functions
* of liblzma return LZMA_MEM_ERROR.
*
* The allocator should not waste time zeroing the allocated buffers.
* This is not only about speed, but also memory usage, since the
* operating system kernel doesn't necessarily allocate the requested
* memory in physical memory until it is actually used. With small
* input files, liblzma may actually need only a fraction of the
* memory that it requested for allocation.
*
* \note LZMA_MEM_ERROR is also used when the size of the
* allocation would be greater than SIZE_MAX. Thus,
* don't assume that the custom allocator must have
* returned NULL if some function from liblzma
* returns LZMA_MEM_ERROR.
*/
void* function(void *opaque, size_t nmemb, size_t size) alloc;
/**
* \brief Pointer to a custom memory freeing function
*
* If you don't want a custom freeing function, but still
* want a custom allocator, set this to NULL and liblzma
* will use the standard free().
*
* \param opaque lzma_allocator.opaque (see below)
* \param ptr Pointer returned by lzma_allocator.alloc(),
* or when it is set to NULL, a pointer returned
* by the standard malloc().
*/
void function(void *opaque, void *ptr) free;
/**
* \brief Pointer passed to .alloc() and .free()
*
* opaque is passed as the first argument to lzma_allocator.alloc()
* and lzma_allocator.free(). This intended to ease implementing
* custom memory allocation functions for use with liblzma.
*
* If you don't need this, you should set this to NULL.
*/
void *opaque;
}
/**
* \brief Internal data structure
*
* The contents of this structure is not visible outside the library.
*/
struct lzma_internal {}
/**
* \brief Passing data to and from liblzma
*
* The lzma_stream structure is used for
* - passing pointers to input and output buffers to liblzma;
* - defining custom memory hander functions; and
* - holding a pointer to coder-specific internal data structures.
*
* Typical usage:
*
* - After allocating lzma_stream (on stack or with malloc()), it must be
* initialized to LZMA_STREAM_INIT (see LZMA_STREAM_INIT for details).
*
* - Initialize a coder to the lzma_stream, for example by using
* lzma_easy_encoder() or lzma_auto_decoder(). Some notes:
* - In contrast to zlib, strm->next_in and strm->next_out are
* ignored by all initialization functions, thus it is safe
* to not initialize them yet.
* - The initialization functions always set strm->total_in and
* strm->total_out to zero.
* - If the initialization function fails, no memory is left allocated
* that would require freeing with lzma_end() even if some memory was
* associated with the lzma_stream structure when the initialization
* function was called.
*
* - Use lzma_code() to do the actual work.
*
* - Once the coding has been finished, the existing lzma_stream can be
* reused. It is OK to reuse lzma_stream with different initialization
* function without calling lzma_end() first. Old allocations are
* automatically freed.
*
* - Finally, use lzma_end() to free the allocated memory. lzma_end() never
* frees the lzma_stream structure itself.
*
* Application may modify the values of total_in and total_out as it wants.
* They are updated by liblzma to match the amount of data read and
* written, but aren't used for anything else.
*/
struct lzma_stream
{
const(ubyte)* next_in; /**< Pointer to the next input byte. */
size_t avail_in; /**< Number of available input bytes in next_in. */
ulong total_in; /**< Total number of bytes read by liblzma. */
ubyte* next_out; /**< Pointer to the next output position. */
size_t avail_out; /**< Amount of free space in next_out. */
ulong total_out; /**< Total number of bytes written by liblzma. */
/**
* \brief Custom memory allocation functions
*
* In most cases this is NULL which makes liblzma use
* the standard malloc() and free().
*/
lzma_allocator *allocator;
/** Internal state is not visible to applications. */
lzma_internal *internal;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. Excluding the initialization of this structure,
* you should not touch these, because the names of these variables
* may change.
*/
void *reserved_ptr1;
void *reserved_ptr2;
void *reserved_ptr3;
void *reserved_ptr4;
ulong reserved_int1;
ulong reserved_int2;
size_t reserved_int3;
size_t reserved_int4;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
}
/**
* Initialization for lzma_stream
*
* Not needed in d, just use lzma_stream.init
*/
//void LZMA_STREAM_INIT(){};
/**
* \brief Encode or decode data
*
* Once the lzma_stream has been successfully initialized (e.g. with
* lzma_stream_encoder()), the actual encoding or decoding is done
* using this function. The application has to update strm->next_in,
* strm->avail_in, strm->next_out, and strm->avail_out to pass input
* to and get output from liblzma.
*
* See the description of the coder-specific initialization function to find
* out what `action' values are supported by the coder.
*/
nothrow lzma_ret lzma_code(lzma_stream *strm, lzma_action action);
/**
* \brief Free memory allocated for the coder data structures
*
* \param strm Pointer to lzma_stream that is at least initialized
* with LZMA_STREAM_INIT.
*
* After lzma_end(strm), strm->internal is guaranteed to be NULL. No other
* members of the lzma_stream structure are touched.
*
* \note zlib indicates an error if application end()s unfinished
* stream structure. liblzma doesn't do this, and assumes that
* application knows what it is doing.
*/
nothrow void lzma_end(lzma_stream *strm);
/**
* \brief Get the memory usage of decoder filter chain
*
* This function is currently supported only when *strm has been initialized
* with a function that takes a memlimit argument. With other functions, you
* should use e.g. lzma_raw_encoder_memusage() or lzma_raw_decoder_memusage()
* to estimate the memory requirements.
*
* This function is useful e.g. after LZMA_MEMLIMIT_ERROR to find out how big
* the memory usage limit should have been to decode the input. Note that
* this may give misleading information if decoding .xz Streams that have
* multiple Blocks, because each Block can have different memory requirements.
*
* \return How much memory is currently allocated for the filter
* decoders. If no filter chain is currently allocated,
* some non-zero value is still returned, which is less than
* or equal to what any filter chain would indicate as its
* memory requirement.
*
* If this function isn't supported by *strm or some other error
* occurs, zero is returned.
*/
nothrow pure ulong lzma_memusage(const lzma_stream *strm);
/**
* \brief Get the current memory usage limit
*
* This function is supported only when *strm has been initialized with
* a function that takes a memlimit argument.
*
* \return On success, the current memory usage limit is returned
* (always non-zero). On error, zero is returned.
*/
nothrow pure ulong lzma_memlimit_get(const lzma_stream *strm);
/**
* \brief Set the memory usage limit
*
* This function is supported only when *strm has been initialized with
* a function that takes a memlimit argument.
*
* \return - LZMA_OK: New memory usage limit successfully set.
* - LZMA_MEMLIMIT_ERROR: The new limit is too small.
* The limit was not changed.
* - LZMA_PROG_ERROR: Invalid arguments, e.g. *strm doesn't
* support memory usage limit or memlimit was zero.
*/
nothrow lzma_ret lzma_memlimit_set(lzma_stream *strm, ulong memlimit);
+91
View File
@@ -0,0 +1,91 @@
/**
* \file lzma/bcj.h
* \brief Branch/Call/Jump conversion filters
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.bcj;
import deimos.lzma;
extern(C):
/* Filter IDs for lzma_filter.id */
enum LZMA_FILTER_X86 = 0x04UL;
/**<
* Filter for x86 binaries
*/
enum LZMA_FILTER_POWERPC = 0x05UL;
/**<
* Filter for Big endian PowerPC binaries
*/
enum LZMA_FILTER_IA64 = 0x06UL;
/**<
* Filter for IA-64 (Itanium) binaries.
*/
enum LZMA_FILTER_ARM = 0x07UL;
/**<
* Filter for ARM binaries.
*/
enum LZMA_FILTER_ARMTHUMB = 0x08UL;
/**<
* Filter for ARM-Thumb binaries.
*/
enum LZMA_FILTER_SPARC = 0x09UL;
/**<
* Filter for SPARC binaries.
*/
/**
* \brief Options for BCJ filters
*
* The BCJ filters never change the size of the data. Specifying options
* for them is optional: if pointer to options is NULL, default value is
* used. You probably never need to specify options to BCJ filters, so just
* set the options pointer to NULL and be happy.
*
* If options with non-default values have been specified when encoding,
* the same options must also be specified when decoding.
*
* \note At the moment, none of the BCJ filters support
* LZMA_SYNC_FLUSH. If LZMA_SYNC_FLUSH is specified,
* LZMA_OPTIONS_ERROR will be returned. If there is need,
* partial support for LZMA_SYNC_FLUSH can be added in future.
* Partial means that flushing would be possible only at
* offsets that are multiple of 2, 4, or 16 depending on
* the filter, except x86 which cannot be made to support
* LZMA_SYNC_FLUSH predictably.
*/
struct lzma_options_bcj
{
/**
* \brief Start offset for conversions
*
* This setting is useful only when the same filter is used
* _separately_ for multiple sections of the same executable file,
* and the sections contain cross-section branch/call/jump
* instructions. In that case it is beneficial to set the start
* offset of the non-first sections so that the relative addresses
* of the cross-section branch/call/jump instructions will use the
* same absolute addresses as in the first section.
*
* When the pointer to options is NULL, the default value (zero)
* is used.
*/
uint start_offset;
}
+523
View File
@@ -0,0 +1,523 @@
/**
* \file lzma/block.h
* \brief .xz Block handling
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.block;
import deimos.lzma;
extern(C):
//TODO: initialize fields to void?
/**
* \brief Options for the Block and Block Header encoders and decoders
*
* Different Block handling functions use different parts of this structure.
* Some read some members, other functions write, and some do both. Only the
* members listed for reading need to be initialized when the specified
* functions are called. The members marked for writing will be assigned
* new values at some point either by calling the given function or by
* later calls to lzma_code().
*/
struct lzma_block
{
/**
* \brief Block format version
*
* To prevent API and ABI breakages if new features are needed in
* the Block field, a version number is used to indicate which
* fields in this structure are in use. For now, version must always
* be zero. With non-zero version, most Block related functions will
* return LZMA_OPTIONS_ERROR.
*
* Read by:
* - All functions that take pointer to lzma_block as argument,
* including lzma_block_header_decode().
*
* Written by:
* - lzma_block_header_decode()
*/
uint version_;
/**
* \brief Size of the Block Header field
*
* This is always a multiple of four.
*
* Read by:
* - lzma_block_header_encode()
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_size()
* - lzma_block_buffer_encode()
*/
uint header_size;
enum LZMA_BLOCK_HEADER_SIZE_MIN = 8;
enum LZMA_BLOCK_HEADER_SIZE_MAX = 1024;
/**
* \brief Type of integrity Check
*
* The Check ID is not stored into the Block Header, thus its value
* must be provided also when decoding.
*
* Read by:
* - lzma_block_header_encode()
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_check check;
/**
* \brief Size of the Compressed Data in bytes
*
* Encoding: If this is not LZMA_VLI_UNKNOWN, Block Header encoder
* will store this value to the Block Header. Block encoder doesn't
* care about this value, but will set it once the encoding has been
* finished.
*
* Decoding: If this is not LZMA_VLI_UNKNOWN, Block decoder will
* verify that the size of the Compressed Data field matches
* compressed_size.
*
* Usually you don't know this value when encoding in streamed mode,
* and thus cannot write this field into the Block Header.
*
* In non-streamed mode you can reserve space for this field before
* encoding the actual Block. After encoding the data, finish the
* Block by encoding the Block Header. Steps in detail:
*
* - Set compressed_size to some big enough value. If you don't know
* better, use LZMA_VLI_MAX, but remember that bigger values take
* more space in Block Header.
*
* - Call lzma_block_header_size() to see how much space you need to
* reserve for the Block Header.
*
* - Encode the Block using lzma_block_encoder() and lzma_code().
* It sets compressed_size to the correct value.
*
* - Use lzma_block_header_encode() to encode the Block Header.
* Because space was reserved in the first step, you don't need
* to call lzma_block_header_size() anymore, because due to
* reserving, header_size has to be big enough. If it is "too big",
* lzma_block_header_encode() will add enough Header Padding to
* make Block Header to match the size specified by header_size.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_vli compressed_size;
/**
* \brief Uncompressed Size in bytes
*
* This is handled very similarly to compressed_size above.
*
* uncompressed_size is needed by fewer functions than
* compressed_size. This is because uncompressed_size isn't
* needed to validate that Block stays within proper limits.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_vli uncompressed_size;
/**
* \brief Array of filters
*
* There can be 1-4 filters. The end of the array is marked with
* .id = LZMA_VLI_UNKNOWN.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode(): Note that this does NOT free()
* the old filter options structures. All unused filters[] will
* have .id == LZMA_VLI_UNKNOWN and .options == NULL. If
* decoding fails, all filters[] are guaranteed to be
* LZMA_VLI_UNKNOWN and NULL.
*
* \note Because of the array is terminated with
* .id = LZMA_VLI_UNKNOWN, the actual array must
* have LZMA_FILTERS_MAX + 1 members or the Block
* Header decoder will overflow the buffer.
*/
lzma_filter *filters;
/**
* \brief Raw value stored in the Check field
*
* After successful coding, the first lzma_check_size(check) bytes
* of this array contain the raw value stored in the Check field.
*
* Note that CRC32 and CRC64 are stored in little endian byte order.
* Take it into account if you display the Check values to the user.
*
* Written by:
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
ubyte[LZMA_CHECK_SIZE_MAX] raw_check;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* with the currently supported options, so it is safe to leave these
* uninitialized.
*/
void *reserved_ptr1;
void *reserved_ptr2;
void *reserved_ptr3;
uint reserved_int1;
uint reserved_int2;
lzma_vli reserved_int3;
lzma_vli reserved_int4;
lzma_vli reserved_int5;
lzma_vli reserved_int6;
lzma_vli reserved_int7;
lzma_vli reserved_int8;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
lzma_bool reserved_bool1;
lzma_bool reserved_bool2;
lzma_bool reserved_bool3;
lzma_bool reserved_bool4;
lzma_bool reserved_bool5;
lzma_bool reserved_bool6;
lzma_bool reserved_bool7;
lzma_bool reserved_bool8;
}
/**
* \brief Decode the Block Header Size field
*
* To decode Block Header using lzma_block_header_decode(), the size of the
* Block Header has to be known and stored into lzma_block.header_size.
* The size can be calculated from the first byte of a Block using this macro.
* Note that if the first byte is 0x00, it indicates beginning of Index; use
* this macro only when the byte is not 0x00.
*
* There is no encoding macro, because Block Header encoder is enough for that.
*/
template lzma_block_header_size_decode(uint b)
{
enum lzma_block_header_size_decode = (b+1)*4;
}
/**
* \brief Calculate Block Header Size
*
* Calculate the minimum size needed for the Block Header field using the
* settings specified in the lzma_block structure. Note that it is OK to
* increase the calculated header_size value as long as it is a multiple of
* four and doesn't exceed LZMA_BLOCK_HEADER_SIZE_MAX. Increasing header_size
* just means that lzma_block_header_encode() will add Header Padding.
*
* \return - LZMA_OK: Size calculated successfully and stored to
* block->header_size.
* - LZMA_OPTIONS_ERROR: Unsupported version, filters or
* filter options.
* - LZMA_PROG_ERROR: Invalid values like compressed_size == 0.
*
* \note This doesn't check that all the options are valid i.e. this
* may return LZMA_OK even if lzma_block_header_encode() or
* lzma_block_encoder() would fail. If you want to validate the
* filter chain, consider using lzma_memlimit_encoder() which as
* a side-effect validates the filter chain.
*/
nothrow lzma_ret lzma_block_header_size(lzma_block *block);
/**
* \brief Encode Block Header
*
* The caller must have calculated the size of the Block Header already with
* lzma_block_header_size(). If a value larger than the one calculated by
* lzma_block_header_size() is used, the Block Header will be padded to the
* specified size.
*
* \param out Beginning of the output buffer. This must be
* at least block->header_size bytes.
* \param block Block options to be encoded.
*
* \return - LZMA_OK: Encoding was successful. block->header_size
* bytes were written to output buffer.
* - LZMA_OPTIONS_ERROR: Invalid or unsupported options.
* - LZMA_PROG_ERROR: Invalid arguments, for example
* block->header_size is invalid or block->filters is NULL.
*/
nothrow lzma_ret lzma_block_header_encode(const (lzma_block)* block, ubyte* out_);
/**
* \brief Decode Block Header
*
* block->version should be set to the highest value supported by the
* application; currently the only possible version is zero. This function
* will set version to the lowest value that still supports all the features
* required by the Block Header.
*
* The size of the Block Header must have already been decoded with
* lzma_block_header_size_decode() macro and stored to block->header_size.
*
* block->filters must have been allocated, but they don't need to be
* initialized (possible existing filter options are not freed).
*
* \param block Destination for Block options.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() (and also free()
* if an error occurs).
* \param in Beginning of the input buffer. This must be
* at least block->header_size bytes.
*
* \return - LZMA_OK: Decoding was successful. block->header_size
* bytes were read from the input buffer.
* - LZMA_OPTIONS_ERROR: The Block Header specifies some
* unsupported options such as unsupported filters. This can
* happen also if block->version was set to a too low value
* compared to what would be required to properly represent
* the information stored in the Block Header.
* - LZMA_DATA_ERROR: Block Header is corrupt, for example,
* the CRC32 doesn't match.
* - LZMA_PROG_ERROR: Invalid arguments, for example
* block->header_size is invalid or block->filters is NULL.
*/
nothrow lzma_ret lzma_block_header_decode(lzma_block* block,
lzma_allocator* allocator, const(ubyte)* in_);
/**
* \brief Validate and set Compressed Size according to Unpadded Size
*
* Block Header stores Compressed Size, but Index has Unpadded Size. If the
* application has already parsed the Index and is now decoding Blocks,
* it can calculate Compressed Size from Unpadded Size. This function does
* exactly that with error checking:
*
* - Compressed Size calculated from Unpadded Size must be positive integer,
* that is, Unpadded Size must be big enough that after Block Header and
* Check fields there's still at least one byte for Compressed Size.
*
* - If Compressed Size was present in Block Header, the new value
* calculated from Unpadded Size is compared against the value
* from Block Header.
*
* \note This function must be called _after_ decoding the Block Header
* field so that it can properly validate Compressed Size if it
* was present in Block Header.
*
* \return - LZMA_OK: block->compressed_size was set successfully.
* - LZMA_DATA_ERROR: unpadded_size is too small compared to
* block->header_size and lzma_check_size(block->check).
* - LZMA_PROG_ERROR: Some values are invalid. For example,
* block->header_size must be a multiple of four and
* between 8 and 1024 inclusive.
*/
nothrow lzma_ret lzma_block_compressed_size(
lzma_block* block, lzma_vli unpadded_size);
/**
* \brief Calculate Unpadded Size
*
* The Index field stores Unpadded Size and Uncompressed Size. The latter
* can be taken directly from the lzma_block structure after coding a Block,
* but Unpadded Size needs to be calculated from Block Header Size,
* Compressed Size, and size of the Check field. This is where this function
* is needed.
*
* \return Unpadded Size on success, or zero on error.
*/
nothrow lzma_vli lzma_block_unpadded_size(const lzma_block* block);
/**
* \brief Calculate the total encoded size of a Block
*
* This is equivalent to lzma_block_unpadded_size() except that the returned
* value includes the size of the Block Padding field.
*
* \return On success, total encoded size of the Block. On error,
* zero is returned.
*/
nothrow pure lzma_vli lzma_block_total_size(const(lzma_block*) block);
/**
* \brief Initialize .xz Block encoder
*
* Valid actions for lzma_code() are LZMA_RUN, LZMA_SYNC_FLUSH (only if the
* filter chain supports it), and LZMA_FINISH.
*
* \return - LZMA_OK: All good, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_UNSUPPORTED_CHECK: block->check specifies a Check ID
* that is not supported by this buid of liblzma. Initializing
* the encoder failed.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_encoder(
lzma_stream* strm, lzma_block* block);
/**
* \brief Initialize .xz Block decoder
*
* Valid actions for lzma_code() are LZMA_RUN and LZMA_FINISH. Using
* LZMA_FINISH is not required. It is supported only for convenience.
*
* \return - LZMA_OK: All good, continue with lzma_code().
* - LZMA_UNSUPPORTED_CHECK: Initialization was successful, but
* the given Check ID is not supported, thus Check will be
* ignored.
* - LZMA_PROG_ERROR
* - LZMA_MEM_ERROR
*/
nothrow lzma_ret lzma_block_decoder(
lzma_stream *strm, lzma_block *block);
/**
* \brief Calculate maximum output size for single-call Block encoding
*
* This is equivalent to lzma_stream_buffer_bound() but for .xz Blocks.
* See the documentation of lzma_stream_buffer_bound().
*/
nothrow size_t lzma_block_buffer_bound(size_t uncompressed_size);
/**
* \brief Single-call .xz Block encoder
*
* In contrast to the multi-call encoder initialized with
* lzma_block_encoder(), this function encodes also the Block Header. This
* is required to make it possible to write appropriate Block Header also
* in case the data isn't compressible, and different filter chain has to be
* used to encode the data in uncompressed form using uncompressed chunks
* of the LZMA2 filter.
*
* When the data isn't compressible, header_size, compressed_size, and
* uncompressed_size are set just like when the data was compressible, but
* it is possible that header_size is too small to hold the filter chain
* specified in block->filters, because that isn't necessarily the filter
* chain that was actually used to encode the data. lzma_block_unpadded_size()
* still works normally, because it doesn't read the filters array.
*
* \param block Block options: block->version, block->check,
* and block->filters must have been initialized.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_buffer_encode(
lzma_block *block, lzma_allocator *allocator,
const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Single-call .xz Block decoder
*
* This is single-call equivalent of lzma_block_decoder(), and requires that
* the caller has already decoded Block Header and checked its memory usage.
*
* \param block Block options just like with lzma_block_decoder().
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_OPTIONS_ERROR
* - LZMA_DATA_ERROR
* - LZMA_MEM_ERROR
* - LZMA_BUF_ERROR: Output buffer was too small.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_buffer_decode(
lzma_block *block, lzma_allocator *allocator,
const(ubyte)* in_, size_t *in_pos, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
+141
View File
@@ -0,0 +1,141 @@
/**
* \file lzma/check.h
* \brief Integrity checks
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.check;
import deimos.lzma;
extern(C):
/**
* \brief Type of the integrity check (Check ID)
*
* The .xz format supports multiple types of checks that are calculated
* from the uncompressed data. They vary in both speed and ability to
* detect errors.
*/
enum lzma_check
{
LZMA_CHECK_NONE = 0,
/**<
* No Check is calculated.
*
* Size of the Check field: 0 bytes
*/
LZMA_CHECK_CRC32 = 1,
/**<
* CRC32 using the polynomial from the IEEE 802.3 standard
*
* Size of the Check field: 4 bytes
*/
LZMA_CHECK_CRC64 = 4,
/**<
* CRC64 using the polynomial from the ECMA-182 standard
*
* Size of the Check field: 8 bytes
*/
LZMA_CHECK_SHA256 = 10
}
/**
* \brief Maximum valid Check ID
*
* The .xz file format specification specifies 16 Check IDs (0-15). Some
* of them are only reserved, that is, no actual Check algorithm has been
* assigned. When decoding, liblzma still accepts unknown Check IDs for
* future compatibility. If a valid but unsupported Check ID is detected,
* liblzma can indicate a warning; see the flags LZMA_TELL_NO_CHECK,
* LZMA_TELL_UNSUPPORTED_CHECK, and LZMA_TELL_ANY_CHECK in container.h.
*/
enum LZMA_CHECK_ID_MAX = 15;
/**
* \brief Test if the given Check ID is supported
*
* Return true if the given Check ID is supported by this liblzma build.
* Otherwise false is returned. It is safe to call this with a value that
* is not in the range [0, 15]; in that case the return value is always false.
*
* You can assume that LZMA_CHECK_NONE and LZMA_CHECK_CRC32 are always
* supported (even if liblzma is built with limited features).
*/
nothrow lzma_bool lzma_check_is_supported(lzma_check check);
/**
* \brief Get the size of the Check field with the given Check ID
*
* Although not all Check IDs have a check algorithm associated, the size of
* every Check is already frozen. This function returns the size (in bytes) of
* the Check field with the specified Check ID. The values are:
* { 0, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64, 64 }
*
* If the argument is not in the range [0, 15], UINT32_MAX is returned.
*/
nothrow uint lzma_check_size(lzma_check check);
/**
* \brief Maximum size of a Check field
*/
enum LZMA_CHECK_SIZE_MAX = 64;
/**
* \brief Calculate CRC32
*
* Calculate CRC32 using the polynomial from the IEEE 802.3 standard.
*
* \param buf Pointer to the input buffer
* \param size Size of the input buffer
* \param crc Previously returned CRC value. This is used to
* calculate the CRC of a big buffer in smaller chunks.
* Set to zero when starting a new calculation.
*
* \return Updated CRC value, which can be passed to this function
* again to continue CRC calculation.
*/
nothrow pure uint lzma_crc32(
const(ubyte)* buf, size_t size, uint crc);
/**
* \brief Calculate CRC64
*
* Calculate CRC64 using the polynomial from the ECMA-182 standard.
*
* This function is used similarly to lzma_crc32(). See its documentation.
*/
nothrow pure ulong lzma_crc64(
const(ubyte)* buf, size_t size, ulong crc);
/*
* SHA-256 functions are currently not exported to public API.
* Contact Lasse Collin if you think it should be.
*/
/**
* \brief Get the type of the integrity check
*
* This function can be called only immediately after lzma_code() has
* returned LZMA_NO_CHECK, LZMA_UNSUPPORTED_CHECK, or LZMA_GET_CHECK.
* Calling this function in any other situation has undefined behavior.
*/
nothrow lzma_check lzma_get_check(const lzma_stream *strm);
+413
View File
@@ -0,0 +1,413 @@
/**
* \file lzma/container.h
* \brief File formats
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.container;
import deimos.lzma;
extern(C):
/************
* Encoding *
************/
/**
* \brief Default compression preset
*
* It's not straightforward to recommend a default preset, because in some
* cases keeping the resource usage relatively low is more important that
* getting the maximum compression ratio.
*/
enum uint LZMA_PRESET_DEFAULT = 6U;
/**
* \brief Mask for preset level
*
* This is useful only if you need to extract the level from the preset
* variable. That should be rare.
*/
enum uint LZMA_PRESET_LEVEL_MASK = 0x1FU;
/*
* Preset flags
*
* Currently only one flag is defined.
*/
/**
* \brief Extreme compression preset
*
* This flag modifies the preset to make the encoding significantly slower
* while improving the compression ratio only marginally. This is useful
* when you don't mind wasting time to get as small result as possible.
*
* This flag doesn't affect the memory usage requirements of the decoder (at
* least not significantly). The memory usage of the encoder may be increased
* a little but only at the lowest preset levels (0-3).
*/
enum uint LZMA_PRESET_EXTREME = (1U << 31);
/**
* \brief Calculate approximate memory usage of easy encoder
*
* This function is a wrapper for lzma_raw_encoder_memusage().
*
* \param preset Compression preset (level and possible flags)
*
* \return Number of bytes of memory required for the given
* preset when encoding. If an error occurs, for example
* due to unsupported preset, UINT64_MAX is returned.
*/
nothrow pure ulong lzma_easy_encoder_memusage(uint preset);
/**
* \brief Calculate approximate decoder memory usage of a preset
*
* This function is a wrapper for lzma_raw_decoder_memusage().
*
* \param preset Compression preset (level and possible flags)
*
* \return Number of bytes of memory required to decompress a file
* that was compressed using the given preset. If an error
* occurs, for example due to unsupported preset, UINT64_MAX
* is returned.
*/
nothrow pure ulong lzma_easy_decoder_memusage(uint preset);
/**
* \brief Initialize .xz Stream encoder using a preset number
*
* This function is intended for those who just want to use the basic features
* if liblzma (that is, most developers out there).
*
* \param strm Pointer to lzma_stream that is at least initialized
* with LZMA_STREAM_INIT.
* \param preset Compression preset to use. A preset consist of level
* number and zero or more flags. Usually flags aren't
* used, so preset is simply a number [0, 9] which match
* the options -0 ... -9 of the xz command line tool.
* Additional flags can be be set using bitwise-or with
* the preset level number, e.g. 6 | LZMA_PRESET_EXTREME.
* \param check Integrity check type to use. See check.h for available
* checks. The xz command line tool defaults to
* LZMA_CHECK_CRC64, which is a good choice if you are
* unsure. LZMA_CHECK_CRC32 is good too as long as the
* uncompressed file is not many gigabytes.
*
* \return - LZMA_OK: Initialization succeeded. Use lzma_code() to
* encode your data.
* - LZMA_MEM_ERROR: Memory allocation failed.
* - LZMA_OPTIONS_ERROR: The given compression preset is not
* supported by this build of liblzma.
* - LZMA_UNSUPPORTED_CHECK: The given check type is not
* supported by this liblzma build.
* - LZMA_PROG_ERROR: One or more of the parameters have values
* that will never be valid. For example, strm == NULL.
*
* If initialization fails (return value is not LZMA_OK), all the memory
* allocated for *strm by liblzma is always freed. Thus, there is no need
* to call lzma_end() after failed initialization.
*
* If initialization succeeds, use lzma_code() to do the actual encoding.
* Valid values for `action' (the second argument of lzma_code()) are
* LZMA_RUN, LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, and LZMA_FINISH. In future,
* there may be compression levels or flags that don't support LZMA_SYNC_FLUSH.
*/
nothrow lzma_ret lzma_easy_encoder(
lzma_stream *strm, uint preset, lzma_check check);
/**
* \brief Single-call .xz Stream encoding using a preset number
*
* The maximum required output buffer size can be calculated with
* lzma_stream_buffer_bound().
*
* \param preset Compression preset to use. See the description
* in lzma_easy_encoder().
* \param check Type of the integrity check to calculate from
* uncompressed data.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_easy_buffer_encode(
uint preset, lzma_check check,
lzma_allocator *allocator, const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Initialize .xz Stream encoder using a custom filter chain
*
* \param strm Pointer to properly prepared lzma_stream
* \param filters Array of filters. This must be terminated with
* filters[n].id = LZMA_VLI_UNKNOWN. See filter.h for
* more information.
* \param check Type of the integrity check to calculate from
* uncompressed data.
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_encoder(lzma_stream *strm,
const lzma_filter *filters, lzma_check check);
/**
* \brief Initialize .lzma encoder (legacy file format)
*
* The .lzma format is sometimes called the LZMA_Alone format, which is the
* reason for the name of this function. The .lzma format supports only the
* LZMA1 filter. There is no support for integrity checks like CRC32.
*
* Use this function if and only if you need to create files readable by
* legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format
* is strongly recommended.
*
* The valid action values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* No kind of flushing is supported, because the file format doesn't make
* it possible.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_alone_encoder(
lzma_stream *strm, const lzma_options_lzma *options);
/**
* \brief Calculate output buffer size for single-call Stream encoder
*
* When trying to compress uncompressible data, the encoded size will be
* slightly bigger than the input data. This function calculates how much
* output buffer space is required to be sure that lzma_stream_buffer_encode()
* doesn't return LZMA_BUF_ERROR.
*
* The calculated value is not exact, but it is guaranteed to be big enough.
* The actual maximum output space required may be slightly smaller (up to
* about 100 bytes). This should not be a problem in practice.
*
* If the calculated maximum size doesn't fit into size_t or would make the
* Stream grow past LZMA_VLI_MAX (which should never happen in practice),
* zero is returned to indicate the error.
*
* \note The limit calculated by this function applies only to
* single-call encoding. Multi-call encoding may (and probably
* will) have larger maximum expansion when encoding
* uncompressible data. Currently there is no function to
* calculate the maximum expansion of multi-call encoding.
*/
nothrow size_t lzma_stream_buffer_bound(size_t uncompressed_size);
/**
* \brief Single-call .xz Stream encoder
*
* \param filters Array of filters. This must be terminated with
* filters[n].id = LZMA_VLI_UNKNOWN. See filter.h
* for more information.
* \param check Type of the integrity check to calculate from
* uncompressed data.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_buffer_encode(
lzma_filter *filters, lzma_check check,
lzma_allocator *allocator, const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/************
* Decoding *
************/
/**
* This flag makes lzma_code() return LZMA_NO_CHECK if the input stream
* being decoded has no integrity check. Note that when used with
* lzma_auto_decoder(), all .lzma files will trigger LZMA_NO_CHECK
* if LZMA_TELL_NO_CHECK is used.
*/
enum uint LZMA_TELL_NO_CHECK = 0x01U;
/**
* This flag makes lzma_code() return LZMA_UNSUPPORTED_CHECK if the input
* stream has an integrity check, but the type of the integrity check is not
* supported by this liblzma version or build. Such files can still be
* decoded, but the integrity check cannot be verified.
*/
enum uint LZMA_TELL_UNSUPPORTED_CHECK = 0x02U;
/**
* This flag makes lzma_code() return LZMA_GET_CHECK as soon as the type
* of the integrity check is known. The type can then be got with
* lzma_get_check().
*/
enum uint LZMA_TELL_ANY_CHECK = 0x04U;
/**
* This flag enables decoding of concatenated files with file formats that
* allow concatenating compressed files as is. From the formats currently
* supported by liblzma, only the .xz format allows concatenated files.
* Concatenated files are not allowed with the legacy .lzma format.
*
* This flag also affects the usage of the `action' argument for lzma_code().
* When LZMA_CONCATENATED is used, lzma_code() won't return LZMA_STREAM_END
* unless LZMA_FINISH is used as `action'. Thus, the application has to set
* LZMA_FINISH in the same way as it does when encoding.
*
* If LZMA_CONCATENATED is not used, the decoders still accept LZMA_FINISH
* as `action' for lzma_code(), but the usage of LZMA_FINISH isn't required.
*/
enum uint LZMA_CONCATENATED = 0x08U;
/**
* \brief Initialize .xz Stream decoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param memlimit Memory usage limit as bytes. Use UINT64_MAX
* to effectively disable the limiter.
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_TELL_ANY_CHECK, LZMA_CONCATENATED
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR: Cannot allocate memory.
* - LZMA_OPTIONS_ERROR: Unsupported flags
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_decoder(
lzma_stream *strm, ulong memlimit, uint flags);
/**
* \brief Decode .xz Streams and .lzma files with autodetection
*
* This decoder autodetects between the .xz and .lzma file formats, and
* calls lzma_stream_decoder() or lzma_alone_decoder() once the type
* of the input file has been detected.
*
* \param strm Pointer to properly prepared lzma_stream
* \param memlimit Memory usage limit as bytes. Use UINT64_MAX
* to effectively disable the limiter.
* \param flags Bitwise-or of flags, or zero for no flags.
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR: Cannot allocate memory.
* - LZMA_OPTIONS_ERROR: Unsupported flags
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_auto_decoder(
lzma_stream *strm, ulong memlimit, uint flags);
/**
* \brief Initialize .lzma decoder (legacy file format)
*
* Valid `action' arguments to lzma_code() are LZMA_RUN and LZMA_FINISH.
* There is no need to use LZMA_FINISH, but allowing it may simplify
* certain types of applications.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_alone_decoder(
lzma_stream *strm, ulong memlimit);
/**
* \brief Single-call .xz Stream decoder
*
* \param memlimit Pointer to how much memory the decoder is allowed
* to allocate. The value pointed by this pointer is
* modified if and only if LZMA_MEMLIMIT_ERROR is
* returned.
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_CONCATENATED. Note that LZMA_TELL_ANY_CHECK
* is not allowed and will return LZMA_PROG_ERROR.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if decoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_DATA_ERROR
* - LZMA_NO_CHECK: This can be returned only if using
* the LZMA_TELL_NO_CHECK flag.
* - LZMA_UNSUPPORTED_CHECK: This can be returned only if using
* the LZMA_TELL_UNSUPPORTED_CHECK flag.
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR: Memory usage limit was reached.
* The minimum required memlimit value was stored to *memlimit.
* - LZMA_BUF_ERROR: Output buffer was too small.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_buffer_decode(
ulong *memlimit, uint flags, lzma_allocator *allocator,
const (ubyte)* in_, size_t *in_pos, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
+79
View File
@@ -0,0 +1,79 @@
/**
* \file lzma/delta.h
* \brief Delta filter
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.delta;
import deimos.lzma;
extern(C):
/**
* \brief Filter ID
*
* Filter ID of the Delta filter. This is used as lzma_filter.id.
*/
enum LZMA_FILTER_DELTA = 0x03UL;
/**
* \brief Type of the delta calculation
*
* Currently only byte-wise delta is supported. Other possible types could
* be, for example, delta of 16/32/64-bit little/big endian integers, but
* these are not currently planned since byte-wise delta is almost as good.
*/
enum lzma_delta_type
{
LZMA_DELTA_TYPE_BYTE
}
/**
* \brief Options for the Delta filter
*
* These options are needed by both encoder and decoder.
*/
struct lzma_options_delta
{
/** For now, this must always be LZMA_DELTA_TYPE_BYTE. */
lzma_delta_type type;
/**
* \brief Delta distance
*
* With the only currently supported type, LZMA_DELTA_TYPE_BYTE,
* the distance is as bytes.
*
* Examples:
* - 16-bit stereo audio: distance = 4 bytes
* - 24-bit RGB image data: distance = 3 bytes
*/
uint dist;
enum LZMA_DELTA_DIST_MIN = 1;
enum LZMA_DELTA_DIST_MAX = 256;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* when type is LZMA_DELTA_TYPE_BYTE, so it is safe to leave these
* uninitialized.
*/
uint reserved_int1;
uint reserved_int2;
uint reserved_int3;
uint reserved_int4;
void *reserved_ptr1;
void *reserved_ptr2;
}
+416
View File
@@ -0,0 +1,416 @@
/**
* \file lzma/filter.h
* \brief Common filter related types and functions
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.filter;
import deimos.lzma;
extern(C):
/**
* \brief Maximum number of filters in a chain
*
* A filter chain can have 1-4 filters, of which three are allowed to change
* the size of the data. Usually only one or two filters are needed.
*/
enum LZMA_FILTERS_MAX = 4;
/**
* \brief Filter options
*
* This structure is used to pass Filter ID and a pointer filter's
* options to liblzma. A few functions work with a single lzma_filter
* structure, while most functions expect a filter chain.
*
* A filter chain is indicated with an array of lzma_filter structures.
* The array is terminated with .id = LZMA_VLI_UNKNOWN. Thus, the filter
* array must have LZMA_FILTERS_MAX + 1 elements (that is, five) to
* be able to hold any arbitrary filter chain. This is important when
* using lzma_block_header_decode() from block.h, because too small
* array would make liblzma write past the end of the filters array.
*/
struct lzma_filter
{
/**
* \brief Filter ID
*
* Use constants whose name begin with `LZMA_FILTER_' to specify
* different filters. In an array of lzma_filter structures, use
* LZMA_VLI_UNKNOWN to indicate end of filters.
*
* \note This is not an enum, because on some systems enums
* cannot be 64-bit.
*/
lzma_vli id;
/**
* \brief Pointer to filter-specific options structure
*
* If the filter doesn't need options, set this to NULL. If id is
* set to LZMA_VLI_UNKNOWN, options is ignored, and thus
* doesn't need be initialized.
*/
void *options;
}
/**
* \brief Test if the given Filter ID is supported for encoding
*
* Return true if the give Filter ID is supported for encoding by this
* liblzma build. Otherwise false is returned.
*
* There is no way to list which filters are available in this particular
* liblzma version and build. It would be useless, because the application
* couldn't know what kind of options the filter would need.
*/
nothrow lzma_bool lzma_filter_encoder_is_supported(lzma_vli id);
/**
* \brief Test if the given Filter ID is supported for decoding
*
* Return true if the give Filter ID is supported for decoding by this
* liblzma build. Otherwise false is returned.
*/
nothrow lzma_bool lzma_filter_decoder_is_supported(lzma_vli id);
/**
* \brief Copy the filters array
*
* Copy the Filter IDs and filter-specific options from src to dest.
* Up to LZMA_FILTERS_MAX filters are copied, plus the terminating
* .id == LZMA_VLI_UNKNOWN. Thus, dest should have at least
* LZMA_FILTERS_MAX + 1 elements space unless the caller knows that
* src is smaller than that.
*
* Unless the filter-specific options is NULL, the Filter ID has to be
* supported by liblzma, because liblzma needs to know the size of every
* filter-specific options structure. The filter-specific options are not
* validated. If options is NULL, any unsupported Filter IDs are copied
* without returning an error.
*
* Old filter-specific options in dest are not freed, so dest doesn't
* need to be initialized by the caller in any way.
*
* If an error occurs, memory possibly already allocated by this function
* is always freed.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR: Unsupported Filter ID and its options
* is not NULL.
* - LZMA_PROG_ERROR: src or dest is NULL.
*/
nothrow lzma_ret lzma_filters_copy(const lzma_filter*src,
lzma_filter *dest, lzma_allocator *allocator);
/**
* \brief Calculate approximate memory requirements for raw encoder
*
* This function can be used to calculate the memory requirements for
* Block and Stream encoders too because Block and Stream encoders don't
* need significantly more memory than raw encoder.
*
* \param filters Array of filters terminated with
* .id == LZMA_VLI_UNKNOWN.
*
* \return Number of bytes of memory required for the given
* filter chain when encoding. If an error occurs,
* for example due to unsupported filter chain,
* UINT64_MAX is returned.
*/
pure nothrow ulong lzma_raw_encoder_memusage(const lzma_filter *filters);
/**
* \brief Calculate approximate memory requirements for raw decoder
*
* This function can be used to calculate the memory requirements for
* Block and Stream decoders too because Block and Stream decoders don't
* need significantly more memory than raw decoder.
*
* \param filters Array of filters terminated with
* .id == LZMA_VLI_UNKNOWN.
*
* \return Number of bytes of memory required for the given
* filter chain when decoding. If an error occurs,
* for example due to unsupported filter chain,
* UINT64_MAX is returned.
*/
nothrow pure ulong lzma_raw_decoder_memusage(const lzma_filter *filters);
/**
* \brief Initialize raw encoder
*
* This function may be useful when implementing custom file formats.
*
* \param strm Pointer to properly prepared lzma_stream
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
*
* The `action' with lzma_code() can be LZMA_RUN, LZMA_SYNC_FLUSH (if the
* filter chain supports it), or LZMA_FINISH.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_raw_encoder(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Initialize raw decoder
*
* The initialization of raw decoder goes similarly to raw encoder.
*
* The `action' with lzma_code() can be LZMA_RUN or LZMA_FINISH. Using
* LZMA_FINISH is not required, it is supported just for convenience.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_raw_decoder(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Update the filter chain in the encoder
*
* This function is for advanced users only. This function has two slightly
* different purposes:
*
* - After LZMA_FULL_FLUSH when using Stream encoder: Set a new filter
* chain, which will be used starting from the next Block.
*
* - After LZMA_SYNC_FLUSH using Raw, Block, or Stream encoder: Change
* the filter-specific options in the middle of encoding. The actual
* filters in the chain (Filter IDs) cannot be changed. In the future,
* it might become possible to change the filter options without
* using LZMA_SYNC_FLUSH.
*
* While rarely useful, this function may be called also when no data has
* been compressed yet. In that case, this function will behave as if
* LZMA_FULL_FLUSH (Stream encoder) or LZMA_SYNC_FLUSH (Raw or Block
* encoder) had been used right before calling this function.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_filters_update(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Single-call raw encoder
*
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*
* \note There is no function to calculate how big output buffer
* would surely be big enough. (lzma_stream_buffer_bound()
* works only for lzma_stream_buffer_encode(); raw encoder
* won't necessarily meet that bound.)
*/
nothrow lzma_ret lzma_raw_buffer_encode(
const lzma_filter *filters, lzma_allocator *allocator,
const(ubyte) *in_, size_t in_size, ubyte *out_,
size_t *out_pos, size_t out_size);
/**
* \brief Single-call raw decoder
*
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*/
nothrow lzma_ret lzma_raw_buffer_decode(const lzma_filter *filters,
lzma_allocator *allocator,
const(ubyte) *in_, size_t *in_pos, size_t in_size,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Get the size of the Filter Properties field
*
* This function may be useful when implementing custom file formats
* using the raw encoder and decoder.
*
* \param size Pointer to uint32_t to hold the size of the properties
* \param filter Filter ID and options (the size of the properties may
* vary depending on the options)
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*
* \note This function validates the Filter ID, but does not
* necessarily validate the options. Thus, it is possible
* that this returns LZMA_OK while the following call to
* lzma_properties_encode() returns LZMA_OPTIONS_ERROR.
*/
nothrow lzma_ret lzma_properties_size(
uint *size, const lzma_filter *filter);
/**
* \brief Encode the Filter Properties field
*
* \param filter Filter ID and options
* \param props Buffer to hold the encoded options. The size of
* buffer must have been already determined with
* lzma_properties_size().
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*
* \note Even this function won't validate more options than actually
* necessary. Thus, it is possible that encoding the properties
* succeeds but using the same options to initialize the encoder
* will fail.
*
* \note If lzma_properties_size() indicated that the size
* of the Filter Properties field is zero, calling
* lzma_properties_encode() is not required, but it
* won't do any harm either.
*/
nothrow lzma_ret lzma_properties_encode(
const lzma_filter *filter, ubyte *props);
/**
* \brief Decode the Filter Properties field
*
* \param filter filter->id must have been set to the correct
* Filter ID. filter->options doesn't need to be
* initialized (it's not freed by this function). The
* decoded options will be stored to filter->options.
* filter->options is set to NULL if there are no
* properties or if an error occurs.
* \param allocator Custom memory allocator used to allocate the
* options. Set to NULL to use the default malloc(),
* and in case of an error, also free().
* \param props Input buffer containing the properties.
* \param props_size Size of the properties. This must be the exact
* size; giving too much or too little input will
* return LZMA_OPTIONS_ERROR.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
*/
nothrow lzma_ret lzma_properties_decode(
lzma_filter *filter, lzma_allocator *allocator,
const ubyte *props, size_t props_size);
/**
* \brief Calculate encoded size of a Filter Flags field
*
* Knowing the size of Filter Flags is useful to know when allocating
* memory to hold the encoded Filter Flags.
*
* \param size Pointer to integer to hold the calculated size
* \param filter Filter ID and associated options whose encoded
* size is to be calculated
*
* \return - LZMA_OK: *size set successfully. Note that this doesn't
* guarantee that filter->options is valid, thus
* lzma_filter_flags_encode() may still fail.
* - LZMA_OPTIONS_ERROR: Unknown Filter ID or unsupported options.
* - LZMA_PROG_ERROR: Invalid options
*
* \note If you need to calculate size of List of Filter Flags,
* you need to loop over every lzma_filter entry.
*/
nothrow lzma_ret lzma_filter_flags_size(
uint *size, const lzma_filter *filter);
/**
* \brief Encode Filter Flags into given buffer
*
* In contrast to some functions, this doesn't allocate the needed buffer.
* This is due to how this function is used internally by liblzma.
*
* \param filter Filter ID and options to be encoded
* \param out Beginning of the output buffer
* \param out_pos out[*out_pos] is the next write position. This
* is updated by the encoder.
* \param out_size out[out_size] is the first byte to not write.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: Invalid or unsupported options.
* - LZMA_PROG_ERROR: Invalid options or not enough output
* buffer space (you should have checked it with
* lzma_filter_flags_size()).
*/
nothrow lzma_ret lzma_filter_flags_encode(const lzma_filter *filter,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Decode Filter Flags from given buffer
*
* The decoded result is stored into *filter. The old value of
* filter->options is not free()d.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_filter_flags_decode(
lzma_filter *filter, lzma_allocator *allocator,
const ubyte *in_, size_t *in_pos, size_t in_size);
+50
View File
@@ -0,0 +1,50 @@
/**
* \file lzma/hardware.h
* \brief Hardware information
*
* Since liblzma can consume a lot of system resources, it also provides
* ways to limit the resource usage. Applications linking against liblzma
* need to do the actual decisions how much resources to let liblzma to use.
* To ease making these decisions, liblzma provides functions to find out
* the relevant capabilities of the underlaying hardware. Currently there
* is only a function to find out the amount of RAM, but in the future there
* will be also a function to detect how many concurrent threads the system
* can run.
*
* \note On some operating systems, these function may temporarily
* load a shared library or open file descriptor(s) to find out
* the requested hardware information. Unless the application
* assumes that specific file descriptors are not touched by
* other threads, this should have no effect on thread safety.
* Possible operations involving file descriptors will restart
* the syscalls if they return EINTR.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.hardware;
import deimos.lzma;
extern(C):
/**
* \brief Get the total amount of physical memory (RAM) in bytes
*
* This function may be useful when determining a reasonable memory
* usage limit for decompressing or how much memory it is OK to use
* for compressing.
*
* \return On success, the total amount of physical memory in bytes
* is returned. If the amount of RAM cannot be determined,
* zero is returned. This can happen if an error occurs
* or if there is no code in liblzma to detect the amount
* of RAM on the specific operating system.
*/
nothrow ulong lzma_physmem();
+663
View File
@@ -0,0 +1,663 @@
/**
* \file lzma/index.h
* \brief Handling of .xz Index and related information
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.index;
import deimos.lzma;
extern(C):
/**
* \brief Opaque data type to hold the Index(es) and other information
*
* lzma_index often holds just one .xz Index and possibly the Stream Flags
* of the same Stream and size of the Stream Padding field. However,
* multiple lzma_indexes can be concatenated with lzma_index_cat() and then
* there may be information about multiple Streams in the same lzma_index.
*
* Notes about thread safety: Only one thread may modify lzma_index at
* a time. All functions that take non-const pointer to lzma_index
* modify it. As long as no thread is modifying the lzma_index, getting
* information from the same lzma_index can be done from multiple threads
* at the same time with functions that take a const pointer to
* lzma_index or use lzma_index_iter. The same iterator must be used
* only by one thread at a time, of course, but there can be as many
* iterators for the same lzma_index as needed.
*/
struct lzma_index {};
/**
* \brief Iterator to get information about Blocks and Streams
*/
struct lzma_index_iter
{
struct StreamStruct
{
/**
* \brief Pointer to Stream Flags
*
* This is NULL if Stream Flags have not been set for
* this Stream with lzma_index_stream_flags().
*/
const lzma_stream_flags *flags;
const void *reserved_ptr1;
const void *reserved_ptr2;
const void *reserved_ptr3;
/**
* \brief Stream number in the lzma_index
*
* The first Stream is 1.
*/
lzma_vli number;
/**
* \brief Number of Blocks in the Stream
*
* If this is zero, the block structure below has
* undefined values.
*/
lzma_vli block_count;
/**
* \brief Compressed start offset of this Stream
*
* The offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*/
lzma_vli compressed_offset;
/**
* \brief Uncompressed start offset of this Stream
*
* The offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*/
lzma_vli uncompressed_offset;
/**
* \brief Compressed size of this Stream
*
* This includes all headers except the possible
* Stream Padding after this Stream.
*/
lzma_vli compressed_size;
/**
* \brief Uncompressed size of this Stream
*/
lzma_vli uncompressed_size;
/**
* \brief Size of Stream Padding after this Stream
*
* If it hasn't been set with lzma_index_stream_padding(),
* this defaults to zero. Stream Padding is always
* a multiple of four bytes.
*/
lzma_vli padding;
lzma_vli reserved_vli1;
lzma_vli reserved_vli2;
lzma_vli reserved_vli3;
lzma_vli reserved_vli4;
}
StreamStruct stream;
struct BlockStruct
{
/**
* \brief Block number in the file
*
* The first Block is 1.
*/
lzma_vli number_in_file;
/**
* \brief Compressed start offset of this Block
*
* This offset is relative to the beginning of the
* lzma_index (i.e. usually the beginning of the .xz file).
* Normally this is where you should seek in the .xz file
* to start decompressing this Block.
*/
lzma_vli compressed_file_offset;
/**
* \brief Uncompressed start offset of this Block
*
* This offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*
* When doing random-access reading, it is possible that
* the target offset is not exactly at Block boundary. One
* will need to compare the target offset against
* uncompressed_file_offset or uncompressed_stream_offset,
* and possibly decode and throw away some amount of data
* before reaching the target offset.
*/
lzma_vli uncompressed_file_offset;
/**
* \brief Block number in this Stream
*
* The first Block is 1.
*/
lzma_vli number_in_stream;
/**
* \brief Compressed start offset of this Block
*
* This offset is relative to the beginning of the Stream
* containing this Block.
*/
lzma_vli compressed_stream_offset;
/**
* \brief Uncompressed start offset of this Block
*
* This offset is relative to the beginning of the Stream
* containing this Block.
*/
lzma_vli uncompressed_stream_offset;
/**
* \brief Uncompressed size of this Block
*
* You should pass this to the Block decoder if you will
* decode this Block. It will allow the Block decoder to
* validate the uncompressed size.
*/
lzma_vli uncompressed_size;
/**
* \brief Unpadded size of this Block
*
* You should pass this to the Block decoder if you will
* decode this Block. It will allow the Block decoder to
* validate the unpadded size.
*/
lzma_vli unpadded_size;
/**
* \brief Total compressed size
*
* This includes all headers and padding in this Block.
* This is useful if you need to know how many bytes
* the Block decoder will actually read.
*/
lzma_vli total_size;
lzma_vli reserved_vli1;
lzma_vli reserved_vli2;
lzma_vli reserved_vli3;
lzma_vli reserved_vli4;
const void *reserved_ptr1;
const void *reserved_ptr2;
const void *reserved_ptr3;
const void *reserved_ptr4;
}
BlockStruct block;
/*
* Internal data which is used to store the state of the iterator.
* The exact format may vary between liblzma versions, so don't
* touch these in any way.
*/
union InternalData
{
const void *p;
size_t s;
lzma_vli v;
}
InternalData[6] internal;
}
/**
* Operation mode for lzma_index_iter_next()
*/
enum lzma_index_iter_mode
{
LZMA_INDEX_ITER_ANY = 0,
/**<
* \brief Get the next Block or Stream
*
* Go to the next Block if the current Stream has at least
* one Block left. Otherwise go to the next Stream even if
* it has no Blocks. If the Stream has no Blocks
* (lzma_index_iter.stream.block_count == 0),
* lzma_index_iter.block will have undefined values.
*/
LZMA_INDEX_ITER_STREAM = 1,
/**<
* \brief Get the next Stream
*
* Go to the next Stream even if the current Stream has
* unread Blocks left. If the next Stream has at least one
* Block, the iterator will point to the first Block.
* If there are no Blocks, lzma_index_iter.block will have
* undefined values.
*/
LZMA_INDEX_ITER_BLOCK = 2,
/**<
* \brief Get the next Block
*
* Go to the next Block if the current Stream has at least
* one Block left. If the current Stream has no Blocks left,
* the next Stream with at least one Block is located and
* the iterator will be made to point to the first Block of
* that Stream.
*/
LZMA_INDEX_ITER_NONEMPTY_BLOCK = 3
}
/**
* \brief Calculate memory usage of lzma_index
*
* On disk, the size of the Index field depends on both the number of Records
* stored and how big values the Records store (due to variable-length integer
* encoding). When the Index is kept in lzma_index structure, the memory usage
* depends only on the number of Records/Blocks stored in the Index(es), and
* in case of concatenated lzma_indexes, the number of Streams. The size in
* RAM is almost always significantly bigger than in the encoded form on disk.
*
* This function calculates an approximate amount of memory needed hold
* the given number of Streams and Blocks in lzma_index structure. This
* value may vary between CPU architectures and also between liblzma versions
* if the internal implementation is modified.
*/
nothrow ulong lzma_index_memusage(
lzma_vli streams, lzma_vli blocks);
/**
* \brief Calculate the memory usage of an existing lzma_index
*
* This is a shorthand for lzma_index_memusage(lzma_index_stream_count(i),
* lzma_index_block_count(i)).
*/
nothrow ulong lzma_index_memused(const lzma_index *i);
/**
* \brief Allocate and initialize a new lzma_index structure
*
* \return On success, a pointer to an empty initialized lzma_index is
* returned. If allocation fails, NULL is returned.
*/
nothrow lzma_index* lzma_index_init(lzma_allocator *allocator);
/**
* \brief Deallocate lzma_index
*
* If i is NULL, this does nothing.
*/
nothrow void lzma_index_end(lzma_index *i, lzma_allocator *allocator);
/**
* \brief Add a new Block to lzma_index
*
* \param i Pointer to a lzma_index structure
* \param allocator Pointer to lzma_allocator, or NULL to
* use malloc()
* \param unpadded_size Unpadded Size of a Block. This can be
* calculated with lzma_block_unpadded_size()
* after encoding or decoding the Block.
* \param uncompressed_size Uncompressed Size of a Block. This can be
* taken directly from lzma_block structure
* after encoding or decoding the Block.
*
* Appending a new Block does not invalidate iterators. For example,
* if an iterator was pointing to the end of the lzma_index, after
* lzma_index_append() it is possible to read the next Block with
* an existing iterator.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR: Compressed or uncompressed size of the
* Stream or size of the Index field would grow too big.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_append(
lzma_index *i, lzma_allocator *allocator,
lzma_vli unpadded_size, lzma_vli uncompressed_size);
/**
* \brief Set the Stream Flags
*
* Set the Stream Flags of the last (and typically the only) Stream
* in lzma_index. This can be useful when reading information from the
* lzma_index, because to decode Blocks, knowing the integrity check type
* is needed.
*
* The given Stream Flags are copied into internal preallocated structure
* in the lzma_index, thus the caller doesn't need to keep the *stream_flags
* available after calling this function.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR: Unsupported stream_flags->version.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_stream_flags(
lzma_index *i, const lzma_stream_flags *stream_flags);
/**
* \brief Get the types of integrity Checks
*
* If lzma_index_stream_flags() is used to set the Stream Flags for
* every Stream, lzma_index_checks() can be used to get a bitmask to
* indicate which Check types have been used. It can be useful e.g. if
* showing the Check types to the user.
*
* The bitmask is 1 << check_id, e.g. CRC32 is 1 << 1 and SHA-256 is 1 << 10.
*/
nothrow pure uint lzma_index_checks(const lzma_index *i);
/**
* \brief Set the amount of Stream Padding
*
* Set the amount of Stream Padding of the last (and typically the only)
* Stream in the lzma_index. This is needed when planning to do random-access
* reading within multiple concatenated Streams.
*
* By default, the amount of Stream Padding is assumed to be zero bytes.
*
* \return - LZMA_OK
* - LZMA_DATA_ERROR: The file size would grow too big.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_stream_padding(
lzma_index *i, lzma_vli stream_padding);
/**
* \brief Get the number of Streams
*/
nothrow pure lzma_vli lzma_index_stream_count(const lzma_index *i);
/**
* \brief Get the number of Blocks
*
* This returns the total number of Blocks in lzma_index. To get number
* of Blocks in individual Streams, use lzma_index_iter.
*/
nothrow pure lzma_vli lzma_index_block_count(const lzma_index *i);
/**
* \brief Get the size of the Index field as bytes
*
* This is needed to verify the Backward Size field in the Stream Footer.
*/
nothrow pure lzma_vli lzma_index_size(const lzma_index *i);
/**
* \brief Get the total size of the Stream
*
* If multiple lzma_indexes have been combined, this works as if the Blocks
* were in a single Stream. This is useful if you are going to combine
* Blocks from multiple Streams into a single new Stream.
*/
nothrow pure lzma_vli lzma_index_stream_size(const lzma_index *i);
/**
* \brief Get the total size of the Blocks
*
* This doesn't include the Stream Header, Stream Footer, Stream Padding,
* or Index fields.
*/
nothrow pure lzma_vli lzma_index_total_size(const lzma_index *i);
/**
* \brief Get the total size of the file
*
* When no lzma_indexes have been combined with lzma_index_cat() and there is
* no Stream Padding, this function is identical to lzma_index_stream_size().
* If multiple lzma_indexes have been combined, this includes also the headers
* of each separate Stream and the possible Stream Padding fields.
*/
nothrow pure lzma_vli lzma_index_file_size(const lzma_index *i);
/**
* \brief Get the uncompressed size of the file
*/
nothrow pure lzma_vli lzma_index_uncompressed_size(const lzma_index *i);
/**
* \brief Initialize an iterator
*
* \param iter Pointer to a lzma_index_iter structure
* \param i lzma_index to which the iterator will be associated
*
* This function associates the iterator with the given lzma_index, and calls
* lzma_index_iter_rewind() on the iterator.
*
* This function doesn't allocate any memory, thus there is no
* lzma_index_iter_end(). The iterator is valid as long as the
* associated lzma_index is valid, that is, until lzma_index_end() or
* using it as source in lzma_index_cat(). Specifically, lzma_index doesn't
* become invalid if new Blocks are added to it with lzma_index_append() or
* if it is used as the destination in lzma_index_cat().
*
* It is safe to make copies of an initialized lzma_index_iter, for example,
* to easily restart reading at some particular position.
*/
nothrow void lzma_index_iter_init(
lzma_index_iter *iter, const lzma_index *i);
/**
* \brief Rewind the iterator
*
* Rewind the iterator so that next call to lzma_index_iter_next() will
* return the first Block or Stream.
*/
nothrow void lzma_index_iter_rewind(lzma_index_iter *iter);
/**
* \brief Get the next Block or Stream
*
* \param iter Iterator initialized with lzma_index_iter_init()
* \param mode Specify what kind of information the caller wants
* to get. See lzma_index_iter_mode for details.
*
* \return If next Block or Stream matching the mode was found, *iter
* is updated and this function returns false. If no Block or
* Stream matching the mode is found, *iter is not modified
* and this function returns true. If mode is set to an unknown
* value, *iter is not modified and this function returns true.
*/
nothrow lzma_bool lzma_index_iter_next(
lzma_index_iter *iter, lzma_index_iter_mode mode);
/**
* \brief Locate a Block
*
* If it is possible to seek in the .xz file, it is possible to parse
* the Index field(s) and use lzma_index_iter_locate() to do random-access
* reading with granularity of Block size.
*
* \param iter Iterator that was earlier initialized with
* lzma_index_iter_init().
* \param target Uncompressed target offset which the caller would
* like to locate from the Stream
*
* If the target is smaller than the uncompressed size of the Stream (can be
* checked with lzma_index_uncompressed_size()):
* - Information about the Stream and Block containing the requested
* uncompressed offset is stored into *iter.
* - Internal state of the iterator is adjusted so that
* lzma_index_iter_next() can be used to read subsequent Blocks or Streams.
* - This function returns false.
*
* If target is greater than the uncompressed size of the Stream, *iter
* is not modified, and this function returns true.
*/
nothrow lzma_bool lzma_index_iter_locate(
lzma_index_iter *iter, lzma_vli target);
/**
* \brief Concatenate lzma_indexes
*
* Concatenating lzma_indexes is useful when doing random-access reading in
* multi-Stream .xz file, or when combining multiple Streams into single
* Stream.
*
* \param dest lzma_index after which src is appended
* \param src lzma_index to be appended after dest. If this
* function succeeds, the memory allocated for src
* is freed or moved to be part of dest, and all
* iterators pointing to src will become invalid.
* \param allocator Custom memory allocator; can be NULL to use
* malloc() and free().
*
* \return - LZMA_OK: lzma_indexes were concatenated successfully.
* src is now a dangling pointer.
* - LZMA_DATA_ERROR: *dest would grow too big.
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_cat(
lzma_index *dest, lzma_index *src, lzma_allocator *allocator);
/**
* \brief Duplicate lzma_index
*
* \return A copy of the lzma_index, or NULL if memory allocation failed.
*/
nothrow lzma_index * lzma_index_dup(
const lzma_index *i, lzma_allocator *allocator);
/**
* \brief Initialize .xz Index encoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param i Pointer to lzma_index which should be encoded.
*
* The valid `action' values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* It is enough to use only one of them (you can choose freely; use LZMA_RUN
* to support liblzma versions older than 5.0.0).
*
* \return - LZMA_OK: Initialization succeeded, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_encoder(
lzma_stream *strm, const lzma_index *i);
/**
* \brief Initialize .xz Index decoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param i The decoded Index will be made available via
* this pointer. Initially this function will
* set *i to NULL (the old value is ignored). If
* decoding succeeds (lzma_code() returns
* LZMA_STREAM_END), *i will be set to point
* to a new lzma_index, which the application
* has to later free with lzma_index_end().
* \param memlimit How much memory the resulting lzma_index is
* allowed to require.
*
* The valid `action' values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* It is enough to use only one of them (you can choose freely; use LZMA_RUN
* to support liblzma versions older than 5.0.0).
*
* \return - LZMA_OK: Initialization succeeded, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_decoder(
lzma_stream *strm, lzma_index **i, ulong memlimit);
/**
* \brief Single-call .xz Index encoder
*
* \param i lzma_index to be encoded
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Output buffer is too small. Use
* lzma_index_size() to find out how much output
* space is needed.
* - LZMA_PROG_ERROR
*
* \note This function doesn't take allocator argument since all
* the internal data is allocated on stack.
*/
nothrow lzma_ret lzma_index_buffer_encode(const lzma_index *i,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Single-call .xz Index decoder
*
* \param i If decoding succeeds, *i will point to a new
* lzma_index, which the application has to
* later free with lzma_index_end(). If an error
* occurs, *i will be NULL. The old value of *i
* is always ignored and thus doesn't need to be
* initialized by the caller.
* \param memlimit Pointer to how much memory the resulting
* lzma_index is allowed to require. The value
* pointed by this pointer is modified if and only
* if LZMA_MEMLIMIT_ERROR is returned.
* \param allocator Pointer to lzma_allocator, or NULL to use malloc()
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR: Memory usage limit was reached.
* The minimum required memlimit value was stored to *memlimit.
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_buffer_decode(lzma_index **i,
ulong *memlimit, lzma_allocator *allocator,
const ubyte *in_, size_t *in_pos, size_t in_size);
+103
View File
@@ -0,0 +1,103 @@
/**
* \file lzma/index_hash.h
* \brief Validate Index by using a hash function
*
* Hashing makes it possible to use constant amount of memory to validate
* Index of arbitrary size.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.index_hash;
import deimos.lzma;
extern(C):
/**
* \brief Opaque data type to hold the Index hash
*/
struct lzma_index_hash {};
/**
* \brief Allocate and initialize a new lzma_index_hash structure
*
* If index_hash is NULL, a new lzma_index_hash structure is allocated,
* initialized, and a pointer to it returned. If allocation fails, NULL
* is returned.
*
* If index_hash is non-NULL, it is reinitialized and the same pointer
* returned. In this case, return value cannot be NULL or a different
* pointer than the index_hash that was given as an argument.
*/
nothrow lzma_index_hash * lzma_index_hash_init(
lzma_index_hash *index_hash, lzma_allocator *allocator);
/**
* \brief Deallocate lzma_index_hash structure
*/
nothrow void lzma_index_hash_end(
lzma_index_hash *index_hash, lzma_allocator *allocator);
/**
* \brief Add a new Record to an Index hash
*
* \param index Pointer to a lzma_index_hash structure
* \param unpadded_size Unpadded Size of a Block
* \param uncompressed_size Uncompressed Size of a Block
*
* \return - LZMA_OK
* - LZMA_DATA_ERROR: Compressed or uncompressed size of the
* Stream or size of the Index field would grow too big.
* - LZMA_PROG_ERROR: Invalid arguments or this function is being
* used when lzma_index_hash_decode() has already been used.
*/
nothrow lzma_ret lzma_index_hash_append(lzma_index_hash *index_hash,
lzma_vli unpadded_size, lzma_vli uncompressed_size);
/**
* \brief Decode and validate the Index field
*
* After telling the sizes of all Blocks with lzma_index_hash_append(),
* the actual Index field is decoded with this function. Specifically,
* once decoding of the Index field has been started, no more Records
* can be added using lzma_index_hash_append().
*
* This function doesn't use lzma_stream structure to pass the input data.
* Instead, the input buffer is specified using three arguments. This is
* because it matches better the internal APIs of liblzma.
*
* \param index_hash Pointer to a lzma_index_hash structure
* \param in Pointer to the beginning of the input buffer
* \param in_pos in[*in_pos] is the next byte to process
* \param in_size in[in_size] is the first byte not to process
*
* \return - LZMA_OK: So far good, but more input is needed.
* - LZMA_STREAM_END: Index decoded successfully and it matches
* the Records given with lzma_index_hash_append().
* - LZMA_DATA_ERROR: Index is corrupt or doesn't match the
* information given with lzma_index_hash_append().
* - LZMA_BUF_ERROR: Cannot progress because *in_pos >= in_size.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_hash_decode(lzma_index_hash *index_hash,
const ubyte *in_, size_t *in_pos, size_t in_size);
/**
* \brief Get the size of the Index field as bytes
*
* This is needed to verify the Backward Size field in the Stream Footer.
*/
nothrow pure lzma_vli lzma_index_hash_size(
const lzma_index_hash *index_hash);
+422
View File
@@ -0,0 +1,422 @@
/**
* \file lzma/lzma.h
* \brief LZMA1 and LZMA2 filters
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.lzma;
import deimos.lzma;
extern(C):
/**
* \brief LZMA1 Filter ID
*
* LZMA1 is the very same thing as what was called just LZMA in LZMA Utils,
* 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from
* accidentally using LZMA when they actually want LZMA2.
*
* LZMA1 shouldn't be used for new applications unless you _really_ know
* what you are doing. LZMA2 is almost always a better choice.
*/
enum LZMA_FILTER_LZMA1 = 0x4000000000000001UL;
/**
* \brief LZMA2 Filter ID
*
* Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds
* support for LZMA_SYNC_FLUSH, uncompressed chunks (smaller expansion
* when trying to compress uncompressible data), possibility to change
* lc/lp/pb in the middle of encoding, and some other internal improvements.
*/
enum LZMA_FILTER_LZMA2 = 0x21UL;
/**
* \brief Match finders
*
* Match finder has major effect on both speed and compression ratio.
* Usually hash chains are faster than binary trees.
*
* If you will use LZMA_SYNC_FLUSH often, the hash chains may be a better
* choice, because binary trees get much higher compression ratio penalty
* with LZMA_SYNC_FLUSH.
*
* The memory usage formulas are only rough estimates, which are closest to
* reality when dict_size is a power of two. The formulas are more complex
* in reality, and can also change a little between liblzma versions. Use
* lzma_raw_encoder_memusage() to get more accurate estimate of memory usage.
*/
enum lzma_match_finder
{
LZMA_MF_HC3 = 0x03,
/**<
* \brief Hash Chain with 2- and 3-byte hashing
*
* Minimum nice_len: 3
*
* Memory usage:
* - dict_size <= 16 MiB: dict_size * 7.5
* - dict_size > 16 MiB: dict_size * 5.5 + 64 MiB
*/
LZMA_MF_HC4 = 0x04,
/**<
* \brief Hash Chain with 2-, 3-, and 4-byte hashing
*
* Minimum nice_len: 4
*
* Memory usage:
* - dict_size <= 32 MiB: dict_size * 7.5
* - dict_size > 32 MiB: dict_size * 6.5
*/
LZMA_MF_BT2 = 0x12,
/**<
* \brief Binary Tree with 2-byte hashing
*
* Minimum nice_len: 2
*
* Memory usage: dict_size * 9.5
*/
LZMA_MF_BT3 = 0x13,
/**<
* \brief Binary Tree with 2- and 3-byte hashing
*
* Minimum nice_len: 3
*
* Memory usage:
* - dict_size <= 16 MiB: dict_size * 11.5
* - dict_size > 16 MiB: dict_size * 9.5 + 64 MiB
*/
LZMA_MF_BT4 = 0x14
/**<
* \brief Binary Tree with 2-, 3-, and 4-byte hashing
*
* Minimum nice_len: 4
*
* Memory usage:
* - dict_size <= 32 MiB: dict_size * 11.5
* - dict_size > 32 MiB: dict_size * 10.5
*/
}
/**
* \brief Test if given match finder is supported
*
* Return true if the given match finder is supported by this liblzma build.
* Otherwise false is returned. It is safe to call this with a value that
* isn't listed in lzma_match_finder enumeration; the return value will be
* false.
*
* There is no way to list which match finders are available in this
* particular liblzma version and build. It would be useless, because
* a new match finder, which the application developer wasn't aware,
* could require giving additional options to the encoder that the older
* match finders don't need.
*/
nothrow lzma_bool lzma_mf_is_supported(lzma_match_finder match_finder);
/**
* \brief Compression modes
*
* This selects the function used to analyze the data produced by the match
* finder.
*/
enum lzma_mode
{
LZMA_MODE_FAST = 1,
/**<
* \brief Fast compression
*
* Fast mode is usually at its best when combined with
* a hash chain match finder.
*/
LZMA_MODE_NORMAL = 2
/**<
* \brief Normal compression
*
* This is usually notably slower than fast mode. Use this
* together with binary tree match finders to expose the
* full potential of the LZMA1 or LZMA2 encoder.
*/
}
/**
* \brief Test if given compression mode is supported
*
* Return true if the given compression mode is supported by this liblzma
* build. Otherwise false is returned. It is safe to call this with a value
* that isn't listed in lzma_mode enumeration; the return value will be false.
*
* There is no way to list which modes are available in this particular
* liblzma version and build. It would be useless, because a new compression
* mode, which the application developer wasn't aware, could require giving
* additional options to the encoder that the older modes don't need.
*/
nothrow lzma_bool lzma_mode_is_supported(lzma_mode mode);
/**
* \brief Options specific to the LZMA1 and LZMA2 filters
*
* Since LZMA1 and LZMA2 share most of the code, it's simplest to share
* the options structure too. For encoding, all but the reserved variables
* need to be initialized unless specifically mentioned otherwise.
* lzma_lzma_preset() can be used to get a good starting point.
*
* For raw decoding, both LZMA1 and LZMA2 need dict_size, preset_dict, and
* preset_dict_size (if preset_dict != NULL). LZMA1 needs also lc, lp, and pb.
*/
struct lzma_options_lzma
{
/**
* \brief Dictionary size in bytes
*
* Dictionary size indicates how many bytes of the recently processed
* uncompressed data is kept in memory. One method to reduce size of
* the uncompressed data is to store distance-length pairs, which
* indicate what data to repeat from the dictionary buffer. Thus,
* the bigger the dictionary, the better the compression ratio
* usually is.
*
* Maximum size of the dictionary depends on multiple things:
* - Memory usage limit
* - Available address space (not a problem on 64-bit systems)
* - Selected match finder (encoder only)
*
* Currently the maximum dictionary size for encoding is 1.5 GiB
* (i.e. (UINT32_C(1) << 30) + (UINT32_C(1) << 29)) even on 64-bit
* systems for certain match finder implementation reasons. In the
* future, there may be match finders that support bigger
* dictionaries.
*
* Decoder already supports dictionaries up to 4 GiB - 1 B (i.e.
* UINT32_MAX), so increasing the maximum dictionary size of the
* encoder won't cause problems for old decoders.
*
* Because extremely small dictionaries sizes would have unneeded
* overhead in the decoder, the minimum dictionary size is 4096 bytes.
*
* \note When decoding, too big dictionary does no other harm
* than wasting memory.
*/
uint dict_size;
enum LZMA_DICT_SIZE_MIN = 4096U;
enum LZMA_DICT_SIZE_DEFAULT = (1U << 23);
/**
* \brief Pointer to an initial dictionary
*
* It is possible to initialize the LZ77 history window using
* a preset dictionary. It is useful when compressing many
* similar, relatively small chunks of data independently from
* each other. The preset dictionary should contain typical
* strings that occur in the files being compressed. The most
* probable strings should be near the end of the preset dictionary.
*
* This feature should be used only in special situations. For
* now, it works correctly only with raw encoding and decoding.
* Currently none of the container formats supported by
* liblzma allow preset dictionary when decoding, thus if
* you create a .xz or .lzma file with preset dictionary, it
* cannot be decoded with the regular decoder functions. In the
* future, the .xz format will likely get support for preset
* dictionary though.
*/
const ubyte *preset_dict;
/**
* \brief Size of the preset dictionary
*
* Specifies the size of the preset dictionary. If the size is
* bigger than dict_size, only the last dict_size bytes are
* processed.
*
* This variable is read only when preset_dict is not NULL.
* If preset_dict is not NULL but preset_dict_size is zero,
* no preset dictionary is used (identical to only setting
* preset_dict to NULL).
*/
uint preset_dict_size;
/**
* \brief Number of literal context bits
*
* How many of the highest bits of the previous uncompressed
* eight-bit byte (also known as `literal') are taken into
* account when predicting the bits of the next literal.
*
* E.g. in typical English text, an upper-case letter is
* often followed by a lower-case letter, and a lower-case
* letter is usually followed by another lower-case letter.
* In the US-ASCII character set, the highest three bits are 010
* for upper-case letters and 011 for lower-case letters.
* When lc is at least 3, the literal coding can take advantage of
* this property in the uncompressed data.
*
* There is a limit that applies to literal context bits and literal
* position bits together: lc + lp <= 4. Without this limit the
* decoding could become very slow, which could have security related
* results in some cases like email servers doing virus scanning.
* This limit also simplifies the internal implementation in liblzma.
*
* There may be LZMA1 streams that have lc + lp > 4 (maximum possible
* lc would be 8). It is not possible to decode such streams with
* liblzma.
*/
uint lc;
enum LZMA_LCLP_MIN = 0;
enum LZMA_LCLP_MAX = 4;
enum LZMA_LC_DEFAULT = 3;
/**
* \brief Number of literal position bits
*
* lp affects what kind of alignment in the uncompressed data is
* assumed when encoding literals. A literal is a single 8-bit byte.
* See pb below for more information about alignment.
*/
uint lp;
enum LZMA_LP_DEFAULT = 0;
/**
* \brief Number of position bits
*
* pb affects what kind of alignment in the uncompressed data is
* assumed in general. The default means four-byte alignment
* (2^ pb =2^2=4), which is often a good choice when there's
* no better guess.
*
* When the aligment is known, setting pb accordingly may reduce
* the file size a little. E.g. with text files having one-byte
* alignment (US-ASCII, ISO-8859-*, UTF-8), setting pb=0 can
* improve compression slightly. For UTF-16 text, pb=1 is a good
* choice. If the alignment is an odd number like 3 bytes, pb=0
* might be the best choice.
*
* Even though the assumed alignment can be adjusted with pb and
* lp, LZMA1 and LZMA2 still slightly favor 16-byte alignment.
* It might be worth taking into account when designing file formats
* that are likely to be often compressed with LZMA1 or LZMA2.
*/
uint pb;
enum LZMA_PB_MIN = 0;
enum LZMA_PB_MAX = 4;
enum LZMA_PB_DEFAULT = 2;
/** Compression mode */
lzma_mode mode;
/**
* \brief Nice length of a match
*
* This determines how many bytes the encoder compares from the match
* candidates when looking for the best match. Once a match of at
* least nice_len bytes long is found, the encoder stops looking for
* better candidates and encodes the match. (Naturally, if the found
* match is actually longer than nice_len, the actual length is
* encoded; it's not truncated to nice_len.)
*
* Bigger values usually increase the compression ratio and
* compression time. For most files, 32 to 128 is a good value,
* which gives very good compression ratio at good speed.
*
* The exact minimum value depends on the match finder. The maximum
* is 273, which is the maximum length of a match that LZMA1 and
* LZMA2 can encode.
*/
uint nice_len;
/** Match finder ID */
lzma_match_finder mf;
/**
* \brief Maximum search depth in the match finder
*
* For every input byte, match finder searches through the hash chain
* or binary tree in a loop, each iteration going one step deeper in
* the chain or tree. The searching stops if
* - a match of at least nice_len bytes long is found;
* - all match candidates from the hash chain or binary tree have
* been checked; or
* - maximum search depth is reached.
*
* Maximum search depth is needed to prevent the match finder from
* wasting too much time in case there are lots of short match
* candidates. On the other hand, stopping the search before all
* candidates have been checked can reduce compression ratio.
*
* Setting depth to zero tells liblzma to use an automatic default
* value, that depends on the selected match finder and nice_len.
* The default is in the range [4, 200] or so (it may vary between
* liblzma versions).
*
* Using a bigger depth value than the default can increase
* compression ratio in some cases. There is no strict maximum value,
* but high values (thousands or millions) should be used with care:
* the encoder could remain fast enough with typical input, but
* malicious input could cause the match finder to slow down
* dramatically, possibly creating a denial of service attack.
*/
uint depth;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* with the currently supported options, so it is safe to leave these
* uninitialized.
*/
uint reserved_int1;
uint reserved_int2;
uint reserved_int3;
uint reserved_int4;
uint reserved_int5;
uint reserved_int6;
uint reserved_int7;
uint reserved_int8;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
void *reserved_ptr1;
void *reserved_ptr2;
}
/**
* \brief Set a compression preset to lzma_options_lzma structure
*
* 0 is the fastest and 9 is the slowest. These match the switches -0 .. -9
* of the xz command line tool. In addition, it is possible to bitwise-or
* flags to the preset. Currently only LZMA_PRESET_EXTREME is supported.
* The flags are defined in container.h, because the flags are used also
* with lzma_easy_encoder().
*
* The preset values are subject to changes between liblzma versions.
*
* This function is available only if LZMA1 or LZMA2 encoder has been enabled
* when building liblzma.
*
* \return On success, false is returned. If the preset is not
* supported, true is returned.
*/
nothrow lzma_bool lzma_lzma_preset(
lzma_options_lzma *options, uint preset);
+219
View File
@@ -0,0 +1,219 @@
/**
* \file lzma/stream_flags.h
* \brief .xz Stream Header and Stream Footer encoder and decoder
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.stream_flags;
import deimos.lzma;
extern(C):
/**
* \brief Size of Stream Header and Stream Footer
*
* Stream Header and Stream Footer have the same size and they are not
* going to change even if a newer version of the .xz file format is
* developed in future.
*/
enum LZMA_STREAM_HEADER_SIZE = 12;
/**
* \brief Options for encoding/decoding Stream Header and Stream Footer
*/
struct lzma_stream_flags
{
/**
* \brief Stream Flags format version
*
* To prevent API and ABI breakages if new features are needed in
* Stream Header or Stream Footer, a version number is used to
* indicate which fields in this structure are in use. For now,
* version must always be zero. With non-zero version, the
* lzma_stream_header_encode() and lzma_stream_footer_encode()
* will return LZMA_OPTIONS_ERROR.
*
* lzma_stream_header_decode() and lzma_stream_footer_decode()
* will always set this to the lowest value that supports all the
* features indicated by the Stream Flags field. The application
* must check that the version number set by the decoding functions
* is supported by the application. Otherwise it is possible that
* the application will decode the Stream incorrectly.
*/
uint version_;
/**
* \brief Backward Size
*
* Backward Size must be a multiple of four bytes. In this Stream
* format version, Backward Size is the size of the Index field.
*
* Backward Size isn't actually part of the Stream Flags field, but
* it is convenient to include in this structure anyway. Backward
* Size is present only in the Stream Footer. There is no need to
* initialize backward_size when encoding Stream Header.
*
* lzma_stream_header_decode() always sets backward_size to
* LZMA_VLI_UNKNOWN so that it is convenient to use
* lzma_stream_flags_compare() when both Stream Header and Stream
* Footer have been decoded.
*/
lzma_vli backward_size;
enum LZMA_BACKWARD_SIZE_MIN = 4;
enum LZMA_BACKWARD_SIZE_MAX = (1UL << 34);
/**
* \brief Check ID
*
* This indicates the type of the integrity check calculated from
* uncompressed data.
*/
lzma_check check;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the
* names of these variables may change.
*
* (We will never be able to use all of these since Stream Flags
* is just two bytes plus Backward Size of four bytes. But it's
* nice to have the proper types when they are needed.)
*/
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
lzma_bool reserved_bool1;
lzma_bool reserved_bool2;
lzma_bool reserved_bool3;
lzma_bool reserved_bool4;
lzma_bool reserved_bool5;
lzma_bool reserved_bool6;
lzma_bool reserved_bool7;
lzma_bool reserved_bool8;
uint reserved_int1;
uint reserved_int2;
}
/**
* \brief Encode Stream Header
*
* \param options Stream Header options to be encoded.
* options->backward_size is ignored and doesn't
* need to be initialized.
* \param out Beginning of the output buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: options->version is not supported by
* this liblzma version.
* - LZMA_PROG_ERROR: Invalid options.
*/
nothrow lzma_ret lzma_stream_header_encode(
const lzma_stream_flags *options, ubyte *out_);
/**
* \brief Encode Stream Footer
*
* \param options Stream Footer options to be encoded.
* \param out Beginning of the output buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: options->version is not supported by
* this liblzma version.
* - LZMA_PROG_ERROR: Invalid options.
*/
nothrow lzma_ret lzma_stream_footer_encode(
const lzma_stream_flags *options, ubyte *out_);
/**
* \brief Decode Stream Header
*
* \param options Target for the decoded Stream Header options.
* \param in Beginning of the input buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* options->backward_size is always set to LZMA_VLI_UNKNOWN. This is to
* help comparing Stream Flags from Stream Header and Stream Footer with
* lzma_stream_flags_compare().
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR: Magic bytes don't match, thus the given
* buffer cannot be Stream Header.
* - LZMA_DATA_ERROR: CRC32 doesn't match, thus the header
* is corrupt.
* - LZMA_OPTIONS_ERROR: Unsupported options are present
* in the header.
*
* \note When decoding .xz files that contain multiple Streams, it may
* make sense to print "file format not recognized" only if
* decoding of the Stream Header of the _first_ Stream gives
* LZMA_FORMAT_ERROR. If non-first Stream Header gives
* LZMA_FORMAT_ERROR, the message used for LZMA_DATA_ERROR is
* probably more appropriate.
*
* For example, Stream decoder in liblzma uses LZMA_DATA_ERROR if
* LZMA_FORMAT_ERROR is returned by lzma_stream_header_decode()
* when decoding non-first Stream.
*/
nothrow lzma_ret lzma_stream_header_decode(
lzma_stream_flags *options, const ubyte *in_);
/**
* \brief Decode Stream Footer
*
* \param options Target for the decoded Stream Header options.
* \param in Beginning of the input buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR: Magic bytes don't match, thus the given
* buffer cannot be Stream Footer.
* - LZMA_DATA_ERROR: CRC32 doesn't match, thus the Stream Footer
* is corrupt.
* - LZMA_OPTIONS_ERROR: Unsupported options are present
* in Stream Footer.
*
* \note If Stream Header was already decoded successfully, but
* decoding Stream Footer returns LZMA_FORMAT_ERROR, the
* application should probably report some other error message
* than "file format not recognized", since the file more likely
* is corrupt (possibly truncated). Stream decoder in liblzma
* uses LZMA_DATA_ERROR in this situation.
*/
nothrow lzma_ret lzma_stream_footer_decode(
lzma_stream_flags *options, const ubyte *in_);
/**
* \brief Compare two lzma_stream_flags structures
*
* backward_size values are compared only if both are not
* LZMA_VLI_UNKNOWN.
*
* \return - LZMA_OK: Both are equal. If either had backward_size set
* to LZMA_VLI_UNKNOWN, backward_size values were not
* compared or validated.
* - LZMA_DATA_ERROR: The structures differ.
* - LZMA_OPTIONS_ERROR: version in either structure is greater
* than the maximum supported version (currently zero).
* - LZMA_PROG_ERROR: Invalid value, e.g. invalid check or
* backward_size.
*/
nothrow pure lzma_ret lzma_stream_flags_compare(
const lzma_stream_flags *a, const lzma_stream_flags *b);
+108
View File
@@ -0,0 +1,108 @@
/**
* \file lzma/version.h
* \brief Version number
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.version_;
import deimos.lzma;
import std.conv;
extern(C):
/*
* Version number split into components
*/
enum LZMA_VERSION_MAJOR = 5;
enum LZMA_VERSION_MINOR = 0;
enum LZMA_VERSION_PATCH = 3;
enum LZMA_VERSION_STABILITY = LZMA_VERSION_STABILITY_STABLE;
/*
#ifndef LZMA_VERSION_COMMIT
# define LZMA_VERSION_COMMIT ""
#endif*/
enum LZMA_VERSION_COMMIT = "";
/*
* Map symbolic stability levels to integers.
*/
enum LZMA_VERSION_STABILITY_ALPHA = 0;
enum LZMA_VERSION_STABILITY_BETA = 1;
enum LZMA_VERSION_STABILITY_STABLE = 2;
/**
* \brief Compile-time version number
*
* The version number is of format xyyyzzzs where
* - x = major
* - yyy = minor
* - zzz = revision
* - s indicates stability: 0 = alpha, 1 = beta, 2 = stable
*
* The same xyyyzzz triplet is never reused with different stability levels.
* For example, if 5.1.0alpha has been released, there will never be 5.1.0beta
* or 5.1.0 stable.
*
* \note The version number of liblzma has nothing to with
* the version number of Igor Pavlov's LZMA SDK.
*/
enum LZMA_VERSION = (LZMA_VERSION_MAJOR * 10000000U
+ LZMA_VERSION_MINOR * 10000U
+ LZMA_VERSION_PATCH * 10U
+ LZMA_VERSION_STABILITY);
/*
* Macros to construct the compile-time version string
*/
static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_ALPHA)
enum LZMA_VERSION_STABILITY_STRING = "alpha";
else static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_BETA)
enum LZMA_VERSION_STABILITY_STRING = "beta";
else static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_STABLE)
enum LZMA_VERSION_STABILITY_STRING = "";
else
static assert(false, "Incorrect LZMA_VERSION_STABILITY");
/**
* \brief Compile-time version as a string
*
* This can be for example "4.999.5alpha", "4.999.8beta", or "5.0.0" (stable
* versions don't have any "stable" suffix). In future, a snapshot built
* from source code repository may include an additional suffix, for example
* "4.999.8beta-21-g1d92". The commit ID won't be available in numeric form
* in LZMA_VERSION macro.
*/
enum LZMA_VERSION_STRING =
to!string(LZMA_VERSION_MAJOR) ~ "." ~ to!string(LZMA_VERSION_MINOR) ~
"." ~ to!string(LZMA_VERSION_PATCH) ~ LZMA_VERSION_STABILITY_STRING ~
LZMA_VERSION_COMMIT;
/**
* \brief Run-time version number as an integer
*
* Return the value of LZMA_VERSION macro at the compile time of liblzma.
* This allows the application to compare if it was built against the same,
* older, or newer version of liblzma that is currently running.
*/
nothrow uint lzma_version_number();
/**
* \brief Run-time version as a string
*
* This function may be useful if you want to display which version of
* liblzma your application is currently using.
*/
nothrow immutable(char)* lzma_version_string();
+167
View File
@@ -0,0 +1,167 @@
/**
* \file lzma/vli.h
* \brief Variable-length integer handling
*
* In the .xz format, most integers are encoded in a variable-length
* representation, which is sometimes called little endian base-128 encoding.
* This saves space when smaller values are more likely than bigger values.
*
* The encoding scheme encodes seven bits to every byte, using minimum
* number of bytes required to represent the given value. Encodings that use
* non-minimum number of bytes are invalid, thus every integer has exactly
* one encoded representation. The maximum number of bits in a VLI is 63,
* thus the vli argument must be less than or equal to UINT64_MAX / 2. You
* should use LZMA_VLI_MAX for clarity.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.vli;
import deimos.lzma;
extern(C):
/**
* \brief Maximum supported value of a variable-length integer
*/
enum LZMA_VLI_MAX = (ulong.max / 2);
/**
* \brief VLI value to denote that the value is unknown
*/
enum LZMA_VLI_UNKNOWN = ulong.max;
/**
* \brief Maximum supported encoded length of variable length integers
*/
enum LZMA_VLI_BYTES_MAX = 9;
/**
* \brief VLI constant suffix
*/
//#define LZMA_VLI_C(n) UINT64_C(n)
/**
* \brief Variable-length integer type
*
* Valid VLI values are in the range [0, LZMA_VLI_MAX]. Unknown value is
* indicated with LZMA_VLI_UNKNOWN, which is the maximum value of the
* underlaying integer type.
*
* lzma_vli will be uint64_t for the foreseeable future. If a bigger size
* is needed in the future, it is guaranteed that 2 * LZMA_VLI_MAX will
* not overflow lzma_vli. This simplifies integer overflow detection.
*/
alias ulong lzma_vli;
/**
* \brief Validate a variable-length integer
*
* This is useful to test that application has given acceptable values
* for example in the uncompressed_size and compressed_size variables.
*
* \return True if the integer is representable as VLI or if it
* indicates unknown value.
*/
bool lzma_vli_is_valid(lzma_vli vli)
{
return vli <= LZMA_VLI_MAX || (vli) == LZMA_VLI_UNKNOWN;
}
/**
* \brief Encode a variable-length integer
*
* This function has two modes: single-call and multi-call. Single-call mode
* encodes the whole integer at once; it is an error if the output buffer is
* too small. Multi-call mode saves the position in *vli_pos, and thus it is
* possible to continue encoding if the buffer becomes full before the whole
* integer has been encoded.
*
* \param vli Integer to be encoded
* \param vli_pos How many VLI-encoded bytes have already been written
* out. When starting to encode a new integer in
* multi-call mode, *vli_pos must be set to zero.
* To use single-call encoding, set vli_pos to NULL.
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return Slightly different return values are used in multi-call and
* single-call modes.
*
* Single-call (vli_pos == NULL):
* - LZMA_OK: Integer successfully encoded.
* - LZMA_PROG_ERROR: Arguments are not sane. This can be due
* to too little output space; single-call mode doesn't use
* LZMA_BUF_ERROR, since the application should have checked
* the encoded size with lzma_vli_size().
*
* Multi-call (vli_pos != NULL):
* - LZMA_OK: So far all OK, but the integer is not
* completely written out yet.
* - LZMA_STREAM_END: Integer successfully encoded.
* - LZMA_BUF_ERROR: No output space was provided.
* - LZMA_PROG_ERROR: Arguments are not sane.
*/
nothrow lzma_ret lzma_vli_encode(lzma_vli vli, size_t *vli_pos,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Decode a variable-length integer
*
* Like lzma_vli_encode(), this function has single-call and multi-call modes.
*
* \param vli Pointer to decoded integer. The decoder will
* initialize it to zero when *vli_pos == 0, so
* application isn't required to initialize *vli.
* \param vli_pos How many bytes have already been decoded. When
* starting to decode a new integer in multi-call
* mode, *vli_pos must be initialized to zero. To
* use single-call decoding, set vli_pos to NULL.
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
*
* \return Slightly different return values are used in multi-call and
* single-call modes.
*
* Single-call (vli_pos == NULL):
* - LZMA_OK: Integer successfully decoded.
* - LZMA_DATA_ERROR: Integer is corrupt. This includes hitting
* the end of the input buffer before the whole integer was
* decoded; providing no input at all will use LZMA_DATA_ERROR.
* - LZMA_PROG_ERROR: Arguments are not sane.
*
* Multi-call (vli_pos != NULL):
* - LZMA_OK: So far all OK, but the integer is not
* completely decoded yet.
* - LZMA_STREAM_END: Integer successfully decoded.
* - LZMA_DATA_ERROR: Integer is corrupt.
* - LZMA_BUF_ERROR: No input was provided.
* - LZMA_PROG_ERROR: Arguments are not sane.
*/
nothrow lzma_ret lzma_vli_decode(lzma_vli *vli, size_t *vli_pos,
const(ubyte)* in_, size_t *in_pos, size_t in_size);
/**
* \brief Get the number of bytes required to encode a VLI
*
* \return Number of bytes on success (1-9). If vli isn't valid,
* zero is returned.
*/
nothrow pure uint lzma_vli_size(lzma_vli vli);
+714 -305
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright 2012, 2013, 2014, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm 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 RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module lzma;
version(HAVE_LZMA) {} else static assert(0, "LZMA is not available (HAVE_LZMA version is not defined)");
import deimos.lzma;
import std.conv;
import std.exception;
import std.string : format;
version (Windows)
{ pragma(lib, "liblzma"); }
else
{ pragma(lib, "lzma"); }
align(1) struct LZMAHeader
{
align(1):
ubyte compressionParameters;
uint dictionarySize;
long decompressedSize = -1;
}
static assert(LZMAHeader.sizeof == 13);
ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
{
lzma_stream strm;
lzmaEnforce(lzma_alone_decoder(&strm, ulong.max), "lzma_alone_decoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[1024];
size_t pos = 0;
void decompress(in ubyte[] chunk)
{
strm.next_in = chunk.ptr;
strm.avail_in = chunk.length;
again:
strm.next_out = outBuf.ptr + pos;
strm.avail_out = outBuf.length - pos;
auto ret = lzma_code(&strm, lzma_action.LZMA_RUN);
pos = strm.next_out - outBuf.ptr;
if (ret == lzma_ret.LZMA_OK && strm.avail_in && !strm.avail_out)
{
outBuf.length = outBuf.length * 2;
goto again;
}
lzmaEnforce!true(ret, "lzma_code (LZMA_RUN)");
enforce(strm.avail_in == 0, "Not all data was read");
}
header.decompressedSize = -1; // Required as Flash uses End-of-Stream marker
fixDictSize(header.dictionarySize);
decompress(cast(ubyte[])(&header)[0..1]);
decompress(compressedData);
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code (LZMA_FINISH)");
// enforce(strm.avail_out == 0,
// "Decompressed size mismatch (expected %d/0x%X, got %d/0x%X)".format(
// outBuf.length, outBuf.length,
// outBuf.length - strm.avail_out, outBuf.length - strm.avail_out,
// ));
outBuf = outBuf[0..pos];
return outBuf;
}
ubyte[] lzmaCompress(in ubyte[] decompressedData, LZMAHeader* header)
{
lzma_options_lzma opts;
enforce(lzma_lzma_preset(&opts, 9 | LZMA_PRESET_EXTREME) == false, "lzma_lzma_preset error");
lzma_stream strm;
lzmaEnforce(lzma_alone_encoder(&strm, &opts), "lzma_alone_encoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[decompressedData.length * 11 / 10 + 1024];
strm.next_out = outBuf.ptr;
strm.avail_out = outBuf.length;
strm.next_in = decompressedData.ptr;
strm.avail_in = decompressedData.length;
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code (LZMA_RUN)");
enforce(strm.avail_in == 0, "Not all data was read");
enforce(strm.avail_out != 0, "Ran out of compression space");
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code (LZMA_FINISH)");
*header = *cast(LZMAHeader*)outBuf.ptr;
return outBuf[LZMAHeader.sizeof..to!size_t(strm.total_out)];
}
private void lzmaEnforce(bool STREAM_END_OK=false)(lzma_ret v, string f)
{
if (v != lzma_ret.LZMA_OK && (!STREAM_END_OK || v != lzma_ret.LZMA_STREAM_END))
throw new Exception(text(f, " error: ", v));
}
/// Work around an artificial lzma_alone_decoder limitation in liblzma
/// which prevents it from accepting any streams with a dictionary size
/// that is not 2^n or 2^n + 2^(n-1).
/// See xz\src\liblzma\common\alone_decoder.c (git commit e7b424d267), line 87
private void fixDictSize(ref uint d)
{
--d;
d |= d >> 2;
d |= d >> 3;
d |= d >> 4;
d |= d >> 8;
d |= d >> 16;
++d;
}
+4 -2
View File
@@ -16,6 +16,8 @@
module murmurhash2a;
import std.conv;
struct MurmurHash2A
{
private static string mmix(string h, string k) { return "{ "~k~" *= m; "~k~" ^= "~k~" >> r; "~k~" *= m; "~h~" *= m; "~h~" ^= "~k~"; }"; }
@@ -67,8 +69,8 @@ public:
void Add(ref ubyte v) { Add(&v, v.sizeof); }
void Add(ref int v) { Add(&v, v.sizeof); }
void Add(ref uint v) { Add(&v, v.sizeof); }
void Add(string s) { Add(s.ptr, s.length); }
void Add(ubyte[] s) { Add(s.ptr, s.length); }
void Add(string s) { Add(s.ptr, to!uint(s.length)); }
void Add(ubyte[] s) { Add(s.ptr, to!uint(s.length)); }
private:
+1 -1
View File
@@ -35,5 +35,5 @@ void main(string[] args)
assembler.assemble(arg);
}
auto abc = as.toABC();
write(getName(args[1]) ~ ".abc", abc.write());
write(setExtension(args[1], "abc"), abc.write());
}
+1 -1
View File
@@ -32,7 +32,7 @@ void main(string[] args)
{
scope abc = ABCFile.read(cast(ubyte[])read(arg));
scope as = ASProgram.fromABC(abc);
scope disassembler = new Disassembler(as, getName(arg), getName(getBaseName(arg)));
scope disassembler = new Disassembler(as, stripExtension(arg), stripExtension(baseName(arg)));
disassembler.disassemble();
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -71,7 +71,7 @@ void main(string[] args)
if (header.fileLength != data.length + 8)
throw new Exception("Incorrect file length in file header");
write(arg ~ ".tempdata", data);
if (system(`7z a -tgzip -mx=9 -mfb=258 "` ~ arg ~ `.tempdata.gz" "` ~ arg ~ `.tempdata"`) || !exists(arg ~ ".tempdata.gz"))
if (spawnProcess(["7z", "a", "-tgzip", "-mx=9", "-mfb=258", arg ~ ".tempdata.gz", arg ~ ".tempdata"]).wait() || !exists(arg ~ ".tempdata.gz"))
throw new Exception("7-Zip failed");
remove(arg ~ ".tempdata");
auto gzipdata = cast(ubyte[])read(arg ~ ".tempdata.gz");
+1 -1
View File
@@ -41,7 +41,7 @@ void main(string[] args)
enforce(tag.data.length >= 6);
ushort id = *cast(short*)tag.data.ptr;
ubyte[] bin = tag.data[6..$];
std.file.write(format("%s-%d.bin", getName(arg), id), bin);
std.file.write(format("%s-%d.bin", stripExtension(arg), id), bin);
}
enforce(found, "No DefineBinaryData tags found");
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -33,6 +33,7 @@ void main(string[] args)
{
auto bin = cast(ubyte[])read(args[3]);
tag.data = tag.data[0..6] ~ bin;
tag.length = cast(uint)tag.data.length;
write(args[1], swf.write());
return;
}
+5 -10
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -19,8 +19,6 @@
module swfdecompress;
import std.file;
import std.zlib;
import zlibx;
import swffile;
void main(string[] args)
@@ -29,13 +27,10 @@ void main(string[] args)
throw new Exception("No file specified");
foreach (arg; args[1..$])
{
auto swf = cast(ubyte[])read(arg);
auto header = cast(SWFFile.Header*)swf.ptr;
if (header.signature[0] == cast(ubyte)'F')
auto swf = SWFFile.read(cast(ubyte[])read(arg));
if (swf.header.signature[0] == cast(ubyte)'F')
throw new Exception("Already uncompressed");
if (header.signature[0] != cast(ubyte)'C')
throw new Exception("Unknown format");
header.signature[0] = cast(ubyte)'F'; // uncompressed
write(arg, swf[0..8] ~ exactUncompress(swf[8..$], header.fileLength-8));
swf.header.signature[0] = cast(ubyte)'F'; // uncompressed
write(arg, swf.write());
}
}
+66 -12
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -18,8 +18,12 @@
module swffile;
import std.conv;
import std.exception;
import std.string : format;
import std.zlib;
import zlibx;
version (HAVE_LZMA) import lzma;
/**
* Implements a shallow representation of a .swf file.
@@ -36,12 +40,22 @@ final class SWFFile
align(1) struct Header
{
align(1):
char[3] signature;
ubyte ver;
uint fileLength;
static assert(Header.sizeof == 8);
}
align(1) struct LZMAHeader
{
align(1):
uint compressedLength;
ubyte compressionParameters;
uint dictionarySize;
static assert(LZMAHeader.sizeof == 9);
}
struct Rect
{
//int xMin, xMax, yMin, yMax;
@@ -52,6 +66,7 @@ final class SWFFile
{
ushort type;
ubyte[] data;
uint length; // may be >data.length if file is truncated
bool forceLongLength;
}
@@ -78,14 +93,32 @@ private final class SWFReader
swf = new SWFFile();
readRaw((&swf.header)[0..1]);
if ((swf.header.signature[0] != 'F' && swf.header.signature[0] != 'C') || swf.header.signature[1] != 'W' || swf.header.signature[2] != 'S')
throw new Exception("Invalid file signature");
enforce(swf.header.signature == "FWS" || swf.header.signature == "CWS" || swf.header.signature == "ZWS", "Invalid file signature");
if (swf.header.signature[0] == 'C')
buf = buf[0..swf.header.sizeof] ~ exactUncompress(buf[swf.header.sizeof..$], swf.header.fileLength-swf.header.sizeof);
else
if (swf.header.signature[0] == 'Z')
{
buf = buf[0..8] ~ exactUncompress(buf[8..$], swf.header.fileLength-8);
if (swf.header.fileLength != buf.length)
throw new Exception("Incorrect file length in file header");
version (HAVE_LZMA)
{
SWFFile.LZMAHeader lzHeader;
readRaw((&lzHeader)[0..1]);
lzma.LZMAHeader lzInfo;
lzInfo.compressionParameters = lzHeader.compressionParameters;
lzInfo.dictionarySize = lzHeader.dictionarySize;
lzInfo.decompressedSize = swf.header.fileLength - swf.header.sizeof;
enforce(swf.header.sizeof + lzHeader.sizeof + lzHeader.compressedLength == buf.length, "Trailing data in LZMA-compressed SWF file");
buf = buf[0..swf.header.sizeof] ~ lzmaDecompress(lzInfo, buf[swf.header.sizeof + lzHeader.sizeof .. $]);
pos = swf.header.sizeof;
}
else
enforce(false, "This version was built without LZMA support");
}
//enforce(swf.header.fileLength == buf.length,
// "Incorrect file length in file header (expected %d, got %d)"
// .format(swf.header.fileLength , buf.length));
swf.frameSize = readRect();
swf.frameRate = readU16();
swf.frameCount = readU16();
@@ -100,10 +133,12 @@ private final class SWFReader
pos += raw.length;
}
/// May read less than len on EOF
void[] readRaw(size_t len)
{
auto data = buf[pos..pos+len];
pos += len;
auto end = pos+len;
auto data = buf[pos..end<$?end:$];
pos = end;
return data;
}
@@ -145,6 +180,7 @@ private final class SWFReader
if (length < 0x3F)
t.forceLongLength = true;
}
t.length = length;
t.data = cast(ubyte[])readRaw(length);
return t;
}
@@ -234,24 +270,42 @@ private final class SWFWriter
foreach (ref tag; swf.tags)
{
ushort u = cast(ushort)(tag.type << 6);
if (tag.data.length < 0x3F && !tag.forceLongLength)
if (tag.length < 0x3F && !tag.forceLongLength)
{
u |= tag.data.length;
u |= tag.length;
buf ~= toArray(u);
}
else
{
u |= 0x3F;
buf ~= toArray(u);
uint l = tag.data.length;
uint l = to!uint(tag.length);
buf ~= toArray(l);
}
buf ~= tag.data;
}
swf.header.fileLength = 8 + buf.length;
swf.header.fileLength = to!uint(swf.header.sizeof + buf.length);
if (swf.header.signature[0] == 'C')
buf = cast(ubyte[])compress(buf, 9);
else
if (swf.header.signature[0] == 'Z')
{
version (HAVE_LZMA)
{
lzma.LZMAHeader lzInfo;
buf = lzmaCompress(buf, &lzInfo);
SWFFile.LZMAHeader lzHeader;
lzHeader.compressionParameters = lzInfo.compressionParameters;
lzHeader.dictionarySize = lzInfo.dictionarySize;
lzHeader.compressedLength = to!uint(buf.length);
buf = cast(ubyte[])(&lzHeader)[0..1] ~ buf;
}
else
enforce(false, "This version was built without LZMA support");
}
buf = toArray(swf.header) ~ buf;
return buf;
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm 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 RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module swflzmacompress;
import std.exception;
import std.file;
import std.getopt;
import std.string;
import swffile;
void main(string[] args)
{
bool force, updateVersion;
getopt(args,
"--force", &force,
"--update-version", &updateVersion,
);
if (args.length == 1)
throw new Exception("No file specified");
enum MIN_LZMA_VER = 13;
foreach (arg; args[1..$])
{
auto swf = SWFFile.read(cast(ubyte[])read(arg));
enforce(swf.header.signature[0] != cast(ubyte)'Z', "Already LZMA-compressed");
if (swf.header.ver < MIN_LZMA_VER)
{
if (updateVersion)
{
if (swf.header.ver < 8 && !force)
throw new Exception(format(
"SWF version %d has different file format than version %d, " ~
"required for LZMA. Resulting file may not work. " ~
"Use --force to override and update version anyway.",
swf.header.ver, MIN_LZMA_VER
));
swf.header.ver = MIN_LZMA_VER;
}
else
if (!force)
throw new Exception(format(
"SWF version %d is too old to support SWF LZMA compression, " ~
"which requires version %d. " ~
"Use --update-version to update the version number, " ~
"or --force to compress anyway without updating it.",
swf.header.ver, MIN_LZMA_VER
));
}
swf.header.signature[0] = cast(ubyte)'Z'; // LZMA
write(arg, swf.write());
}
}
+51 -13
View File
@@ -2,9 +2,12 @@
module zlibx;
import std.zlib, etc.c.zlib;
import core.memory;
import std.string : format;
import std.zlib, etc.c.zlib, std.conv;
static import etc.c.zlib;
alias std.zlib.Z_SYNC_FLUSH Z_SYNC_FLUSH;
debug import std.stdio : stderr;
/// Avoid bug(?) in D zlib implementation with 7zip-generated zlib streams
ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
@@ -15,32 +18,67 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
uint err;
zs.next_in = srcbuf.ptr;
zs.avail_in = srcbuf.length;
zs.avail_in = to!uint(srcbuf.length);
zs.next_out = destbuf.ptr;
zs.avail_out = destbuf.length;
zs.avail_out = to!uint(destbuf.length);
err = etc.c.zlib.inflateInit2(&zs, 15);
if (err)
{
delete destbuf;
GC.free(destbuf.ptr);
throw new ZlibException(err);
}
err = etc.c.zlib.inflate(&zs, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END)
while (true)
{
Lerr:
delete destbuf;
etc.c.zlib.inflateEnd(&zs);
throw new ZlibException(err);
err = etc.c.zlib.inflate(&zs, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END)
{
Lerr:
GC.free(destbuf.ptr);
etc.c.zlib.inflateEnd(&zs);
throw new ZlibException(err);
}
if (err == Z_STREAM_END)
break;
else
if (zs.avail_out == 0)
{
debug stderr.writefln("Wrong uncompressed file length (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
auto out_pos = zs.next_out - destbuf.ptr;
destbuf.length = 1024 + destbuf.length * 2;
zs.next_out = destbuf.ptr + out_pos;
zs.avail_out = to!uint(destbuf.length - out_pos);
continue;
}
else
if (zs.avail_in == 0)
{
debug stderr.writefln("Unterminated Zlib stream (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
break;
}
else
throw new Exception(format("Unexpected zlib state (err=%d, avail_in == %d, avail_out = %d)",
err, zs.avail_in , zs.avail_out));
}
if (zs.avail_out != 0)
throw new Exception("Too little data in stream");
if (zs.avail_in != 0 || zs.avail_out != 0)
debug stderr.writefln("Zlib stream incongruity (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
err = etc.c.zlib.inflateEnd(&zs);
if (err != Z_OK)
goto Lerr;
return destbuf;
if (zs.avail_out != 0)
debug stderr.writefln("Too little data in zlib stream: expected %d, got %d", destlen, destlen - zs.avail_out);
return destbuf[0..$-zs.avail_out];
}