rsgt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/README.markdown +139 -0
- data/Rakefile +10 -0
- data/bin/rsgt +5 -0
- data/lib/rsgt.rb +42 -0
- data/lib/rsgt/audio_extractor.rb +25 -0
- data/lib/rsgt/cli.rb +89 -0
- data/lib/rsgt/command_runner.rb +41 -0
- data/lib/rsgt/encrypted_steam_file.rb +44 -0
- data/lib/rsgt/multipacker.rb +114 -0
- data/lib/rsgt/multipacker/config_processor.rb +44 -0
- data/lib/rsgt/repacker.rb +37 -0
- data/lib/rsgt/rs_custom_song_toolkit.rb +41 -0
- data/lib/rsgt/saved_game.rb +37 -0
- data/lib/rsgt/saved_game/learn_a_song/song.rb +46 -0
- data/lib/rsgt/saved_game/score_attack/song.rb +38 -0
- data/lib/rsgt/saved_game/song_collection.rb +16 -0
- data/lib/rsgt/saved_game/statistics.rb +91 -0
- data/lib/rsgt/saved_game/statistics/song.rb +58 -0
- data/lib/rsgt/steam.rb +45 -0
- data/lib/rsgt/unpacked_psarc.rb +72 -0
- data/lib/rsgt/version.rb +3 -0
- data/lib/rsgt/vocals_extractor.rb +24 -0
- data/lib/rsgt/wav_shifter.rb +54 -0
- data/lib/rsgt/wwise_converter.rb +99 -0
- data/rsgt.gemspec +31 -0
- data/spec/lib/rsgt/audio_extractor_spec.rb +14 -0
- data/spec/lib/rsgt/command_runner_spec.rb +37 -0
- data/spec/lib/rsgt/repacker_spec.rb +24 -0
- data/spec/lib/rsgt/rs_custom_song_toolkit_spec.rb +47 -0
- data/spec/lib/rsgt/saved_game/learn_a_song/song_spec.rb +15 -0
- data/spec/lib/rsgt/saved_game_spec.rb +49 -0
- data/spec/lib/rsgt/unpacked_psarc_spec.rb +35 -0
- data/spec/lib/rsgt/vocals_extractor_spec.rb +14 -0
- data/spec/lib/rsgt/wav_shifter_spec.rb +30 -0
- data/spec/lib/rsgt/wwise_converter_spec.rb +22 -0
- data/spec/spec_helper.rb +11 -0
- metadata +168 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
module RSGuitarTech
|
2
|
+
class UnpackedPSARC
|
3
|
+
|
4
|
+
def self.from_psarc(psarc, opts)
|
5
|
+
raise ArgumentError, "A block is required" unless block_given?
|
6
|
+
raise ArgumentError, "#{psarc} not found!" unless File.exist?(psarc)
|
7
|
+
|
8
|
+
puts "Unpacking psarc to temporary location..."
|
9
|
+
# Use pyrocksmith to unpack the file, and check that it worked..
|
10
|
+
CommandRunner.run! ["pyrocksmith", "--unpack", psarc, "--no-crypto"]
|
11
|
+
expected_extraction = File.basename psarc, ".*"
|
12
|
+
raise ArgumentError unless File.exist?(expected_extraction + "/appid.appid")
|
13
|
+
|
14
|
+
# Move it to a tmpdir, and yield
|
15
|
+
Dir.mktmpdir do |dir|
|
16
|
+
FileUtils.mv expected_extraction, dir
|
17
|
+
yield self.new(psarc, dir, opts)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :psarc, :tmpdir, :opts
|
22
|
+
|
23
|
+
def initialize(psarc, tmpdir, opts)
|
24
|
+
@psarc = psarc
|
25
|
+
@tmpdir = tmpdir
|
26
|
+
@opts = opts
|
27
|
+
end
|
28
|
+
|
29
|
+
def repack!
|
30
|
+
puts "Repacking altered psarc..."
|
31
|
+
CommandRunner.run! ["pyrocksmith", "--no-crypto", "--pack", "#{tmpdir}/#{expected_extraction}"]
|
32
|
+
puts "Done! ✅"
|
33
|
+
end
|
34
|
+
|
35
|
+
def expected_extraction
|
36
|
+
File.basename psarc, ".*"
|
37
|
+
end
|
38
|
+
|
39
|
+
def manifest(type)
|
40
|
+
Dir.glob("#{tmpdir}/#{expected_extraction}/manifests/*/*.json").detect { |filename| filename.include? type.to_s }
|
41
|
+
end
|
42
|
+
|
43
|
+
def sng_bin(type)
|
44
|
+
Dir.glob("#{tmpdir}/#{expected_extraction}/songs/bin/macos/*.sng").detect { |filename| filename.include? type.to_s }
|
45
|
+
end
|
46
|
+
|
47
|
+
def sng_xml
|
48
|
+
Dir.glob("#{tmpdir}/#{expected_extraction}/songs/bin/macos/*.xml").first
|
49
|
+
end
|
50
|
+
|
51
|
+
def audio_track(track: :main)
|
52
|
+
audio_tracks[track]
|
53
|
+
end
|
54
|
+
|
55
|
+
def ogg_track
|
56
|
+
audio_tracks[:main].gsub ".wem", ".ogg"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def audio_tracks
|
62
|
+
@audio_tracks ||= begin
|
63
|
+
both_wems = Dir.glob("#{tmpdir}/#{expected_extraction}/audio/mac/*.wem").sort_by { |wem| File.stat(wem).size }
|
64
|
+
|
65
|
+
{
|
66
|
+
main: both_wems.last,
|
67
|
+
preview: both_wems.first
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/rsgt/version.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module RSGuitarTech
|
2
|
+
class VocalsExtractor
|
3
|
+
|
4
|
+
attr_accessor :psarc, :opts, :unpacked
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
@psarc = File.expand_path(opts.delete :psarc)
|
8
|
+
@opts = opts
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract!
|
12
|
+
puts ".. Extracting vocals from sng to xml..."
|
13
|
+
UnpackedPSARC.from_psarc(psarc, opts) do |unpacked|
|
14
|
+
CommandRunner.run! RSCustomSongToolkit.sng2xml(
|
15
|
+
manifest: unpacked.manifest(:vocals),
|
16
|
+
input: unpacked.sng_bin(:vocals)
|
17
|
+
)
|
18
|
+
raise StandardError, "Failed to extract vocals" unless File.exist? unpacked.sng_xml
|
19
|
+
|
20
|
+
FileUtils.cp unpacked.sng_xml, "."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RSGuitarTech
|
2
|
+
class WavShifter
|
3
|
+
|
4
|
+
attr_accessor :wav, :dir, :amount
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
@wav = File.expand_path(opts.delete :wav)
|
8
|
+
@dir = opts[:dir]
|
9
|
+
@amount = opts[:amount].to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def shift!
|
13
|
+
case dir
|
14
|
+
when "forward" then forward!
|
15
|
+
when /back/ then backward!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def forward!
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def backward!
|
26
|
+
Dir.mktmpdir do |tmpdir|
|
27
|
+
puts "Trimming #{amount}ms from beginning..."
|
28
|
+
status = CommandRunner.run! [
|
29
|
+
"ffmpeg", "-i", wav,
|
30
|
+
"-ss", start_trim_timestamp,
|
31
|
+
"-acodec", "copy",
|
32
|
+
"#{tmpdir}/shifted.wav"
|
33
|
+
]
|
34
|
+
if status.success?
|
35
|
+
puts "Success!"
|
36
|
+
FileUtils.cp "#{tmpdir}/shifted.wav", wav
|
37
|
+
else
|
38
|
+
puts "Failed!"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
TS_CONV = [1, 1000, 60000, 3600000]
|
44
|
+
|
45
|
+
def start_trim_timestamp
|
46
|
+
h = (amount / TS_CONV[3])
|
47
|
+
m = (amount % TS_CONV[3] / TS_CONV[2])
|
48
|
+
s = (amount % TS_CONV[2] / TS_CONV[1])
|
49
|
+
ms = (amount % TS_CONV[1])
|
50
|
+
|
51
|
+
"%02d:%02d:%02d.%03d" % [h, m, s, ms]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module RSGuitarTech
|
2
|
+
class WWiseConverter
|
3
|
+
|
4
|
+
TEMPLATE_TAR_BZ = "/Applications/RocksmithCustomSongToolkit.app/Contents/Resources/WwiseYEAR.tar.bz2"
|
5
|
+
|
6
|
+
attr_accessor :source_file, :wwise_version, :quality, :chorus, :dir
|
7
|
+
|
8
|
+
def initialize(source_file, opts = {})
|
9
|
+
@source_file = source_file
|
10
|
+
@wwise_version = opts[:wwise_ver]
|
11
|
+
@quality = opts[:quality]
|
12
|
+
@chorus = opts[:chorus]
|
13
|
+
end
|
14
|
+
|
15
|
+
def convert!
|
16
|
+
Dir.mktmpdir do |dir|
|
17
|
+
@dir = dir
|
18
|
+
|
19
|
+
extract_base_template
|
20
|
+
prepare_template
|
21
|
+
copy_source
|
22
|
+
ffmpeg_preview
|
23
|
+
run_wwise
|
24
|
+
if wwise_version[0..3].to_i >= 2016
|
25
|
+
downgrade_headers "#{template_dir}/GeneratedSoundBanks/Windows/144151451.wem"
|
26
|
+
downgrade_headers "#{template_dir}/GeneratedSoundBanks/Windows/309608343.wem"
|
27
|
+
end
|
28
|
+
yield self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract_base_template
|
33
|
+
just_tar = template_tar_bz.gsub(".bz2", "")
|
34
|
+
CommandRunner.run! ["bunzip2", "-k", template_tar_bz] # Unzip Wwise2016.tar.bz2 to Wwise2016.tar
|
35
|
+
CommandRunner.run! ["tar", "-xzvf", just_tar, "-C", dir] # Untar Wwise2016.tar to the workdir
|
36
|
+
end
|
37
|
+
|
38
|
+
def prepare_template
|
39
|
+
CommandRunner.run! ["mkdir", "-p", "#{template_dir}/.cache/Windows/SFX"]
|
40
|
+
CommandRunner.run! ["sed", "-i.bak", "'s/%QF1%/#{quality}/'", default_work_unit.shellescape], skip_escaping: true
|
41
|
+
CommandRunner.run! ["sed", "-i.bak", "'s/%QF2%/#{quality}/'", default_work_unit.shellescape], skip_escaping: true
|
42
|
+
end
|
43
|
+
|
44
|
+
def copy_source
|
45
|
+
FileUtils.cp source_file, "#{template_dir}/Originals/SFX/Audio.wav"
|
46
|
+
end
|
47
|
+
|
48
|
+
def ffmpeg_preview
|
49
|
+
CommandRunner.run! ["ffmpeg", "-i", source_file, "-ss", chorus, "-t", "30", "#{template_dir}/Originals/SFX/Audio_preview.wav"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_wwise
|
53
|
+
wwise_cmd = [
|
54
|
+
wwise_cli,
|
55
|
+
"#{template_dir}/Template.wproj",
|
56
|
+
"-GenerateSoundBanks",
|
57
|
+
"-Platform",
|
58
|
+
"Windows",
|
59
|
+
"-NoWwiseDat",
|
60
|
+
"-ClearAudioFileCache",
|
61
|
+
"-Save"
|
62
|
+
]
|
63
|
+
wwise_cmd << "-Verbose" if RSGuitarTech.verbose
|
64
|
+
CommandRunner.run! wwise_cmd
|
65
|
+
end
|
66
|
+
|
67
|
+
def downgrade_headers(file)
|
68
|
+
CommandRunner.run! %Q{printf "$(printf '\\x%02X' 03)" | dd of=#{file} bs=1 seek=40 count=1 conv=notrunc}, skip_escaping: true
|
69
|
+
CommandRunner.run! %Q{printf "$(printf '\\x%02X' 00)" | dd of=#{file} bs=1 seek=41 count=1 conv=notrunc}, skip_escaping: true
|
70
|
+
CommandRunner.run! %Q{printf "$(printf '\\x%02X' 00)" | dd of=#{file} bs=1 seek=42 count=1 conv=notrunc}, skip_escaping: true
|
71
|
+
CommandRunner.run! %Q{printf "$(printf '\\x%02X' 00)" | dd of=#{file} bs=1 seek=43 count=1 conv=notrunc}, skip_escaping: true
|
72
|
+
end
|
73
|
+
|
74
|
+
def results
|
75
|
+
{
|
76
|
+
main: "#{template_dir}/GeneratedSoundBanks/Windows/144151451.wem",
|
77
|
+
preview: "#{template_dir}/GeneratedSoundBanks/Windows/309608343.wem"
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def template_dir
|
84
|
+
"#{dir}/Template"
|
85
|
+
end
|
86
|
+
|
87
|
+
def template_tar_bz
|
88
|
+
TEMPLATE_TAR_BZ.dup.gsub "YEAR", wwise_version[0..3]
|
89
|
+
end
|
90
|
+
|
91
|
+
def wwise_cli
|
92
|
+
"/Applications/Audiokinetic/Wwise #{wwise_version}/Wwise.app/Contents/Tools/WwiseCLI.sh"
|
93
|
+
end
|
94
|
+
|
95
|
+
def default_work_unit
|
96
|
+
"#{template_dir}/Interactive Music Hierarchy/Default Work Unit.wwu"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/rsgt.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "rsgt/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.name = "rsgt"
|
8
|
+
s.version = RSGuitarTech::VERSION
|
9
|
+
s.authors = ["Justin Aiken"]
|
10
|
+
s.email = ["60tonangel@gmail.com"]
|
11
|
+
s.license = "MIT"
|
12
|
+
s.homepage = "https://github.com/JustinAiken/rsgt"
|
13
|
+
s.summary = %q{Rocksmith}
|
14
|
+
s.description = %q{Rocksmith}
|
15
|
+
|
16
|
+
s.rubyforge_project = "rsgt"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.bindir = "bin"
|
22
|
+
s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
23
|
+
|
24
|
+
s.add_dependency "bindata"
|
25
|
+
s.add_dependency "trollop", "~> 2.1"
|
26
|
+
s.add_dependency "rainbow", "~> 2.2"
|
27
|
+
s.add_dependency "activesupport", ">= 3.0"
|
28
|
+
|
29
|
+
s.add_development_dependency "rspec", "~> 3.5"
|
30
|
+
s.add_development_dependency "pry"
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::AudioExtractor do
|
4
|
+
let(:extractor) { described_class.new psarc: psarc }
|
5
|
+
let(:psarc) { "foo_m.psarc" }
|
6
|
+
|
7
|
+
describe "#extract!" do
|
8
|
+
it "extracts" do
|
9
|
+
allow(extractor).to receive :puts
|
10
|
+
expect(RSGuitarTech::UnpackedPSARC).to receive :from_psarc
|
11
|
+
extractor.extract!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::CommandRunner do
|
4
|
+
let(:runner) { described_class }
|
5
|
+
let(:args) { "ls" }
|
6
|
+
let(:skip_escaping) { false }
|
7
|
+
subject(:run!) { runner.run! args, skip_escaping: skip_escaping }
|
8
|
+
|
9
|
+
it { should be_success }
|
10
|
+
|
11
|
+
context "with spaces" do
|
12
|
+
let(:args) { "ls -al" }
|
13
|
+
|
14
|
+
after { run! }
|
15
|
+
|
16
|
+
it "escapes the command" do
|
17
|
+
expect(Open3).to receive(:capture3).with "ls\\ -al"
|
18
|
+
end
|
19
|
+
|
20
|
+
context "but skip_escaping: true" do
|
21
|
+
let(:skip_escaping) { true }
|
22
|
+
|
23
|
+
it "runs the command as-is" do
|
24
|
+
expect(Open3).to receive(:capture3).with "ls -al"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "verbosely" do
|
30
|
+
before { allow(RSGuitarTech).to receive(:verbose).and_return true }
|
31
|
+
|
32
|
+
it "does some puts" do
|
33
|
+
expect_any_instance_of(described_class).to receive(:puts).at_least :once
|
34
|
+
run!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::Repacker do
|
4
|
+
let(:repacker) { described_class.new psarc: "foo_m.psarc", vocals_xml: vocals_xml, audio: audio }
|
5
|
+
let(:vocals_xml) { "foo_vocals.xml" }
|
6
|
+
let(:audio) { "audio.wav" }
|
7
|
+
|
8
|
+
let(:unpacked) { double RSGuitarTech::UnpackedPSARC, manifest: "manifest.json", audio_track: "track.wem" }
|
9
|
+
let(:converter) { double RSGuitarTech::WWiseConverter, results: {main: "main.wav"} }
|
10
|
+
|
11
|
+
describe "#repack!" do
|
12
|
+
before { allow(repacker).to receive :puts }
|
13
|
+
|
14
|
+
it "repacks" do
|
15
|
+
expect(RSGuitarTech::UnpackedPSARC).to receive(:from_psarc).and_yield unpacked
|
16
|
+
expect_any_instance_of(RSGuitarTech::WWiseConverter).to receive(:convert!).and_yield converter
|
17
|
+
expect(FileUtils).to receive(:cp).with "main.wav", "track.wem"
|
18
|
+
expect(RSGuitarTech::CommandRunner).to receive :run!
|
19
|
+
expect(unpacked).to receive :repack!
|
20
|
+
|
21
|
+
repacker.repack!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::RSCustomSongToolkit do
|
4
|
+
let(:rs_custom) { described_class }
|
5
|
+
|
6
|
+
it ".ww2ogg" do
|
7
|
+
expect(rs_custom.ww2ogg "foo.wem").to eq [
|
8
|
+
"wine",
|
9
|
+
"/Applications/RocksmithCustomSongToolkit.app/Contents/Resources/ww2ogg.exe",
|
10
|
+
"foo.wem",
|
11
|
+
"--pcb",
|
12
|
+
described_class::PCB_PATH
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
it ".revorb" do
|
17
|
+
expect(rs_custom.revorb "foo.ogg").to eq [
|
18
|
+
"wine",
|
19
|
+
"/Applications/RocksmithCustomSongToolkit.app/Contents/Resources/revorb.exe",
|
20
|
+
"foo.ogg"
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
it ".sng2xml" do
|
25
|
+
expect(rs_custom.sng2xml manifest: "manifest.json", input: "foo.sng").to eq [
|
26
|
+
"wine",
|
27
|
+
"/Applications/RocksmithCustomSongToolkit.app/Contents/Resources/sng2014.exe",
|
28
|
+
"--sng2xml",
|
29
|
+
"--manifest=manifest.json",
|
30
|
+
"--input=foo.sng",
|
31
|
+
"--arrangement=Vocal",
|
32
|
+
"--platform=Mac"
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
it ".xml2sng" do
|
37
|
+
expect(rs_custom.xml2sng manifest: "manifest.json", input: "foo.xml").to eq [
|
38
|
+
"wine",
|
39
|
+
"/Applications/RocksmithCustomSongToolkit.app/Contents/Resources/sng2014.exe",
|
40
|
+
"--xml2sng",
|
41
|
+
"--manifest=manifest.json",
|
42
|
+
"--input=foo.xml",
|
43
|
+
"--arrangement=Vocal",
|
44
|
+
"--platform=Mac"
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::SavedGame::LearnASong::Song do
|
4
|
+
pending ".from"
|
5
|
+
|
6
|
+
describe "#==" do
|
7
|
+
subject { described_class.new id: "123", timestamp: nil, dynamic_difficulty: nil, play_next_stats: nil }
|
8
|
+
let(:song_b) { described_class.new id: "123", timestamp: nil, dynamic_difficulty: nil, play_next_stats: nil }
|
9
|
+
let(:song_c) { described_class.new id: "456", timestamp: nil, dynamic_difficulty: nil, play_next_stats: nil }
|
10
|
+
|
11
|
+
it { should eq song_b }
|
12
|
+
it { should_not eq song_c }
|
13
|
+
it { should_not eq nil }
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RSGuitarTech::SavedGame do
|
4
|
+
let(:saved_game) { described_class.new json, :filename }
|
5
|
+
|
6
|
+
pending ".from"
|
7
|
+
|
8
|
+
describe "#options" do
|
9
|
+
subject { saved_game.options }
|
10
|
+
let(:json) { {"Options" => {"foo" => "bar"}} }
|
11
|
+
it { should eq "foo" => "bar" }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#statistics" do
|
15
|
+
subject { saved_game.statistics }
|
16
|
+
let(:json) { {"Stats" => {"foo" => "bar"}} }
|
17
|
+
it { should be_a RSGuitarTech::SavedGame::Statistics }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#song_statistics" do
|
21
|
+
subject(:song_statistics) { saved_game.song_statistics }
|
22
|
+
let(:json) { {"Stats" => {"Songs" => {"abc" => "songdata"}}} }
|
23
|
+
it { should be_a RSGuitarTech::SavedGame::SongCollection }
|
24
|
+
|
25
|
+
it "is a keyed collection of Statistics::Song's" do
|
26
|
+
expect(song_statistics["abc"]).to be_a RSGuitarTech::SavedGame::Statistics::Song
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#score_attack" do
|
31
|
+
subject(:score_attack) { saved_game.score_attack }
|
32
|
+
let(:json) { {"SongsSA" => {"abc" => "songdata"}} }
|
33
|
+
it { should be_a RSGuitarTech::SavedGame::SongCollection }
|
34
|
+
|
35
|
+
it "is a keyed collection of Statistics::Song's" do
|
36
|
+
expect(score_attack["abc"]).to be_a RSGuitarTech::SavedGame::ScoreAttack::Song
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#learn_a_song" do
|
41
|
+
subject(:learn_a_song) { saved_game.learn_a_song }
|
42
|
+
let(:json) { {"Songs" => {"abc" => "songdata"}} }
|
43
|
+
it { should be_a RSGuitarTech::SavedGame::SongCollection }
|
44
|
+
|
45
|
+
it "is a keyed collection of Statistics::Song's" do
|
46
|
+
expect(learn_a_song["abc"]).to be_a RSGuitarTech::SavedGame::LearnASong::Song
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|