sabrina 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e428137b1b9c97072a5d0094df0d556abe7ced83
4
+ data.tar.gz: 558afde8551a7c6ecfea61ccbf4d33d3701d1945
5
+ SHA512:
6
+ metadata.gz: c71b152645a6f7805c3fccc0c5969e3593e70fcb9173b4d8c8923aa14820dd682501c774ae163b4d7d83f07ab471c1121120f36278080eb34b9ca7c81a06f8d3
7
+ data.tar.gz: 473823ca339a40dac88ca86a3beb6d65258c3e4c5bced4c57d36b53d6f9a9077881650f5d4c6604bd60e3f253d03e39944dc53c61f66a7faa4c22c0ef5bb5bf4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Winterbraid
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.
@@ -0,0 +1,46 @@
1
+ # {include:file:README.rdoc}
2
+ module Sabrina; end
3
+
4
+ require 'set'
5
+ require 'fileutils'
6
+ require 'json'
7
+
8
+ begin
9
+ require 'oily_png'
10
+ Sabrina.const_set(:PNG, 'oily_png')
11
+ rescue
12
+ require 'chunky_png'
13
+ Sabrina.const_set(:PNG, 'chunky_png')
14
+ end
15
+
16
+ require 'sabrina/meta.rb'
17
+
18
+ require 'sabrina/config.rb'
19
+ require 'sabrina/config/main.rb'
20
+ require 'sabrina/config/charmap_in.rb'
21
+ require 'sabrina/config/charmap_out.rb'
22
+ require 'sabrina/config/charmap_out_special.rb'
23
+
24
+ require 'sabrina/lz77.rb'
25
+ require 'sabrina/children_manager.rb'
26
+
27
+ require 'sabrina/bytestream/byte_input.rb'
28
+ require 'sabrina/bytestream/byte_output.rb'
29
+ require 'sabrina/bytestream/rom_operations.rb'
30
+ require 'sabrina/bytestream.rb'
31
+
32
+ require 'sabrina/sprite.rb'
33
+ require 'sabrina/palette.rb'
34
+ require 'sabrina/gba_string.rb'
35
+
36
+ require 'sabrina/rom.rb'
37
+ require 'sabrina/monster.rb'
38
+
39
+ require 'sabrina/plugin/register.rb'
40
+ require 'sabrina/plugin/load.rb'
41
+ require 'sabrina/plugin.rb'
42
+
43
+ require 'sabrina/plugins/spritesheet.rb'
44
+ require 'sabrina/plugins/stats.rb'
45
+
46
+ Sabrina::Config.load_user_config
@@ -0,0 +1,266 @@
1
+ module Sabrina
2
+ # A generic class for dealing with byte data related to a ROM.
3
+ #
4
+ # It is required that +:rom+ and either +:table+ and +:index+ (recommended)
5
+ # or +:offset+ be set in order to enable writing back to a ROM file.
6
+ #
7
+ # There should be no need to use this class directly.
8
+ class Bytestream
9
+ extend ByteInput
10
+ include ByteOutput
11
+ include RomOperations
12
+
13
+ # Stores an array of debug strings related to the latest write to ROM.
14
+ #
15
+ # @return [Array]
16
+ # @see RomOperations#write_to_rom
17
+ attr_reader :last_write
18
+
19
+ # The ROM being used for operations.
20
+ #
21
+ # @return [Rom]
22
+ # @see #rom=
23
+ attr_reader :rom
24
+
25
+ # The index in the ROM table.
26
+ #
27
+ # @return [Integer]
28
+ # @see #index=
29
+ attr_reader :index
30
+
31
+ # The table code to search for data.
32
+ #
33
+ # @return [String or Symbol]
34
+ # @see #table=
35
+ attr_reader :table
36
+
37
+ # The directory for saving files. Should be created if specified but not
38
+ # existing.
39
+ #
40
+ # @return [String]
41
+ attr_accessor :work_dir
42
+
43
+ # The filename to save to, sans the extension. Subclass +to_file+ methods
44
+ # should take care of adding the right extension.
45
+ #
46
+ # @return [String]
47
+ attr_accessor :filename
48
+
49
+ class << self
50
+ # Takes an integer or a "0x"- or "x"-prefixed string and
51
+ # converts it to a valid numerical offset.
52
+ #
53
+ # @param [Integer] p_offset
54
+ # @return [Integer]
55
+ def parse_offset(p_offset)
56
+ o = p_offset.to_s.downcase
57
+
58
+ if /[^0-9a-fx]/ =~ o
59
+ fail "\'#{p_offset}\' does not look like a valid offset." \
60
+ ' Supply an integer, or a hex optionally prefixed' \
61
+ " with \'0x\' or \'x\'."
62
+ end
63
+
64
+ /[a-fx]/ =~ o ? p_offset.rpartition('x').last.hex : p_offset.to_i
65
+ end
66
+ end
67
+
68
+ # Returns a new instance of Bytestream, taking a hash of arguments
69
+ # as the option. The hash may contain the following keys, all of
70
+ # which are optional and init to +nil+ or +false+. Any invalid keys
71
+ # should be ignored.
72
+ #
73
+ # Subclasses should call this method with subclass-specific data.
74
+ # There should be no need to call this method directly.
75
+ #
76
+ # Subclasses should override the +load_settings(h)+ private method
77
+ # to allow for additional options.
78
+ #
79
+ # @param [Hash] h
80
+ # @option h [Object] :representation The internal representation
81
+ # of the byte data. Subclasses should be able to convert bytes
82
+ # to and from the representation via the {ByteOutput#present} and
83
+ # {ByteOutput#generate_bytes} methods.
84
+ # @option h [Boolean] :pointer_mode Whether to expect pointers in the
85
+ # table instead of actual data. This voids +:index_length+.
86
+ # @option h [Boolean] :lz77 Whether to read and write ROM data as
87
+ # {Lz77}-compressed.
88
+ # @option h [Boolean] :is_gba_string Whether to read ROM data as
89
+ # a GBA-encoded, 0xFF-terminated string.
90
+ # @option h [Rom] :rom A ROM to be used for reading and writing data.
91
+ # @option h [Symbol, String] :table The table to read offset data from.
92
+ # This should be the name of an option specified in the +rom_data+ hash.
93
+ # +:table+ and +:index+ will take precedence over +:offset+ if present.
94
+ # This is the recommended way of dealing with monster data,
95
+ # as it will allow automagically updating the offset upon writing
96
+ # or switching ROMs.
97
+ # @option h [Integer] :index The index to read from the table.
98
+ # See {#index=} for details.
99
+ # @option h [Integer] :index_length The length of a single table
100
+ # entry, necessary for seeking.
101
+ # @option h [Integer] :offset The position in ROM to read and write to.
102
+ # This will only be used if +:table+ or +:index+ are not present.
103
+ # See {Bytestream.parse_offset} for details.
104
+ # @option h [Integer] :length The byte length to read from the ROM.
105
+ # This will be ignored if +:lz77+ compression is enabled.
106
+ def initialize(h = {})
107
+ # Subclasses may want to override defaults, hence ||=
108
+ @work_dir ||= nil
109
+ @filename ||= nil
110
+
111
+ @representation ||= nil
112
+ @bytes_cache ||= nil
113
+ @lz77_cache ||= nil
114
+ @length_cache ||= nil
115
+
116
+ @last_write ||= nil
117
+
118
+ @old_offset ||= nil
119
+ @old_length ||= nil
120
+
121
+ @lz77 ||= false
122
+ @is_gba_string ||= false
123
+
124
+ @rom ||= nil
125
+ @table ||= nil
126
+ @index ||= nil
127
+ @offset ||= nil
128
+ @length ||= nil
129
+
130
+ @pointer_mode ||= false
131
+ @index_length ||= nil
132
+
133
+ load_settings(h)
134
+ present
135
+ end
136
+
137
+ # Returns the associated offset, first trying the table and then +:offset+.
138
+ # Returns +nil+ if neither has sufficient data.
139
+ #
140
+ # @return [Integer]
141
+ def offset
142
+ if @rom && @table && @index
143
+ return @rom.read_offset_from_table(@table, @index) if @pointer_mode
144
+ return @rom.table_to_offset(@table, @index, @index_length)
145
+ end
146
+ @offset
147
+ end
148
+
149
+ # Returns the offset as a reversed byte string.
150
+ # This is the format used internally by ROMs to reference data.
151
+ #
152
+ # @return [String]
153
+ def pointer
154
+ Rom.offset_to_pointer(offset)
155
+ end
156
+
157
+ # Sets the index under which to search for data in the +:table+.
158
+ # Compared to setting :index=>index in the options hash, this will
159
+ # assume the index is
160
+ #
161
+ # This will clear the internal cache.
162
+ #
163
+ # @param [Integer] p_index For monsters, this should be the
164
+ # real index (accounting for blank spaces between
165
+ # dex numbers 251 and 252) of the monster you want to act on.
166
+ # Ideally, a wrapper object should calculate this for you.
167
+ # @return [self]
168
+ def index=(p_index)
169
+ @index = p_index
170
+ clear_cache
171
+ @index
172
+ end
173
+
174
+ # Sets the ROM with which the byte data is to be associated.
175
+ # This is the same as setting +:rom+ in the options hash.
176
+ #
177
+ # This will clear the internal cache.
178
+ #
179
+ # @param [Rom] p_rom
180
+ # @return [Rom] new rom.
181
+ def rom=(p_rom)
182
+ @rom = p_rom
183
+ clear_cache(lz77: false)
184
+ @rom
185
+ end
186
+
187
+ # Sets the table name identifier and optionally the index.
188
+ # The table identifier should be a key in +rom_data+ config.
189
+ #
190
+ # This is the same as setting +:table+, +:index+
191
+ # in the options hash.
192
+ #
193
+ # This will clear the internal cache.
194
+ #
195
+ # @param [String, Symbol] p_table
196
+ # @param [Integer] p_index see {#index=} for details.
197
+ # @return [self]
198
+ def table=(p_table, p_index = nil)
199
+ @table = p_table
200
+ @index = p_index if p_index
201
+ clear_cache
202
+ [@table, @index]
203
+ end
204
+
205
+ # Sets the offset of the data in +:rom+.
206
+ # This is the same as setting +:offset+ in the options hash.
207
+ #
208
+ # This will clear the internal cache.
209
+ #
210
+ # @param [Integer, String] p_offset See {Bytestream.parse_offset} for details.
211
+ # @return [Integer] new offset.
212
+ def offset=(p_offset)
213
+ return self if p_offset == @offset
214
+ @old_offset = @offset
215
+ @offset = Bytestream.parse_offset(p_offset)
216
+ clear_cache
217
+ @offset
218
+ end
219
+
220
+ # Tells the object that the ROM uses {Lz77} compression
221
+ # for the data.
222
+ # This is the same as setting +:lz77+=>+true+ in the options hash.
223
+ #
224
+ # @return [self]
225
+ def lz77_mode
226
+ @lz77 = true
227
+ self
228
+ end
229
+
230
+ # Tells the object that the data is a GBA-encoded, 0xFF-terminated
231
+ # string.
232
+ # This is the same as setting :is_gba_string=>true in the options hash.
233
+ #
234
+ # @return [self]
235
+ def string_mode
236
+ @is_gba_string = true
237
+ self
238
+ end
239
+
240
+ private
241
+
242
+ # Loads a hash of options. Subclasses should override this to allow
243
+ # additional options.
244
+ #
245
+ # @param [Hash] h see {#initialize}.
246
+ # @return [self]
247
+ def load_settings(h)
248
+ @representation = h.fetch(:representation, @representation)
249
+ @bytes_cache = h.fetch(:bytes_cache, @bytes_cache)
250
+
251
+ @lz77 = h.fetch(:lz77, @lz77 || false)
252
+ @is_gba_string = h.fetch(:is_gba_string, @is_gba_string)
253
+
254
+ @rom = h.fetch(:rom, @rom)
255
+ @offset = Bytestream.parse_offset(h[:offset]) if h.key?(:offset)
256
+ @length = h.fetch(:length, @length)
257
+
258
+ @table = h.fetch(:table, @table)
259
+ @index = h.fetch(:index, @index)
260
+
261
+ @pointer_mode = h.fetch(:pointer_mode, @pointer_mode)
262
+ @index_length = h.fetch(:index_length, @index_length)
263
+ self
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,126 @@
1
+ module Sabrina
2
+ class Bytestream
3
+ # Constructors that allow {Bytestream} to create byte data
4
+ # from ROMs or other sources. All methods should accept an optional
5
+ # final hash of options.
6
+ #
7
+ # Subclasses should override or add to these methods as necessary,
8
+ # using the final hash to pass the internal representation and
9
+ # any other necessary parameters and defaults.
10
+ module ByteInput
11
+ # Creates a new {Bytestream} object from bytes.
12
+ #
13
+ # @param [String] b
14
+ # @param [Hash] h see {Bytestream#initialize}
15
+ # @return [Bytestream]
16
+ def from_bytes(b, h = {})
17
+ h.merge!(bytes_cache: b)
18
+
19
+ new(h)
20
+ end
21
+
22
+ # Creates a new {Bytestream} object that will read +length+
23
+ # bytes of data from a ROM +table+ and +index+, expecting each entry
24
+ # before the index to be +index_length+ bytes.
25
+ #
26
+ # @param [Rom] rom
27
+ # @param [String, Symbol] table a key specified in rom_data config.
28
+ # @param [Integer] index the index to read from the table.
29
+ # For monsters, it should be the real (not dex) number
30
+ # of the monster you wish to act on.
31
+ # @param [Integer] index_length the expected length of a single entry.
32
+ # @param [Integer] length how many bytes to read from the calculated
33
+ # offset.
34
+ # @param [Hash] h see {Bytestream#initialize}
35
+ # @return [Bytestream]
36
+ def from_table(rom, table, index, index_length, length = index_length, h = {})
37
+ h.merge!(
38
+ rom: rom,
39
+ table: table,
40
+ index: index,
41
+ index_length: index_length,
42
+ length: length
43
+ )
44
+
45
+ new(h)
46
+ end
47
+
48
+ # Creates a new {Bytestream} object that will read a pointer
49
+ # from a ROM table and index. This is the recommended method to
50
+ # read monster data from a ROM.
51
+ #
52
+ # @param [Rom] rom
53
+ # @param [String, Symbol] table a key specified in rom_data config.
54
+ # @param [Integer] index the index to read from the table.
55
+ # For monsters, it should be the real (not dex) number
56
+ # of the monster you wish to act on.
57
+ # @param [Hash] h see {Bytestream#initialize}
58
+ # @return [Bytestream]
59
+ def from_table_as_pointer(rom, table, index, h = {})
60
+ h.merge!(
61
+ rom: rom,
62
+ table: table,
63
+ index: index,
64
+ pointer_mode: true
65
+ )
66
+
67
+ new(h)
68
+ end
69
+
70
+ # Creates a new {Bytestream} object by reading +length+
71
+ # bytes from a ROM offset.
72
+ #
73
+ # @param [Rom] rom
74
+ # @param [Integer, String] offset The offset to seek to in the ROM.
75
+ # See {Bytestream.parse_offset} for details.
76
+ # @param [Integer] length
77
+ # @param [Hash] h see {Bytestream#initialize}
78
+ # @return [Bytestream]
79
+ def from_rom(rom, offset, length = nil, h = {})
80
+ h.merge!(
81
+ rom: rom,
82
+ offset: offset,
83
+ length: length
84
+ )
85
+
86
+ new(h)
87
+ end
88
+
89
+ # Creates a new {Bytestream} object from a ROM offset, attempting
90
+ # to read it as {Lz77}-compressed data.
91
+ #
92
+ # @param [Rom] rom
93
+ # @param [Integer, String] offset The offset to seek to in the ROM.
94
+ # See {Bytestream.parse_offset} for details.
95
+ # @param [Hash] h see {Bytestream#initialize}
96
+ # @return [Bytestream]
97
+ def from_rom_as_lz77(rom, offset, h = {})
98
+ h.merge!(
99
+ rom: rom,
100
+ offset: offset,
101
+ lz77: true
102
+ )
103
+
104
+ new(h)
105
+ end
106
+
107
+ # Same as {#from_bytes}, but takes a hexadecimal string that will be
108
+ # converted to bytes.
109
+ #
110
+ # @param [String] s
111
+ # @param [Hash] h see {Bytestream#initialize}
112
+ # @return [Bytestream]
113
+ def from_hex(s, h = {})
114
+ if /[^0-9a-fx]/ =~ s.downcase
115
+ fail "\'#{s}\' does not look like a hex string."
116
+ end
117
+
118
+ b = s.rpartition('x').last.scan(/../).map { |x| x.hex.chr }.join('')
119
+
120
+ h.merge!(bytes_cache: b)
121
+
122
+ new(h)
123
+ end
124
+ end
125
+ end
126
+ end