snes_utils 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f246381ab8ed69066585483ece562451ceca114af637eae26480ae617560738
4
- data.tar.gz: f63b5921657411c56261f4130e943c0e9cb01d6c0bd9a81ffeb32b588ee0796d
3
+ metadata.gz: 9bce5ff7aea0b674c5c19db5c83979f66b4993e9b0de36a19c432cc7bcb09745
4
+ data.tar.gz: 78b040ccc9a686496535c135e3908ab63bfcd9c2c4fece2761a77f5588f49a9d
5
5
  SHA512:
6
- metadata.gz: 133a82e1e257c37cf735b74fc9d727567b3d61d907bb51b7db5eb1f575d6bc579170dabae4abc9d9aaae9e5a2198a03c6ea65a5a504311f1750699f541b68aae
7
- data.tar.gz: 3ffb2de928787bdd9f56ff87b1cd6b3dd45ab5c4737e54800c00d4c417a44d34d397de025d27fdfb4116ff9c0a3eac58238683b5c9431394b9dc98cfe16b67a4
6
+ metadata.gz: dfd37be40699f3a075c5b5c1e1779bb8fe4aed85da84d1f64be3a325d427416c070df8677d3f054d63edc63339080c467211feb903006878726fc8d66c84fee8
7
+ data.tar.gz: 44d97b051f1b75b94e3f823ec41efdbd08100a24674fb0255e3e40130bf2418506b6bd31035813ae31ff0253c2874155ed5fa40caf10806bd21ff4f745f32e4d
data/bin/png2snes CHANGED
@@ -8,6 +8,8 @@ OptionParser.new do |opts|
8
8
  opts.on('-f', '--file FILENAME', 'PNG source file') { |o| options[:filename] = o }
9
9
  opts.on('-b', '--bpp BPP', 'BPP') { |o| options[:bpp] = o }
10
10
  opts.on('-a', '--alpha ALPHA', 'ALPHA') { |o| options[:alpha] = o }
11
+ opts.on('-m', '--mode7', 'mode 7') { |o| options[:mode7] = o }
12
+ opts.on('-o', '--offset OFFSET', 'mode 7 palette offset') { |o| options[:m7_palette_offset] = o }
11
13
  end.parse!
12
14
 
13
15
  raise OptionParser::MissingArgument, 'Must specify PNG source file' if options[:filename].nil?
@@ -18,6 +20,15 @@ else
18
20
  alpha = nil
19
21
  end
20
22
 
21
- c = SnesUtils::Png2Snes.new options[:filename], bpp: options[:bpp].to_i, alpha: alpha
23
+ if options[:m7_palette_offset]
24
+ m7_palette_offset = options[:m7_palette_offset].to_i
25
+ else
26
+ m7_palette_offset = nil
27
+ end
28
+
29
+ c = SnesUtils::Png2Snes.new(options[:filename], bpp: options[:bpp].to_i,
30
+ alpha: alpha,
31
+ mode7: options[:mode7],
32
+ m7_palette_offset: m7_palette_offset)
22
33
  c.write_palette
23
34
  c.write_image
data/bin/vas CHANGED
@@ -11,6 +11,7 @@ OptionParser.new do |opts|
11
11
  end
12
12
 
13
13
  opts.on('-f', '--file FILENAME', 'ASM file') { |o| options[:filename] = o }
14
+ opts.on("-o", "--out FILENAME", "output file") { |o| options[:outfile] = o }
14
15
  end.parse!
15
16
 
16
- SnesUtils::Vas.new(options[:filename]).assemble
17
+ SnesUtils::Vas.new(options[:filename], options[:outfile]).assemble
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SnesUtils
4
+ module Superfx
5
+ class Definitions
6
+ HEX_DIGIT = '[0-9a-f]'
7
+ REG = "[rR](\\d{1,2})"
8
+ LNK = "([1-4])"
9
+ HEX = "\\$?(#{HEX_DIGIT})"
10
+ HEX8 = "\\$?(#{HEX_DIGIT}{1,2})"
11
+ HEX16 = "\\$?(#{HEX_DIGIT}{1,4})"
12
+
13
+ SINGLE_OPERAND_INSTRUCTIONS = %i[imp imm4 rn rm rel lnk].freeze
14
+ DOUBLE_OPERAND_INSTRUCTIONS = %i[rn_rn rn_imm8 rn_imm16 rn_addr rn_addrl addr_rn addrl_rn].freeze
15
+ REL_INSTRUCTIONS = %i[rel].freeze
16
+ BIT_INSTRUCTIONS = %i[].freeze
17
+ SFX_INSTRUCTIONS = %i[imm4 rn rm rn_rn rn_imm8 rn_imm16 rn_addr rn_addrl addr_rn addrl_rn].freeze
18
+ MOV_INSTRUCTIONS = %i[rn_rn]
19
+ SHORT_ADDR_INSTRUCTIONS = %i[rn_addr addr_rn]
20
+ INV_DEST_INSTRUCTIONS = %i[addr_rn addrl_rn]
21
+
22
+ MODES_REGEXES = {
23
+ imp: /^$/, # nothing
24
+ imm4: /^##{HEX}$/, # #0-f
25
+ lnk: /^##{LNK}$/, # #1-4
26
+ rn: /^#{REG}$/, # R1
27
+ rm: /^\(#{REG}\)$/, # (R1)
28
+ rel: /^#{HEX16}$/i, # label / 1234
29
+ rn_rn: /^#{REG},#{REG}$/, # R1, R2
30
+ rn_imm8: /^#{REG},##{HEX8}$/, # R1, #12
31
+ rn_imm16: /^#{REG},##{HEX16}$/, # R1, #1234
32
+ rn_addr: /^#{REG},\(#{HEX16}\)$/, # R1, (1234) ; -> actual data is 1234 / 2
33
+ rn_addrl: /^#{REG},\(#{HEX16}\)$/, # R1, (1234) ; label
34
+ addr_rn: /^\(#{HEX16}\),#{REG}$/, # (1234), R1 ; -> actual data is 1234 / 2
35
+ addrl_rn: /^\(#{HEX16}\),#{REG}$/, # (1234), R1 ; label
36
+ }.freeze
37
+
38
+ OPCODES_DATA = [
39
+ # From game pak ROM to register
40
+ { opcode: 0xef, mnemonic: 'GETB', mode: :imp, length: 1, alt: nil },
41
+ { opcode: 0xef, mnemonic: 'GETBH', mode: :imp, length: 2, alt: 0x3d },
42
+ { opcode: 0xef, mnemonic: 'GETBL', mode: :imp, length: 2, alt: 0x3e },
43
+ { opcode: 0xef, mnemonic: 'GETBS', mode: :imp, length: 2, alt: 0x3f },
44
+ { opcode: 0xdf, mnemonic: 'GETC', mode: :imp, length: 1, alt: nil },
45
+
46
+ # From game pak RAM to register
47
+ { opcode: 0x4, mnemonic: 'LDW', mode: :rm, length: 1, alt: nil },
48
+ { opcode: 0x4, mnemonic: 'LDB', mode: :rm, length: 1, alt: 0x3d },
49
+ { opcode: 0xf, mnemonic: 'LM', mode: :rn_addrl, length: 4, alt: 0x3d },
50
+ { opcode: 0xa, mnemonic: 'LMS', mode: :rn_addr, length: 3, alt: 0x3d },
51
+
52
+ # From register to game pak RAM
53
+ { opcode: 0x3, mnemonic: 'STW', mode: :rm, length: 1, alt: nil },
54
+ { opcode: 0x3, mnemonic: 'STB', mode: :rm, length: 2, alt: 0x3d },
55
+ { opcode: 0xf, mnemonic: 'SM', mode: :addrl_rn, length: 4, alt: 0x3e },
56
+ { opcode: 0xa, mnemonic: 'SMS', mode: :addr_rn, length: 3, alt: 0x3e },
57
+ { opcode: 0x90, mnemonic: 'SBK', mode: :imp, length: 1, alt: nil },
58
+
59
+ # From register to register
60
+ { opcode: 0x2010, mnemonic: 'MOVE', mode: :rn_rn, length: 2, alt: nil },
61
+ { opcode: 0x20b0, mnemonic: 'MOVES', mode: :rn_rn, length: 2, alt: nil },
62
+
63
+ # Immediate data to register
64
+ { opcode: 0xf, mnemonic: 'IWT', mode: :rn_imm16, length: 3, alt: nil },
65
+ { opcode: 0xa, mnemonic: 'IBT', mode: :rn_imm8, length: 2, alt: nil },
66
+
67
+ # Arithmetic operations
68
+ { opcode: 0x5, mnemonic: 'ADD', mode: :rn, length: 1, alt: nil },
69
+ { opcode: 0x5, mnemonic: 'ADD', mode: :imm4, length: 2, alt: 0x3e },
70
+ { opcode: 0x5, mnemonic: 'ADC', mode: :rn, length: 2, alt: 0x3d },
71
+ { opcode: 0x5, mnemonic: 'ADC', mode: :imm4, length: 2, alt: 0x3f },
72
+
73
+ { opcode: 0x6, mnemonic: 'SUB', mode: :rn, length: 1, alt: nil },
74
+ { opcode: 0x6, mnemonic: 'SUB', mode: :imm4, length: 2, alt: 0x3e },
75
+ { opcode: 0x6, mnemonic: 'SBC', mode: :rn, length: 2, alt: 0x3d },
76
+
77
+ { opcode: 0x6, mnemonic: 'CMP', mode: :rn, length: 2, alt: 0x3f },
78
+
79
+ { opcode: 0x8, mnemonic: 'MULT', mode: :rn, length: 1, alt: nil },
80
+ { opcode: 0x8, mnemonic: 'MULT', mode: :imm4, length: 2, alt: 0x3e },
81
+ { opcode: 0x8, mnemonic: 'UMULT', mode: :rn, length: 2, alt: 0x3d },
82
+ { opcode: 0x8, mnemonic: 'UMULT', mode: :imm4, length: 2, alt: 0x3f },
83
+ { opcode: 0x9f, mnemonic: 'FMULT', mode: :imp, length: 1, alt: nil },
84
+ { opcode: 0x9f, mnemonic: 'LMULT', mode: :imp, length: 2, alt: 0x3d },
85
+
86
+ { opcode: 0x96, mnemonic: 'DIV2', mode: :imp, length: 2, alt: 0x3d },
87
+
88
+ { opcode: 0xd, mnemonic: 'INC', mode: :rn, length: 1, alt: nil },
89
+ { opcode: 0xe, mnemonic: 'DEC', mode: :rn, length: 1, alt: nil },
90
+
91
+ # Logical operations
92
+ { opcode: 0x7, mnemonic: 'AND', mode: :rn, length: 1, alt: nil },
93
+ { opcode: 0x7, mnemonic: 'AND', mode: :imm4, length: 2, alt: 0x3e },
94
+ { opcode: 0xc, mnemonic: 'OR', mode: :rn, length: 1, alt: nil },
95
+ { opcode: 0xc, mnemonic: 'OR', mode: :imm4, length: 2, alt: 0x3e },
96
+ { opcode: 0x4f, mnemonic: 'NOT', mode: :imp, length: 1, alt: nil },
97
+ { opcode: 0xc, mnemonic: 'XOR', mode: :rn, length: 2, alt: 0x3d },
98
+ { opcode: 0xc, mnemonic: 'XOR', mode: :imm4, length: 2, alt: 0x3f },
99
+ { opcode: 0x7, mnemonic: 'BIC', mode: :rn, length: 2, alt: 0x3d },
100
+ { opcode: 0x7, mnemonic: 'BIC', mode: :imm4, length: 2, alt: 0x3f },
101
+
102
+ # Shift
103
+ { opcode: 0x96, mnemonic: 'ASR', mode: :imp, length: 1, alt: nil },
104
+ { opcode: 0x03, mnemonic: 'LSR', mode: :imp, length: 1, alt: nil },
105
+ { opcode: 0x04, mnemonic: 'ROL', mode: :imp, length: 1, alt: nil },
106
+ { opcode: 0x97, mnemonic: 'ROR', mode: :imp, length: 1, alt: nil },
107
+
108
+ # Byte transfer
109
+ { opcode: 0xc0, mnemonic: 'HIB', mode: :imp, length: 1, alt: nil },
110
+ { opcode: 0x9e, mnemonic: 'LOB', mode: :imp, length: 1, alt: nil },
111
+ { opcode: 0x70, mnemonic: 'MERGE', mode: :imp, length: 1, alt: nil },
112
+ { opcode: 0x95, mnemonic: 'SEX', mode: :imp, length: 1, alt: nil },
113
+ { opcode: 0x4d, mnemonic: 'SWAP', mode: :imp, length: 1, alt: nil },
114
+
115
+ # Jump, branch and loop
116
+ { opcode: 0x9, mnemonic: 'JMP', mode: :rn, length: 1, alt: nil },
117
+ { opcode: 0x9, mnemonic: 'LJMP', mode: :rn, length: 2, alt: 0x3d },
118
+ { opcode: 0x05, mnemonic: 'BRA', mode: :rel, length: 2, alt: nil },
119
+ { opcode: 0x06, mnemonic: 'BGE', mode: :rel, length: 2, alt: nil },
120
+ { opcode: 0x07, mnemonic: 'BLT', mode: :rel, length: 2, alt: nil },
121
+ { opcode: 0x08, mnemonic: 'BNE', mode: :rel, length: 2, alt: nil },
122
+ { opcode: 0x09, mnemonic: 'BEQ', mode: :rel, length: 2, alt: nil },
123
+ { opcode: 0x0a, mnemonic: 'BPL', mode: :rel, length: 2, alt: nil },
124
+ { opcode: 0x0b, mnemonic: 'BMI', mode: :rel, length: 2, alt: nil },
125
+ { opcode: 0x0c, mnemonic: 'BCC', mode: :rel, length: 2, alt: nil },
126
+ { opcode: 0x0d, mnemonic: 'BCS', mode: :rel, length: 2, alt: nil },
127
+ { opcode: 0x0e, mnemonic: 'BVC', mode: :rel, length: 2, alt: nil },
128
+ { opcode: 0x0f, mnemonic: 'BVS', mode: :rel, length: 2, alt: nil },
129
+ { opcode: 0x3c, mnemonic: 'LOOP', mode: :imp, length: 1, alt: nil },
130
+ { opcode: 0x9, mnemonic: 'LINK', mode: :imm4, length: 1, alt: nil },
131
+
132
+ # Bank set-up
133
+ { opcode: 0xdf, mnemonic: 'ROMB', mode: :imp, length: 2, alt: 0x3f },
134
+ { opcode: 0xdf, mnemonic: 'RAMB', mode: :imp, length: 2, alt: 0x3e },
135
+
136
+ # Plot related
137
+ { opcode: 0x4e, mnemonic: 'CMODE', mode: :imp, length: 2, alt: 0x3d },
138
+ { opcode: 0x4e, mnemonic: 'COLOR', mode: :imp, length: 1, alt: nil },
139
+ { opcode: 0x4c, mnemonic: 'PLOT', mode: :imp, length: 1, alt: nil },
140
+ { opcode: 0x4c, mnemonic: 'RPIX', mode: :imp, length: 2, alt: 0x3d },
141
+
142
+ # Prefix flag
143
+ { opcode: 0x3d, mnemonic: 'ALT1', mode: :imp, length: 1, alt: nil },
144
+ { opcode: 0x3e, mnemonic: 'ALT2', mode: :imp, length: 1, alt: nil },
145
+ { opcode: 0x3f, mnemonic: 'ALT3', mode: :imp, length: 1, alt: nil },
146
+
147
+ # Prefix register
148
+ { opcode: 0xb, mnemonic: 'FROM', mode: :rn, length: 1, alt: nil },
149
+ { opcode: 0x1, mnemonic: 'TO', mode: :rn, length: 1, alt: nil },
150
+ { opcode: 0x2, mnemonic: 'WITH', mode: :rn, length: 1, alt: nil },
151
+
152
+ # GSU Control
153
+ { opcode: 0x02, mnemonic: 'CACHE', mode: :imp, length: 1, alt: nil },
154
+ { opcode: 0x01, mnemonic: 'NOP', mode: :imp, length: 1, alt: nil },
155
+ { opcode: 0x00, mnemonic: 'STOP', mode: :imp, length: 1, alt: nil }].freeze
156
+ end
157
+ end
158
+ end
@@ -1,22 +1,34 @@
1
1
  module SnesUtils
2
2
  class Png2Snes
3
- def initialize(file_path, bpp:4, alpha:nil)
3
+ CHAR_SIZE = 8
4
+
5
+ def initialize(file_path, bpp:4, alpha:nil, mode7: false, m7_palette_offset: nil)
4
6
  @file_path = file_path
5
7
  @file_dir = File.dirname(@file_path)
6
8
  @file_name = File.basename(@file_path, File.extname(@file_path))
7
9
  @image = ChunkyPNG::Image.from_file(@file_path)
8
- @pixels = pixels_to_bgr5
9
10
 
11
+ @mode7 = mode7
12
+
13
+ raise ArgumentError, 'Image width and height must be a multiple of sprite size' if (@image.width % CHAR_SIZE != 0) or (@image.height % CHAR_SIZE != 0)
14
+
15
+ @pixels = pixels_to_bgr5
10
16
  @palette = @pixels.uniq
11
- @char_size = 8
12
17
 
13
- raise ArgumentError, 'BPP must be 2, 4, or 8' unless [2, 4, 8].include? bpp
14
- @bpp = bpp
18
+ if @mode7
19
+ raise ArgumentError, 'mode 7 palette offset must be multiple of 16' if m7_palette_offset % 16 != 0
20
+ raise ArgumentError, 'mode 7 palette offset must be less than 112' if m7_palette_offset > 112
21
+ @m7_palette_offset = m7_palette_offset
15
22
 
16
- raise ArgumentError, 'Image width and height must be a multiple of sprite size' if (@image.width % @char_size != 0) or (@image.height % @char_size != 0)
23
+ unshift_alpha(alpha) if alpha
24
+ fill_palette
25
+ else
26
+ raise ArgumentError, 'BPP must be 2, 4, or 8' unless [2, 4, 8].include? bpp
27
+ @bpp = bpp
17
28
 
18
- unshift_alpha(alpha) if alpha
19
- fill_palette
29
+ unshift_alpha(alpha) if alpha
30
+ fill_palette
31
+ end
20
32
  end
21
33
 
22
34
  def pixels_to_bgr5
@@ -34,13 +46,17 @@ module SnesUtils
34
46
  end
35
47
 
36
48
  def fill_palette
37
- target_size = 2**@bpp
49
+ target_size = @mode7 ? mode_7_pal_size : 2**@bpp
38
50
  missing_colors = target_size - @palette.count
39
51
  raise ArgumentError, "Palette size too large for target BPP (#{@palette.count})" if missing_colors < 0
40
52
 
41
53
  @palette += [0] * missing_colors
42
54
  end
43
55
 
56
+ def mode_7_pal_size
57
+ 128 - @m7_palette_offset
58
+ end
59
+
44
60
  def write hex, file_path
45
61
  File.open(file_path, 'w+b') do |file|
46
62
  file.write([hex.join].pack('H*'))
@@ -49,7 +65,7 @@ module SnesUtils
49
65
 
50
66
  def write_palette
51
67
  palette_hex = @palette.map { |c| ('%04x' % c).scan(/.{2}/).reverse.join }
52
- write palette_hex, File.expand_path("#{@file_name}-pal.bin", @file_dir)
68
+ write palette_hex, File.expand_path("#{@file_name}.pal", @file_dir)
53
69
  end
54
70
 
55
71
  def pixel_indices
@@ -61,16 +77,20 @@ module SnesUtils
61
77
  def extract_sprites
62
78
  pixel_idx = pixel_indices
63
79
 
64
- sprite_per_row = @image.width / @char_size
65
- sprite_per_col = @image.height / @char_size
80
+ sprite_per_row = @image.width / CHAR_SIZE
81
+ sprite_per_col = @image.height / CHAR_SIZE
66
82
  sprite_per_sheet = sprite_per_row * sprite_per_col
67
83
 
68
84
  sprites = []
69
85
  (0..sprite_per_sheet-1).each do |s|
70
86
  sprite = []
71
- (0..@char_size-1).each do |r|
72
- offset = (s/sprite_per_row)*sprite_per_row * @char_size**2 + s % sprite_per_row * @char_size
73
- sprite += pixel_idx[offset + r*sprite_per_row*@char_size, @char_size]
87
+ (0..CHAR_SIZE-1).each do |r|
88
+ offset = (s/sprite_per_row)*sprite_per_row * CHAR_SIZE**2 + s % sprite_per_row * CHAR_SIZE
89
+ if @mode7
90
+ sprite += @pixels[offset + r*sprite_per_row*CHAR_SIZE, CHAR_SIZE]
91
+ else
92
+ sprite += pixel_idx[offset + r*sprite_per_row*CHAR_SIZE, CHAR_SIZE]
93
+ end
74
94
  end
75
95
  sprites.push(sprite)
76
96
  end
@@ -88,7 +108,11 @@ module SnesUtils
88
108
  end
89
109
 
90
110
  def write_image
91
- sprite_per_row = @image.width / @char_size
111
+ @mode7 ? write_image_m7 : write_image_m06
112
+ end
113
+
114
+ def write_image_m06
115
+ sprite_per_row = @image.width / CHAR_SIZE
92
116
  sprites = extract_sprites
93
117
  sprites_bitplanes = sprites.map { |s| extract_bitplanes s }
94
118
 
@@ -98,9 +122,9 @@ module SnesUtils
98
122
 
99
123
  bitplane_bits = ""
100
124
  sprite_bitplane_pairs.each do |bitplane|
101
- (0..@char_size-1).each do |r|
102
- offset = r*@char_size
103
- bitplane_bits += bitplane[0][offset, @char_size].join + bitplane[1][offset, @char_size].join
125
+ (0..CHAR_SIZE-1).each do |r|
126
+ offset = r*CHAR_SIZE
127
+ bitplane_bits += bitplane[0][offset, CHAR_SIZE].join + bitplane[1][offset, CHAR_SIZE].join
104
128
  end
105
129
  end
106
130
  image_bits += bitplane_bits
@@ -108,8 +132,15 @@ module SnesUtils
108
132
  end
109
133
 
110
134
  image_hex = image_bits.scan(/.{8}/).map { |b| "%02x" % b.to_i(2) }
111
- write image_hex, File.expand_path("#{@file_name}.bin", @file_dir)
135
+ write image_hex, File.expand_path("#{@file_name}.tiles", @file_dir)
112
136
  end
113
137
 
138
+ def write_image_m7
139
+ sprites = extract_sprites
140
+
141
+ indices = sprites.flatten.map { |color| "%02x" % (@palette.index(color) + @m7_palette_offset) }
142
+
143
+ write indices, File.expand_path("#{@file_name}.tiles", @file_dir)
144
+ end
114
145
  end
115
146
  end
data/lib/snes_utils.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  require 'readline'
2
2
  require 'chunky_png'
3
- require 'matrix'
4
3
  require 'csv'
5
- require 'byebug'
6
4
 
7
5
  require 'mini_assembler/mini_assembler'
8
6
  require 'mini_assembler/definitions'
9
7
  require 'mini_assembler/wdc65816/definitions'
10
8
  require 'mini_assembler/spc700/definitions'
9
+ require 'mini_assembler/superfx/definitions'
11
10
  require 'png2snes/png2snes'
12
11
  require 'tmx2snes/tmx2snes'
13
12