Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe93f5e764 | ||
|
|
0fbfba1186 | ||
|
|
6bdf6dae39 | ||
|
|
62ef183d95 | ||
|
|
f6eb080be7 | ||
|
|
a17a03e6f6 | ||
|
|
0486c1a54a | ||
|
|
a2d22e7606 | ||
|
|
6568793aaf | ||
|
|
b0bdb250c5 | ||
|
|
99e49a9385 | ||
|
|
6313739b5e | ||
|
|
8ed3295199 | ||
|
|
31ad8dcef7 | ||
|
|
ef8e86388a | ||
|
|
d4f7a070b6 | ||
|
|
e5e01ea90f | ||
|
|
fbebd86f47 | ||
|
|
e9fbbef9b7 | ||
|
|
9c28d20e99 | ||
|
|
3449822efd | ||
|
|
3ded602166 | ||
|
|
08008d5643 | ||
|
|
98e9198c36 | ||
|
|
f6dbbf08ee | ||
|
|
4251e409a2 | ||
|
|
7ad6949b45 | ||
|
|
9bd472e4f8 | ||
|
|
efada3d5c1 | ||
|
|
05d9624f68 | ||
|
|
a2ea734202 | ||
|
|
32909f3893 | ||
|
|
472a9cec57 | ||
|
|
6cbb185114 | ||
|
|
f32f6fb128 | ||
|
|
d6d7ecf47e | ||
|
|
0cefc587e6 | ||
|
|
a45b4e2bdc | ||
|
|
e383172765 | ||
|
|
c62b5438d7 | ||
|
|
a1a214ae72 | ||
|
|
222fa1109c | ||
|
|
9fa2d122fa | ||
|
|
93adc953c2 | ||
|
|
dc222f9e43 | ||
|
|
fb2f96b81a | ||
|
|
14f5e669ee | ||
|
|
4e3d43df5f | ||
|
|
c1d6b29d94 | ||
|
|
8fc7253bba | ||
|
|
ac2bde2550 | ||
|
|
99dfd797ba | ||
|
|
f4824cd3b4 | ||
|
|
87047b920c | ||
|
|
f17e97848b | ||
|
|
336665a213 | ||
|
|
fdb8489382 | ||
|
|
afe42b5d1f | ||
|
|
3a9829467f | ||
|
|
8e969d9e76 | ||
|
|
d755065785 | ||
|
|
ea3dfd30b1 | ||
|
|
86a4a2e0b8 | ||
|
|
704045594a | ||
|
|
5b4c31d2a0 | ||
|
|
a56d074dad | ||
|
|
d722bdcb3c | ||
|
|
ef9e768b07 | ||
|
|
d321be2b25 | ||
|
|
6543d17fc4 | ||
|
|
e20332aeba | ||
|
|
d77e2b679e | ||
|
|
9d0d0355e5 | ||
|
|
5795d5f374 | ||
|
|
8e8d204269 | ||
|
|
20f416d9d8 | ||
|
|
e3634e59cd | ||
|
|
3c5101f6c0 | ||
|
|
c2042660f2 | ||
|
|
57d05c6c35 | ||
|
|
532700df03 | ||
|
|
fe9b49bfd3 | ||
|
|
e7b65d6d50 | ||
|
|
a304843b5b | ||
|
|
ebe6639e8a | ||
|
|
09be72ea9c | ||
|
|
151bf87038 | ||
|
|
950f16fd23 | ||
|
|
0dd9dcdb2f | ||
|
|
5e69e44c17 | ||
|
|
15d20019d5 | ||
|
|
b0795bfcde | ||
|
|
bd93ae35d7 | ||
|
|
e9bf251777 | ||
|
|
de68fea1f1 | ||
|
|
03c02cbc4a | ||
|
|
fa08188324 | ||
|
|
9278ae9910 | ||
|
|
e479fbc25a | ||
|
|
10464de2a2 | ||
|
|
b371fb5bec | ||
|
|
216beb2edd | ||
|
|
b67c0f8751 | ||
|
|
8fe2227446 | ||
|
|
1f0ef64b83 | ||
|
|
701e5b6f80 | ||
|
|
fc46ad02e9 | ||
|
|
e7a5dd92c6 | ||
|
|
352456d257 | ||
|
|
d6544ae7cb | ||
|
|
37cd4b415b | ||
|
|
62d0756dbc | ||
|
|
48b221535f | ||
|
|
beb7229bf8 | ||
|
|
296196ff71 | ||
|
|
170e7d6a8b | ||
|
|
2c63eb592a | ||
|
|
16a5f649de | ||
|
|
111cd75eb4 | ||
|
|
e103048af6 | ||
|
|
61df70dd0e | ||
|
|
51536e4add | ||
|
|
fb5c03ada5 | ||
|
|
2226b0059f | ||
|
|
69cf64b85c | ||
|
|
2b49e77267 | ||
|
|
fab198a648 | ||
|
|
4e4fd2b56d | ||
|
|
7e4f7f5bac | ||
|
|
7a92e9fe81 | ||
|
|
41002f7085 | ||
|
|
3b131fa932 | ||
|
|
34316bd4c9 | ||
|
|
e49a6b3fec | ||
|
|
bfa02943c6 | ||
|
|
cf8835b87d | ||
|
|
4c411ac1f2 | ||
|
|
76cadf30cf | ||
|
|
2498673231 | ||
|
|
a1b9ea05c0 | ||
|
|
a766cf5919 | ||
|
|
e6f54db4c1 | ||
|
|
51e82bf985 | ||
|
|
282e8cbfc1 | ||
|
|
4f53b789ee | ||
|
|
0c5cbde348 | ||
|
|
ff799d002f | ||
|
|
1bf7a9a593 | ||
|
|
1167b14471 | ||
|
|
d3e8e47d54 | ||
|
|
3f9be2b192 | ||
|
|
a690c82b7d | ||
|
|
2d7886bb0b | ||
|
|
4cb179a1c6 | ||
|
|
0031be1f75 | ||
|
|
c7b7a5bce8 | ||
|
|
ac5d4b5e74 | ||
|
|
eb07838668 | ||
|
|
b80fe48bee | ||
|
|
a083ed265f | ||
|
|
986ea47d03 | ||
|
|
3520d34cc2 | ||
|
|
58ab6e3bf0 | ||
|
|
413aa9b5f0 | ||
|
|
5da1e26835 | ||
|
|
53a4173fd3 | ||
|
|
5737243606 | ||
|
|
be727e67c6 | ||
|
|
9d67c46835 | ||
|
|
756a2fd060 | ||
|
|
8ad1f85a45 | ||
|
|
ad6ae5d087 | ||
|
|
580a1364d0 | ||
|
|
d4f90f9c01 | ||
|
|
cecff913f1 | ||
|
|
7a4c78dac3 | ||
|
|
ee47075fda | ||
|
|
100f6b6bf4 | ||
|
|
2e48e8deef | ||
|
|
f49fe820b6 | ||
|
|
52a2cde1b0 | ||
|
|
608a8ca51d | ||
|
|
d11270a553 | ||
|
|
68a0845c53 | ||
|
|
824a945247 | ||
|
|
c89f2bbe70 |
+182
-36
@@ -1,36 +1,182 @@
|
||||
RABCDAsm Changelog
|
||||
==================
|
||||
|
||||
RABCDAsm v1.3 (2010.11.11)
|
||||
--------------------------
|
||||
|
||||
* Fixed double precision problem
|
||||
* 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
|
||||
* Documentation markdown fixes
|
||||
|
||||
RABCDAsm v1.2 (2010.11.06)
|
||||
--------------------------
|
||||
|
||||
* 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
|
||||
|
||||
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
|
||||
* Warning: this breaks compatibility with v1.0 disassemblies
|
||||
* Fixed relative include paths
|
||||
* Add optional byte offsets to labels, which allows lossless representation
|
||||
of jumps inside instructions and outside the function bounds
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.0 (2010.05.05)
|
||||
--------------------------
|
||||
|
||||
* Initial release.
|
||||
RABCDAsm Changelog
|
||||
==================
|
||||
|
||||
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)
|
||||
--------------------------
|
||||
|
||||
* Fixed handling of orphan methods with multiple references
|
||||
* Treat out-of-bounds namespace/multiname indices as null
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.7 (2011.06.14)
|
||||
--------------------------
|
||||
|
||||
* Fixed handling of invalid UTF-8 in strings
|
||||
* Fixed handling of null namespaces and multinames
|
||||
* Fixed handling of exception info in corrupted method bodies
|
||||
* Fixed handling of orphan private namespaces
|
||||
* Fixed handling of out-of-bounds class/method indices
|
||||
* Disassembler now creates one file per class and script-level method
|
||||
* Disassembler now outputs blank lines after `si32` etc. instructions as well
|
||||
* `abcexport` now delimits the index with a dash in output filenames
|
||||
* Improved shortening of `double` literals
|
||||
* Documentation updates
|
||||
|
||||
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
|
||||
from their referenced contexts
|
||||
* 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
|
||||
the same file as their main class
|
||||
* Inline methods are now in separate files, placed according to their usage
|
||||
context
|
||||
* 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
|
||||
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
|
||||
* The package can now be built using a simple, dedicated build tool
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.5 (2011.04.14)
|
||||
--------------------------
|
||||
|
||||
* Fixed v1.4 constant pool regression
|
||||
* Added support for memory-access and sign-extend opcodes
|
||||
* Speed optimizations
|
||||
* Documentation updates
|
||||
|
||||
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
|
||||
count
|
||||
* Finish Metadata support
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.3 (2010.11.11)
|
||||
--------------------------
|
||||
|
||||
* Fixed double precision problem
|
||||
* 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
|
||||
* Documentation markdown fixes
|
||||
|
||||
RABCDAsm v1.2 (2010.11.06)
|
||||
--------------------------
|
||||
|
||||
* 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
|
||||
|
||||
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
|
||||
of jumps inside instructions and outside the function bounds
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.0 (2010.05.05)
|
||||
--------------------------
|
||||
|
||||
* Initial release.
|
||||
|
||||
@@ -1,447 +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
|
||||
|
||||
`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 ancilliary 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`.
|
||||
|
||||
[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 1][d1]. It uses one
|
||||
third-party library, [std2][] (for some fancy template stuff).
|
||||
|
||||
Assuming you have [dmd][], [git][] and [svn][] installed, compiling should be
|
||||
as straight-forward as:
|
||||
|
||||
git clone git://github.com/CyberShadow/RABCDAsm.git
|
||||
cd RABCDAsm
|
||||
svn co http://svn.dsource.org/projects/std2/trunk/std2/std2
|
||||
dmd rabcdasm abcfile asprogram disassembler autodata murmurhash2a
|
||||
dmd rabcasm abcfile asprogram assembler autodata murmurhash2a
|
||||
dmd abcexport swffile zlibx
|
||||
dmd abcreplace swffile zlibx
|
||||
dmd swfdecompress swffile zlibx
|
||||
dmd swf7zcompress swffile zlibx
|
||||
|
||||
[d1]: http://www.digitalmars.com/d/1.0/
|
||||
[std2]: http://dsource.org/projects/std2
|
||||
[dmd]: http://www.digitalmars.com/d/download.html
|
||||
[git]: http://git-scm.com/
|
||||
[svn]: http://subversion.apache.org/
|
||||
|
||||
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 `file0.abc` ... `fileN.abc` (often just `file0.abc`). Each
|
||||
file corresponds to an ABC block inside the SWF file.
|
||||
|
||||
To disassemble one of the `.abc` files:
|
||||
|
||||
rabcdasm file0.abc
|
||||
|
||||
This will create a `file0` directory, which will contain `file0.main.asasm`
|
||||
(the main program file), `file0.privatens.asasm` (private namespace alias
|
||||
definitions) and a file per ActionScript class.
|
||||
|
||||
To assemble the `.asasm` files back, and update the SWF file:
|
||||
|
||||
rabcasm file0/file0.main.asasm
|
||||
abcreplace file0.swf 0 file0/file0.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`.
|
||||
|
||||
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/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`).
|
||||
|
||||
`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 (`file0.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. Also, some records (classes and methods) are
|
||||
currently not sorted by reference count, which may cause `rabcasm` to generate
|
||||
slightly larger files than the originals (due to variable-length encoding of
|
||||
integers).
|
||||
|
||||
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 a blank rectangle.
|
||||
|
||||
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
|
||||
|
||||
3. The [Fiddler][] Web Debugging Proxy can be very useful for analysing
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
* Metadata is currently ignored. I haven't noticed any metadata blocks in any
|
||||
SWF files I've disassembled.
|
||||
|
||||
* `rabcasm` may create a broken file due to not ordering classes by ancestry.
|
||||
|
||||
The problem originates from the fact that a class's ancestors (extended
|
||||
class and implemented interfaces) are stored as multinames, and not as
|
||||
class indices. (This makes sense, since classes may extend objects outside
|
||||
the current ABC file.) Since `rabcasm` currently doesn't decode multinames,
|
||||
it is unaware of the class dependencies, and may thus write the classes out
|
||||
of order. This results in a file that, when opened, will fail to load with
|
||||
an error message similar to:
|
||||
|
||||
VerifyError: Error #1014: Class AncestorClassName could not be found.
|
||||
|
||||
The simple work-around is to re-order the classes as they are declared in
|
||||
the `.main.asasm` file, and place ancestors before descendants.
|
||||
|
||||
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]: 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* `(` *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`.
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@ module abcexport;
|
||||
|
||||
import std.file;
|
||||
import std.path;
|
||||
import std.string;
|
||||
import std.conv;
|
||||
import std.stdio;
|
||||
import swffile;
|
||||
|
||||
@@ -45,11 +45,11 @@ void main(string[] args)
|
||||
while (*p++) {} // skip name
|
||||
abc = tag.data[p-tag.data.ptr..$];
|
||||
}
|
||||
write(getName(arg) ~ .toString(count++) ~ ".abc", abc);
|
||||
std.file.write(stripExtension(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
|
||||
}
|
||||
if (count == 0)
|
||||
throw new Exception("No DoABC tags found");
|
||||
}
|
||||
catch (Object o)
|
||||
writefln("Error while processing %s: %s", arg, o);
|
||||
catch (Exception e)
|
||||
writefln("Error while processing %s: %s", arg, e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -19,9 +19,11 @@
|
||||
module abcfile;
|
||||
|
||||
import std.string : format; // exception formatting
|
||||
import std.conv;
|
||||
import std.exception;
|
||||
|
||||
/**
|
||||
* Implements a shallow representation of an .abc file.
|
||||
/**
|
||||
* Implements a shallow representation of an .abc file.
|
||||
* Loading and saving an .abc file using this class should produce
|
||||
* output identical to the input.
|
||||
*/
|
||||
@@ -45,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()
|
||||
{
|
||||
@@ -125,13 +131,8 @@ class ABCFile
|
||||
|
||||
struct Metadata
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
uint key, value;
|
||||
}
|
||||
|
||||
uint name;
|
||||
Item[] items;
|
||||
uint[] keys, values;
|
||||
}
|
||||
|
||||
struct Instance
|
||||
@@ -176,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 = (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
|
||||
@@ -206,6 +207,8 @@ class ABCFile
|
||||
Instruction[] instructions;
|
||||
ExceptionInfo[] exceptions;
|
||||
TraitsInfo[] traits;
|
||||
|
||||
Error[] errors;
|
||||
}
|
||||
|
||||
/// Destination for a jump or exception block boundary
|
||||
@@ -215,13 +218,20 @@ 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;
|
||||
@@ -387,7 +397,7 @@ enum MethodFlags : ubyte
|
||||
NEED_REST = 0x04, // This flag creates an ActionScript 3.0 rest arguments array. Must not be used with NEED_ARGUMENTS. See Chapter 3.
|
||||
HAS_OPTIONAL = 0x08, // Must be set if this method has optional parameters and the options field is present in this method_info structure.
|
||||
SET_DXNS = 0x40, // Must be set if this method uses the dxns or dxnslate opcodes.
|
||||
HAS_PARAM_NAMES = 0x80, // Must be set when the param_names field is present in this method_info structure.
|
||||
HAS_PARAM_NAMES = 0x80, // Must be set when the param_names field is present in this method_info structure.
|
||||
}
|
||||
|
||||
const string[8] MethodFlagNames = ["NEED_ARGUMENTS", "NEED_ACTIVATION", "NEED_REST", "HAS_OPTIONAL", "0x10", "0x20", "SET_DXNS", "HAS_PARAM_NAMES"];
|
||||
@@ -435,6 +445,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,
|
||||
@@ -655,7 +666,7 @@ struct OpcodeInfo
|
||||
}
|
||||
|
||||
const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x00 */ {"0x00", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x00 */ {"db", [OpcodeArgumentType.UByteLiteral]},
|
||||
/* 0x01 */ {"bkpt", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x02 */ {"nop", []},
|
||||
/* 0x03 */ {"throw", []},
|
||||
@@ -708,16 +719,16 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x32 */ {"hasnext2", [OpcodeArgumentType.UIntLiteral, OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x33 */ {"pushdecimal", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x34 */ {"pushdnan", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x35 */ {"li8", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x36 */ {"li16", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x37 */ {"li32", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x38 */ {"lf32", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x39 */ {"lf64", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x3A */ {"si8", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x3B */ {"si16", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x3C */ {"si32", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x3D */ {"sf32", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x3E */ {"sf64", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x35 */ {"li8", []},
|
||||
/* 0x36 */ {"li16", []},
|
||||
/* 0x37 */ {"li32", []},
|
||||
/* 0x38 */ {"lf32", []},
|
||||
/* 0x39 */ {"lf64", []},
|
||||
/* 0x3A */ {"si8", []},
|
||||
/* 0x3B */ {"si16", []},
|
||||
/* 0x3C */ {"si32", []},
|
||||
/* 0x3D */ {"sf32", []},
|
||||
/* 0x3E */ {"sf64", []},
|
||||
/* 0x3F */ {"0x3F", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x40 */ {"newfunction", [OpcodeArgumentType.Method]},
|
||||
/* 0x41 */ {"call", [OpcodeArgumentType.UIntLiteral]},
|
||||
@@ -735,9 +746,9 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x4D */ {"callinterface", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x4E */ {"callsupervoid", [OpcodeArgumentType.Multiname, OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x4F */ {"callpropvoid", [OpcodeArgumentType.Multiname, OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x50 */ {"sxi1", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x51 */ {"sxi8", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x52 */ {"sxi16", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x50 */ {"sxi1", []},
|
||||
/* 0x51 */ {"sxi8", []},
|
||||
/* 0x52 */ {"sxi16", []},
|
||||
/* 0x53 */ {"applytype", [OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x54 */ {"0x54", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x55 */ {"newobject", [OpcodeArgumentType.UIntLiteral]},
|
||||
@@ -750,7 +761,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]},
|
||||
@@ -758,11 +769,11 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x64 */ {"getglobalscope", []},
|
||||
/* 0x65 */ {"getscopeobject", [OpcodeArgumentType.UByteLiteral]},
|
||||
/* 0x66 */ {"getproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x67 */ {"getpropertylate", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x67 */ {"getpropertylate", []},
|
||||
/* 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]},
|
||||
@@ -784,10 +795,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", []},
|
||||
@@ -922,6 +933,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;
|
||||
@@ -992,13 +1013,13 @@ private final class ABCReader
|
||||
foreach (ref value; abc.bodies)
|
||||
value = readMethodBody();
|
||||
}
|
||||
catch (Object o)
|
||||
throw new Exception(format("Error at %d (0x%X): %s", pos, pos, o));
|
||||
catch (Exception e)
|
||||
throw new Exception(format("Error at %d (0x%X):", pos, pos), e);
|
||||
}
|
||||
|
||||
ubyte readU8()
|
||||
{
|
||||
assert(pos < buf.length);
|
||||
enforce(pos < buf.length, "End of file reached");
|
||||
return buf[pos++];
|
||||
}
|
||||
|
||||
@@ -1014,27 +1035,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;
|
||||
}
|
||||
@@ -1046,7 +1079,7 @@ private final class ABCReader
|
||||
|
||||
void readExact(void* ptr, size_t len)
|
||||
{
|
||||
assert(pos+len <= buf.length);
|
||||
enforce(pos+len <= buf.length, "End of file reached");
|
||||
(cast(ubyte*)ptr)[0..len] = buf[pos..pos+len];
|
||||
pos += len;
|
||||
}
|
||||
@@ -1061,8 +1094,9 @@ private final class ABCReader
|
||||
|
||||
string readString()
|
||||
{
|
||||
string s = new char[readU30()];
|
||||
readExact(s.ptr, s.length);
|
||||
char[] buf = new char[readU30()];
|
||||
readExact(buf.ptr, buf.length);
|
||||
string s = assumeUnique(buf);
|
||||
if (s.length == 0)
|
||||
s = ""; // not null!
|
||||
return s;
|
||||
@@ -1173,12 +1207,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;
|
||||
}
|
||||
|
||||
@@ -1275,17 +1308,204 @@ private final class ABCReader
|
||||
r.instructions = null;
|
||||
|
||||
size_t len = readU30();
|
||||
uint[] instructionAtOffset = new uint[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.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;
|
||||
}
|
||||
@@ -1302,103 +1522,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);
|
||||
|
||||
instructionAtOffset[] = uint.max;
|
||||
uint[] instructionOffsets;
|
||||
while (pos < end)
|
||||
{
|
||||
uint instructionOffset = offset;
|
||||
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)
|
||||
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;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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();
|
||||
@@ -1471,7 +1628,7 @@ private final class ABCWriter
|
||||
foreach (ref value; abc.instances)
|
||||
writeInstance(value);
|
||||
|
||||
assert(abc.classes.length == abc.instances.length);
|
||||
assert(abc.classes.length == abc.instances.length, "Number of classes and instances differs");
|
||||
foreach (ref value; abc.classes)
|
||||
writeClass(value);
|
||||
|
||||
@@ -1506,7 +1663,6 @@ private final class ABCWriter
|
||||
writeU8(cast(ubyte)(v>>16));
|
||||
}
|
||||
|
||||
/// Note: may return values larger than 0xFFFFFFFF.
|
||||
void writeU32(ulong v)
|
||||
{
|
||||
if ( v < 128)
|
||||
@@ -1546,12 +1702,13 @@ private final class ABCWriter
|
||||
writeU32(cast(ulong)v);
|
||||
}
|
||||
|
||||
void writeU30(uint v)
|
||||
void writeU30(ulong v)
|
||||
{
|
||||
enforce(v < (1<<30));
|
||||
writeU32(v);
|
||||
}
|
||||
|
||||
void writeExact(void* ptr, size_t len)
|
||||
void writeExact(const(void)* ptr, size_t len)
|
||||
{
|
||||
while (pos+len > buf.length)
|
||||
buf.length = buf.length * 2;
|
||||
@@ -1643,7 +1800,7 @@ private final class ABCWriter
|
||||
}
|
||||
if (v.flags & MethodFlags.HAS_PARAM_NAMES)
|
||||
{
|
||||
assert(v.paramNames.length == v.paramTypes.length);
|
||||
assert(v.paramNames.length == v.paramTypes.length, "Mismatching number of parameter names and types");
|
||||
foreach (value; v.paramNames)
|
||||
writeU30(value);
|
||||
}
|
||||
@@ -1658,12 +1815,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)
|
||||
@@ -1716,7 +1873,7 @@ private final class ABCWriter
|
||||
if (v.attr & TraitAttributes.Metadata)
|
||||
{
|
||||
writeU30(v.metadata.length);
|
||||
foreach (ref value; v.metadata)
|
||||
foreach (value; v.metadata)
|
||||
writeU30(value);
|
||||
}
|
||||
}
|
||||
@@ -1745,23 +1902,24 @@ 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
|
||||
auto globalBuf = buf;
|
||||
auto globalPos = pos;
|
||||
buf = new ubyte[1024];
|
||||
static ubyte[1024*16] methodBuf;
|
||||
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);
|
||||
@@ -1770,7 +1928,7 @@ private final class ABCWriter
|
||||
throw new Exception("Mismatching number of arguments");
|
||||
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
switch (type)
|
||||
final switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to encode OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
@@ -1816,9 +1974,6 @@ private final class ABCWriter
|
||||
writeS24(0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1828,7 +1983,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;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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
|
||||
@@ -27,7 +27,7 @@ void main(string[] args)
|
||||
if (args.length != 4)
|
||||
throw new Exception("Bad arguments. Usage: abcreplace file.swf index code.abc");
|
||||
auto swf = SWFFile.read(cast(ubyte[])read(args[1]));
|
||||
auto index = toUint(args[2]);
|
||||
auto index = to!uint(args[2]);
|
||||
uint count;
|
||||
foreach (ref tag; swf.tags)
|
||||
if ((tag.type == TagType.DoABC || tag.type == TagType.DoABC2) && count++ == index)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}"))((?{def:String}[^"]*))((?{def:StringEdge}"))/" 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}"))((?{def:String}[^"]*))((?{def:StringEdge}"))/" region1="def:Keyword" region2="def:PairStart" region3="def:Outlined" region4="def:PairEnd"/--> <!-- BUG: does not support escape sequences -->
|
||||
<regexp
|
||||
match="/(\brefid\b)\M\s*"(.*)"$/"
|
||||
region1="def:Keyword"
|
||||
region2="def:Outlined"/>
|
||||
<!--block start="/(\brefid\b)\s*(?{def:StringEdge}")/" end="/(?{def:StringEdge}")/" 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">
|
||||
@@ -74,10 +79,12 @@
|
||||
<word name="implements"/>
|
||||
<word name="initscopedepth"/>
|
||||
<word name="instance"/>
|
||||
<word name="item"/>
|
||||
<word name="localcount"/>
|
||||
<word name="majorversion"/>
|
||||
<word name="maxscopedepth"/>
|
||||
<word name="maxstack"/>
|
||||
<word name="metadata"/>
|
||||
<word name="method"/>
|
||||
<word name="minorversion"/>
|
||||
<word name="name"/>
|
||||
|
||||
+668
-436
File diff suppressed because it is too large
Load Diff
+517
-259
File diff suppressed because it is too large
Load Diff
+147
-82
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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
|
||||
@@ -19,61 +19,68 @@
|
||||
module autodata;
|
||||
|
||||
import murmurhash2a;
|
||||
import std2.traits;
|
||||
import std.traits;
|
||||
public import std.conv;
|
||||
|
||||
string addAutoField(string name, bool reverseSort = false)
|
||||
{
|
||||
//return `mixin(handler.process!(typeof(` ~ name ~ `), "` ~ name ~ `")());`; // doesn't work due to DMD bug 3959
|
||||
return `{ static const _AutoDataStr = handler.process!(typeof(this.` ~ name ~ `), "` ~ name ~ `", ` ~ (reverseSort ? "true" : "false") ~`)(); mixin(_AutoDataStr); }`;
|
||||
return `mixin(typeof(handler).getMixin!(typeof(` ~ name ~ `), "` ~ name ~ `", ` ~ (reverseSort ? "true" : "false") ~`));`;
|
||||
}
|
||||
|
||||
template AutoCompare()
|
||||
{
|
||||
hash_t toHash()
|
||||
static if (is(typeof(this)==class))
|
||||
{
|
||||
alias typeof(this) _AutoDataTypeReference;
|
||||
alias Object _AutoDataOtherTypeReference;
|
||||
|
||||
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); }
|
||||
}
|
||||
else // struct
|
||||
{
|
||||
alias const(typeof(this)*) _AutoDataTypeReference;
|
||||
alias const(typeof(this)*) _AutoDataOtherTypeReference;
|
||||
|
||||
hash_t toHash() const { return _AutoDataHash(); }
|
||||
bool opEquals(ref const typeof(this) s) const { return _AutoDataEquals(&s); }
|
||||
int opCmp(ref const typeof(this) s) const { return _AutoDataCmp(&s); }
|
||||
}
|
||||
|
||||
@trusted private hash_t _AutoDataHash() const
|
||||
{
|
||||
HashDataHandler handler;
|
||||
handler.hasher.Begin();
|
||||
processData!(void, "", "")(handler);
|
||||
processData!(void, q{}, q{})(handler);
|
||||
return handler.hasher.End();
|
||||
}
|
||||
|
||||
static if (is(typeof(this)==class))
|
||||
alias Object _AutoDataOtherType;
|
||||
else
|
||||
alias typeof(this) _AutoDataOtherType;
|
||||
|
||||
int opEquals(_AutoDataOtherType other)
|
||||
private bool _AutoDataEquals(_AutoDataOtherTypeReference other) const
|
||||
{
|
||||
EqualsDataHandler!(typeof(this)) handler;
|
||||
static if (is(typeof(this)==class))
|
||||
{
|
||||
handler.other = cast(typeof(this)) other;
|
||||
if (handler.other is null)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
handler.other = other;
|
||||
return processData!(bool, "auto _AutoDataOther = handler.other;", "return true;")(handler);
|
||||
auto handler = EqualsDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
|
||||
if (handler.other is null)
|
||||
return false;
|
||||
return processData!(bool, q{auto _AutoDataOther = handler.other;}, q{return true;})(handler);
|
||||
}
|
||||
|
||||
int opCmp(_AutoDataOtherType other)
|
||||
private int _AutoDataCmp(_AutoDataOtherTypeReference other) const
|
||||
{
|
||||
CmpDataHandler!(typeof(this)) handler;
|
||||
static if (is(typeof(this)==class))
|
||||
{
|
||||
handler.other = cast(typeof(this)) other;
|
||||
if (handler.other is null)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
handler.other = other;
|
||||
return processData!(int, "auto _AutoDataOther = handler.other;", "return 0;")(handler);
|
||||
auto handler = CmpDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
|
||||
if (handler.other is null)
|
||||
return false;
|
||||
return processData!(int, q{auto _AutoDataOther = handler.other;}, "return 0;")(handler);
|
||||
}
|
||||
}
|
||||
|
||||
template AutoToString()
|
||||
{
|
||||
string toString()
|
||||
static if (is(typeof(this)==class))
|
||||
override string toString() const { return _AutoDataToString(); }
|
||||
else // struct
|
||||
string toString() const { return _AutoDataToString(); }
|
||||
|
||||
string _AutoDataToString() const
|
||||
{
|
||||
ToStringDataHandler handler;
|
||||
return processData!(string, "string _AutoDataResult;", "return _AutoDataResult;")(handler);
|
||||
@@ -82,20 +89,11 @@ template AutoToString()
|
||||
|
||||
template ProcessAllData()
|
||||
{
|
||||
R processData(R, string prolog, string epilog, H)(ref H handler)
|
||||
R processData(R, string prolog, string epilog, H)(ref H handler) const
|
||||
{
|
||||
mixin(prolog);
|
||||
foreach (i, T; this.tupleof)
|
||||
{
|
||||
//mixin(addAutoField(T.stringof)); // doesn't work
|
||||
static if (this.tupleof[i].stringof == typeof(this.tupleof[i]).stringof)
|
||||
static assert(0, "DMD bug 2881 detected - can't use enums with ProcessAllData");
|
||||
else
|
||||
static if (is (typeof(this) == class))
|
||||
mixin(addAutoField(this.tupleof[i].stringof[5..$])); // remove "this."
|
||||
else
|
||||
mixin(addAutoField(this.tupleof[i].stringof[8..$])); // remove "(*this)."
|
||||
}
|
||||
mixin(addAutoField(this.tupleof[i].stringof[5..$])); // remove "this."
|
||||
mixin(epilog);
|
||||
}
|
||||
}
|
||||
@@ -103,29 +101,30 @@ template ProcessAllData()
|
||||
/// For data handlers that only need to look at the raw data (currently only HashDataHandler)
|
||||
template RawDataHandlerWrapper()
|
||||
{
|
||||
static string process(T, string name, bool reverseSort)()
|
||||
template getMixin(T, string name, bool reverseSort)
|
||||
{
|
||||
return processRecursive!(T, "this." ~ name, "");
|
||||
enum getMixin = getMixinRecursive!(T, "this." ~ name, "");
|
||||
}
|
||||
|
||||
static string processRecursive(T, string name, string loopDepth)()
|
||||
template getMixinRecursive(T, string name, string loopDepth)
|
||||
{
|
||||
static if (!hasAliasing!(T))
|
||||
return processRaw("&" ~ name, name ~ ".sizeof");
|
||||
else
|
||||
static if (is(T U : U[]))
|
||||
{
|
||||
string s = "{ bool _AutoDataNullTest = " ~ name ~ " is null; " ~ processRaw("&_AutoDataNullTest", "bool.sizeof") ~ "}";
|
||||
static if (!hasAliasing!(U))
|
||||
s ~= processRaw(name ~ ".ptr", name ~ ".length");
|
||||
else
|
||||
s ~= "foreach (ref _AutoDataArrayItem" ~ loopDepth ~ "; " ~ name ~ ") {" ~ processRecursive!(U, "_AutoDataArrayItem" ~ loopDepth, loopDepth~"Item")() ~ "}";
|
||||
return s;
|
||||
}
|
||||
enum getMixinRecursive =
|
||||
"{ bool _AutoDataNullTest = " ~ name ~ " is null; " ~ getRawMixin!("&_AutoDataNullTest", "bool.sizeof") ~ "}" ~
|
||||
(!hasAliasing!(U) ?
|
||||
getRawMixin!(name ~ ".ptr", name ~ ".length")
|
||||
:
|
||||
"foreach (ref _AutoDataArrayItem" ~ loopDepth ~ "; " ~ name ~ ") {" ~ getMixinRecursive!(U, "_AutoDataArrayItem" ~ loopDepth, loopDepth~"Item") ~ "}"
|
||||
);
|
||||
else
|
||||
static if (is(typeof((new T).toHash())))
|
||||
//static assert(0, "aoeu: " ~ T.stringof);
|
||||
return name ~ ".processData!(void, ``, ``)(handler);";
|
||||
static if (!hasAliasing!(T))
|
||||
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
|
||||
else
|
||||
static if (is(T==struct))
|
||||
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
|
||||
else
|
||||
static if (is(T==class))
|
||||
enum getMixinRecursive = "if ("~name~" !is null) " ~ name ~ ".processData!(void, ``, ``)(handler);";
|
||||
else
|
||||
static assert(0, "Don't know how to process type: " ~ T.stringof);
|
||||
}
|
||||
@@ -137,9 +136,9 @@ struct HashDataHandler
|
||||
|
||||
MurmurHash2A hasher;
|
||||
|
||||
static string processRaw(string ptr, string len)
|
||||
template getRawMixin(string ptr, string len)
|
||||
{
|
||||
return "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
|
||||
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", to!int(" ~ len ~ "));";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +146,17 @@ struct EqualsDataHandler(O)
|
||||
{
|
||||
O other;
|
||||
|
||||
static string process(T, string name, bool reverseSort)()
|
||||
template nullCheck(T, string name)
|
||||
{
|
||||
string s;
|
||||
static if (is(T U : U[]))
|
||||
s ~= " if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
|
||||
s ~= "if (this." ~ name ~ " != _AutoDataOther." ~ name ~ ") return false;";
|
||||
return s;
|
||||
static if (is(typeof(T.init is null)))
|
||||
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
|
||||
else
|
||||
enum nullCheck = "";
|
||||
}
|
||||
|
||||
template getMixin(T, string name, bool reverseSort)
|
||||
{
|
||||
enum getMixin = nullCheck!(T, name) ~ "if (this." ~ name ~ " != _AutoDataOther." ~ name ~ ") return false;";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,32 +164,94 @@ struct CmpDataHandler(O)
|
||||
{
|
||||
O other;
|
||||
|
||||
static string process(T, string name, bool reverseSort)()
|
||||
template getMixin(T, string name, bool reverseSort)
|
||||
{
|
||||
string reverseStr = reverseSort ? "-" : "";
|
||||
string s;
|
||||
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
|
||||
}
|
||||
|
||||
template nullCheck(T, string name, string reverseStr)
|
||||
{
|
||||
static if (is(typeof(T.init is null)))
|
||||
enum nullCheck = "
|
||||
if (this."~name~" is null && _AutoDataOther."~name~" is null)
|
||||
{ /* skip */ }
|
||||
else
|
||||
if (this."~name~" is null && _AutoDataOther."~name~" !is null)
|
||||
return " ~ reverseStr ~ "(-1);
|
||||
else
|
||||
if (this."~name~" !is null && _AutoDataOther."~name~" is null)
|
||||
return " ~ reverseStr ~ "( 1);
|
||||
else";
|
||||
else
|
||||
enum nullCheck = "";
|
||||
}
|
||||
|
||||
template getMixinComposite(T, string name, bool reverseSort)
|
||||
{
|
||||
enum reverseStr = reverseSort ? "-" : "";
|
||||
static if (is(T U : U[]))
|
||||
s ~= "{ int _AutoDataCmp = cast(int)(this." ~ name ~ " !is null) - cast(int)(_AutoDataOther." ~ name ~ " !is null); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
enum arrCode = "{ int _AutoDataCmp = cast(int)(this." ~ name ~ " !is null) - cast(int)(_AutoDataOther." ~ name ~ " !is null); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
else
|
||||
enum arrCode = "";
|
||||
|
||||
static if (is(T == string) && is(std.string.cmp))
|
||||
s ~= "{ int _AutoDataCmp = std.string.cmp(this." ~ name ~ ", _AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
enum dataCode = "{ int _AutoDataCmp = std.string.cmp(this." ~ name ~ ", _AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
else
|
||||
static if (is(T == int))
|
||||
s ~= "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
|
||||
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
|
||||
else
|
||||
static if (is(typeof(T.opCmp)))
|
||||
s ~= "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
enum dataCode = nullCheck!(T, name, reverseStr)
|
||||
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(cast()_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
else
|
||||
s ~= "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
|
||||
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
|
||||
return s;
|
||||
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
|
||||
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
|
||||
enum code = arrCode ~ dataCode;
|
||||
}
|
||||
}
|
||||
|
||||
struct ToStringDataHandler
|
||||
{
|
||||
static string process(T, string name, bool reverseSort)()
|
||||
template getMixinSingle(T, string name)
|
||||
{
|
||||
return "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ 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 ~ " = `;" ~
|
||||
getMixinBody!(T, name) ~
|
||||
"_AutoDataResult ~= ` `;";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/// 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://dlang.org/ ) compiler to build.");
|
||||
|
||||
version(D_Version2):
|
||||
|
||||
version(DigitalMars)
|
||||
const DEFAULT_COMPILER = "dmd";
|
||||
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 compiler, flags;
|
||||
|
||||
void compile(string program)
|
||||
{
|
||||
stderr.writeln("* Building ", program);
|
||||
enforce(system(format("rdmd --build-only --compiler=%s %s %s", compiler, flags, program)) == 0, "Compilation of " ~ program ~ " failed");
|
||||
}
|
||||
|
||||
void test(string code, string extraFlags=null)
|
||||
{
|
||||
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(system(format("rdmd --force --compiler=%s -od. %s %s %s", compiler, flags, extraFlags, FN)) == 0, "Test failed");
|
||||
stderr.writeln(" >>> OK");
|
||||
}
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto programs = ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"];
|
||||
|
||||
compiler = getenv("DC");
|
||||
if (compiler is null)
|
||||
compiler = DEFAULT_COMPILER;
|
||||
|
||||
flags = getenv("DCFLAGS");
|
||||
if (flags is null)
|
||||
flags = DEFAULT_FLAGS;
|
||||
|
||||
string[] optionArgs, programArgs;
|
||||
foreach (arg; args[1..$])
|
||||
(arg.startsWith("-") ? optionArgs : programArgs) ~= arg;
|
||||
|
||||
if (optionArgs.length)
|
||||
flags = optionArgs.join(" ");
|
||||
if (programArgs.length)
|
||||
programs = programArgs;
|
||||
|
||||
stderr.writeln("* Checking for working compiler...");
|
||||
test(`
|
||||
void main() {}
|
||||
`);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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 internal[6];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
+1071
-281
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2012, 2013, 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 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;
|
||||
|
||||
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)
|
||||
{
|
||||
enforce(header.decompressedSize > 0, "Decompression with unknown size is unsupported");
|
||||
|
||||
lzma_stream strm;
|
||||
lzmaEnforce(lzma_alone_decoder(&strm, ulong.max), "lzma_alone_decoder");
|
||||
scope(exit) lzma_end(&strm);
|
||||
|
||||
auto outBuf = new ubyte[to!size_t(header.decompressedSize)];
|
||||
strm.next_out = outBuf.ptr;
|
||||
strm.avail_out = outBuf.length;
|
||||
|
||||
void decompress(in ubyte[] chunk)
|
||||
{
|
||||
strm.next_in = chunk.ptr;
|
||||
strm.avail_in = chunk.length;
|
||||
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_RUN), "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");
|
||||
|
||||
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;
|
||||
}
|
||||
+5
-3
@@ -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~"; }"; }
|
||||
@@ -31,7 +33,7 @@ public:
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void Add ( void * vdata, int len )
|
||||
void Add ( const(void) * vdata, int len )
|
||||
{
|
||||
ubyte * data = cast(ubyte*)vdata;
|
||||
m_size += len;
|
||||
@@ -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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -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(getBaseName(arg)));
|
||||
scope disassembler = new Disassembler(as, stripExtension(arg), stripExtension(baseName(arg)));
|
||||
disassembler.disassemble();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010, 2011 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 swfbinexport;
|
||||
|
||||
import std.file;
|
||||
import std.path;
|
||||
import std.string;
|
||||
import std.stdio;
|
||||
import std.exception;
|
||||
import swffile;
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
if (args.length == 1)
|
||||
throw new Exception("No file specified");
|
||||
foreach (arg; args[1..$])
|
||||
try
|
||||
{
|
||||
scope swf = SWFFile.read(cast(ubyte[])read(arg));
|
||||
bool found;
|
||||
foreach (ref tag; swf.tags)
|
||||
if (tag.type == TagType.DefineBinaryData)
|
||||
{
|
||||
found = true;
|
||||
enforce(tag.data.length >= 6);
|
||||
ushort id = *cast(short*)tag.data.ptr;
|
||||
ubyte[] bin = tag.data[6..$];
|
||||
std.file.write(format("%s-%d.bin", stripExtension(arg), id), bin);
|
||||
}
|
||||
enforce(found, "No DefineBinaryData tags found");
|
||||
}
|
||||
catch (Exception e)
|
||||
writefln("Error while processing %s: %s", arg, e);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 swfbinreplace;
|
||||
|
||||
import std.file;
|
||||
import std.conv;
|
||||
import swffile;
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
if (args.length != 4)
|
||||
throw new Exception("Bad arguments. Usage: swfbinreplace file.swf id data.bin");
|
||||
auto swf = SWFFile.read(cast(ubyte[])read(args[1]));
|
||||
auto id = to!ushort(args[2]);
|
||||
foreach (ref tag; swf.tags)
|
||||
if (tag.type == TagType.DefineBinaryData && tag.data.length >= 6 && *cast(short*)tag.data.ptr == id)
|
||||
{
|
||||
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;
|
||||
}
|
||||
throw new Exception("DefineBinaryData tag with specified ID not found in file");
|
||||
}
|
||||
+5
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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
|
||||
@@ -18,11 +18,14 @@
|
||||
|
||||
module swffile;
|
||||
|
||||
import std.conv;
|
||||
import std.exception;
|
||||
import std.zlib;
|
||||
import zlibx;
|
||||
version (HAVE_LZMA) import lzma;
|
||||
|
||||
/**
|
||||
* Implements a shallow representation of a .swf file.
|
||||
/**
|
||||
* Implements a shallow representation of a .swf file.
|
||||
* Loading and saving a .swf file using this class should produce
|
||||
* output identical to the input (aside zlib compression differences).
|
||||
*/
|
||||
@@ -36,12 +39,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 +65,7 @@ final class SWFFile
|
||||
{
|
||||
ushort type;
|
||||
ubyte[] data;
|
||||
uint length; // may be >data.length if file is truncated
|
||||
bool forceLongLength;
|
||||
}
|
||||
|
||||
@@ -78,14 +92,30 @@ 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");
|
||||
swf.frameSize = readRect();
|
||||
swf.frameRate = readU16();
|
||||
swf.frameCount = readU16();
|
||||
@@ -100,10 +130,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 +177,7 @@ private final class SWFReader
|
||||
if (length < 0x3F)
|
||||
t.forceLongLength = true;
|
||||
}
|
||||
t.length = length;
|
||||
t.data = cast(ubyte[])readRaw(length);
|
||||
return t;
|
||||
}
|
||||
@@ -152,73 +185,73 @@ private final class SWFReader
|
||||
|
||||
enum TagType
|
||||
{
|
||||
End = 0,
|
||||
ShowFrame = 1,
|
||||
DefineShape = 2,
|
||||
FreeCharacter = 3,
|
||||
PlaceObject = 4,
|
||||
RemoveObject = 5,
|
||||
DefineBits = 6,
|
||||
DefineButton = 7,
|
||||
JPEGTables = 8,
|
||||
SetBackgroundColor = 9,
|
||||
DefineFont = 10,
|
||||
DefineText = 11,
|
||||
DoAction = 12,
|
||||
DefineFontInfo = 13,
|
||||
DefineSound = 14,
|
||||
StartSound = 15,
|
||||
DefineButtonSound = 17,
|
||||
SoundStreamHead = 18,
|
||||
SoundStreamBlock = 19,
|
||||
DefineBitsLossless = 20,
|
||||
DefineBitsJPEG2 = 21,
|
||||
DefineShape2 = 22,
|
||||
DefineButtonCxform = 23,
|
||||
Protect = 24,
|
||||
PathsArePostScript = 25,
|
||||
PlaceObject2 = 26,
|
||||
RemoveObject2 = 28,
|
||||
DefineShape3 = 32,
|
||||
DefineText2 = 33,
|
||||
DefineButton2 = 34,
|
||||
DefineBitsJPEG3 = 35,
|
||||
DefineBitsLossless2 = 36,
|
||||
DefineSprite = 39,
|
||||
ProductInfo = 41,
|
||||
FrameLabel = 43,
|
||||
SoundStreamHead2 = 45,
|
||||
DefineMorphShape = 46,
|
||||
DefineFont2 = 48,
|
||||
DefineEditText = 37,
|
||||
ExportAssets = 56,
|
||||
ImportAssets = 57,
|
||||
EnableDebugger = 58,
|
||||
DoInitAction = 59,
|
||||
DefineVideoStream = 60,
|
||||
VideoFrame = 61,
|
||||
DefineFontInfo2 = 62,
|
||||
DebugID = 63,
|
||||
EnableDebugger2 = 64,
|
||||
ScriptLimits = 65,
|
||||
SetTabIndex = 66,
|
||||
FileAttributes = 69,
|
||||
PlaceObject3 = 70,
|
||||
ImportAssets2 = 71,
|
||||
DoABC = 72,
|
||||
DefineFontAlignZones = 73,
|
||||
CSMTextSettings = 74,
|
||||
DefineFont3 = 75,
|
||||
SymbolClass = 76,
|
||||
Metadata = 77,
|
||||
DefineScalingGrid = 78,
|
||||
DoABC2 = 82,
|
||||
DefineShape4 = 83,
|
||||
DefineMorphShape2 = 84,
|
||||
DefineSceneAndFrameLabelData = 86,
|
||||
DefineBinaryData = 87,
|
||||
DefineFontName = 88,
|
||||
DefineFont4 = 91
|
||||
End = 0,
|
||||
ShowFrame = 1,
|
||||
DefineShape = 2,
|
||||
FreeCharacter = 3,
|
||||
PlaceObject = 4,
|
||||
RemoveObject = 5,
|
||||
DefineBits = 6,
|
||||
DefineButton = 7,
|
||||
JPEGTables = 8,
|
||||
SetBackgroundColor = 9,
|
||||
DefineFont = 10,
|
||||
DefineText = 11,
|
||||
DoAction = 12,
|
||||
DefineFontInfo = 13,
|
||||
DefineSound = 14,
|
||||
StartSound = 15,
|
||||
DefineButtonSound = 17,
|
||||
SoundStreamHead = 18,
|
||||
SoundStreamBlock = 19,
|
||||
DefineBitsLossless = 20,
|
||||
DefineBitsJPEG2 = 21,
|
||||
DefineShape2 = 22,
|
||||
DefineButtonCxform = 23,
|
||||
Protect = 24,
|
||||
PathsArePostScript = 25,
|
||||
PlaceObject2 = 26,
|
||||
RemoveObject2 = 28,
|
||||
DefineShape3 = 32,
|
||||
DefineText2 = 33,
|
||||
DefineButton2 = 34,
|
||||
DefineBitsJPEG3 = 35,
|
||||
DefineBitsLossless2 = 36,
|
||||
DefineSprite = 39,
|
||||
ProductInfo = 41,
|
||||
FrameLabel = 43,
|
||||
SoundStreamHead2 = 45,
|
||||
DefineMorphShape = 46,
|
||||
DefineFont2 = 48,
|
||||
DefineEditText = 37,
|
||||
ExportAssets = 56,
|
||||
ImportAssets = 57,
|
||||
EnableDebugger = 58,
|
||||
DoInitAction = 59,
|
||||
DefineVideoStream = 60,
|
||||
VideoFrame = 61,
|
||||
DefineFontInfo2 = 62,
|
||||
DebugID = 63,
|
||||
EnableDebugger2 = 64,
|
||||
ScriptLimits = 65,
|
||||
SetTabIndex = 66,
|
||||
FileAttributes = 69,
|
||||
PlaceObject3 = 70,
|
||||
ImportAssets2 = 71,
|
||||
DoABC = 72,
|
||||
DefineFontAlignZones = 73,
|
||||
CSMTextSettings = 74,
|
||||
DefineFont3 = 75,
|
||||
SymbolClass = 76,
|
||||
Metadata = 77,
|
||||
DefineScalingGrid = 78,
|
||||
DoABC2 = 82,
|
||||
DefineShape4 = 83,
|
||||
DefineMorphShape2 = 84,
|
||||
DefineSceneAndFrameLabelData = 86,
|
||||
DefineBinaryData = 87,
|
||||
DefineFontName = 88,
|
||||
DefineFont4 = 91
|
||||
}
|
||||
|
||||
private final class SWFWriter
|
||||
@@ -230,28 +263,46 @@ private final class SWFWriter
|
||||
buf ~= swf.frameSize.bytes;
|
||||
buf ~= toArray(swf.frameRate);
|
||||
buf ~= toArray(swf.frameCount);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
module zlibx;
|
||||
|
||||
import std.zlib, etc.c.zlib;
|
||||
import std.zlib, etc.c.zlib, std.conv;
|
||||
static import etc.c.zlib;
|
||||
alias std.zlib.Z_SYNC_FLUSH Z_SYNC_FLUSH;
|
||||
|
||||
@@ -13,12 +13,12 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
|
||||
|
||||
auto destbuf = new ubyte[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)
|
||||
@@ -37,7 +37,7 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
|
||||
}
|
||||
if (zs.avail_out != 0)
|
||||
throw new Exception("Too little data in stream");
|
||||
|
||||
|
||||
err = etc.c.zlib.inflateEnd(&zs);
|
||||
if (err != Z_OK)
|
||||
goto Lerr;
|
||||
|
||||
Reference in New Issue
Block a user