├── spec ├── fixtures │ ├── gbr │ │ ├── gbr_file │ │ │ ├── invalid-header.gbr │ │ │ └── valid.gbr │ │ ├── palettes │ │ │ └── partial.gbr │ │ ├── producer │ │ │ └── partial.gbr │ │ ├── tile_pal │ │ │ └── partial.gbr │ │ ├── tile_data │ │ │ └── partial.gbr │ │ ├── tile_settings │ │ │ └── partial.gbr │ │ ├── tile_import │ │ │ └── partial.gbr │ │ └── tile_export │ │ │ └── partial.gbr │ ├── gbm │ │ ├── map │ │ │ └── partial.gbm │ │ ├── gbm_file │ │ │ ├── valid.gbm │ │ │ ├── invalid-header.gbm │ │ │ ├── invalid-marker.gbm │ │ │ └── invalid-version.gbm │ │ ├── map_settings │ │ │ └── partial.gbm │ │ ├── map_tile_data │ │ │ └── partial.gbm │ │ ├── producer │ │ │ └── partial.gbm │ │ └── map_export_settings │ │ │ └── partial.gbm │ └── gbt │ │ └── mod_file │ │ ├── valid.mod │ │ └── invalid-identifier.mod ├── gbtiles │ ├── gbt │ │ ├── export │ │ │ └── asm │ │ │ │ ├── effects │ │ │ │ └── volume_spec.rb │ │ │ │ ├── channels │ │ │ │ ├── wav_spec.rb │ │ │ │ ├── noise_spec.rb │ │ │ │ └── pulse_spec.rb │ │ │ │ └── channel_spec.rb │ │ └── import │ │ │ └── mod_file_spec.rb │ ├── gbm │ │ └── map │ │ │ ├── objects │ │ │ ├── producer_spec.rb │ │ │ ├── map_spec.rb │ │ │ ├── map_tile_data_spec.rb │ │ │ ├── map_settings_spec.rb │ │ │ └── map_export_settings_spec.rb │ │ │ └── import │ │ │ └── gbm_file_spec.rb │ ├── gbr │ │ ├── tile_set │ │ │ └── objects │ │ │ │ ├── producer_spec.rb │ │ │ │ ├── palettes_spec.rb │ │ │ │ ├── tile_pal_spec.rb │ │ │ │ ├── tile_import_spec.rb │ │ │ │ ├── tile_settings_spec.rb │ │ │ │ ├── tile_export_spec.rb │ │ │ │ └── tile_data_spec.rb │ │ └── import │ │ │ └── gbr_file_spec.rb │ └── helpers │ │ └── data_type_spec.rb └── spec_helper.rb ├── .rspec ├── .gitignore ├── lib └── gbtiles │ ├── version.rb │ ├── helpers │ ├── fixnum.rb │ └── data_type.rb │ ├── gbm │ ├── layer.rb │ ├── map │ │ ├── objects │ │ │ ├── unknown.rb │ │ │ ├── producer.rb │ │ │ ├── map_tile_data_record.rb │ │ │ ├── map_tile_data.rb │ │ │ ├── map.rb │ │ │ ├── map_settings.rb │ │ │ └── map_export_settings.rb │ │ ├── object.rb │ │ ├── object_type.rb │ │ └── map_set.rb │ ├── export │ │ └── asm │ │ │ ├── asm.h.erb │ │ │ ├── asm.s.erb │ │ │ └── asm.rb │ ├── cli │ │ └── convert.rb │ └── import │ │ └── gbm_file.rb │ ├── gbr │ ├── tile_set │ │ ├── color_set.rb │ │ ├── split_order.rb │ │ ├── objects │ │ │ ├── unknown.rb │ │ │ ├── producer.rb │ │ │ ├── palettes.rb │ │ │ ├── tile_pal.rb │ │ │ ├── tile_import.rb │ │ │ ├── tile_settings.rb │ │ │ ├── tile_data.rb │ │ │ └── tile_export.rb │ │ ├── object.rb │ │ ├── export_type.rb │ │ ├── sgb_palettes.rb │ │ ├── object_type.rb │ │ └── tile_set.rb │ ├── export │ │ └── asm │ │ │ ├── asm.h.erb │ │ │ ├── asm.s.erb │ │ │ └── asm.rb │ ├── cli │ │ └── convert.rb │ └── import │ │ └── gbr_file.rb │ └── gbt │ ├── mod_data │ ├── pattern.rb │ ├── sample.rb │ └── mod_data.rb │ ├── export │ └── asm │ │ ├── asm.h.erb │ │ ├── effects │ │ ├── volume.rb │ │ ├── jump.rb │ │ ├── arpeggio.rb │ │ ├── cut_note.rb │ │ ├── break_and_set_step.rb │ │ ├── pan.rb │ │ └── speed.rb │ │ ├── asm.s.erb │ │ ├── converter.rb │ │ ├── asm.rb │ │ ├── channels │ │ ├── noise.rb │ │ ├── pulse.rb │ │ └── wav.rb │ │ └── channel.rb │ ├── cli │ └── convert.rb │ └── import │ └── mod_file.rb ├── Gemfile ├── .travis.yml ├── bin ├── gbm ├── gbr └── gbt ├── LICENSE ├── gbtiles.gemspec └── README.md /spec/fixtures/gbr/gbr_file/invalid-header.gbr: -------------------------------------------------------------------------------- 1 | ABC -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --format doc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /gbtiles-*.gem 2 | /Gemfile.lock 3 | /coverage 4 | -------------------------------------------------------------------------------- /lib/gbtiles/version.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | VERSION = "0.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /spec/fixtures/gbm/map/partial.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/map/partial.gbm -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | 4 | gem "codeclimate-test-reporter", group: :test, require: nil 5 | -------------------------------------------------------------------------------- /spec/fixtures/gbm/gbm_file/valid.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/gbm_file/valid.gbm -------------------------------------------------------------------------------- /spec/fixtures/gbr/gbr_file/valid.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/gbr_file/valid.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbt/mod_file/valid.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbt/mod_file/valid.mod -------------------------------------------------------------------------------- /spec/fixtures/gbr/palettes/partial.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/palettes/partial.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbr/producer/partial.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/producer/partial.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbr/tile_pal/partial.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/tile_pal/partial.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbr/tile_data/partial.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/tile_data/partial.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbm/map_settings/partial.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/map_settings/partial.gbm -------------------------------------------------------------------------------- /spec/fixtures/gbr/tile_settings/partial.gbr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbr/tile_settings/partial.gbr -------------------------------------------------------------------------------- /spec/fixtures/gbm/gbm_file/invalid-header.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/gbm_file/invalid-header.gbm -------------------------------------------------------------------------------- /spec/fixtures/gbm/gbm_file/invalid-marker.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/gbm_file/invalid-marker.gbm -------------------------------------------------------------------------------- /spec/fixtures/gbm/gbm_file/invalid-version.gbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbm/gbm_file/invalid-version.gbm -------------------------------------------------------------------------------- /spec/fixtures/gbt/mod_file/invalid-identifier.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashaus/gbtiles/HEAD/spec/fixtures/gbt/mod_file/invalid-identifier.mod -------------------------------------------------------------------------------- /lib/gbtiles/helpers/fixnum.rb: -------------------------------------------------------------------------------- 1 | class Fixnum 2 | def bits range 3 | len = range.last - range.first + 1 4 | self >> range.first & ~(-1 >> len << len) 5 | end 6 | end -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - "2.0.0" 5 | 6 | addons: 7 | code_climate: 8 | repo_token: 55ac17c969568018f9001220 9 | 10 | script: bundle exec rspec 11 | -------------------------------------------------------------------------------- /spec/fixtures/gbr/tile_import/partial.gbr: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /lib/gbtiles/gbm/layer.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | LAYER = { 4 | :bkg => 0x00, 5 | :win => 0x80, 6 | :sprite => 0x00 7 | } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/fixtures/gbr/tile_export/partial.gbr: -------------------------------------------------------------------------------- 1 | dialog.z80DialogTilesDialogTiles -------------------------------------------------------------------------------- /spec/fixtures/gbm/map_tile_data/partial.gbm: -------------------------------------------------------------------------------- 1 | LOREM IPSUMDOLORSIT AMET  -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/unknown.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class Unknown < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :object_data 8 | 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/color_set.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | COLOR_SET = { 5 | :pocket => 0, 6 | :game_boy => 1, 7 | :gbc => 2, 8 | :sgb => 3 9 | } 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/mod_data/pattern.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module MODData 4 | class Pattern 5 | 6 | attr_accessor :rows 7 | 8 | def initialize 9 | @rows = [] 10 | end 11 | 12 | end 13 | end 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/split_order.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | SPLIT_ORDER = { 5 | :lrtb => 0, 6 | :horizontal => 0, 7 | 8 | :tblr => 1, 9 | :vertical => 1 10 | } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/fixtures/gbm/producer/partial.gbm: -------------------------------------------------------------------------------- 1 | Gameboy Map Builder1.8Home: http://www.casema.net/~hpmulder -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/unknown.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class Unknown < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :object_data 8 | 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/asm.h.erb: -------------------------------------------------------------------------------- 1 | /** 2 | * .H created with 3 | * GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBT 4 | */ 5 | 6 | // Helpers 7 | #define <%= label %>Bank <%= bank %> 8 | #define <%= label %>Count <%= mod_data.song_length %> 9 | 10 | // Extern 11 | extern unsigned char <%= label %>[]; 12 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/volume.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_volume 7 | throw "Volume is implemented on a per-channel basis" 8 | end 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/gbtiles/gbt/export/asm/effects/volume_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channels/wav" 2 | 3 | RSpec.describe GBTiles::GBT::Export::ASM::Effects, "#effect_volume" do 4 | it "always throws an error" do 5 | @channel = GBTiles::GBT::Export::ASM::Channel.new 1 6 | expect { @channel.effect_volume }.to raise_error 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/object.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | class Object 5 | 6 | attr_accessor :object_id 7 | attr_accessor :object_type 8 | 9 | def initialize object_type 10 | @object_type = object_type 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/export_type.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | EXPORT_TYPE = { 5 | :rgbds_assembly => 0x00, 6 | :rgbds_object => 0x01, 7 | :tasm_assembly => 0x02, 8 | :gbdk_c => 0x03, 9 | :binary => 0x04 10 | } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/jump.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_jump 7 | { 8 | :number => 8, 9 | :params => effect_param 10 | } 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/object.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | class Object 5 | 6 | attr_accessor :object_type 7 | attr_accessor :object_id 8 | attr_accessor :master_id 9 | 10 | def initialize object_type 11 | @object_type = object_type 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/arpeggio.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_arpeggio 7 | { 8 | :number => 1, 9 | :params => effect_param 10 | } 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/cut_note.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_cut_note 7 | { 8 | :number => 2, 9 | :params => effect_param_2 10 | } 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/gbm/map_export_settings/partial.gbm: -------------------------------------------------------------------------------- 1 | dialog.z80DialogMapDialogMap -------------------------------------------------------------------------------- /lib/gbtiles/gbt/mod_data/sample.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module MODData 4 | class Sample 5 | 6 | attr_accessor :name 7 | attr_accessor :length 8 | attr_accessor :finetune 9 | attr_accessor :volume 10 | attr_accessor :repeat_point 11 | attr_accessor :repeat_length 12 | 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/break_and_set_step.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_break_and_set_step 7 | { 8 | :number => 9, 9 | :params => effect_param_as_bcd 10 | } 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/object_type.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | OBJECT_TYPE = { 5 | :producer => 0x01, 6 | :map => 0x02, 7 | :map_tile_data => 0x03, 8 | :map_settings => 0x07, 9 | :map_export_settings => 0x09, 10 | :deleted => 0xFF 11 | } 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/sgb_palettes.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | SGB_PALETTES = { 5 | :none => 0, 6 | :constant_per_entry => 1, 7 | :per_entry_2_bits => 2, # 2_bits_per_entry 8 | :per_entry_4_bits => 3, # 4_bits_per_entry 9 | :per_entry_1_byte => 4 # 1_byte_per_entry 10 | } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/object_type.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | OBJECT_TYPE = { 5 | :producer => 0x01, 6 | :tile_data => 0x02, 7 | :tile_settings => 0x03, 8 | :tile_export => 0x04, 9 | :tile_import => 0x05, 10 | :palettes => 0x0D, 11 | :tile_pal => 0x0E, 12 | :deleted => 0xFF 13 | } 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /bin/gbm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "gli" 3 | include GLI::App 4 | 5 | require "gbtiles/version" 6 | require "gbtiles/gbm/cli/convert" 7 | 8 | program_desc "Converts .GBM files to different formats for the Game Boy" 9 | 10 | version GBTiles::VERSION 11 | 12 | pre do |global,command,options,args| 13 | true 14 | end 15 | 16 | post do |global,command,options,args| 17 | true 18 | end 19 | 20 | on_error do |exception| 21 | true 22 | end 23 | 24 | exit run(ARGV) 25 | -------------------------------------------------------------------------------- /bin/gbr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "gli" 3 | include GLI::App 4 | 5 | require "gbtiles/version" 6 | require "gbtiles/gbr/cli/convert" 7 | 8 | program_desc "Converts .GBR files to different formats for the Game Boy" 9 | 10 | version GBTiles::VERSION 11 | 12 | pre do |global,command,options,args| 13 | true 14 | end 15 | 16 | post do |global,command,options,args| 17 | true 18 | end 19 | 20 | on_error do |exception| 21 | true 22 | end 23 | 24 | exit run(ARGV) 25 | -------------------------------------------------------------------------------- /bin/gbt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "gli" 3 | include GLI::App 4 | 5 | require "gbtiles/version" 6 | require "gbtiles/gbt/cli/convert" 7 | 8 | program_desc "Converts .MOD files to different formats for the Game Boy" 9 | 10 | version GBTiles::VERSION 11 | 12 | pre do |global,command,options,args| 13 | true 14 | end 15 | 16 | post do |global,command,options,args| 17 | true 18 | end 19 | 20 | on_error do |exception| 21 | true 22 | end 23 | 24 | exit run(ARGV) 25 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/mod_data/mod_data.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module MODData 4 | class MODData 5 | 6 | attr_accessor :name 7 | attr_accessor :samples 8 | attr_accessor :song_length 9 | attr_accessor :pattern_table 10 | attr_accessor :identifier 11 | attr_accessor :patterns 12 | 13 | def initialize 14 | @samples = [] 15 | @pattern_table = [] 16 | @patterns = [] 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/pan.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_pan 7 | left = effect_param_2.between?(0, 11) ? 1 : 0 8 | right = effect_param_2.between?(4, 15) ? 1 : 0 9 | 10 | { 11 | :number => 0, 12 | :params => (left << (3 + channel_number)) | (right << (channel_number - 1)) 13 | } 14 | end 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/effects/speed.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBT 3 | module Export 4 | module ASM 5 | module Effects 6 | def effect_speed 7 | speed = effect_param 8 | 9 | if speed > 0x1F then 10 | throw "Unsupported BPM speed effect: #{speed}" 11 | end 12 | 13 | { 14 | :number => 10, 15 | :params => convert_speed(speed) 16 | } 17 | end 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/asm.s.erb: -------------------------------------------------------------------------------- 1 | ; .S created with 2 | ; GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBT 3 | 4 | .area _CODE_<%= bank -%> 5 | 6 | <% for i in 0..(mod_data.pattern_table.max) do %> 7 | <%= "_#{label}#{i}" %>: 8 | <% for j in 0..63 -%> 9 | .db <%= converter.convert(mod_data.patterns[i].rows[j]) %> 10 | <% end -%> 11 | <% end -%> 12 | 13 | .globl <%= "_#{label}" %> 14 | .dw <%= "_#{label}" %> 15 | 16 | <%= "_#{label}" %>: 17 | <% for i in 0..(mod_data.song_length - 1) -%> 18 | .dw <%= "_#{label}#{i}" %> 19 | <% end -%> 20 | .dw 0x0000 21 | -------------------------------------------------------------------------------- /spec/gbtiles/gbt/export/asm/channels/wav_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channels/wav" 2 | 3 | RSpec.describe GBTiles::GBT::Export::ASM::Channels::Wav, "#initialize" do 4 | before do 5 | @channel = GBTiles::GBT::Export::ASM::Channels::Wav.new 6 | end 7 | 8 | it "is not a pulse channel" do 9 | expect(@channel.is_pulse?).to eql false 10 | end 11 | 12 | it "is a wav channel" do 13 | expect(@channel.is_wav?).to eql true 14 | end 15 | 16 | it "is not a noise channel" do 17 | expect(@channel.is_noise?).to eql false 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/gbtiles/gbt/export/asm/channels/noise_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channels/noise" 2 | 3 | RSpec.describe GBTiles::GBT::Export::ASM::Channels::Noise, "#initialize" do 4 | before do 5 | @channel = GBTiles::GBT::Export::ASM::Channels::Noise.new 6 | end 7 | 8 | it "is not a pulse channel" do 9 | expect(@channel.is_pulse?).to eql false 10 | end 11 | 12 | it "is not a wav channel" do 13 | expect(@channel.is_wav?).to eql false 14 | end 15 | 16 | it "is a noise channel" do 17 | expect(@channel.is_noise?).to eql true 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/export/asm/asm.h.erb: -------------------------------------------------------------------------------- 1 | <% tile_export = tile_set.tile_export.first -%> 2 | /** 3 | * .H created with 4 | * GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBR 5 | * 6 | * .GBR created with 7 | * <%= tile_set.producer.first.name %> (v<%= tile_set.producer.first.version %>) 8 | * <%= tile_set.producer.first.info %> 9 | */ 10 | 11 | // Helpers 12 | #define <%= tile_export.label_name %>Bank <%= bank %> 13 | #define <%= tile_export.label_name %>Count <%= tile_export.upto - tile_export.from + 1 %> 14 | 15 | // Extern 16 | extern unsigned char <%= tile_export.label_name %>[]; 17 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/export/asm/asm.s.erb: -------------------------------------------------------------------------------- 1 | <% tile_export = tile_set.tile_export.first -%> 2 | ; .S created with 3 | ; GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBR 4 | ; 5 | ; .GBR created with 6 | ; <%= tile_set.producer.first.name %> (v<%= tile_set.producer.first.version %>) 7 | ; <%= tile_set.producer.first.info %> 8 | 9 | .area _CODE_<%= bank %> 10 | 11 | .globl _<%= tile_export.label_name %> 12 | .dw _<%= tile_export.label_name %> 13 | 14 | _<%= tile_export.label_name %>: 15 | <% (tile_export.from..tile_export.upto).each do |i| -%> 16 | .db <%= tile_set.tile_data.first.render_tile(i) %> 17 | <% end %> -------------------------------------------------------------------------------- /lib/gbtiles/gbm/export/asm/asm.h.erb: -------------------------------------------------------------------------------- 1 | <% map_export_settings = map_set.map_export_settings.first -%> 2 | /** 3 | * .H created with 4 | * GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBM 5 | * 6 | * .GBM created with 7 | * <%= map_set.producer.first.name %> (v<%= map_set.producer.first.version %>) 8 | * <%= map_set.producer.first.info %> 9 | */ 10 | 11 | <% map_set.map.each do |map| -%> 12 | // Helper: Map dimensions 13 | #define <%= map_export_settings.label_name %>Width <%= map.width %> 14 | #define <%= map_export_settings.label_name %>Height <%= map.height %> 15 | #define <%= map_export_settings.label_name %>Bank <%= bank %> 16 | 17 | // Extern: <%= map_export_settings.label_name %> 18 | extern unsigned char <%= map_export_settings.label_name %>[]; 19 | <% end %> -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/objects/producer_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/map/objects/producer" 2 | 3 | RSpec.describe GBTiles::GBM::Map::Objects::Producer, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbm/producer/partial.gbm", "rb" 6 | @producer = GBTiles::GBM::Map::Objects::Producer.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@producer.object_type).to eql GBTiles::GBM::Map::OBJECT_TYPE[:producer] 11 | end 12 | 13 | it "reads the name" do 14 | expect(@producer.name).to eql "Gameboy Map Builder" 15 | end 16 | 17 | it "reads the version" do 18 | expect(@producer.version).to eql "1.8" 19 | end 20 | 21 | it "reads the info" do 22 | expect(@producer.info).to eql "Home: http://www.casema.net/~hpmulder" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/producer.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class Producer < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :name 8 | attr_accessor :version 9 | attr_accessor :info 10 | 11 | def initialize 12 | super GBTiles::GBM::Map::OBJECT_TYPE[:producer] 13 | end 14 | 15 | def self.initFromBitString src 16 | object = GBTiles::GBM::Map::Objects::Producer.new 17 | 18 | object.name = GBTiles::DataType.string!(src, 128) 19 | object.version = GBTiles::DataType.string!(src, 10) 20 | object.info = GBTiles::DataType.string!(src, 128) 21 | 22 | object 23 | end 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/producer_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/producer" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::Producer, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbr/producer/partial.gbr", "rb" 6 | @producer = GBTiles::GBR::TileSet::Objects::Producer.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@producer.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:producer] 11 | end 12 | 13 | it "reads the name" do 14 | expect(@producer.name).to eql "Gameboy Tile Designer" 15 | end 16 | 17 | it "reads the version" do 18 | expect(@producer.version).to eql "2.2" 19 | end 20 | 21 | it "reads the info" do 22 | expect(@producer.info).to eql "Home: www.casema.net/~hpmulder" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/producer.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class Producer < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :name 8 | attr_accessor :version 9 | attr_accessor :info 10 | 11 | def initialize 12 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:producer] 13 | end 14 | 15 | def self.initFromBitString src 16 | object = GBTiles::GBR::TileSet::Objects::Producer.new 17 | 18 | object.name = GBTiles::DataType.string!(src, 30) 19 | object.version = GBTiles::DataType.string!(src, 10) 20 | object.info = GBTiles::DataType.string!(src, 80) 21 | 22 | object 23 | end 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/palettes_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/palettes" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::Palettes, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbr/palettes/partial.gbr", "rb" 6 | @palettes = GBTiles::GBR::TileSet::Objects::Palettes.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@palettes.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:palettes] 11 | end 12 | 13 | it "reads the id" do 14 | expect(@palettes.id).to eql 1 15 | end 16 | 17 | it "reads the count" do 18 | expect(@palettes.count).to eql 8 19 | end 20 | 21 | it "reads the colors" do 22 | expect(@palettes.colors.length).to eql @palettes.count 23 | end 24 | 25 | it "reads the sgb_count" 26 | it "reads the sgb_colors" 27 | end 28 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/tile_pal_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/tile_pal" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::TilePal, "#initFromBitString" do 4 | before do 5 | file = File.open "spec/fixtures/gbr/tile_pal/partial.gbr", "rb" 6 | @tile_pal = GBTiles::GBR::TileSet::Objects::TilePal.initFromBitString file.read 7 | end 8 | 9 | it "expect object type to be :tile_pal" do 10 | expect(@tile_pal.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_pal] 11 | end 12 | 13 | it "reads the id" do 14 | expect(@tile_pal.id).to eql 1 15 | end 16 | it "reads the count" do 17 | expect(@tile_pal.count).to eql 128 18 | end 19 | it "reads the color_set" do 20 | expect(@tile_pal.color_set.length).to eql @tile_pal.count 21 | end 22 | it "reads the sgb_count" do 23 | expect(@tile_pal.sgb_count).to eql 0 24 | end 25 | it "reads the sgb_color_set" do 26 | expect(@tile_pal.sgb_color_set.length).to eql 0 27 | end 28 | end -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/map_tile_data_record.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class MapTileDataRecord 6 | 7 | attr_accessor :tile_number 8 | attr_accessor :gbc_palette 9 | attr_accessor :sgb_palette 10 | attr_accessor :flipped_horizontally 11 | attr_accessor :flipped_vertically 12 | 13 | def initialize 14 | end 15 | 16 | def self.initFromBitString number 17 | record = GBTiles::GBM::Map::Objects::MapTileDataRecord.new 18 | 19 | record.tile_number = number.bits(0..9) 20 | record.gbc_palette = number.bits(10..14) 21 | record.sgb_palette = number.bits(16..18) 22 | record.flipped_horizontally = number.bits(22..22) 23 | record.flipped_vertically = number.bits(23..23) 24 | 25 | record 26 | end 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/export/asm/asm.s.erb: -------------------------------------------------------------------------------- 1 | <% map_export_settings = map_set.map_export_settings.first -%> 2 | ; .S created with 3 | ; GBTiles Ruby Gem (v<%= GBTiles::VERSION %>) - GBM 4 | ; 5 | ; .GBM created with 6 | ; <%= map_set.producer.first.name %> (v<%= map_set.producer.first.version %>) 7 | ; <%= map_set.producer.first.info %> 8 | 9 | .area _CODE_<%= bank -%> 10 | 11 | <% map_set.map.each do |map| -%> 12 | <% map_tile_data = map_set.map_tile_data(map.master_id).first -%> 13 | 14 | ; Map <%= "(#{map.name})" unless map.name.nil? %> 15 | 16 | .globl _<%= map_export_settings.label_name %> 17 | .dw _<%= map_export_settings.label_name %> 18 | 19 | _<%= map_export_settings.label_name %>: 20 | <% (1..(map.width*map.height/16.0).ceil).each do |row| -%> 21 | <% row_tile_data = map_tile_data.row(row, 16) -%> 22 | <% if !row_tile_data.nil? -%> 23 | .db <%= row_tile_data 24 | .map {|x| sprintf("0x%02x", x.tile_number + GBTiles::GBM::LAYER[layer]) } 25 | .join(',') 26 | %> 27 | <% end -%> 28 | <% end -%> 29 | <% end %> 30 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/palettes.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class Palettes < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :id 8 | attr_accessor :count 9 | attr_accessor :colors 10 | attr_accessor :sgb_count 11 | attr_accessor :sgb_colors 12 | 13 | def initialize 14 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:palettes] 15 | end 16 | 17 | def self.initFromBitString src 18 | object = GBTiles::GBR::TileSet::Objects::Palettes.new 19 | 20 | object.id = GBTiles::DataType.word!(src) 21 | object.count = GBTiles::DataType.word!(src) 22 | object.colors = src.slice!(0, object.count) 23 | object.sgb_count = GBTiles::DataType.word!(src) 24 | object.sgb_colors = src.slice!(0, object.sgb_count) 25 | 26 | object 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/tile_pal.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class TilePal < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :id 8 | attr_accessor :count 9 | attr_accessor :color_set 10 | attr_accessor :sgb_count 11 | attr_accessor :sgb_color_set 12 | 13 | def initialize 14 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_pal] 15 | end 16 | 17 | def self.initFromBitString src 18 | object = GBTiles::GBR::TileSet::Objects::TilePal.new 19 | 20 | object.id = GBTiles::DataType.word!(src) 21 | object.count = GBTiles::DataType.word!(src) 22 | object.color_set = src.slice!(0, object.count) 23 | object.sgb_count = GBTiles::DataType.word!(src) 24 | object.sgb_color_set = src.slice!(0, object.sgb_count) 25 | 26 | object 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/objects/map_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/map/objects/map" 2 | 3 | RSpec.describe GBTiles::GBM::Map::Objects::Map, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbm/map/partial.gbm", "rb" 6 | @map = GBTiles::GBM::Map::Objects::Map.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@map.object_type).to eql GBTiles::GBM::Map::OBJECT_TYPE[:map] 11 | end 12 | 13 | it "reads the name" do 14 | expect(@map.name).to eql nil 15 | end 16 | 17 | it "reads the width" do 18 | expect(@map.width).to eql 20 19 | end 20 | 21 | it "reads the height" do 22 | expect(@map.height).to eql 4 23 | end 24 | 25 | it "reads the prop_count" do 26 | expect(@map.prop_count).to eql 0 27 | end 28 | 29 | it "reads the tile_file" do 30 | expect(@map.tile_file).to eql "C:\\valid.gbr" 31 | end 32 | 33 | it "reads the tile_count" do 34 | expect(@map.tile_count).to eql 128 35 | end 36 | 37 | it "reads the prop_color_count" do 38 | expect(@map.prop_color_count).to eql 2 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Bashkim Isai 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/map_tile_data.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class MapTileData < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :records 8 | 9 | def initialize 10 | super GBTiles::GBM::Map::OBJECT_TYPE[:map_tile_data] 11 | 12 | @records = [] 13 | end 14 | 15 | def self.initFromBitString src 16 | object = GBTiles::GBM::Map::Objects::MapTileData.new 17 | 18 | while !src.empty? 19 | # Get the record 20 | number = src.slice!(0..2) # Get 24-bits (3 bytes) 21 | number = 0.chr + number # Convert from 24-bit to 32-bit 22 | number = number.unpack("N").first # Unpack integer 23 | 24 | object.records << GBTiles::GBM::Map::Objects::MapTileDataRecord.initFromBitString(number) 25 | end 26 | 27 | object 28 | end 29 | 30 | def row row, width = 16 31 | @records[(width * (row - 1))..((width * row) - 1)] 32 | end 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /gbtiles.gemspec: -------------------------------------------------------------------------------- 1 | # Ensure we require the local version and not one we might have installed already 2 | require File.join([File.dirname(__FILE__),"lib","gbtiles","version.rb"]) 3 | spec = Gem::Specification.new do |s| 4 | s.name = "gbtiles" 5 | s.version = GBTiles::VERSION 6 | s.date = Time.now.getutc 7 | s.authors = ["Bashkim Isai"] 8 | s.license = "MIT" 9 | s.homepage = "http://github.com/bashaus/gbtiles" 10 | s.platform = Gem::Platform::RUBY 11 | s.summary = "Converts .GBR, .GBM and .MOD files to different Game Boy formats" 12 | s.description = "Allows files created with Harry Mulder's Game Boy Map Builder " + 13 | "and Game Boy Tile Designer to be converted for use in " + 14 | "Game Boy game development. Also allows .MOD tracker files " + 15 | "to be converted for use with AntonioND's GBT Player." 16 | 17 | s.files = Dir["lib/**/*", "bin/*", "README.md", "LICENSE"] 18 | 19 | s.require_paths << "lib" 20 | s.bindir = "bin" 21 | s.executables << "gbr" 22 | s.executables << "gbm" 23 | s.executables << "gbt" 24 | 25 | s.add_runtime_dependency("gli", "~> 2.13") 26 | 27 | s.add_development_dependency("rspec", "3.3.0") 28 | end -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/map.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class Map < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :name 8 | attr_accessor :width 9 | attr_accessor :height 10 | attr_accessor :prop_count 11 | attr_accessor :tile_file 12 | attr_accessor :tile_count 13 | attr_accessor :prop_color_count 14 | 15 | def initialize 16 | super GBTiles::GBM::Map::OBJECT_TYPE[:map] 17 | end 18 | 19 | def self.initFromBitString src 20 | object = GBTiles::GBM::Map::Objects::Map.new 21 | 22 | object.name = GBTiles::DataType.string!(src, 128) 23 | object.width = GBTiles::DataType.long!(src) 24 | object.height = GBTiles::DataType.long!(src) 25 | object.prop_count = GBTiles::DataType.long!(src) 26 | object.tile_file = GBTiles::DataType.string!(src, 256) 27 | object.tile_count = GBTiles::DataType.long!(src) 28 | object.prop_color_count = GBTiles::DataType.long!(src) 29 | 30 | object 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/cli/convert.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/data_type" 2 | require "gbtiles/gbr/import/gbr_file" 3 | require "gbtiles/gbr/export/asm/asm" 4 | 5 | desc "Converts .GBR files to different formats for the Game Boy" 6 | arg_name "input" 7 | command :convert do |c| 8 | 9 | c.desc "Output filename [*.s]" 10 | c.flag :output 11 | 12 | c.desc "ROM Bank" 13 | c.flag :bank 14 | 15 | c.action do |global_options,options,args| 16 | # Prepare input file 17 | if !args[0].nil? then 18 | # Use file 19 | input_file = File.open(args[0], "rb") 20 | else 21 | # Use STDIN 22 | input_file = $stdin 23 | end 24 | 25 | # Prepare output file 26 | if !options[:output].nil? then 27 | # Use file 28 | output_file = File.open(options[:output], "w") 29 | else 30 | # Use STDOUT 31 | output_file = $stdout 32 | end 33 | 34 | # Do import 35 | import = GBTiles::GBR::Import::GBRFile.open(input_file) 36 | input_file.close 37 | 38 | # Do export 39 | export = GBTiles::GBR::Export::ASM::ASM.new 40 | 41 | if !options[:bank].nil? then 42 | export.bank = options[:bank].to_i 43 | end 44 | 45 | export.tile_set = import.tile_set 46 | export.write(output_file) 47 | output_file.close 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/objects/map_tile_data_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/map/objects/map_tile_data" 2 | 3 | RSpec.describe GBTiles::GBM::Map::Objects::MapTileData, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbm/map_tile_data/partial.gbm", "rb" 6 | @map_tile_data = GBTiles::GBM::Map::Objects::MapTileData.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@map_tile_data.object_type).to eql GBTiles::GBM::Map::OBJECT_TYPE[:map_tile_data] 11 | end 12 | 13 | it "has the correct number of records" do 14 | expect(@map_tile_data.records.count).to eql 80 15 | end 16 | 17 | context "first character (2x2)" do 18 | before do 19 | @tile = @map_tile_data.records[21] 20 | end 21 | 22 | it "reads the tile_number" do 23 | expect(@tile.tile_number).to eql 76 24 | end 25 | 26 | it "reads the gbc_palette" do 27 | expect(@tile.gbc_palette).to eql 0 28 | end 29 | 30 | it "reads the sgb_palette" do 31 | expect(@tile.sgb_palette).to eql 0 32 | end 33 | 34 | it "reads the flipped_horizontally" do 35 | expect(@tile.flipped_horizontally).to eql 0 36 | end 37 | 38 | it "reads the flipped_vertically" do 39 | expect(@tile.flipped_vertically).to eql 0 40 | end 41 | end 42 | 43 | end -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/converter.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channels/pulse" 2 | require "gbtiles/gbt/export/asm/channels/wav" 3 | require "gbtiles/gbt/export/asm/channels/noise" 4 | 5 | require "erb" 6 | 7 | module GBTiles 8 | module GBT 9 | module Export 10 | module ASM 11 | class Converter 12 | 13 | CHANNELS = { 14 | 1 => GBTiles::GBT::Export::ASM::Channels::Pulse.new(1), 15 | 2 => GBTiles::GBT::Export::ASM::Channels::Pulse.new(2), 16 | 3 => GBTiles::GBT::Export::ASM::Channels::Wav.new, 17 | 4 => GBTiles::GBT::Export::ASM::Channels::Noise.new 18 | } 19 | 20 | def convert data 21 | CHANNELS[1].data = data[0..3] 22 | CHANNELS[2].data = data[4..7] 23 | CHANNELS[3].data = data[8..11] 24 | CHANNELS[4].data = data[12..15] 25 | 26 | [ 27 | CHANNELS[1].convert.map { |x| sprintf('0x%02X', x) }.join(', '), 28 | CHANNELS[2].convert.map { |x| sprintf('0x%02X', x) }.join(', '), 29 | CHANNELS[3].convert.map { |x| sprintf('0x%02X', x) }.join(', '), 30 | CHANNELS[4].convert.map { |x| sprintf('0x%02X', x) }.join(', ') 31 | ].join(', ') 32 | end 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/gbtiles/gbt/export/asm/channels/pulse_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channels/pulse" 2 | 3 | RSpec.describe GBTiles::GBT::Export::ASM::Channels::Pulse, "#initialize" do 4 | before do 5 | @channel = GBTiles::GBT::Export::ASM::Channels::Pulse.new(1) 6 | end 7 | 8 | it "is a pulse channel" do 9 | expect(@channel.is_pulse?).to eql true 10 | end 11 | 12 | it "is not a wav channel" do 13 | expect(@channel.is_wav?).to eql false 14 | end 15 | 16 | it "is not a noise channel" do 17 | expect(@channel.is_noise?).to eql false 18 | end 19 | end 20 | 21 | RSpec.describe GBTiles::GBT::Export::ASM::Channels::Pulse, "#convert" do 22 | before do 23 | @channel = GBTiles::GBT::Export::ASM::Channels::Pulse.new(1) 24 | end 25 | 26 | context "no effect" do 27 | before do 28 | @channel.data = [ 0x00, 0x00, 0x00, 0x00 ] 29 | end 30 | 31 | it "returns no result" do 32 | expect(@channel.convert).to eql [ 0x0 ] 33 | end 34 | end 35 | 36 | context "volume" do 37 | it "can be max" do 38 | @channel.data = [ 0x02, 0x3A, 0x8C, 0x40 ] 39 | expect(@channel.convert).to eql [ 0x93, 0x0F ] 40 | end 41 | 42 | it "can be min" do 43 | @channel.data = [ 0x00, 0x00, 0x0C, 0x00 ] 44 | expect(@channel.convert).to eql [ 0x20 ] 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/tile_import_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/tile_import" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::TileImport, "#initFromBitString" do 4 | before do 5 | file = File.open "spec/fixtures/gbr/tile_import/partial.gbr", "rb" 6 | @tile_import = GBTiles::GBR::TileSet::Objects::TileImport.initFromBitString file.read 7 | end 8 | 9 | it "expect object type to be :tile_import" do 10 | expect(@tile_import.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_import] 11 | end 12 | 13 | it "reads the tile id" do 14 | expect(@tile_import.tile_id).to eql 1 15 | end 16 | 17 | it "reads the file name" do 18 | expect(@tile_import.file_name).to be_nil 19 | end 20 | 21 | it "reads the file type" do 22 | expect(@tile_import.file_type).to eql 0 23 | end 24 | 25 | it "reads the from tile" do 26 | expect(@tile_import.from_tile).to eql 0 27 | end 28 | 29 | it "reads the to tile" do 30 | expect(@tile_import.to_tile).to eql 0 31 | end 32 | 33 | it "reads the tile count" do 34 | expect(@tile_import.tile_count).to eql 127 35 | end 36 | 37 | it "reads the color conversion" do 38 | expect(@tile_import.color_conversion).to eql 0 39 | end 40 | 41 | it "reads the first byte" do 42 | expect(@tile_import.first_byte).to eql 0 43 | end 44 | 45 | it "reads the binary file type" do 46 | expect(@tile_import.binary_file_type).to eql 0 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/cli/convert.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/data_type" 2 | require "gbtiles/gbm/import/gbm_file" 3 | require "gbtiles/gbm/export/asm/asm" 4 | 5 | desc "Converts .GBM files to different formats for the Game Boy" 6 | arg_name "input" 7 | command :convert do |c| 8 | 9 | c.desc "Output filename [*.s]" 10 | c.flag :output 11 | 12 | c.desc "ROM Bank" 13 | c.flag :bank 14 | 15 | c.desc "Layer type of the map [bkg, win, sprite]" 16 | c.flag :layer, 17 | :must_match => [:bkg, :win, :sprite], 18 | :default_value => :bkg 19 | 20 | c.action do |global_options,options,args| 21 | # Prepare input file 22 | if !args[0].nil? then 23 | # Use file 24 | input_file = File.open(args[0], "rb") 25 | else 26 | # Use STDIN 27 | input_file = $stdin 28 | end 29 | 30 | # Prepare output file 31 | if !options[:output].nil? then 32 | # Use file 33 | output_file = File.open(options[:output], "w") 34 | else 35 | # Use STDOUT 36 | output_file = $stdout 37 | end 38 | 39 | # Do import 40 | import = GBTiles::GBM::Import::GBMFile.open(input_file) 41 | input_file.close 42 | 43 | # Do export 44 | export = GBTiles::GBM::Export::ASM::ASM.new 45 | 46 | if !options[:bank].nil? then 47 | export.bank = options[:bank].to_i 48 | end 49 | 50 | export.map_set = import.map_set 51 | export.layer = options[:layer] 52 | export.write(output_file) 53 | output_file.close 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/tile_import.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class TileImport < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :tile_id 8 | attr_accessor :file_name 9 | attr_accessor :file_type 10 | attr_accessor :from_tile 11 | attr_accessor :to_tile 12 | attr_accessor :tile_count 13 | attr_accessor :color_conversion 14 | attr_accessor :first_byte 15 | attr_accessor :binary_file_type 16 | 17 | def initialize 18 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_import] 19 | end 20 | 21 | def self.initFromBitString src 22 | object = GBTiles::GBR::TileSet::Objects::TileImport.new 23 | 24 | object.tile_id = GBTiles::DataType.word!(src) 25 | object.file_name = GBTiles::DataType.string!(src, 128) 26 | object.file_type = GBTiles::DataType.byte!(src) 27 | object.from_tile = GBTiles::DataType.word!(src) 28 | object.to_tile = GBTiles::DataType.word!(src) 29 | object.tile_count = GBTiles::DataType.word!(src) 30 | object.color_conversion = GBTiles::DataType.byte!(src) 31 | object.first_byte = GBTiles::DataType.long!(src) 32 | object.binary_file_type = GBTiles::DataType.byte!(src) 33 | 34 | object 35 | end 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/cli/convert.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/data_type" 2 | require "gbtiles/gbt/import/mod_file" 3 | require "gbtiles/gbt/export/asm/asm" 4 | 5 | desc "Converts .MOD files to different formats for the Game Boy" 6 | arg_name "input" 7 | command :convert do |c| 8 | 9 | c.desc "Output filename [*.s]" 10 | c.flag :output 11 | 12 | c.desc "ROM Bank" 13 | c.default_value 2 14 | c.flag :bank 15 | 16 | c.desc "Label" 17 | c.flag :label 18 | 19 | c.action do |global_options,options,args| 20 | # Prepare input file 21 | if !args[0].nil? then 22 | # Use file 23 | input_file = File.open(args[0], "rb") 24 | else 25 | # Use STDIN 26 | input_file = $stdin 27 | end 28 | 29 | # Prepare output file 30 | if !options[:output].nil? then 31 | # Use file 32 | output_file = File.open(options[:output], "w") 33 | else 34 | # Use STDOUT 35 | output_file = $stdout 36 | end 37 | 38 | # Do import 39 | import = GBTiles::GBT::Import::MODFile.open(input_file) 40 | input_file.close 41 | 42 | # Do export 43 | export = GBTiles::GBT::Export::ASM::ASM.new 44 | export.bank = options[:bank].to_i 45 | export.mod_data = import.mod_data 46 | 47 | if !options[:label].nil? then 48 | # Use label from CLI 49 | export.label = options[:label] 50 | elsif !options[:output].nil? then 51 | # Use filename 52 | export.label = File.basename(options[:output], ".*") 53 | end 54 | 55 | export.write(output_file) 56 | output_file.close 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/tile_settings_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/tile_settings" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::TileSettings, "#initFromBitString" do 4 | before do 5 | file = File.open "spec/fixtures/gbr/tile_settings/partial.gbr", "rb" 6 | @tile_settings = GBTiles::GBR::TileSet::Objects::TileSettings.initFromBitString file.read 7 | end 8 | 9 | it "expect object type to be :tile_settings" do 10 | expect(@tile_settings.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_settings] 11 | end 12 | 13 | it "reads the tile_id" do 14 | expect(@tile_settings.tile_id).to eql 1 15 | end 16 | 17 | it "reads the simple" do 18 | expect(@tile_settings.simple).to eql false 19 | end 20 | 21 | it "reads the flags" do 22 | expect(@tile_settings.flags).to eql 1 23 | end 24 | 25 | it "reads the left_color" do 26 | expect(@tile_settings.left_color).to eql 0 27 | end 28 | 29 | it "reads the right_color" do 30 | expect(@tile_settings.right_color).to eql 0 31 | end 32 | 33 | it "reads the split_width" do 34 | expect(@tile_settings.split_width).to eql 1 35 | end 36 | 37 | it "reads the split_height" do 38 | expect(@tile_settings.split_height).to eql 1 39 | end 40 | 41 | it "reads the split_order" do 42 | expect(@tile_settings.split_order).to eql 4294901760 43 | end 44 | 45 | it "reads the color_set" do 46 | expect(@tile_settings.color_set).to eql 255 47 | end 48 | 49 | it "reads the bookmarks" 50 | 51 | it "reads the auto_update" 52 | 53 | end -------------------------------------------------------------------------------- /lib/gbtiles/gbr/export/asm/asm.rb: -------------------------------------------------------------------------------- 1 | require "erb" 2 | 3 | module GBTiles 4 | module GBR 5 | module Export 6 | module ASM 7 | class ASM 8 | include ERB::Util 9 | 10 | attr_accessor :bank 11 | attr_accessor :tile_set 12 | 13 | def initialize 14 | end 15 | 16 | def prerender 17 | if @tile_set.nil? then 18 | raise "Missing required tile set" 19 | end 20 | 21 | @bank ||= @tile_set.tile_export.first.bank 22 | end 23 | 24 | def render_s 25 | prerender 26 | 27 | template = File.open(File.join(__dir__, "asm.s.erb"), "r").read 28 | 29 | ERB.new(template, nil, "-").result(binding) 30 | end 31 | 32 | def render_h 33 | prerender 34 | 35 | template = File.open(File.join(__dir__, "asm.h.erb"), "r").read 36 | 37 | ERB.new(template, nil, "-").result(binding) 38 | end 39 | 40 | def write output_stream 41 | output_stream.write(render_s) 42 | 43 | if output_stream.is_a? File then 44 | header_filename = "#{File.basename(output_stream.path, ".s")}.h" 45 | header_path = "#{File.dirname(File.expand_path(output_stream.path))}/#{header_filename}" 46 | header_stream = File.open(header_path, "w") 47 | header_stream.write(render_h) 48 | header_stream.close 49 | end 50 | end 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/tile_settings.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class TileSettings < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :tile_id 8 | attr_accessor :simple 9 | attr_accessor :flags 10 | attr_accessor :left_color 11 | attr_accessor :right_color 12 | attr_accessor :split_width 13 | attr_accessor :split_height 14 | attr_accessor :split_order 15 | attr_accessor :color_set 16 | attr_accessor :bookmarks 17 | attr_accessor :auto_update 18 | 19 | def initialize 20 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_settings] 21 | end 22 | 23 | def self.initFromBitString src 24 | object = GBTiles::GBR::TileSet::Objects::TileSettings.new 25 | 26 | object.tile_id = GBTiles::DataType.word!(src) 27 | object.simple = GBTiles::DataType.boolean!(src) 28 | object.flags = GBTiles::DataType.byte!(src) 29 | object.left_color = GBTiles::DataType.byte!(src) 30 | object.right_color = GBTiles::DataType.byte!(src) 31 | object.split_width = GBTiles::DataType.word!(src) 32 | object.split_height = GBTiles::DataType.word!(src) 33 | object.split_order = GBTiles::DataType.long!(src) 34 | object.color_set = GBTiles::DataType.byte!(src) 35 | # @bookmarks Word(3) 36 | # @auto_update Boolean 37 | 38 | object 39 | end 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GBTiles 2 | 3 | [![Build Status](https://travis-ci.org/bashaus/gbtiles.svg?branch=master)](https://travis-ci.org/bashaus/gbtiles) 4 | [![Code Climate](https://codeclimate.com/github/bashaus/gbtiles/badges/gpa.svg)](https://codeclimate.com/github/bashaus/gbtiles) 5 | [![Test Coverage](https://codeclimate.com/github/bashaus/gbtiles/badges/coverage.svg)](https://codeclimate.com/github/bashaus/gbtiles/coverage) 6 | 7 | Converts `.GBR` files created with 8 | [Harry Mulder's Tile Designer (GBTD)](http://www.devrs.com/gb/hmgd/gbtd.html) 9 | and `.GBM` files created with 10 | [Harry Mulder's Map Builder (GBMB)](http://www.devrs.com/gb/hmgd/gbmb.html) 11 | to different formats for use with the Game Boy and 12 | [GBDK](http://gbdk.sourceforge.net/). 13 | 14 | N.B.: Not all of the functionality provided by Harry Mulder's GBR and GBM 15 | file format specification have been implemented; however there is enough to 16 | generate the basics for tiles and maps. 17 | 18 | Also converts `.MOD` tracker files setup for the Game Boy for use with 19 | [AntonioND's GBT Player](https://github.com/AntonioND/gbt-player). 20 | 21 | 22 | 23 | ## Installation 24 | 25 | Install globally: 26 | 27 | gem install gbtiles 28 | 29 | Include in your `Gemfile`: 30 | 31 | gem "gbtiles", "~> 0.2.0" 32 | 33 | 34 | 35 | ## Usage 36 | 37 | To view help information for each of the commands, simply type one of the 38 | following: 39 | 40 | # Converts .GBM files to different formats for the Game Boy 41 | gbm convert --help 42 | 43 | # Converts .GBR files to different formats for the Game Boy 44 | gbr convert --help 45 | 46 | # Converts .MOD files to different formats for the Game Boy 47 | gbt convert --help 48 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/export/asm/asm.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/layer" 2 | 3 | require "erb" 4 | 5 | module GBTiles 6 | module GBM 7 | module Export 8 | module ASM 9 | class ASM 10 | include ERB::Util 11 | 12 | attr_accessor :bank 13 | attr_accessor :map_set 14 | attr_accessor :layer 15 | 16 | def initialize 17 | end 18 | 19 | def prerender 20 | if @map_set.nil? then 21 | raise "Missing required map" 22 | end 23 | 24 | if @layer.nil? then 25 | raise "Missing required layer (bkg, win, sprite)" 26 | end 27 | 28 | @bank ||= @map_set.map_export_settings.first.bank 29 | end 30 | 31 | def render_s 32 | prerender 33 | 34 | template = File.open(File.join(__dir__, "asm.s.erb"), "r").read 35 | 36 | ERB.new(template, nil, "-").result(binding) 37 | end 38 | 39 | def render_h 40 | prerender 41 | 42 | template = File.open(File.join(__dir__, "asm.h.erb"), "r").read 43 | 44 | ERB.new(template, nil, "-").result(binding) 45 | end 46 | 47 | def write output_stream 48 | output_stream.write(render_s) 49 | 50 | if output_stream.is_a? File then 51 | header_filename = "#{File.basename(output_stream.path, ".s")}.h" 52 | header_path = "#{File.dirname(File.expand_path(output_stream.path))}/#{header_filename}" 53 | header_stream = File.open(header_path, "w") 54 | header_stream.write(render_h) 55 | header_stream.close 56 | end 57 | end 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/gbtiles/helpers/data_type_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/data_type" 2 | 3 | RSpec.describe GBTiles::DataType, "#string" do 4 | context "of undefined length" do 5 | it "returns a truncated string at a string terminator" do 6 | string = GBTiles::DataType.string("Lorem ipsum\0dolar sit a met") 7 | expect(string).to eql("Lorem ipsum") 8 | end 9 | 10 | it "returns a truncated string at the first instance of a string terminator" do 11 | string = GBTiles::DataType.string("Lorem ipsum dolar\0sit a\0met") 12 | expect(string).to eql("Lorem ipsum dolar") 13 | end 14 | 15 | it "returns null when passed a null string" do 16 | string = GBTiles::DataType.string(nil) 17 | expect(string).to eql(nil) 18 | end 19 | end 20 | 21 | context "of defined length" do 22 | it "ignores a later string terminator" do 23 | string = GBTiles::DataType.string("123456789\00123456789", 5) 24 | expect(string).to eql("12345") 25 | end 26 | 27 | it "accepts an early string terminator" do 28 | string = GBTiles::DataType.string("He\0llo", 5) 29 | expect(string).to eql("He") 30 | end 31 | 32 | it "returns null when passed a null string" do 33 | string = GBTiles::DataType.string(nil, 10) 34 | expect(string).to eql(nil) 35 | end 36 | end 37 | end 38 | 39 | RSpec.describe GBTiles::DataType, "#string!" do 40 | it "strips the character using the dangerous method" do 41 | string = "\x47\x42\x4f\x30\x01\x00\x00\x00\x78\x00\x00\x00\x47\x61\x6d\x65".b 42 | initial_length = string.length 43 | GBTiles::DataType.string!(string, 3) 44 | expect(string.length).to eql(initial_length - 3) 45 | end 46 | 47 | it "returns nil when nil is provided" do 48 | string = nil 49 | GBTiles::DataType.string!(string) 50 | expect(string).to be_nil 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/asm.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/converter" 2 | require "erb" 3 | 4 | module GBTiles 5 | module GBT 6 | module Export 7 | module ASM 8 | class ASM 9 | include ERB::Util 10 | 11 | attr_accessor :bank 12 | attr_accessor :label 13 | attr_accessor :mod_data 14 | attr_accessor :converter 15 | 16 | def initialize 17 | @converter = GBTiles::GBT::Export::ASM::Converter.new 18 | end 19 | 20 | def prerender 21 | if !@bank.is_a? Numeric then 22 | raise "Bank must be numeric" 23 | end 24 | 25 | if @mod_data.nil? then 26 | raise "Missing required mod data" 27 | end 28 | 29 | @label ||= mod_data.name 30 | end 31 | 32 | def render_s 33 | prerender 34 | 35 | template = File.open(File.join(__dir__, "asm.s.erb"), "r").read 36 | 37 | ERB.new(template, nil, "-").result(binding) 38 | end 39 | 40 | def render_h 41 | prerender 42 | 43 | template = File.open(File.join(__dir__, "asm.h.erb"), "r").read 44 | 45 | ERB.new(template, nil, "-").result(binding) 46 | end 47 | 48 | def write output_stream 49 | output_stream.write(render_s) 50 | 51 | if output_stream.is_a? File then 52 | header_filename = "#{File.basename(output_stream.path, ".s")}.h" 53 | header_path = "#{File.dirname(File.expand_path(output_stream.path))}/#{header_filename}" 54 | header_stream = File.open(header_path, "w") 55 | header_stream.write(render_h) 56 | header_stream.close 57 | end 58 | end 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/tile_set.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | class TileSet 5 | 6 | attr_accessor :objects 7 | 8 | def initialize 9 | @objects = [] 10 | end 11 | 12 | def producer 13 | @objects.select{ |a| 14 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:producer] 15 | } 16 | end 17 | 18 | def tile_data 19 | @objects.select{ |a| 20 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_data] 21 | } 22 | end 23 | 24 | def tile_settings 25 | @objects.select{ |a| 26 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_settings] 27 | } 28 | end 29 | 30 | def tile_export 31 | @objects.select{ |a| 32 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_export] 33 | } 34 | end 35 | 36 | def tile_import 37 | @objects.select{ |a| 38 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_import] 39 | } 40 | end 41 | 42 | def palettes 43 | @objects.select{ |a| 44 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:palettes] 45 | } 46 | end 47 | 48 | def tile_pal 49 | @objects.select{ |a| 50 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_pal] 51 | } 52 | end 53 | 54 | def deleted 55 | @objects.select{ |a| 56 | a.object_type == GBTiles::GBR::TileSet::OBJECT_TYPE[:deleted] 57 | } 58 | end 59 | 60 | def unknown 61 | @objects.select{ |a| 62 | GBTiles::GBR::TileSet::OBJECT_TYPE.key(a.object_type).nil? 63 | } 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/objects/map_settings_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/map/objects/map_settings" 2 | 3 | RSpec.describe GBTiles::GBM::Map::Objects::MapSettings, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbm/map_settings/partial.gbm", "rb" 6 | @map_settings = GBTiles::GBM::Map::Objects::MapSettings.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@map_settings.object_type).to eql GBTiles::GBM::Map::OBJECT_TYPE[:map_settings] 11 | end 12 | 13 | it "reads the form_width" do 14 | expect(@map_settings.form_width).to eql 605 15 | end 16 | 17 | it "reads the form_height" do 18 | expect(@map_settings.form_height).to eql 498 19 | end 20 | 21 | it "reads the form_maximized" do 22 | expect(@map_settings.form_maximized).to eql false 23 | end 24 | 25 | it "reads the info_panel" do 26 | expect(@map_settings.info_panel).to eql true 27 | end 28 | 29 | it "reads the grid" do 30 | expect(@map_settings.grid).to eql true 31 | end 32 | 33 | it "reads the double_markers" do 34 | expect(@map_settings.double_markers).to eql false 35 | end 36 | 37 | it "reads the prop_colors" do 38 | expect(@map_settings.prop_colors).to eql false 39 | end 40 | 41 | it "reads the zoom" do 42 | expect(@map_settings.zoom).to eql 2 43 | end 44 | 45 | it "reads the color_set" do 46 | expect(@map_settings.color_set).to eql 0 47 | end 48 | 49 | it "reads the bookmarks" do 50 | # expect(@map_settings.bookmarks).to eql "\xFF" 51 | end 52 | 53 | it "reads the block_fill_pattern" do 54 | expect(@map_settings.block_fill_pattern).to eql 4294967295 55 | end 56 | 57 | it "reads the block_fill_width" do 58 | expect(@map_settings.block_fill_width).to eql 255 59 | end 60 | 61 | it "reads the block_fill_height" do 62 | expect(@map_settings.block_fill_height).to eql 256 63 | end 64 | 65 | end -------------------------------------------------------------------------------- /spec/gbtiles/gbt/import/mod_file_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/import/mod_file" 2 | 3 | RSpec.describe GBTiles::GBT::Import::MODFile, "#open" do 4 | context "with an invalid identifier" do 5 | before do 6 | @file = File.open "spec/fixtures/gbt/mod_file/invalid-identifier.mod", "rb" 7 | end 8 | 9 | it "raises an error" do 10 | expect { @mod = GBTiles::GBT::Import::MODFile.open @file }.to raise_error(IOError) 11 | end 12 | end 13 | 14 | context "with a valid file header" do 15 | before do 16 | @file = File.open "spec/fixtures/gbt/mod_file/valid.mod", "rb" 17 | end 18 | 19 | it "doesn't raise an error" do 20 | expect { @mod = GBTiles::GBT::Import::MODFile.open @file }.not_to raise_error(IOError) 21 | end 22 | 23 | context "on file process" do 24 | before do 25 | @mod = GBTiles::GBT::Import::MODFile.open @file 26 | end 27 | 28 | it "reads the name" do 29 | expect(@mod.mod_data.name).to eql "untitled" 30 | end 31 | 32 | it "reads all the samples" do 33 | expect(@mod.mod_data.samples.count).to eql 31 34 | end 35 | 36 | it "reads the song length" do 37 | expect(@mod.mod_data.song_length).to eql 4 38 | end 39 | 40 | it "reads all the pattern table" do 41 | expect(@mod.mod_data.pattern_table.count).to eql 128 42 | end 43 | 44 | it "reads the pattern table data" do 45 | expect(@mod.mod_data.pattern_table[0]).to eql 0 46 | expect(@mod.mod_data.pattern_table[1]).to eql 1 47 | expect(@mod.mod_data.pattern_table[2]).to eql 2 48 | expect(@mod.mod_data.pattern_table[3]).to eql 3 49 | end 50 | 51 | it "reads the identifier" do 52 | expect(@mod.mod_data.identifier).to eql "M.K." 53 | end 54 | 55 | it "reads all the patterns" do 56 | expect(@mod.mod_data.patterns.count).to eql 64 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/gbtiles/helpers/data_type.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module DataType 3 | def self.string src, length = nil 4 | if src.nil? then 5 | return nil 6 | end 7 | 8 | if !length.nil? then 9 | string = src.slice(0, length) 10 | else 11 | string = src 12 | end 13 | 14 | if !string.nil? then 15 | string = string.split(/\0/).first 16 | end 17 | 18 | if !string.nil? then 19 | string = string.unpack("A*")[0] 20 | end 21 | 22 | string 23 | end 24 | 25 | def self.string! src, length = nil 26 | if src.nil? then 27 | return nil 28 | end 29 | 30 | if !length.nil? then 31 | string = src.slice!(0, length) 32 | else 33 | string = src 34 | end 35 | 36 | self.string(string, length) 37 | end 38 | 39 | # WORD 40 | # LITTLE ENDIAN 41 | # 2 BYTES 42 | def self.word src 43 | src.slice(0, 2).unpack("v*")[0] 44 | end 45 | 46 | def self.word! src 47 | self.word(src.slice!(0, 2)) 48 | end 49 | 50 | # BYTE 51 | # 1 BYTE 52 | def self.byte src 53 | src.slice(0, 1).unpack("C*")[0] 54 | end 55 | 56 | def self.byte! src 57 | self.byte(src.slice!(0, 1)) 58 | end 59 | 60 | # BOOLEAN 61 | # 1 BYTE 62 | def self.boolean src 63 | src.slice!(0, 1).unpack("C")[0].eql? 1 64 | end 65 | 66 | def self.boolean! src 67 | self.boolean(src.slice!(0, 1)) 68 | end 69 | 70 | # LITTLE ENDIAN 71 | # 32 BIT INTEGER 72 | # 4 BYTES 73 | def self.long src 74 | src.slice(0, 4).unpack("V*")[0] 75 | end 76 | 77 | def self.long! src 78 | self.long(src.slice!(0, 4)) 79 | end 80 | 81 | # BWORD 82 | # BIG ENDIAN 83 | # 2 BYTES 84 | def self.bword src 85 | src.slice(0, 2).unpack("n*")[0] 86 | end 87 | 88 | def self.bword! src 89 | self.bword(src.slice!(0, 2)) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/map_set.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | class MapSet 5 | 6 | attr_accessor :objects 7 | 8 | def initialize 9 | @objects = [] 10 | end 11 | 12 | def producer master_id = nil 13 | @objects.select{ |a| 14 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:producer] 15 | }.select{ |a| 16 | master_id.nil? || a.object_id = master_id 17 | } 18 | end 19 | 20 | def map master_id = nil 21 | @objects.select{ |a| 22 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:map] 23 | }.select{ |a| 24 | master_id.nil? || a.object_id = master_id 25 | } 26 | end 27 | 28 | def map_tile_data master_id = nil 29 | @objects.select{ |a| 30 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:map_tile_data] 31 | }.select{ |a| 32 | master_id.nil? || a.object_id = master_id 33 | } 34 | end 35 | 36 | def map_settings master_id = nil 37 | @objects.select{ |a| 38 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:map_settings] 39 | }.select{ |a| 40 | master_id.nil? || a.object_id = master_id 41 | } 42 | end 43 | 44 | def map_export_settings master_id = nil 45 | @objects.select{ |a| 46 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:map_export_settings] 47 | }.select{ |a| 48 | master_id.nil? || a.object_id = master_id 49 | } 50 | end 51 | 52 | def deleted 53 | @objects.select{ |a| 54 | a.object_type == GBTiles::GBM::Map::OBJECT_TYPE[:deleted] 55 | } 56 | end 57 | 58 | def unknown 59 | @objects.select{ |a| 60 | GBTiles::GBM::Map::OBJECT_TYPE.key(a.object_type).nil? 61 | } 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/map_settings.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class MapSettings < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :form_width 8 | attr_accessor :form_height 9 | attr_accessor :form_maximized 10 | attr_accessor :info_panel 11 | attr_accessor :grid 12 | attr_accessor :double_markers 13 | attr_accessor :prop_colors 14 | attr_accessor :zoom 15 | attr_accessor :color_set 16 | attr_accessor :bookmarks 17 | attr_accessor :block_fill_pattern 18 | attr_accessor :block_fill_width 19 | attr_accessor :block_fill_height 20 | 21 | def initialize 22 | super GBTiles::GBM::Map::OBJECT_TYPE[:map_settings] 23 | end 24 | 25 | def self.initFromBitString src 26 | object = GBTiles::GBM::Map::Objects::MapSettings.new 27 | 28 | object.form_width = GBTiles::DataType.long!(src) 29 | object.form_height = GBTiles::DataType.long!(src) 30 | object.form_maximized = GBTiles::DataType.boolean!(src) 31 | object.info_panel = GBTiles::DataType.boolean!(src) 32 | object.grid = GBTiles::DataType.boolean!(src) 33 | object.double_markers = GBTiles::DataType.boolean!(src) 34 | object.prop_colors = GBTiles::DataType.boolean!(src) 35 | object.zoom = GBTiles::DataType.word!(src) 36 | object.color_set = GBTiles::DataType.word!(src) 37 | object.bookmarks = src.slice!(3) 38 | object.block_fill_pattern = GBTiles::DataType.long!(src) 39 | object.block_fill_width = GBTiles::DataType.long!(src) 40 | object.block_fill_height = GBTiles::DataType.long!(src) 41 | 42 | object 43 | end 44 | end 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/import/gbr_file_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/import/gbr_file" 2 | 3 | RSpec.describe GBTiles::GBR::Import::GBRFile, "#open" do 4 | context "with an invalid file header" do 5 | before do 6 | @file = File.open "spec/fixtures/gbr/gbr_file/invalid-header.gbr", "rb" 7 | end 8 | 9 | it "raises an error" do 10 | expect { @gbr = GBTiles::GBR::Import::GBRFile.open @file }.to raise_error(IOError) 11 | end 12 | end 13 | 14 | context "with a valid file header" do 15 | before do 16 | @file = File.open "spec/fixtures/gbr/gbr_file/valid.gbr", "rb" 17 | end 18 | 19 | it "doesn't raise an error" do 20 | expect { @gbr = GBTiles::GBR::Import::GBRFile.open @file }.not_to raise_error(IOError) 21 | end 22 | 23 | context "on file process" do 24 | before do 25 | @gbr = GBTiles::GBR::Import::GBRFile.open @file 26 | end 27 | 28 | it "has one producer object" do 29 | expect(@gbr.tile_set.producer.count).to eql 1 30 | end 31 | 32 | it "has one tile_data object" do 33 | expect(@gbr.tile_set.tile_data.count).to eql 1 34 | end 35 | 36 | it "has one tile_settings object" do 37 | expect(@gbr.tile_set.tile_settings.count).to eql 1 38 | end 39 | 40 | it "has one tile_export object" do 41 | expect(@gbr.tile_set.tile_export.count).to eql 1 42 | end 43 | 44 | it "has one tile_import object" do 45 | expect(@gbr.tile_set.tile_import.count).to eql 1 46 | end 47 | 48 | it "has one palette object" do 49 | expect(@gbr.tile_set.palettes.count).to eql 1 50 | end 51 | 52 | it "has one tile_pal object" do 53 | expect(@gbr.tile_set.tile_pal.count).to eql 1 54 | end 55 | 56 | it "has no deleted objects" do 57 | expect(@gbr.tile_set.deleted.count).to eql 0 58 | end 59 | 60 | it "has no unknown objects" do 61 | expect(@gbr.tile_set.unknown.count).to eql 0 62 | end 63 | end 64 | end 65 | end -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/objects/map_export_settings_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/map/objects/map_export_settings" 2 | 3 | RSpec.describe GBTiles::GBM::Map::Objects::MapExportSettings, "#initFromBitString" do 4 | before do 5 | @file = File.open "spec/fixtures/gbm/map_export_settings/partial.gbm", "rb" 6 | @map_export_settings = GBTiles::GBM::Map::Objects::MapExportSettings.initFromBitString @file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@map_export_settings.object_type).to eql GBTiles::GBM::Map::OBJECT_TYPE[:map_export_settings] 11 | end 12 | 13 | it "reads the file_name" do 14 | expect(@map_export_settings.file_name).to eql "dialog.z80" 15 | end 16 | 17 | it "reads the file_type" do 18 | expect(@map_export_settings.file_type).to eql 0 19 | end 20 | 21 | it "reads the section_name" do 22 | expect(@map_export_settings.section_name).to eql "DialogMap" 23 | end 24 | 25 | it "reads the label_name" do 26 | expect(@map_export_settings.label_name).to eql "DialogMap" 27 | end 28 | 29 | it "reads the bank" do 30 | expect(@map_export_settings.bank).to eql 1 31 | end 32 | 33 | it "reads the plane_count" do 34 | expect(@map_export_settings.plane_count).to eql 0 35 | end 36 | 37 | it "reads the plane_order" do 38 | expect(@map_export_settings.plane_order).to eql 0 39 | end 40 | 41 | it "reads the map_layout" do 42 | expect(@map_export_settings.map_layout).to eql 0 43 | end 44 | 45 | it "reads the split" do 46 | expect(@map_export_settings.split).to eql false 47 | end 48 | 49 | it "reads the split_size" do 50 | expect(@map_export_settings.split_size).to eql 0 51 | end 52 | 53 | it "reads the split_bank" do 54 | expect(@map_export_settings.split_bank).to eql false 55 | end 56 | 57 | it "reads the sel_tab" do 58 | expect(@map_export_settings.sel_tab).to eql 0 59 | end 60 | 61 | it "reads the prop_count" do 62 | expect(@map_export_settings.prop_count).to eql 1 63 | end 64 | 65 | it "reads the tile_offset" do 66 | expect(@map_export_settings.tile_offset).to eql 0 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/map/objects/map_export_settings.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBM 3 | module Map 4 | module Objects 5 | class MapExportSettings < GBTiles::GBM::Map::Object 6 | 7 | attr_accessor :file_name 8 | attr_accessor :file_type 9 | attr_accessor :section_name 10 | attr_accessor :label_name 11 | attr_accessor :bank 12 | attr_accessor :plane_count 13 | attr_accessor :plane_order 14 | attr_accessor :map_layout 15 | attr_accessor :split 16 | attr_accessor :split_size 17 | attr_accessor :split_bank 18 | attr_accessor :sel_tab 19 | attr_accessor :prop_count 20 | attr_accessor :tile_offset 21 | 22 | def initialize 23 | super GBTiles::GBM::Map::OBJECT_TYPE[:map_export_settings] 24 | end 25 | 26 | def self.initFromBitString src 27 | object = GBTiles::GBM::Map::Objects::MapExportSettings.new 28 | 29 | object.file_name = GBTiles::DataType.string!(src, 255) 30 | object.file_type = GBTiles::DataType.byte!(src) 31 | object.section_name = GBTiles::DataType.string!(src, 40) 32 | object.label_name = GBTiles::DataType.string!(src, 40) 33 | object.bank = GBTiles::DataType.byte!(src) 34 | object.plane_count = GBTiles::DataType.word!(src) 35 | object.plane_order = GBTiles::DataType.word!(src) 36 | object.map_layout = GBTiles::DataType.word!(src) 37 | object.split = GBTiles::DataType.boolean!(src) 38 | object.split_size = GBTiles::DataType.long!(src) 39 | object.split_bank = GBTiles::DataType.boolean!(src) 40 | object.sel_tab = GBTiles::DataType.byte!(src) 41 | object.prop_count = GBTiles::DataType.word!(src) 42 | object.tile_offset = GBTiles::DataType.word!(src) 43 | 44 | object 45 | end 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/import/mod_file.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/fixnum" 2 | 3 | require "gbtiles/gbt/mod_data/mod_data" 4 | require "gbtiles/gbt/mod_data/sample" 5 | require "gbtiles/gbt/mod_data/pattern" 6 | 7 | module GBTiles 8 | module GBT 9 | module Import 10 | class MODFile 11 | 12 | attr_accessor :mod_data 13 | 14 | def initialize 15 | @mod_data = GBTiles::GBT::MODData::MODData.new 16 | end 17 | 18 | def self.open file 19 | import = GBTiles::GBT::Import::MODFile.new 20 | 21 | import.mod_data.name = GBTiles::DataType.string(file.read(20)) 22 | 23 | for i in 0..30 24 | sample_data = file.read(30) 25 | 26 | sample = GBTiles::GBT::MODData::Sample.new 27 | sample.name = GBTiles::DataType.string!(sample_data, 22) 28 | sample.length = GBTiles::DataType.bword!(sample_data) 29 | sample.finetune = GBTiles::DataType.byte!(sample_data) 30 | sample.volume = GBTiles::DataType.byte!(sample_data) 31 | sample.repeat_point = GBTiles::DataType.bword!(sample_data) 32 | sample.repeat_length = GBTiles::DataType.bword!(sample_data) 33 | 34 | import.mod_data.samples << sample 35 | end 36 | 37 | import.mod_data.song_length = GBTiles::DataType.byte(file.read(1)) 38 | file.read(1) # unused 39 | 40 | for i in 0..127 41 | import.mod_data.pattern_table << GBTiles::DataType.byte(file.read(1)) 42 | end 43 | 44 | import.mod_data.identifier = GBTiles::DataType.string(file.read(4)) 45 | 46 | for i in 0..63 47 | pattern = GBTiles::GBT::MODData::Pattern.new 48 | 49 | for j in 0..63 50 | pattern.rows << file.read(16) 51 | end 52 | 53 | import.mod_data.patterns << pattern 54 | end 55 | 56 | if import.mod_data.identifier != "M.K." then 57 | raise IOError, "Invalid identifier in .MOD: expected `M.K.` got `#{import.mod_data.identifier}`" 58 | end 59 | 60 | import 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/tile_data.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class TileData < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :name 8 | attr_accessor :width 9 | attr_accessor :height 10 | attr_accessor :count 11 | attr_accessor :color_set 12 | attr_accessor :data 13 | 14 | def initialize 15 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_data] 16 | end 17 | 18 | def self.initFromBitString src 19 | object = GBTiles::GBR::TileSet::Objects::TileData.new 20 | 21 | object.name = GBTiles::DataType.string!(src, 30) 22 | object.width = GBTiles::DataType.word!(src) 23 | object.height = GBTiles::DataType.word!(src) 24 | object.count = GBTiles::DataType.word!(src) 25 | object.color_set = src.slice!(0, 4) 26 | object.data = src 27 | 28 | object 29 | end 30 | 31 | def render_tile tile_index 32 | tile = [] 33 | 34 | tile_data = @data.slice( 35 | tile_index * @width * @height, 36 | @width * @height 37 | ) 38 | 39 | (1..@height).each do |row| 40 | byte_0 = 0x00 41 | byte_1 = 0x00 42 | 43 | bitmask = 0x80 44 | 45 | (1..@width).each do |col| 46 | pixel = tile_data[(row - 1) * @height + col - 1] 47 | 48 | case pixel.unpack("C")[0] 49 | when 0 # Black 50 | when 1 # Dark Grey 51 | byte_0 |= bitmask 52 | when 2 # Light Grey 53 | byte_1 |= bitmask 54 | when 3 # White 55 | byte_0 |= bitmask 56 | byte_1 |= bitmask 57 | end 58 | 59 | bitmask >>= 1 60 | end 61 | 62 | tile << sprintf("0x%02x,0x%02x", byte_0, byte_1) 63 | end 64 | 65 | tile.join(",") 66 | end 67 | end 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/channels/noise.rb: -------------------------------------------------------------------------------- 1 | # CHANNEL 4 2 | 3 | require "gbtiles/gbt/export/asm/channel" 4 | 5 | module GBTiles 6 | module GBT 7 | module Export 8 | module ASM 9 | module Channels 10 | class Noise < GBTiles::GBT::Export::ASM::Channel 11 | 12 | def initialize 13 | super(4) 14 | end 15 | 16 | def is_noise? 17 | true 18 | end 19 | 20 | def convert 21 | if sample_period == 0 then 22 | convert_note_not_new 23 | else 24 | convert_note_new 25 | end 26 | end 27 | 28 | def convert_note_not_new 29 | if is_empty_effect? then 30 | return [0] 31 | end 32 | 33 | # Volume effect 34 | if effect_number == 0xC then 35 | return [ 36 | (1 << 5) | convert_volume 37 | ] 38 | end 39 | 40 | # Other effects 41 | begin 42 | converted = convert_effect 43 | 44 | return [ 45 | (1 << 6) | converted[:number], 46 | converted[:params] 47 | ] 48 | rescue Exception => e 49 | # Silence 50 | end 51 | 52 | return [0] 53 | end 54 | 55 | def convert_note_new 56 | instrument = (sample_number - 16) & 0x1F 57 | 58 | # Volume effect 59 | if effect_number == 0xC then 60 | return [ 61 | (1 << 7) | instrument, 62 | convert_volume 63 | ] 64 | end 65 | 66 | # Other effects 67 | begin 68 | converted = convert_effect 69 | 70 | return [ 71 | (1 << 7) | instrument, 72 | (1 << 7) | converted[:number], 73 | converted[:params] 74 | ] 75 | rescue Exception => e 76 | throw "Noise - Invalid command: #{e}" 77 | end 78 | end 79 | end 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/gbtiles/gbm/map/import/gbm_file_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbm/import/gbm_file" 2 | 3 | RSpec.describe GBTiles::GBM::Import::GBMFile, "#open" do 4 | context "with an invalid file header" do 5 | before do 6 | @file = File.open "spec/fixtures/gbm/gbm_file/invalid-header.gbm", "rb" 7 | end 8 | 9 | it "raises an error" do 10 | expect { GBTiles::GBM::Import::GBMFile.open @file }.to raise_error(IOError) 11 | end 12 | end 13 | 14 | context "with an invalid version in the file header" do 15 | before do 16 | @file = File.open "spec/fixtures/gbm/gbm_file/invalid-version.gbm", "rb" 17 | end 18 | 19 | it "raises an error" do 20 | expect { GBTiles::GBM::Import::GBMFile.open @file }.to raise_error(IOError) 21 | end 22 | end 23 | 24 | context "with an invalid header marker" do 25 | before do 26 | @file = File.open "spec/fixtures/gbm/gbm_file/invalid-marker.gbm", "rb" 27 | end 28 | 29 | it "raises an error" do 30 | expect { GBTiles::GBM::Import::GBMFile.open @file }.to raise_error 31 | end 32 | end 33 | 34 | context "with a valid file header" do 35 | before do 36 | @file = File.open "spec/fixtures/gbm/gbm_file/valid.gbm", "rb" 37 | end 38 | 39 | it "doesn't raise an error" do 40 | expect { @gbm = GBTiles::GBM::Import::GBMFile.open @file }.not_to raise_error(IOError) 41 | end 42 | 43 | context "on file process" do 44 | before do 45 | @gbm = GBTiles::GBM::Import::GBMFile.open @file 46 | end 47 | 48 | it "has one producer object" do 49 | expect(@gbm.map_set.producer.count).to eql 1 50 | end 51 | 52 | it "has one map object" do 53 | expect(@gbm.map_set.map.count).to eql 1 54 | end 55 | 56 | it "has one map_tile_data object" do 57 | expect(@gbm.map_set.map_tile_data.count).to eql 1 58 | end 59 | 60 | it "has one map_settings object" do 61 | expect(@gbm.map_set.map_settings.count).to eql 1 62 | end 63 | 64 | it "has one map_export_settings object" do 65 | expect(@gbm.map_set.map_export_settings.count).to eql 1 66 | end 67 | 68 | # it "has no deleted objects" do 69 | # expect(@gbm.map_set.deleted.count).to eql 0 70 | # end 71 | 72 | # it "has no unknown objects" do 73 | # expect(@gbm.map_set.unknown.count).to eql 0 74 | # end 75 | 76 | end 77 | end 78 | end -------------------------------------------------------------------------------- /spec/gbtiles/gbt/export/asm/channel_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/channel" 2 | 3 | RSpec.describe GBTiles::GBT::Export::ASM::Channel, "#note_index" do 4 | before do 5 | @channel = GBTiles::GBT::Export::ASM::Channels::Pulse.new(1) 6 | end 7 | 8 | context "extremes out of bounds" do 9 | it "throws an error on 1713 (upper)" do 10 | @channel.data = [0xF6, 0xB1, 0xFF, 0xFF] 11 | expect { @channel.note_index }.to raise_error 12 | end 13 | 14 | it "throws an error on 27 (lower)" do 15 | @channel.data = [0xF0, 0x1B, 0xFF, 0xFF] 16 | expect { @channel.note_index }.to raise_error 17 | end 18 | end 19 | 20 | context "extremes inside bounds" do 21 | it "accepts the lower bounds" do 22 | @channel.data = [0xF0, 0x1C, 0xFF, 0xFF] 23 | expect(@channel.note_index).to eql 71 24 | end 25 | 26 | it "accepts the upper bounds" do 27 | @channel.data = [0xF6, 0xB0, 0xFF, 0xFF] 28 | expect(@channel.note_index).to eql 0 29 | end 30 | end 31 | 32 | context "finds nearest value" do 33 | it "converts 240 to 34" do 34 | @channel.data = [0xF0, 0xF0, 0xFF, 0xFF] 35 | expect(@channel.note_index).to eql 34 36 | end 37 | 38 | it "converts 246 to 34" do 39 | @channel.data = [0xF0, 0xF6, 0xFF, 0xFF] 40 | expect(@channel.note_index).to eql 34 41 | end 42 | 43 | it "converts 247 to 33" do 44 | @channel.data = [0xF0, 0xF7, 0xFF, 0xFF] 45 | expect(@channel.note_index).to eql 33 46 | end 47 | 48 | it "converts 248 to 33" do 49 | @channel.data = [0xF0, 0xF8, 0xFF, 0xFF] 50 | expect(@channel.note_index).to eql 33 51 | end 52 | 53 | it "converts 254 to 33" do 54 | @channel.data = [0xF0, 0xFE, 0xFF, 0xFF] 55 | expect(@channel.note_index).to eql 33 56 | end 57 | end 58 | end 59 | 60 | RSpec.describe GBTiles::GBT::Export::ASM::Channel, "#data" do 61 | before do 62 | @channel = GBTiles::GBT::Export::ASM::Channels::Pulse.new(1) 63 | end 64 | 65 | it "reads/writes an array to the variable" do 66 | data = [ 0x10, 0x20, 0x30, 0x40 ] 67 | @channel.data = data 68 | expect(@channel.data).to eql data 69 | end 70 | 71 | it "reads/writes an unpacked string" do 72 | data = "\x10\x20\x10\x40".b 73 | @channel.data = data 74 | expect(@channel.data).to eql [ 0x10, 0x20, 0x10, 0x40 ] 75 | expect(@channel.data).to_not eql [ 0x10, 0x20, 0x30, 0x40 ] 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/channels/pulse.rb: -------------------------------------------------------------------------------- 1 | # CHANNEL 1 2 | # CHANNEL 2 3 | 4 | require "gbtiles/gbt/export/asm/channel" 5 | 6 | module GBTiles 7 | module GBT 8 | module Export 9 | module ASM 10 | module Channels 11 | class Pulse < GBTiles::GBT::Export::ASM::Channel 12 | 13 | def initialize(channel_number) 14 | super(channel_number) 15 | end 16 | 17 | def is_pulse? 18 | true 19 | end 20 | 21 | def convert 22 | if sample_period == 0 then 23 | convert_note_not_new 24 | else 25 | convert_note_new 26 | end 27 | end 28 | 29 | def convert_note_not_new 30 | instrument = sample_number & 3 31 | 32 | if is_empty_effect? then 33 | return [0] 34 | end 35 | 36 | # Volume effect 37 | if effect_number == 0xC then 38 | return [ 39 | (1 << 5) | convert_volume 40 | ] 41 | end 42 | 43 | # Other effects 44 | begin 45 | converted = convert_effect 46 | 47 | return [ 48 | (1 << 6) | (instrument << 4) | converted[:number], 49 | converted[:params] 50 | ] 51 | rescue Exception => e 52 | # Silence 53 | end 54 | 55 | return [0] 56 | end 57 | 58 | def convert_note_new 59 | instrument = sample_number & 3 60 | 61 | # Volume effect 62 | if effect_number == 0xC then 63 | return [ 64 | (1 << 7) | note_index, 65 | (instrument << 4) | convert_volume 66 | ] 67 | end 68 | 69 | # Other effects 70 | begin 71 | converted = convert_effect 72 | 73 | return [ 74 | (1 << 7) | note_index, 75 | (1 << 7) | (instrument << 4) | converted[:number], 76 | converted[:params] 77 | ] 78 | rescue Exception => e 79 | throw "Pulse - Invalid command: #{e}" 80 | end 81 | 82 | end 83 | end 84 | end 85 | end 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/tile_export_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/tile_export" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::TileExport, "#initFromBitString" do 4 | before do 5 | file = File.open "spec/fixtures/gbr/tile_export/partial.gbr", "rb" 6 | @tile_export = GBTiles::GBR::TileSet::Objects::TileExport.initFromBitString file.read 7 | end 8 | 9 | it "expect object type to be :tile_export" do 10 | expect(@tile_export.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_export] 11 | end 12 | 13 | it "reads the tile id" do 14 | expect(@tile_export.tile_id).to eql 1 15 | end 16 | 17 | it "reads the filename" do 18 | expect(@tile_export.file_name).to eql "dialog.z80" 19 | end 20 | 21 | it "reads the file type" do 22 | expect(@tile_export.file_type).to eql 0 23 | end 24 | 25 | it "reads the section name" do 26 | expect(@tile_export.section_name).to eql "DialogTiles" 27 | end 28 | 29 | it "reads the label name" do 30 | expect(@tile_export.label_name).to eql "DialogTiles" 31 | end 32 | 33 | it "reads the bank number" do 34 | expect(@tile_export.bank).to eql 1 35 | end 36 | 37 | it "reads the tile array length" do 38 | expect(@tile_export.tile_array).to eql 1 39 | end 40 | 41 | it "reads the format" do 42 | expect(@tile_export.format).to eql 0 43 | end 44 | 45 | it "reads the counter" do 46 | expect(@tile_export.counter).to eql 0 47 | end 48 | 49 | it "reads the range from" do 50 | expect(@tile_export.from).to eql 0 51 | end 52 | 53 | it "reads the range upto" do 54 | expect(@tile_export.upto).to eql 127 55 | end 56 | 57 | it "reads the compression" do 58 | expect(@tile_export.compression).to eql 0 59 | end 60 | 61 | it "reads the include colors boolean" do 62 | expect(@tile_export.include_colors).to eql false 63 | end 64 | 65 | it "reads the SGB palettes" do 66 | expect(@tile_export.sgb_palettes).to eql 0 67 | end 68 | 69 | it "reads the GBC palettes" do 70 | expect(@tile_export.gbc_palettes).to eql 0 71 | end 72 | 73 | it "reads the make meta tiles" do 74 | expect(@tile_export.make_meta_tiles).to eql false 75 | end 76 | 77 | it "reads the meta offset" do 78 | expect(@tile_export.meta_offset).to eql 0 79 | end 80 | 81 | it "reads the meta counter" do 82 | expect(@tile_export.meta_counter).to eql 0 83 | end 84 | 85 | it "reads the split" do 86 | expect(@tile_export.split).to eql false 87 | end 88 | 89 | it "reads the block size" do 90 | expect(@tile_export.block_size).to eql 0 91 | end 92 | 93 | it "reads the sel_tab" do 94 | expect(@tile_export.sel_tab).to eql 0 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/tile_set/objects/tile_export.rb: -------------------------------------------------------------------------------- 1 | module GBTiles 2 | module GBR 3 | module TileSet 4 | module Objects 5 | class TileExport < GBTiles::GBR::TileSet::Object 6 | 7 | attr_accessor :tile_id 8 | attr_accessor :file_name 9 | attr_accessor :file_type 10 | attr_accessor :section_name 11 | attr_accessor :label_name 12 | attr_accessor :bank 13 | attr_accessor :tile_array 14 | attr_accessor :format 15 | attr_accessor :counter 16 | attr_accessor :from 17 | attr_accessor :upto 18 | attr_accessor :compression 19 | attr_accessor :include_colors 20 | attr_accessor :sgb_palettes 21 | attr_accessor :gbc_palettes 22 | attr_accessor :make_meta_tiles 23 | attr_accessor :meta_offset 24 | attr_accessor :meta_counter 25 | attr_accessor :split 26 | attr_accessor :block_size 27 | attr_accessor :sel_tab 28 | 29 | def initialize 30 | super GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_export] 31 | end 32 | 33 | def self.initFromBitString src 34 | object = GBTiles::GBR::TileSet::Objects::TileExport.new 35 | 36 | object.tile_id = GBTiles::DataType.word!(src) 37 | object.file_name = GBTiles::DataType.string!(src, 128) 38 | object.file_type = GBTiles::DataType.byte!(src) 39 | object.section_name = GBTiles::DataType.string!(src, 20) 40 | object.label_name = GBTiles::DataType.string!(src, 20) 41 | object.bank = GBTiles::DataType.byte!(src) 42 | object.tile_array = GBTiles::DataType.byte!(src) 43 | object.format = GBTiles::DataType.byte!(src) 44 | object.counter = GBTiles::DataType.byte!(src) 45 | object.from = GBTiles::DataType.word!(src) 46 | object.upto = GBTiles::DataType.word!(src) 47 | object.compression = GBTiles::DataType.byte!(src) 48 | object.include_colors = GBTiles::DataType.boolean!(src) 49 | object.sgb_palettes = GBTiles::DataType.byte!(src) 50 | object.gbc_palettes = GBTiles::DataType.byte!(src) 51 | object.make_meta_tiles = GBTiles::DataType.boolean!(src) 52 | object.meta_offset = GBTiles::DataType.long!(src) 53 | object.meta_counter = GBTiles::DataType.byte!(src) 54 | object.split = GBTiles::DataType.boolean!(src) 55 | object.block_size = GBTiles::DataType.long!(src) 56 | object.sel_tab = GBTiles::DataType.byte!(src) 57 | 58 | object 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/channels/wav.rb: -------------------------------------------------------------------------------- 1 | # CHANNEL 3 2 | 3 | require "gbtiles/gbt/export/asm/channel" 4 | 5 | module GBTiles 6 | module GBT 7 | module Export 8 | module ASM 9 | module Channels 10 | class Wav < GBTiles::GBT::Export::ASM::Channel 11 | 12 | def initialize 13 | super(3) 14 | end 15 | 16 | def is_wav? 17 | true 18 | end 19 | 20 | def convert_volume 21 | case super 22 | when 0..3 23 | return 0 24 | when 4..7 25 | return 3 26 | when 8..11 27 | return 2 28 | when 12..15 29 | return 1 30 | else 31 | return 1 32 | end 33 | end 34 | 35 | def convert 36 | if sample_period == 0 then 37 | convert_note_not_new 38 | else 39 | convert_note_new 40 | end 41 | end 42 | 43 | def convert_note_not_new 44 | if is_empty_effect? then 45 | return [0] 46 | end 47 | 48 | # Volume effect 49 | if effect_number == 0xC then 50 | return [ 51 | (1 << 5) | convert_volume 52 | ] 53 | end 54 | 55 | # Other effects 56 | begin 57 | converted = convert_effect 58 | 59 | return [ 60 | (1 << 6) | converted[:number], 61 | converted[:params] 62 | ] 63 | 64 | rescue Exception => e 65 | # Silence 66 | end 67 | 68 | return [0] 69 | end 70 | 71 | def convert_note_new 72 | instrument = (sample_number - 8) & 15 73 | 74 | # Volume effect 75 | if effect_number == 0xC then 76 | return [ 77 | (1 << 7) | note_index, 78 | (convert_volume << 4) | instrument 79 | ] 80 | end 81 | 82 | # Other effects 83 | begin 84 | converted = convert_effect 85 | 86 | if (converted[:number] > 7) then 87 | throw "Invalid command: only 0-7 allowed in this mode" 88 | end 89 | 90 | return [ 91 | (1 << 7) | note_index, 92 | (1 << 7) | (converted_num << 4) | instrument, 93 | converted_params 94 | ] 95 | rescue Exception => e 96 | throw "Wav - Invalid command: #{e}" 97 | end 98 | end 99 | 100 | end 101 | end 102 | end 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/gbtiles/gbr/import/gbr_file.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/fixnum" 2 | 3 | require "gbtiles/gbr/tile_set/tile_set" 4 | require "gbtiles/gbr/tile_set/object" 5 | require "gbtiles/gbr/tile_set/object_type" 6 | require "gbtiles/gbr/tile_set/split_order" 7 | require "gbtiles/gbr/tile_set/color_set" 8 | 9 | require "gbtiles/gbr/tile_set/objects/palettes" 10 | require "gbtiles/gbr/tile_set/objects/producer" 11 | require "gbtiles/gbr/tile_set/objects/tile_data" 12 | require "gbtiles/gbr/tile_set/objects/tile_export" 13 | require "gbtiles/gbr/tile_set/objects/tile_import" 14 | require "gbtiles/gbr/tile_set/objects/tile_pal" 15 | require "gbtiles/gbr/tile_set/objects/tile_settings" 16 | require "gbtiles/gbr/tile_set/objects/unknown" 17 | 18 | module GBTiles 19 | module GBR 20 | module Import 21 | class GBRFile 22 | 23 | attr_accessor :version 24 | attr_accessor :tile_set 25 | 26 | def initialize 27 | @tile_set = GBTiles::GBR::TileSet::TileSet.new 28 | end 29 | 30 | def self.open file 31 | import = GBTiles::GBR::Import::GBRFile.new 32 | 33 | # Check to see if this is a valid file type 34 | if (file.read(3) != "GBO") then 35 | raise IOError, "Not a valid file (expected: GBO)" 36 | end 37 | 38 | # Version number 39 | import.version = file.read(1) 40 | 41 | # For each object 42 | while !file.eof? 43 | object_type = GBTiles::DataType.word(file.read(2)) 44 | object_id = GBTiles::DataType.word(file.read(2)) 45 | object_len = GBTiles::DataType.long(file.read(4)) 46 | object_data = file.read(object_len) 47 | 48 | case GBTiles::GBR::TileSet::OBJECT_TYPE.key(object_type) 49 | when :producer 50 | object = GBTiles::GBR::TileSet::Objects::Producer.initFromBitString(object_data) 51 | 52 | when :tile_data 53 | object = GBTiles::GBR::TileSet::Objects::TileData.initFromBitString(object_data) 54 | 55 | when :tile_settings 56 | object = GBTiles::GBR::TileSet::Objects::TileSettings.initFromBitString(object_data) 57 | 58 | when :tile_export 59 | object = GBTiles::GBR::TileSet::Objects::TileExport.initFromBitString(object_data) 60 | 61 | when :tile_import 62 | object = GBTiles::GBR::TileSet::Objects::TileImport.initFromBitString(object_data) 63 | 64 | when :palettes 65 | object = GBTiles::GBR::TileSet::Objects::Palettes.initFromBitString(object_data) 66 | 67 | when :tile_pal 68 | object = GBTiles::GBR::TileSet::Objects::TilePal.initFromBitString(object_data) 69 | 70 | else 71 | object = GBTiles::GBR::TileSet::Objects::Unknown.new object_type 72 | object.object_data = object_data 73 | 74 | end 75 | 76 | object.object_id = object_id 77 | 78 | import.tile_set.objects << object 79 | end 80 | 81 | import 82 | end 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/gbtiles/gbm/import/gbm_file.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/helpers/fixnum" 2 | 3 | require "gbtiles/gbm/map/map_set" 4 | require "gbtiles/gbm/map/object" 5 | require "gbtiles/gbm/map/object_type" 6 | 7 | require "gbtiles/gbm/map/objects/producer" 8 | require "gbtiles/gbm/map/objects/map" 9 | require "gbtiles/gbm/map/objects/map_tile_data" 10 | require "gbtiles/gbm/map/objects/map_tile_data_record" 11 | require "gbtiles/gbm/map/objects/map_settings" 12 | require "gbtiles/gbm/map/objects/map_export_settings" 13 | require "gbtiles/gbm/map/objects/unknown" 14 | 15 | module GBTiles 16 | module GBM 17 | module Import 18 | class GBMFile 19 | 20 | OBJECT_MARKER = "HPJMTL" 21 | 22 | attr_accessor :version 23 | attr_accessor :map_set 24 | 25 | def initialize 26 | @map_set = GBTiles::GBM::Map::MapSet.new 27 | end 28 | 29 | def self.open file 30 | import = GBTiles::GBM::Import::GBMFile.new 31 | 32 | # Check to see if this is a valid file type 33 | if (file.read(3) != "GBO") then 34 | raise IOError, "Not a valid file (expected: GBO)" 35 | end 36 | 37 | # Version number 38 | import.version = file.read(1) 39 | 40 | if import.version != "1" then 41 | raise IOError, "Cannot import version #{import.version}" 42 | end 43 | 44 | # For each object 45 | while !file.eof? 46 | # Check the marker 47 | marker = GBTiles::DataType.string(file.read(6)) 48 | 49 | if !OBJECT_MARKER.eql? marker then 50 | raise "Malformed file, expected marker #{OBJECT_MARKER}, got #{marker}" 51 | end 52 | 53 | object_type = GBTiles::DataType.word(file.read(2)) 54 | object_id = GBTiles::DataType.word(file.read(2)) 55 | master_id = GBTiles::DataType.word(file.read(2)) 56 | object_crc = GBTiles::DataType.long(file.read(4)) 57 | object_len = GBTiles::DataType.long(file.read(4)) 58 | object_data = file.read(object_len) 59 | 60 | case GBTiles::GBM::Map::OBJECT_TYPE.key(object_type) 61 | when :producer 62 | object = GBTiles::GBM::Map::Objects::Producer.initFromBitString object_data 63 | 64 | when :map 65 | object = GBTiles::GBM::Map::Objects::Map.initFromBitString object_data 66 | 67 | when :map_tile_data 68 | object = GBTiles::GBM::Map::Objects::MapTileData.initFromBitString object_data 69 | 70 | when :map_settings 71 | object = GBTiles::GBM::Map::Objects::MapSettings.initFromBitString object_data 72 | 73 | when :map_export_settings 74 | object = GBTiles::GBM::Map::Objects::MapExportSettings.initFromBitString object_data 75 | 76 | else 77 | object = GBTiles::GBM::Map::Objects::Unknown.new object_type 78 | object.object_data = object_data 79 | 80 | end 81 | 82 | object.object_id = object_id 83 | object.master_id = master_id 84 | 85 | import.map_set.objects << object 86 | end 87 | 88 | import 89 | end 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/gbtiles/gbt/export/asm/channel.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbt/export/asm/effects/arpeggio" 2 | require "gbtiles/gbt/export/asm/effects/break_and_set_step" 3 | require "gbtiles/gbt/export/asm/effects/cut_note" 4 | require "gbtiles/gbt/export/asm/effects/jump" 5 | require "gbtiles/gbt/export/asm/effects/pan" 6 | require "gbtiles/gbt/export/asm/effects/speed" 7 | require "gbtiles/gbt/export/asm/effects/volume" 8 | 9 | module GBTiles 10 | module GBT 11 | module Export 12 | module ASM 13 | class Channel 14 | include GBTiles::GBT::Export::ASM::Effects 15 | 16 | NOTES = [ 17 | 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 907, 18 | 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, 19 | 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, 20 | 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, 21 | 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, 22 | 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28 23 | ]; 24 | 25 | attr_accessor :channel_number 26 | 27 | @data = [ 0, 0, 0, 0 ] 28 | 29 | def data 30 | @data 31 | end 32 | 33 | def data=(value) 34 | if value.kind_of?(Array) then 35 | @data = value 36 | else 37 | @data = value.unpack('CCCC') 38 | end 39 | end 40 | 41 | def initialize channel_number 42 | @channel_number = channel_number 43 | end 44 | 45 | # Channel information 46 | 47 | def is_pulse? 48 | false 49 | end 50 | 51 | def is_wav? 52 | false 53 | end 54 | 55 | def is_noise? 56 | false 57 | end 58 | 59 | # Data 60 | 61 | def sample_number 62 | (@data[0] & 0xF0) | ((@data[2] & 0xF0) >> 4) 63 | end 64 | 65 | def sample_period 66 | @data[1] | ((@data[0] & 0xF) << 8) 67 | end 68 | 69 | def is_empty_effect? 70 | effect_number == 0 && effect_param == 0 71 | end 72 | 73 | def effect_number 74 | @data[2] & 0xF 75 | end 76 | 77 | def effect_param 78 | @data[3] 79 | end 80 | 81 | def effect_param_1 82 | (@data[3] & 0xF0) >> 4 83 | end 84 | 85 | def effect_param_2 86 | @data[3] & 0x0F 87 | end 88 | 89 | def effect_param_as_bcd 90 | (((effect_param & 0xF0) >> 4) * 10) + (effect_param & 0xF) 91 | end 92 | 93 | def convert_volume 94 | (effect_param == 64) ? 0xF : (effect_param >> 2); 95 | end 96 | 97 | # Amiga's 50 Hz to GB's 60 Hz 98 | def convert_speed speed 99 | speed * 60 / 50 100 | end 101 | 102 | def convert_effect 103 | if effect_number == 0x0 then 104 | return effect_arpeggio 105 | end 106 | 107 | if effect_number == 0xB then 108 | return effect_jump 109 | end 110 | 111 | if effect_number == 0xC then 112 | return effect_volume 113 | end 114 | 115 | if effect_number == 0xD then 116 | return effect_break_and_set_step 117 | end 118 | 119 | if effect_number == 0xE && effect_param_1 == 0x8 then 120 | return effect_pan 121 | end 122 | 123 | if effect_number == 0xE && effect_param_1 == 0xC then 124 | return effect_cut_note 125 | end 126 | 127 | if effect_number == 0xF then 128 | return effect_speed 129 | end 130 | 131 | throw "Unsupported effect" 132 | end 133 | 134 | def note_index 135 | period = sample_period 136 | 137 | if period <= 0 then 138 | return -1 139 | end 140 | 141 | if !is_noise? then 142 | if !period.between?(NOTES.last, NOTES.first) then 143 | throw "Note `#{period}` out of bounds" 144 | end 145 | end 146 | 147 | NOTES.index( 148 | NOTES.min_by { |x| (period - x).abs } 149 | ) 150 | end 151 | 152 | end 153 | end 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "codeclimate-test-reporter" 2 | CodeClimate::TestReporter.start 3 | 4 | # This file was generated by the `rspec --init` command. Conventionally, all 5 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 6 | # The generated `.rspec` file contains `--require spec_helper` which will cause this 7 | # file to always be loaded, without a need to explicitly require it in any files. 8 | # 9 | # Given that it is always loaded, you are encouraged to keep this file as 10 | # light-weight as possible. Requiring heavyweight dependencies from this file 11 | # will add to the boot time of your test suite on EVERY test run, even for an 12 | # individual file that may not need all of that loaded. Instead, consider making 13 | # a separate helper file that requires the additional dependencies and performs 14 | # the additional setup, and require it from the spec files that actually need it. 15 | # 16 | # The `.rspec` file also contains a few flags that are not defaults but that 17 | # users commonly want. 18 | # 19 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 20 | RSpec.configure do |config| 21 | # rspec-expectations config goes here. You can use an alternate 22 | # assertion/expectation library such as wrong or the stdlib/minitest 23 | # assertions if you prefer. 24 | config.expect_with :rspec do |expectations| 25 | # This option will default to `true` in RSpec 4. It makes the `description` 26 | # and `failure_message` of custom matchers include text for helper methods 27 | # defined using `chain`, e.g.: 28 | # be_bigger_than(2).and_smaller_than(4).description 29 | # # => "be bigger than 2 and smaller than 4" 30 | # ...rather than: 31 | # # => "be bigger than 2" 32 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 33 | expectations.warn_about_potential_false_positives = false 34 | end 35 | 36 | # rspec-mocks config goes here. You can use an alternate test double 37 | # library (such as bogus or mocha) by changing the `mock_with` option here. 38 | config.mock_with :rspec do |mocks| 39 | # Prevents you from mocking or stubbing a method that does not exist on 40 | # a real object. This is generally recommended, and will default to 41 | # `true` in RSpec 4. 42 | mocks.verify_partial_doubles = true 43 | end 44 | 45 | # The settings below are suggested to provide a good initial experience 46 | # with RSpec, but feel free to customize to your heart's content. 47 | =begin 48 | # These two settings work together to allow you to limit a spec run 49 | # to individual examples or groups you care about by tagging them with 50 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 51 | # get run. 52 | config.filter_run :focus 53 | config.run_all_when_everything_filtered = true 54 | 55 | # Limits the available syntax to the non-monkey patched syntax that is recommended. 56 | # For more details, see: 57 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 58 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 59 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 60 | config.disable_monkey_patching! 61 | 62 | # This setting enables warnings. It's recommended, but in some cases may 63 | # be too noisy due to issues in dependencies. 64 | config.warnings = true 65 | 66 | # Many RSpec users commonly either run the entire suite or an individual 67 | # file, and it's useful to allow more verbose output when running an 68 | # individual spec file. 69 | if config.files_to_run.one? 70 | # Use the documentation formatter for detailed output, 71 | # unless a formatter has already been configured 72 | # (e.g. via a command-line flag). 73 | config.default_formatter = 'doc' 74 | end 75 | 76 | # Print the 10 slowest examples and example groups at the 77 | # end of the spec run, to help surface which specs are running 78 | # particularly slow. 79 | config.profile_examples = 10 80 | 81 | # Run specs in random order to surface order dependencies. If you find an 82 | # order dependency and want to debug it, you can fix the order by providing 83 | # the seed, which is printed after each run. 84 | # --seed 1234 85 | config.order = :random 86 | 87 | # Seed global randomization in this process using the `--seed` CLI option. 88 | # Setting this allows you to use `--seed` to deterministically reproduce 89 | # test failures related to randomization by passing the same `--seed` value 90 | # as the one that triggered the failure. 91 | Kernel.srand config.seed 92 | =end 93 | end 94 | -------------------------------------------------------------------------------- /spec/gbtiles/gbr/tile_set/objects/tile_data_spec.rb: -------------------------------------------------------------------------------- 1 | require "gbtiles/gbr/tile_set/objects/tile_data" 2 | 3 | RSpec.describe GBTiles::GBR::TileSet::Objects::TileData, "#initFromBitString" do 4 | before do 5 | file = File.open "spec/fixtures/gbr/tile_data/partial.gbr", "rb" 6 | @tile_data = GBTiles::GBR::TileSet::Objects::TileData.initFromBitString file.read 7 | end 8 | 9 | it "validates object type" do 10 | expect(@tile_data.object_type).to eql GBTiles::GBR::TileSet::OBJECT_TYPE[:tile_data] 11 | end 12 | 13 | it "expects name to be nil" do 14 | expect(@tile_data.name).to be_nil 15 | end 16 | 17 | it "reads the tile width" do 18 | expect(@tile_data.width).to eql 8 19 | end 20 | 21 | it "reads the tile height" do 22 | expect(@tile_data.height).to eql 8 23 | end 24 | 25 | it "reads the number of tiles" do 26 | expect(@tile_data.count).to eql 128 27 | end 28 | 29 | it "the number of tiles matches the data" do 30 | expect(@tile_data.data.length / (@tile_data.width * @tile_data.height)).to eql @tile_data.count 31 | end 32 | 33 | context "renders a tile" do 34 | it "renders" do 35 | blank_tile = "0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00" 36 | 37 | expected_results = { 38 | 48 => "0x00,0x00,0x38,0x38,0x44,0x44,0x4c,0x4c,0x54,0x54,0x64,0x64,0x38,0x38,0x00,0x00", 39 | 49 => "0x00,0x00,0x30,0x30,0x50,0x50,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x7c,0x00,0x00", 40 | 50 => "0x00,0x00,0x38,0x38,0x44,0x44,0x08,0x08,0x10,0x10,0x20,0x20,0x7c,0x7c,0x00,0x00", 41 | 51 => "0x00,0x00,0x38,0x38,0x44,0x44,0x04,0x04,0x18,0x18,0x44,0x44,0x38,0x38,0x00,0x00", 42 | 52 => "0x00,0x00,0x40,0x40,0x40,0x40,0x48,0x48,0x7c,0x7c,0x08,0x08,0x08,0x08,0x00,0x00", 43 | 53 => "0x00,0x00,0x7c,0x7c,0x40,0x40,0x78,0x78,0x04,0x04,0x44,0x44,0x38,0x38,0x00,0x00", 44 | 54 => "0x00,0x00,0x38,0x38,0x44,0x44,0x40,0x40,0x78,0x78,0x44,0x44,0x38,0x38,0x00,0x00", 45 | 55 => "0x00,0x00,0x3c,0x3c,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00", 46 | 56 => "0x00,0x00,0x38,0x38,0x44,0x44,0x44,0x44,0x38,0x38,0x44,0x44,0x38,0x38,0x00,0x00", 47 | 57 => "0x00,0x00,0x38,0x38,0x44,0x44,0x3c,0x3c,0x04,0x04,0x44,0x44,0x38,0x38,0x00,0x00", 48 | 65 => "0x00,0x00,0x38,0x38,0x44,0x44,0x44,0x44,0x7c,0x7c,0x44,0x44,0x44,0x44,0x00,0x00", 49 | 66 => "0x00,0x00,0x70,0x70,0x48,0x48,0x78,0x78,0x44,0x44,0x44,0x44,0x78,0x78,0x00,0x00", 50 | 67 => "0x00,0x00,0x38,0x38,0x44,0x44,0x40,0x40,0x40,0x40,0x44,0x44,0x38,0x38,0x00,0x00", 51 | 68 => "0x00,0x00,0x78,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x78,0x78,0x00,0x00", 52 | 69 => "0x00,0x00,0x7c,0x7c,0x40,0x40,0x78,0x78,0x40,0x40,0x40,0x40,0x7c,0x7c,0x00,0x00", 53 | 70 => "0x00,0x00,0x7c,0x7c,0x40,0x40,0x78,0x78,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00", 54 | 71 => "0x00,0x00,0x38,0x38,0x44,0x44,0x40,0x40,0x4c,0x4c,0x44,0x44,0x38,0x38,0x00,0x00", 55 | 72 => "0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x7c,0x7c,0x44,0x44,0x44,0x44,0x00,0x00", 56 | 73 => "0x00,0x00,0x7c,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x7c,0x00,0x00", 57 | 74 => "0x00,0x00,0x7c,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x50,0x50,0x20,0x20,0x00,0x00", 58 | 75 => "0x00,0x00,0x44,0x44,0x48,0x48,0x70,0x70,0x50,0x50,0x48,0x48,0x44,0x44,0x00,0x00", 59 | 76 => "0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7c,0x7c,0x00,0x00", 60 | 77 => "0x00,0x00,0x44,0x44,0x6c,0x6c,0x54,0x54,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00", 61 | 78 => "0x00,0x00,0x44,0x44,0x64,0x64,0x54,0x54,0x4c,0x4c,0x44,0x44,0x44,0x44,0x00,0x00", 62 | 79 => "0x00,0x00,0x38,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x38,0x00,0x00", 63 | 80 => "0x00,0x00,0x78,0x78,0x44,0x44,0x78,0x78,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00", 64 | 81 => "0x00,0x00,0x38,0x38,0x44,0x44,0x44,0x44,0x54,0x54,0x48,0x48,0x34,0x34,0x00,0x00", 65 | 82 => "0x00,0x00,0x78,0x78,0x44,0x44,0x78,0x78,0x50,0x50,0x48,0x48,0x44,0x44,0x00,0x00", 66 | 83 => "0x00,0x00,0x3c,0x3c,0x40,0x40,0x38,0x38,0x04,0x04,0x44,0x44,0x38,0x38,0x00,0x00", 67 | 84 => "0x00,0x00,0x7c,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00", 68 | 85 => "0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x38,0x00,0x00", 69 | 86 => "0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x28,0x10,0x10,0x10,0x10,0x00,0x00", 70 | 87 => "0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x4a,0x4a,0x2a,0x2a,0x14,0x14,0x00,0x00", 71 | 88 => "0x00,0x00,0x44,0x44,0x28,0x28,0x28,0x28,0x10,0x10,0x28,0x28,0x44,0x44,0x00,0x00", 72 | 89 => "0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00", 73 | 90 => "0x00,0x00,0x7c,0x7c,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x7c,0x7c,0x00,0x00" 74 | } 75 | 76 | (48..(@tile_data.count - 1)).each do |i| 77 | if (expected_results.include?(i)) then 78 | expect(@tile_data.render_tile i).to eql expected_results[i] 79 | else 80 | expect(@tile_data.render_tile i).to eql blank_tile 81 | end 82 | end 83 | end 84 | end 85 | end 86 | --------------------------------------------------------------------------------