snes_utils 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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