byteboozer2 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +16 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +21 -0
- data/README.md +57 -0
- data/Rakefile +17 -0
- data/bin/console +7 -0
- data/bin/setup +5 -0
- data/byteboozer2.gemspec +30 -0
- data/lib/byteboozer2/cruncher.rb +650 -0
- data/lib/byteboozer2/file.rb +29 -0
- data/lib/byteboozer2/version.rb +3 -0
- data/lib/byteboozer2.rb +38 -0
- data/src/decruncher.inc +160 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff05a3ba454e03b5bb263f96bae094a8f1dbeb11
|
4
|
+
data.tar.gz: ed570a340f698b625493d14dbdf90c452352565d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a026071154ecd1351fe99a05fe100baa9e27e96d2680b03d20f73dea17c2569a87135911d20d75edcff8b7f3ab06d8bf3e331066078fa6409de6f258773ca6a
|
7
|
+
data.tar.gz: c6345de354bf6e928e91cfad8b76a1a5ac51a3b1a4a0359af370c49182b13a167fb8f6ee5cea9ce7fae09ca5bf9fcfa635605aa5d4e0032705ad2c7601c71f15
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Metrics/AbcSize:
|
2
|
+
Max: 174.1
|
3
|
+
Metrics/BlockNesting:
|
4
|
+
Max: 8
|
5
|
+
Metrics/ClassLength:
|
6
|
+
Max: 472
|
7
|
+
Metrics/CyclomaticComplexity:
|
8
|
+
Max: 46
|
9
|
+
Metrics/LineLength:
|
10
|
+
Max: 120
|
11
|
+
Metrics/MethodLength:
|
12
|
+
Max: 116
|
13
|
+
Metrics/PerceivedComplexity:
|
14
|
+
Max: 48
|
15
|
+
Style/ZeroLengthPredicate:
|
16
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
byteboozer2 (0.0.1)
|
5
|
+
activemodel (~> 4.2.6)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.2.6)
|
11
|
+
activesupport (= 4.2.6)
|
12
|
+
builder (~> 3.1)
|
13
|
+
activesupport (4.2.6)
|
14
|
+
i18n (~> 0.7)
|
15
|
+
json (~> 1.7, >= 1.7.7)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
18
|
+
tzinfo (~> 1.1)
|
19
|
+
ast (2.2.0)
|
20
|
+
awesome_print (1.6.1)
|
21
|
+
builder (3.2.2)
|
22
|
+
i18n (0.7.0)
|
23
|
+
json (1.8.3)
|
24
|
+
minitest (5.8.4)
|
25
|
+
parser (2.3.0.7)
|
26
|
+
ast (~> 2.2)
|
27
|
+
powerpack (0.1.1)
|
28
|
+
rainbow (2.1.0)
|
29
|
+
rake (11.1.1)
|
30
|
+
rubocop (0.38.0)
|
31
|
+
parser (>= 2.3.0.6, < 3.0)
|
32
|
+
powerpack (~> 0.1)
|
33
|
+
rainbow (>= 1.99.1, < 3.0)
|
34
|
+
ruby-progressbar (~> 1.7)
|
35
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
36
|
+
ruby-progressbar (1.7.5)
|
37
|
+
thread_safe (0.3.5)
|
38
|
+
tzinfo (1.2.2)
|
39
|
+
thread_safe (~> 0.1)
|
40
|
+
unicode-display_width (1.0.2)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
awesome_print (~> 1.6.1)
|
47
|
+
bundler (~> 1.11.2)
|
48
|
+
byteboozer2!
|
49
|
+
json (~> 1.8.3)
|
50
|
+
minitest (~> 5.8.4)
|
51
|
+
rake (~> 11.1.1)
|
52
|
+
rubocop (~> 0.38.0)
|
53
|
+
|
54
|
+
BUNDLED WITH
|
55
|
+
1.11.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (C) 2016 Pawel Krol
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# ByteBoozer2
|
2
|
+
|
3
|
+
`ByteBoozer2` package provides a native Ruby port of David Malmborg's [ByteBoozer 2.0](http://csdb.dk/release/?id=145031), a data cruncher for Commodore files written in C.
|
4
|
+
|
5
|
+
## Version
|
6
|
+
|
7
|
+
Version 0.01 (2016-03-28)
|
8
|
+
|
9
|
+
## Description
|
10
|
+
|
11
|
+
`ByteBoozer 2.0` is very much the same as `ByteBoozer 1.0`, but it generates smaller files and decrunches at about 2x the speed. An additional effort was put into keeping the encoder at about the same speed as before. Obviously it is incompatible with the version 1.0.
|
12
|
+
|
13
|
+
Compressed data is by default written into a file named with `.b2` suffix. Target file must not exist. If you want an executable, use `ecrunch`. If you want to decrunch yourself, use `crunch` or `rcrunch`. The decruncher should be called with `X` and `Y` registers loaded with a hi- and lo-byte address of the crunched file in a memory.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'byteboozer2', '~> 0.0.1'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle install
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install byteboozer2
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
The following operations are supported: crunching files, crunching files and making an executable with start address `$xxxx`, crunching files and relocating data to hex address `$xxxx`.
|
34
|
+
|
35
|
+
require 'byteboozer2'
|
36
|
+
include ByteBoozer2
|
37
|
+
|
38
|
+
# Crunch file:
|
39
|
+
crunch(file_name)
|
40
|
+
|
41
|
+
# Crunch file and make executable with start address $xxxx:
|
42
|
+
ecrunch(file_name, address)
|
43
|
+
|
44
|
+
# Crunch file and relocate data to hex address $xxxx:
|
45
|
+
rcrunch(file_name, address)
|
46
|
+
|
47
|
+
## Development
|
48
|
+
|
49
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `rake install`. To release a new version, update the version number in `version.rb`, and then run `rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/pawelkrol/) at [https://github.com/pawelkrol/byteboozer2_ruby](https://github.com/pawelkrol/byteboozer2_ruby).
|
54
|
+
|
55
|
+
## License
|
56
|
+
|
57
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << 'test'
|
6
|
+
t.libs << 'lib'
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rubygems/package_task'
|
11
|
+
spec = Gem::Specification.load(File.expand_path('../byteboozer2.gemspec', __FILE__))
|
12
|
+
Gem::PackageTask.new(spec).define
|
13
|
+
|
14
|
+
require 'rubocop/rake_task'
|
15
|
+
RuboCop::RakeTask.new
|
16
|
+
|
17
|
+
task default: [:rubocop, :test]
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/byteboozer2.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'byteboozer2/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'byteboozer2'
|
8
|
+
spec.version = ByteBoozer2::VERSION
|
9
|
+
spec.authors = ['Pawel Krol']
|
10
|
+
spec.email = ['djgruby@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'A data cruncher for Commodore files written in pure Ruby'
|
13
|
+
spec.description = 'This is a native Ruby port of David Malmborg\'s ByteBoozer 2.0.'
|
14
|
+
spec.homepage = 'https://github.com/pawelkrol/byteboozer2_ruby'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = 'exe'
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'awesome_print', '~> 1.6.1'
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.11.2'
|
24
|
+
spec.add_development_dependency 'json', '~> 1.8.3'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5.8.4'
|
26
|
+
spec.add_development_dependency 'rake', '~> 11.1.1'
|
27
|
+
spec.add_development_dependency 'rubocop', '~> 0.38.0'
|
28
|
+
|
29
|
+
spec.add_runtime_dependency 'activemodel', '~> 4.2.6'
|
30
|
+
end
|
@@ -0,0 +1,650 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module ByteBoozer2
|
5
|
+
# This class implements ByteBoozer's 2.0 crunching algorithm.
|
6
|
+
class Cruncher
|
7
|
+
include ActiveModel::Validations
|
8
|
+
|
9
|
+
attr_reader :address, :result
|
10
|
+
|
11
|
+
validates_numericality_of :address, only_integer: true,
|
12
|
+
allow_nil: true,
|
13
|
+
greater_than_or_equal_to: 0x0000,
|
14
|
+
less_than_or_equal_to: 0xffff
|
15
|
+
|
16
|
+
def self.crunch(*args)
|
17
|
+
new(*args).crunch
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(data, options = {})
|
21
|
+
@data = data
|
22
|
+
@executable = options[:executable] || false
|
23
|
+
@relocated = options[:relocated] || false
|
24
|
+
@address = options[:address] || 0x0000
|
25
|
+
|
26
|
+
raise ArgumentError unless valid?
|
27
|
+
end
|
28
|
+
|
29
|
+
def crunch!
|
30
|
+
@ibuf_size = @data.length - 2
|
31
|
+
|
32
|
+
# Load ibuf and clear context
|
33
|
+
@ibuf = @data[2..-1]
|
34
|
+
@context = Array.new(@ibuf_size) { new_node }
|
35
|
+
@link = Array.new(@ibuf_size) { 0 }
|
36
|
+
@rle_info = Array.new(@ibuf_size) { OpenStruct.new(value: 0, value_after: 0, length: 0) }
|
37
|
+
|
38
|
+
setup_help_structures
|
39
|
+
find_matches
|
40
|
+
@obuf = Array.new(MEM_SIZE) { 0 }
|
41
|
+
margin = write_output
|
42
|
+
|
43
|
+
pack_len = @put
|
44
|
+
file_len = @put
|
45
|
+
decr_len = 0
|
46
|
+
if @executable
|
47
|
+
decr_len = DECRUNCHER_LENGTH
|
48
|
+
file_len += decr_len + 2
|
49
|
+
else
|
50
|
+
file_len += 4
|
51
|
+
end
|
52
|
+
|
53
|
+
@result = Array.new(file_len) { 0 }
|
54
|
+
|
55
|
+
if @executable
|
56
|
+
start_address = 0x10000 - pack_len
|
57
|
+
transf_address = file_len + 0x6ff
|
58
|
+
|
59
|
+
decr_code[0x1f] = transf_address & 0xff # Transfer from...
|
60
|
+
decr_code[0x20] = transf_address >> 8
|
61
|
+
decr_code[0xbc] = start_address & 0xff # Depack from...
|
62
|
+
decr_code[0xbd] = start_address >> 8
|
63
|
+
decr_code[0x85] = @data[0] # Depack to...
|
64
|
+
decr_code[0x86] = @data[1]
|
65
|
+
decr_code[0xca] = @address & 0xff # Jump to...
|
66
|
+
decr_code[0xcb] = @address >> 8
|
67
|
+
|
68
|
+
@result[0] = 0x01
|
69
|
+
@result[1] = 0x08
|
70
|
+
|
71
|
+
@result[2, decr_len] = decr_code
|
72
|
+
|
73
|
+
@result[2 + decr_len, @put] = @obuf[0, @put]
|
74
|
+
else # Not executable...
|
75
|
+
# Experimantal decision of start address
|
76
|
+
# start_address = 0xfffa - pack_len - 2
|
77
|
+
start_address = (@data[1] << 8) | @data[0]
|
78
|
+
start_address += (@ibuf_size - pack_len - 2 + margin)
|
79
|
+
|
80
|
+
start_address = @address - pack_len - 2 if @relocated
|
81
|
+
|
82
|
+
@result[0] = start_address & 0xff # Load address
|
83
|
+
@result[1] = start_address >> 8
|
84
|
+
@result[2] = @data[0] # Depack to address
|
85
|
+
@result[3] = @data[1]
|
86
|
+
|
87
|
+
@result[4, @put] = @obuf[0, @put]
|
88
|
+
end
|
89
|
+
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
def crunch
|
94
|
+
@result if crunch!
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
DECRUNCHER = [
|
100
|
+
0x0b, 0x08, 0x00, 0x00, 0x9e, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00, 0x78, 0xa9, 0x34, 0x85,
|
101
|
+
0x01, 0xa2, 0xb7, 0xbd, 0x1e, 0x08, 0x95, 0x0f, 0xca, 0xd0, 0xf8, 0x4c, 0x10, 0x00, 0xbd, 0xd6,
|
102
|
+
0x07, 0x9d, 0x00, 0xff, 0xe8, 0xd0, 0xf7, 0xc6, 0x12, 0xc6, 0x15, 0xa5, 0x12, 0xc9, 0x07, 0xb0,
|
103
|
+
0xed, 0x20, 0xa0, 0x00, 0xb0, 0x17, 0x20, 0x8e, 0x00, 0x85, 0x36, 0xa0, 0x00, 0x20, 0xad, 0x00,
|
104
|
+
0x91, 0x77, 0xc8, 0xc0, 0x00, 0xd0, 0xf6, 0x20, 0x83, 0x00, 0xc8, 0xf0, 0xe4, 0x20, 0x8e, 0x00,
|
105
|
+
0xaa, 0xe8, 0xf0, 0x71, 0x86, 0x7b, 0xa9, 0x00, 0xe0, 0x03, 0x2a, 0x20, 0x9b, 0x00, 0x20, 0x9b,
|
106
|
+
0x00, 0xaa, 0xb5, 0xbf, 0xf0, 0x07, 0x20, 0x9b, 0x00, 0xb0, 0xfb, 0x30, 0x07, 0x49, 0xff, 0xa8,
|
107
|
+
0x20, 0xad, 0x00, 0xae, 0xa0, 0xff, 0x65, 0x77, 0x85, 0x74, 0x98, 0x65, 0x78, 0x85, 0x75, 0xa0,
|
108
|
+
0x00, 0xb9, 0xad, 0xde, 0x99, 0x00, 0x00, 0xc8, 0xc0, 0x00, 0xd0, 0xf5, 0x20, 0x83, 0x00, 0xd0,
|
109
|
+
0xa0, 0x18, 0x98, 0x65, 0x77, 0x85, 0x77, 0x90, 0x02, 0xe6, 0x78, 0x60, 0xa9, 0x01, 0x20, 0xa0,
|
110
|
+
0x00, 0x90, 0x05, 0x20, 0x9b, 0x00, 0x10, 0xf6, 0x60, 0x20, 0xa0, 0x00, 0x2a, 0x60, 0x06, 0xbe,
|
111
|
+
0xd0, 0x08, 0x48, 0x20, 0xad, 0x00, 0x2a, 0x85, 0xbe, 0x68, 0x60, 0xad, 0xed, 0xfe, 0xe6, 0xae,
|
112
|
+
0xd0, 0x02, 0xe6, 0xaf, 0x60, 0xa9, 0x37, 0x85, 0x01, 0x4c, 0x00, 0x00, 0x80, 0xdf, 0xfb, 0x00,
|
113
|
+
0x80, 0xef, 0xfd, 0x80, 0xf0
|
114
|
+
].freeze
|
115
|
+
|
116
|
+
DECRUNCHER_LENGTH = DECRUNCHER.length
|
117
|
+
MEM_SIZE = 0x10000
|
118
|
+
|
119
|
+
NUM_BITS_SHORT_0 = 3
|
120
|
+
NUM_BITS_SHORT_1 = 6
|
121
|
+
NUM_BITS_SHORT_2 = 8
|
122
|
+
NUM_BITS_SHORT_3 = 10
|
123
|
+
NUM_BITS_LONG_0 = 4
|
124
|
+
NUM_BITS_LONG_1 = 7
|
125
|
+
NUM_BITS_LONG_2 = 10
|
126
|
+
NUM_BITS_LONG_3 = 13
|
127
|
+
|
128
|
+
LEN_SHORT_0 = 1 << NUM_BITS_SHORT_0
|
129
|
+
LEN_SHORT_1 = 1 << NUM_BITS_SHORT_1
|
130
|
+
LEN_SHORT_2 = 1 << NUM_BITS_SHORT_2
|
131
|
+
LEN_SHORT_3 = 1 << NUM_BITS_SHORT_3
|
132
|
+
LEN_LONG_0 = 1 << NUM_BITS_LONG_0
|
133
|
+
LEN_LONG_1 = 1 << NUM_BITS_LONG_1
|
134
|
+
LEN_LONG_2 = 1 << NUM_BITS_LONG_2
|
135
|
+
LEN_LONG_3 = 1 << NUM_BITS_LONG_3
|
136
|
+
|
137
|
+
MAX_OFFSET = LEN_LONG_3
|
138
|
+
MAX_OFFSET_SHORT = LEN_SHORT_3
|
139
|
+
|
140
|
+
def cost_of_length(len)
|
141
|
+
if len == 1
|
142
|
+
1
|
143
|
+
elsif len >= 2 && len <= 3
|
144
|
+
3
|
145
|
+
elsif len >= 4 && len <= 7
|
146
|
+
5
|
147
|
+
elsif len >= 8 && len <= 15
|
148
|
+
7
|
149
|
+
elsif len >= 16 && len <= 31
|
150
|
+
9
|
151
|
+
elsif len >= 32 && len <= 63
|
152
|
+
11
|
153
|
+
elsif len >= 64 && len <= 127
|
154
|
+
13
|
155
|
+
elsif len >= 128 && len <= 255
|
156
|
+
14
|
157
|
+
else
|
158
|
+
ByteBoozer2.logger.warn 'cost_of_length got wrong value: #{len}'
|
159
|
+
10_000
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def calculate_cost_of_literal(old_cost, lit_len)
|
164
|
+
new_cost = old_cost + 8
|
165
|
+
|
166
|
+
# FIXME, what if lit_len > 255?
|
167
|
+
#
|
168
|
+
# FIXME, cost model for literals does not work
|
169
|
+
# Quick wins on short matches are prioritized before a longer
|
170
|
+
# literal run, which in the end results in a worse result
|
171
|
+
# Most obvious on files hard to crunch
|
172
|
+
case lit_len
|
173
|
+
when 1 then new_cost += 1
|
174
|
+
when 128 then new_cost += 1
|
175
|
+
when 2 then new_cost += 2
|
176
|
+
when 4 then new_cost += 2
|
177
|
+
when 8 then new_cost += 2
|
178
|
+
when 16 then new_cost += 2
|
179
|
+
when 32 then new_cost += 2
|
180
|
+
when 64 then new_cost += 2
|
181
|
+
end
|
182
|
+
|
183
|
+
new_cost
|
184
|
+
end
|
185
|
+
|
186
|
+
def calculate_cost_of_match(len, offset)
|
187
|
+
cost = 1 # Copy-bit
|
188
|
+
cost += cost_of_length(len - 1)
|
189
|
+
cost += 2 # num offset bits
|
190
|
+
cost += cost_of_offset(offset - 1, len - 1)
|
191
|
+
cost
|
192
|
+
end
|
193
|
+
|
194
|
+
def cost_of_offset(offset, len)
|
195
|
+
if len == 1
|
196
|
+
return NUM_BITS_SHORT_0 if cond_short_0(offset)
|
197
|
+
return NUM_BITS_SHORT_1 if cond_short_1(offset)
|
198
|
+
return NUM_BITS_SHORT_2 if cond_short_2(offset)
|
199
|
+
return NUM_BITS_SHORT_3 if cond_short_3(offset)
|
200
|
+
else
|
201
|
+
return NUM_BITS_LONG_0 if cond_long_0(offset)
|
202
|
+
return NUM_BITS_LONG_1 if cond_long_1(offset)
|
203
|
+
return NUM_BITS_LONG_2 if cond_long_2(offset)
|
204
|
+
return NUM_BITS_LONG_3 if cond_long_3(offset)
|
205
|
+
end
|
206
|
+
|
207
|
+
ByteBoozer2.logger.warn 'cost_of_offset got wrong offset: #{offset}'
|
208
|
+
10_000
|
209
|
+
end
|
210
|
+
|
211
|
+
def cond_short_0(o)
|
212
|
+
o >= 0 && o < LEN_SHORT_0
|
213
|
+
end
|
214
|
+
|
215
|
+
def cond_short_1(o)
|
216
|
+
o >= LEN_SHORT_0 && o < LEN_SHORT_1
|
217
|
+
end
|
218
|
+
|
219
|
+
def cond_short_2(o)
|
220
|
+
o >= LEN_SHORT_1 && o < LEN_SHORT_2
|
221
|
+
end
|
222
|
+
|
223
|
+
def cond_short_3(o)
|
224
|
+
o >= LEN_SHORT_2 && o < LEN_SHORT_3
|
225
|
+
end
|
226
|
+
|
227
|
+
def cond_long_0(o)
|
228
|
+
o >= 0 && o < LEN_LONG_0
|
229
|
+
end
|
230
|
+
|
231
|
+
def cond_long_1(o)
|
232
|
+
o >= LEN_LONG_0 && o < LEN_LONG_1
|
233
|
+
end
|
234
|
+
|
235
|
+
def cond_long_2(o)
|
236
|
+
o >= LEN_LONG_1 && o < LEN_LONG_2
|
237
|
+
end
|
238
|
+
|
239
|
+
def cond_long_3(o)
|
240
|
+
o >= LEN_LONG_2 && o < LEN_LONG_3
|
241
|
+
end
|
242
|
+
|
243
|
+
def decr_code
|
244
|
+
@decr_code ||= DECRUNCHER.dup
|
245
|
+
end
|
246
|
+
|
247
|
+
def find_matches
|
248
|
+
matches = Array.new(256) { OpenStruct.new(length: 0, offset: 0) }
|
249
|
+
|
250
|
+
last_node = new_node
|
251
|
+
|
252
|
+
get = @ibuf_size - 1
|
253
|
+
cur = @ibuf[get]
|
254
|
+
|
255
|
+
while get >= 0
|
256
|
+
# Clear matches for current position
|
257
|
+
matches.each do |match|
|
258
|
+
match.length = 0
|
259
|
+
match.offset = 0
|
260
|
+
end
|
261
|
+
|
262
|
+
cur = (cur << 8) & 0xffff # Table 65536 lookup
|
263
|
+
cur |= @ibuf[get - 1] if get > 0
|
264
|
+
scn = @first[cur]
|
265
|
+
scn = @link[scn]
|
266
|
+
|
267
|
+
longest_match = 0
|
268
|
+
|
269
|
+
if @rle_info[get].length == 0 # No RLE-match here...
|
270
|
+
# Scan until start of file, or max offset
|
271
|
+
while get - scn <= MAX_OFFSET && scn > 0 && longest_match < 255
|
272
|
+
# OK, we have a match of length 2 or longer, but max 255 or file start
|
273
|
+
len = 2
|
274
|
+
while len < 255 && scn >= len && @ibuf[scn - len] == @ibuf[get - len]
|
275
|
+
len += 1
|
276
|
+
end
|
277
|
+
|
278
|
+
# Calc offset
|
279
|
+
offset = get - scn
|
280
|
+
|
281
|
+
# Store match only if it's the longest so far
|
282
|
+
if len > longest_match
|
283
|
+
longest_match = len
|
284
|
+
|
285
|
+
# Store the match only if first (= best) of this length
|
286
|
+
while len >= 2 && matches[len].length == 0
|
287
|
+
# If len == 2, check against short offset!
|
288
|
+
if len > 2 || (len == 2 && offset <= MAX_OFFSET_SHORT)
|
289
|
+
matches[len].length = len
|
290
|
+
matches[len].offset = get - scn
|
291
|
+
end
|
292
|
+
|
293
|
+
len -= 1
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
scn = @link[scn] # Table 65535 lookup
|
298
|
+
end
|
299
|
+
|
300
|
+
@first[cur] = @link[@first[cur]] # Waste first entry
|
301
|
+
else # if RLE-match...
|
302
|
+
rle_len = @rle_info[get].length
|
303
|
+
rle_val_after = @rle_info[get].value_after
|
304
|
+
|
305
|
+
# First match with self-RLE, which is always one byte shorter than the RLE itself
|
306
|
+
len = rle_len - 1
|
307
|
+
if len > 1
|
308
|
+
len = 255 if len > 255
|
309
|
+
longest_match = len
|
310
|
+
|
311
|
+
# Store the match
|
312
|
+
while len >= 2
|
313
|
+
matches[len].length = len
|
314
|
+
matches[len].offset = 1
|
315
|
+
|
316
|
+
len -= 1
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Search for more RLE-matches, scan until start of file, or max offset...
|
321
|
+
while get - scn <= MAX_OFFSET && scn > 0 && longest_match < 255
|
322
|
+
|
323
|
+
# Check for longer matches with same value and after...
|
324
|
+
# FIXME: That is not what it does, is it?!
|
325
|
+
if @rle_info[scn].length > longest_match && rle_len > longest_match
|
326
|
+
offset = get - scn
|
327
|
+
len = @rle_info[scn].length
|
328
|
+
|
329
|
+
len = rle_len if len > rle_len
|
330
|
+
|
331
|
+
if len > 2 || (len == 2 && offset <= MAX_OFFSET_SHORT)
|
332
|
+
matches[len].length = len
|
333
|
+
matches[len].offset = offset
|
334
|
+
|
335
|
+
longest_match = len
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Check for matches beyond the RLE...
|
340
|
+
if @rle_info[scn].length >= rle_len && @rle_info[scn].value_after == rle_val_after
|
341
|
+
|
342
|
+
# Here is a match that goes beyond the RLE...
|
343
|
+
# Find out correct offset to use value_after, then search further to see if more bytes equal
|
344
|
+
len = rle_len
|
345
|
+
offset = get - scn + @rle_info[scn].length - rle_len
|
346
|
+
|
347
|
+
if offset <= MAX_OFFSET
|
348
|
+
while len < 255 && get >= offset + len && @ibuf[get - offset - len] == @ibuf[get - len]
|
349
|
+
len += 1
|
350
|
+
end
|
351
|
+
|
352
|
+
if len > longest_match
|
353
|
+
longest_match = len
|
354
|
+
|
355
|
+
# Store the match only if first (= best) of this length
|
356
|
+
while len >= 2 && matches[len].length == 0
|
357
|
+
# If len == 2, check against short offset!
|
358
|
+
if len > 2 || (len == 2 && offset <= MAX_OFFSET_SHORT)
|
359
|
+
matches[len].length = len
|
360
|
+
matches[len].offset = offset
|
361
|
+
end
|
362
|
+
|
363
|
+
len -= 1
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
scn = @link[scn] # Table 65535 lookup
|
370
|
+
end
|
371
|
+
|
372
|
+
if @rle_info[get].length > 2
|
373
|
+
# Expand RLE to next position
|
374
|
+
@rle_info[get - 1].length = @rle_info[get].length - 1
|
375
|
+
@rle_info[get - 1].value = @rle_info[get].value
|
376
|
+
@rle_info[get - 1].value_after = @rle_info[get].value_after
|
377
|
+
else
|
378
|
+
# End of RLE, advance link
|
379
|
+
@first[cur] = @link[@first[cur]] # Waste first entry
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
# Now that we have all matches from this position, visit all nodes reached by the matches
|
384
|
+
255.downto(1).to_a.each do |i|
|
385
|
+
# Find all matches we stored
|
386
|
+
len = matches[i].length
|
387
|
+
offset = matches[i].offset
|
388
|
+
|
389
|
+
next if len == 0
|
390
|
+
target_i = get - len + 1
|
391
|
+
target = @context[target_i]
|
392
|
+
|
393
|
+
# Calculate cost for this jump
|
394
|
+
current_cost = last_node.cost
|
395
|
+
current_cost += calculate_cost_of_match(len, offset)
|
396
|
+
|
397
|
+
# If this match is first or cheapest way to get here, then update node
|
398
|
+
next if target.cost != 0 && target.cost <= current_cost
|
399
|
+
target.cost = current_cost
|
400
|
+
target.next = get + 1
|
401
|
+
target.lit_len = 0
|
402
|
+
target.offset = offset
|
403
|
+
end
|
404
|
+
|
405
|
+
# Calc the cost for this node if using one more literal
|
406
|
+
lit_len = last_node.lit_len + 1
|
407
|
+
lit_cost = calculate_cost_of_literal(last_node.cost, lit_len)
|
408
|
+
|
409
|
+
# If literal run is first or cheapest way to get here, then update node
|
410
|
+
this = @context[get]
|
411
|
+
if this.cost == 0 || this.cost >= lit_cost
|
412
|
+
this.cost = lit_cost
|
413
|
+
this.next = get + 1
|
414
|
+
this.lit_len = lit_len
|
415
|
+
end
|
416
|
+
|
417
|
+
last_node.cost = this.cost
|
418
|
+
last_node.next = this.next
|
419
|
+
last_node.lit_len = this.lit_len
|
420
|
+
|
421
|
+
# Loop to the next position
|
422
|
+
get -= 1
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def new_node
|
427
|
+
OpenStruct.new(cost: 0, next: 0, lit_len: 0, offset: 0)
|
428
|
+
end
|
429
|
+
|
430
|
+
def setup_help_structures
|
431
|
+
# Setup RLE-info
|
432
|
+
get = @ibuf_size - 1
|
433
|
+
while get > 0
|
434
|
+
|
435
|
+
cur = @ibuf[get]
|
436
|
+
if cur == @ibuf[get - 1]
|
437
|
+
|
438
|
+
len = 2
|
439
|
+
len += 1 while get >= len && cur == @ibuf[get - len]
|
440
|
+
|
441
|
+
@rle_info[get].length = len
|
442
|
+
@rle_info[get].value_after = get >= len ? @ibuf[get - len] : cur # Avoid accessing @ibuf[-1]
|
443
|
+
|
444
|
+
get -= len
|
445
|
+
else
|
446
|
+
get -= 1
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# Setup linked list
|
451
|
+
@first = Array.new(MEM_SIZE) { 0 }
|
452
|
+
@last = Array.new(MEM_SIZE) { 0 }
|
453
|
+
|
454
|
+
get = @ibuf_size - 1
|
455
|
+
cur = @ibuf[get]
|
456
|
+
|
457
|
+
while get > 0
|
458
|
+
cur = ((cur << 8) | @ibuf[get - 1]) & 0xffff
|
459
|
+
|
460
|
+
if @first[cur] == 0
|
461
|
+
@first[cur] = @last[cur] = get
|
462
|
+
else
|
463
|
+
@link[@last[cur]] = get
|
464
|
+
@last[cur] = get
|
465
|
+
end
|
466
|
+
|
467
|
+
get -= @rle_info[get].length == 0 ? 1 : @rle_info[get].length - 1 # if RLE-match...
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def wbit(bit)
|
472
|
+
if @cur_cnt == 0
|
473
|
+
@obuf[@cur_index] = @cur_byte
|
474
|
+
@cur_index = @put
|
475
|
+
@cur_cnt = 8
|
476
|
+
@cur_byte = 0
|
477
|
+
@put += 1
|
478
|
+
end
|
479
|
+
|
480
|
+
@cur_byte <<= 1
|
481
|
+
@cur_byte |= bit & 1
|
482
|
+
@cur_cnt -= 1
|
483
|
+
end
|
484
|
+
|
485
|
+
def wbyte(b)
|
486
|
+
@obuf[@put] = b
|
487
|
+
@put += 1
|
488
|
+
end
|
489
|
+
|
490
|
+
def wbytes(get, len)
|
491
|
+
(0..len - 1).each do
|
492
|
+
wbyte(@ibuf[get])
|
493
|
+
get += 1
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
def wflush
|
498
|
+
while @cur_cnt != 0
|
499
|
+
@cur_byte <<= 1
|
500
|
+
@cur_cnt -= 1
|
501
|
+
end
|
502
|
+
@obuf[@cur_index] = @cur_byte
|
503
|
+
end
|
504
|
+
|
505
|
+
def wlength(len)
|
506
|
+
# return if len == 0 # Should never happen
|
507
|
+
|
508
|
+
bit = 0x80
|
509
|
+
bit >>= 1 while len & bit == 0
|
510
|
+
|
511
|
+
while bit > 1
|
512
|
+
wbit(1)
|
513
|
+
bit >>= 1
|
514
|
+
wbit(len & bit == 0 ? 0 : 1)
|
515
|
+
end
|
516
|
+
|
517
|
+
wbit(0) if len < 0x80
|
518
|
+
end
|
519
|
+
|
520
|
+
def woffset(offset, len)
|
521
|
+
i = 0
|
522
|
+
n = 0
|
523
|
+
|
524
|
+
if len == 1
|
525
|
+
if cond_short_0(offset)
|
526
|
+
i = 0
|
527
|
+
n = NUM_BITS_SHORT_0
|
528
|
+
end
|
529
|
+
if cond_short_1(offset)
|
530
|
+
i = 1
|
531
|
+
n = NUM_BITS_SHORT_1
|
532
|
+
end
|
533
|
+
if cond_short_2(offset)
|
534
|
+
i = 2
|
535
|
+
n = NUM_BITS_SHORT_2
|
536
|
+
end
|
537
|
+
if cond_short_3(offset)
|
538
|
+
i = 3
|
539
|
+
n = NUM_BITS_SHORT_3
|
540
|
+
end
|
541
|
+
else
|
542
|
+
if cond_long_0(offset)
|
543
|
+
i = 0
|
544
|
+
n = NUM_BITS_LONG_0
|
545
|
+
end
|
546
|
+
if cond_long_1(offset)
|
547
|
+
i = 1
|
548
|
+
n = NUM_BITS_LONG_1
|
549
|
+
end
|
550
|
+
if cond_long_2(offset)
|
551
|
+
i = 2
|
552
|
+
n = NUM_BITS_LONG_2
|
553
|
+
end
|
554
|
+
if cond_long_3(offset)
|
555
|
+
i = 3
|
556
|
+
n = NUM_BITS_LONG_3
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
# First write number of bits
|
561
|
+
wbit(i & 2 == 0 ? 0 : 1)
|
562
|
+
wbit(i & 1 == 0 ? 0 : 1)
|
563
|
+
|
564
|
+
if n >= 8 # Offset is 2 bytes
|
565
|
+
|
566
|
+
# Then write the bits less than 8
|
567
|
+
b = 1 << n
|
568
|
+
while b > 0x100
|
569
|
+
b >>= 1
|
570
|
+
wbit(b & offset == 0 ? 0 : 1)
|
571
|
+
end
|
572
|
+
|
573
|
+
# Finally write a whole byte, if necessary
|
574
|
+
wbyte(offset & 255 ^ 255) # Inverted (!)
|
575
|
+
offset >>= 8
|
576
|
+
|
577
|
+
else # Offset is 1 byte
|
578
|
+
|
579
|
+
# Then write the bits less than 8
|
580
|
+
b = 1 << n
|
581
|
+
while b > 1
|
582
|
+
b >>= 1
|
583
|
+
wbit(b & offset == 0 ? 1 : 0) # Inverted (!)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def write_output
|
589
|
+
@put = 0
|
590
|
+
|
591
|
+
@cur_byte = 0
|
592
|
+
@cur_cnt = 8
|
593
|
+
@cur_index = @put
|
594
|
+
@put += 1
|
595
|
+
|
596
|
+
max_diff = 0
|
597
|
+
|
598
|
+
need_copy_bit = true
|
599
|
+
|
600
|
+
i = 0
|
601
|
+
while i < @ibuf_size
|
602
|
+
link = @context[i].next
|
603
|
+
# cost = @context[i].cost
|
604
|
+
lit_len = @context[i].lit_len
|
605
|
+
offset = @context[i].offset
|
606
|
+
|
607
|
+
if lit_len == 0
|
608
|
+
# Put match
|
609
|
+
len = link - i
|
610
|
+
|
611
|
+
ByteBoozer2.logger.debug format('$%04x: Mat(%i, %i)', i, len, offset)
|
612
|
+
|
613
|
+
wbit(1) if need_copy_bit
|
614
|
+
wlength(len - 1)
|
615
|
+
woffset(offset - 1, len - 1)
|
616
|
+
|
617
|
+
i = link
|
618
|
+
|
619
|
+
need_copy_bit = true
|
620
|
+
else
|
621
|
+
# Put literal
|
622
|
+
need_copy_bit = false
|
623
|
+
|
624
|
+
while lit_len > 0
|
625
|
+
len = lit_len < 255 ? lit_len : 255
|
626
|
+
|
627
|
+
ByteBoozer2.logger.debug format('$%04x: Lit(%i)', i, len)
|
628
|
+
|
629
|
+
wbit(0)
|
630
|
+
wlength(len)
|
631
|
+
wbytes(i, len)
|
632
|
+
|
633
|
+
need_copy_bit = true if lit_len == 255
|
634
|
+
|
635
|
+
lit_len -= len
|
636
|
+
i += len
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
max_diff = i - @put if i - @put > max_diff
|
641
|
+
end
|
642
|
+
|
643
|
+
wbit(1)
|
644
|
+
wlength(0xff)
|
645
|
+
wflush
|
646
|
+
|
647
|
+
max_diff - i + @put
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ByteBoozer2
|
2
|
+
# This class implements file handling related helper methods.
|
3
|
+
class File
|
4
|
+
attr_accessor :data, :name
|
5
|
+
|
6
|
+
def self.load(*args)
|
7
|
+
new(*args).tap(&:read)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.save(*args)
|
11
|
+
new(*args).tap(&:write)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name, data = nil)
|
15
|
+
@name = name
|
16
|
+
@data = data
|
17
|
+
end
|
18
|
+
|
19
|
+
def read
|
20
|
+
@data = IO.binread(@name).unpack('C*')
|
21
|
+
end
|
22
|
+
|
23
|
+
def write
|
24
|
+
::File.open(@name, ::File::WRONLY | ::File::CREAT | ::File::EXCL, binmode: true, encoding: 'ASCII-8BIT') do |file|
|
25
|
+
file.write @data.pack('C*')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/byteboozer2.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'byteboozer2/cruncher'
|
2
|
+
require 'byteboozer2/file'
|
3
|
+
require 'byteboozer2/version'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
# This module provides compression methods available in ByteBoozer 2.0.
|
7
|
+
module ByteBoozer2
|
8
|
+
def crunch(file_name)
|
9
|
+
compress(file_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ecrunch(file_name, address)
|
13
|
+
compress(file_name, address: address, executable: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.logger
|
17
|
+
@logger ||= Logger.new('byteboozer2.log').tap do |log|
|
18
|
+
log.level = Logger::DEBUG
|
19
|
+
log.progname = 'ByteBoozer2'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.log_level=(level)
|
24
|
+
logger.level = level
|
25
|
+
end
|
26
|
+
|
27
|
+
def rcrunch(file_name, address)
|
28
|
+
compress(file_name, address: address, relocated: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def compress(file_name, *options)
|
34
|
+
file = ByteBoozer2::File.load(file_name)
|
35
|
+
result = ByteBoozer2::Cruncher.crunch(file.data, *options)
|
36
|
+
ByteBoozer2::File.save(file_name + '.b2', result)
|
37
|
+
end
|
38
|
+
end
|
data/src/decruncher.inc
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
; ByteBoozer 2.0 Decruncher (C) 2014 David Malmborg
|
2
|
+
|
3
|
+
; call: Y = AddrLo
|
4
|
+
; X = AddrHi
|
5
|
+
|
6
|
+
;Variables.. #Bytes
|
7
|
+
zp_base = $02 ; -
|
8
|
+
bits = zp_base ;1
|
9
|
+
put = zp_base+2 ;2
|
10
|
+
|
11
|
+
#macro GetNextBit() {.(
|
12
|
+
asl bits
|
13
|
+
bne DgEnd
|
14
|
+
jsr GetNewBits
|
15
|
+
DgEnd
|
16
|
+
.)}
|
17
|
+
|
18
|
+
#macro GetLen() {.(
|
19
|
+
lda #1
|
20
|
+
GlLoop
|
21
|
+
.GetNextBit()
|
22
|
+
bcc GlEnd
|
23
|
+
.GetNextBit()
|
24
|
+
rol
|
25
|
+
bpl GlLoop
|
26
|
+
GlEnd
|
27
|
+
.)}
|
28
|
+
|
29
|
+
Decrunch
|
30
|
+
sty Get1+1
|
31
|
+
sty Get2+1
|
32
|
+
sty Get3+1
|
33
|
+
stx Get1+2
|
34
|
+
stx Get2+2
|
35
|
+
stx Get3+2
|
36
|
+
|
37
|
+
ldx #0
|
38
|
+
jsr GetNewBits
|
39
|
+
sty put-1,x
|
40
|
+
cpx #2
|
41
|
+
bcc *-7
|
42
|
+
lda #$80
|
43
|
+
sta bits
|
44
|
+
DLoop
|
45
|
+
.GetNextBit()
|
46
|
+
bcs Match
|
47
|
+
Literal
|
48
|
+
; Literal run.. get length.
|
49
|
+
.GetLen()
|
50
|
+
sta LLen+1
|
51
|
+
|
52
|
+
ldy #0
|
53
|
+
LLoop
|
54
|
+
Get3 lda $feed,x
|
55
|
+
inx
|
56
|
+
bne *+5
|
57
|
+
jsr GnbInc
|
58
|
+
L1 sta (put),y
|
59
|
+
iny
|
60
|
+
LLen cpy #0
|
61
|
+
bne LLoop
|
62
|
+
|
63
|
+
clc
|
64
|
+
tya
|
65
|
+
adc put
|
66
|
+
sta put
|
67
|
+
bcc *+4
|
68
|
+
inc put+1
|
69
|
+
|
70
|
+
iny
|
71
|
+
beq DLoop
|
72
|
+
|
73
|
+
; Has to continue with a match..
|
74
|
+
|
75
|
+
Match
|
76
|
+
; Match.. get length.
|
77
|
+
.GetLen()
|
78
|
+
sta MLen+1
|
79
|
+
|
80
|
+
; Length 255 -> EOF
|
81
|
+
cmp #$ff
|
82
|
+
beq End
|
83
|
+
|
84
|
+
; Get num bits
|
85
|
+
cmp #2
|
86
|
+
lda #0
|
87
|
+
rol
|
88
|
+
.GetNextBit()
|
89
|
+
rol
|
90
|
+
.GetNextBit()
|
91
|
+
rol
|
92
|
+
tay
|
93
|
+
lda Tab,y
|
94
|
+
beq M8
|
95
|
+
|
96
|
+
; Get bits < 8
|
97
|
+
M_1 .GetNextBit()
|
98
|
+
rol
|
99
|
+
bcs M_1
|
100
|
+
bmi MShort
|
101
|
+
M8
|
102
|
+
; Get byte
|
103
|
+
eor #$ff
|
104
|
+
tay
|
105
|
+
Get2 lda $feed,x
|
106
|
+
inx
|
107
|
+
bne *+5
|
108
|
+
jsr GnbInc
|
109
|
+
jmp Mdone
|
110
|
+
MShort
|
111
|
+
ldy #$ff
|
112
|
+
Mdone
|
113
|
+
;clc
|
114
|
+
adc put
|
115
|
+
sta MLda+1
|
116
|
+
tya
|
117
|
+
adc put+1
|
118
|
+
sta MLda+2
|
119
|
+
|
120
|
+
ldy #$ff
|
121
|
+
MLoop iny
|
122
|
+
MLda lda $beef,y
|
123
|
+
sta (put),y
|
124
|
+
MLen cpy #0
|
125
|
+
bne MLoop
|
126
|
+
|
127
|
+
;sec
|
128
|
+
tya
|
129
|
+
adc put
|
130
|
+
sta put
|
131
|
+
bcc *+4
|
132
|
+
inc put+1
|
133
|
+
|
134
|
+
jmp DLoop
|
135
|
+
|
136
|
+
End rts
|
137
|
+
|
138
|
+
GetNewBits
|
139
|
+
Get1 ldy $feed,x
|
140
|
+
sty bits
|
141
|
+
rol bits
|
142
|
+
inx
|
143
|
+
bne GnbEnd
|
144
|
+
GnbInc inc Get1+2
|
145
|
+
inc Get2+2
|
146
|
+
inc Get3+2
|
147
|
+
GnbEnd
|
148
|
+
rts
|
149
|
+
|
150
|
+
Tab
|
151
|
+
; Short offsets
|
152
|
+
.byte %11011111 ; 3
|
153
|
+
.byte %11111011 ; 6
|
154
|
+
.byte %00000000 ; 8
|
155
|
+
.byte %10000000 ; 10
|
156
|
+
; Long offsets
|
157
|
+
.byte %11101111 ; 4
|
158
|
+
.byte %11111101 ; 7
|
159
|
+
.byte %10000000 ; 10
|
160
|
+
.byte %11110000 ; 13
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: byteboozer2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pawel Krol
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: awesome_print
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.1
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.6.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.11.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.11.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.8.3
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.8.3
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.8.4
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.8.4
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 11.1.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 11.1.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.38.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.38.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activemodel
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.2.6
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.2.6
|
111
|
+
description: This is a native Ruby port of David Malmborg's ByteBoozer 2.0.
|
112
|
+
email:
|
113
|
+
- djgruby@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- bin/console
|
126
|
+
- bin/setup
|
127
|
+
- byteboozer2.gemspec
|
128
|
+
- lib/byteboozer2.rb
|
129
|
+
- lib/byteboozer2/cruncher.rb
|
130
|
+
- lib/byteboozer2/file.rb
|
131
|
+
- lib/byteboozer2/version.rb
|
132
|
+
- src/decruncher.inc
|
133
|
+
homepage: https://github.com/pawelkrol/byteboozer2_ruby
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata: {}
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
requirements: []
|
152
|
+
rubyforge_project:
|
153
|
+
rubygems_version: 2.4.8
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: A data cruncher for Commodore files written in pure Ruby
|
157
|
+
test_files: []
|