gamerom 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.rubocop.yml +35 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +10 -1
- data/Dockerfile +7 -2
- data/Gemfile +4 -4
- data/Gemfile.lock +4 -4
- data/Makefile +1 -1
- data/README.md +2 -2
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/entrypoint.sh +6 -0
- data/exe/gamerom +2 -1
- data/gamerom.gemspec +22 -22
- data/lib/gamerom.rb +6 -6
- data/lib/gamerom/cli.rb +82 -71
- data/lib/gamerom/config.rb +2 -2
- data/lib/gamerom/game.rb +18 -16
- data/lib/gamerom/game_info.rb +9 -10
- data/lib/gamerom/repo.rb +24 -24
- data/lib/gamerom/repo_adapter.rb +26 -0
- data/lib/gamerom/repo_adapters/coolrom.rb +29 -26
- data/lib/gamerom/repo_adapters/romnation.rb +45 -33
- data/lib/gamerom/repo_adapters/vimm.rb +31 -28
- data/lib/gamerom/version.rb +1 -1
- data/lib/mechanizeprogress.rb +14 -10
- metadata +6 -4
data/lib/gamerom/config.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'logger'
|
@@ -6,7 +6,7 @@ require 'rest-client'
|
|
6
6
|
require 'mechanize'
|
7
7
|
|
8
8
|
module Gamerom
|
9
|
-
ROM_ROOT = ENV['ROM_ROOT'] || File.expand_path(
|
9
|
+
ROM_ROOT = ENV['ROM_ROOT'] || File.expand_path('~/.gamerom')
|
10
10
|
CACHE_DIR = ENV['CACHE_DIR'] || "#{ROM_ROOT}/cache"
|
11
11
|
GAME_DIR = ENV['GAME_DIR'] || "#{ROM_ROOT}/games"
|
12
12
|
LOG_DIR = ENV['LOG_DIR'] || "#{ROM_ROOT}/logs"
|
data/lib/gamerom/game.rb
CHANGED
@@ -1,54 +1,56 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'ostruct'
|
5
5
|
require 'yaml'
|
6
6
|
|
7
7
|
module Gamerom
|
8
|
+
# Game - Represents a game ROM
|
8
9
|
class Game < OpenStruct
|
9
10
|
def filenames
|
10
|
-
YAML.load_file(
|
11
|
-
"#{
|
11
|
+
YAML.load_file(state_filename).map do |filename|
|
12
|
+
"#{filepath}/#{filename}"
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
16
|
def filepath
|
16
|
-
"#{Gamerom::GAME_DIR}/#{
|
17
|
+
"#{Gamerom::GAME_DIR}/#{repo.name}/#{platform}/#{region}"
|
17
18
|
end
|
18
19
|
|
19
20
|
def install
|
20
|
-
|
21
|
-
|
21
|
+
repo.install self do |filenames|
|
22
|
+
update_state filenames
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
def installed?
|
26
|
-
File.
|
27
|
+
File.exist? state_filename
|
27
28
|
end
|
28
29
|
|
29
30
|
def state_filename
|
30
|
-
"#{Gamerom::STATE_DIR}/#{
|
31
|
+
"#{Gamerom::STATE_DIR}/#{repo.name}/#{platform}/#{region}/#{id}"
|
31
32
|
end
|
32
33
|
|
33
34
|
def to_s
|
34
35
|
install_status = ''
|
35
|
-
install_status = " (#{shell.set_color
|
36
|
+
install_status = " (#{shell.set_color "installed", :green})" if installed?
|
36
37
|
tags = ''
|
37
|
-
tags = " - tags: #{
|
38
|
-
"#{
|
38
|
+
tags = " - tags: #{tags.join(", ")}" if respond_to?(:tags) && !tags.empty?
|
39
|
+
"#{id} - #{name} - #{region}#{install_status}#{tags}"
|
39
40
|
end
|
40
41
|
|
41
42
|
def uninstall
|
42
|
-
FileUtils.rm_rf
|
43
|
-
FileUtils.rm_rf
|
43
|
+
FileUtils.rm_rf filenames
|
44
|
+
FileUtils.rm_rf state_filename
|
44
45
|
end
|
45
46
|
|
46
|
-
def update_state
|
47
|
-
FileUtils.mkdir_p("#{Gamerom::STATE_DIR}/#{
|
48
|
-
File.write(
|
47
|
+
def update_state(filenames)
|
48
|
+
FileUtils.mkdir_p("#{Gamerom::STATE_DIR}/#{repo.name}/#{platform}/#{region}")
|
49
|
+
File.write(state_filename, filenames.to_yaml)
|
49
50
|
end
|
50
51
|
|
51
52
|
private
|
53
|
+
|
52
54
|
def shell
|
53
55
|
@shell ||= Thor::Shell::Color.new
|
54
56
|
end
|
data/lib/gamerom/game_info.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Gamerom
|
4
|
+
# GameInfo - Extracts region and tags from game name
|
4
5
|
class GameInfo
|
5
6
|
REGIONS = {
|
6
7
|
'1' => 'Japan & Korea',
|
@@ -33,13 +34,13 @@ module Gamerom
|
|
33
34
|
'S' => 'Spain',
|
34
35
|
'Sw' => 'Sweden',
|
35
36
|
'SW' => 'Sweden',
|
36
|
-
'U' =>
|
37
|
+
'U' => 'USA',
|
37
38
|
'UK' => 'England',
|
38
39
|
'Unk' => 'Unknown Country',
|
39
40
|
'Unl' => 'Unlicensed',
|
40
41
|
'PAL' => 'PAL regions (Australia, Europe)',
|
41
42
|
'NTSC' => 'NTSC regions (Japan, USA, Latin America)',
|
42
|
-
}
|
43
|
+
}.freeze
|
43
44
|
|
44
45
|
TAGS = {
|
45
46
|
'!' => :good,
|
@@ -61,23 +62,21 @@ module Gamerom
|
|
61
62
|
'T+' => :newer_translation,
|
62
63
|
'VS' => :vs,
|
63
64
|
'x' => :bad_checksum,
|
64
|
-
}
|
65
|
+
}.freeze
|
66
|
+
|
67
|
+
attr_reader :name
|
65
68
|
|
66
69
|
def initialize(name)
|
67
70
|
@name = name
|
68
71
|
end
|
69
72
|
|
70
|
-
def name
|
71
|
-
@name
|
72
|
-
end
|
73
|
-
|
74
73
|
def region
|
75
74
|
identifiers = @name.scan(/\((?<region>[A-Za-z0-9]+)\)/).flatten
|
76
75
|
region_id = identifiers.find { |i| REGIONS.include? i }
|
77
76
|
if region_id
|
78
77
|
REGIONS[region_id]
|
79
78
|
else
|
80
|
-
|
79
|
+
'USA'
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
@@ -85,7 +84,7 @@ module Gamerom
|
|
85
84
|
tags = []
|
86
85
|
codes = @name.scan(/\[(?<code>[^\]]+)\]/).flatten
|
87
86
|
codes.each do |code|
|
88
|
-
code =
|
87
|
+
code = Regexp.last_match(1) if code.match /^(?<code>[abcfhop])[0-9]*/
|
89
88
|
tags << TAGS[code] if TAGS.include?(code)
|
90
89
|
end
|
91
90
|
tags
|
data/lib/gamerom/repo.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
|
@@ -6,30 +6,34 @@ REPOSITORIES = Dir["#{File.dirname(__FILE__)}/repo_adapters/*"].map do |file|
|
|
6
6
|
File.basename(file, '.rb')
|
7
7
|
end
|
8
8
|
|
9
|
+
require_relative 'repo_adapter'
|
9
10
|
REPOSITORIES.each do |repo|
|
10
11
|
require_relative "repo_adapters/#{repo}"
|
11
12
|
end
|
12
13
|
|
13
14
|
module Gamerom
|
15
|
+
# Repo - Represents a game ROM repository
|
14
16
|
class Repo
|
17
|
+
attr_reader :name
|
18
|
+
|
15
19
|
def self.list
|
16
20
|
REPOSITORIES.map do |repo|
|
17
|
-
|
21
|
+
new repo
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
|
-
def initialize
|
25
|
+
def initialize(name)
|
22
26
|
@name = name
|
23
27
|
@repo = Gamerom::RepoAdapters.const_get(name.capitalize)
|
24
28
|
end
|
25
29
|
|
26
|
-
def install
|
30
|
+
def install(game, &block)
|
27
31
|
@repo.install game, &block
|
28
32
|
end
|
29
33
|
|
30
|
-
def find
|
34
|
+
def find(platform, game_identifier)
|
31
35
|
games(platform).find do |game|
|
32
|
-
if
|
36
|
+
if Integer(game_identifier, exception: false)
|
33
37
|
game.id == game_identifier.to_i
|
34
38
|
else
|
35
39
|
game.name.downcase == game_identifier.downcase
|
@@ -37,45 +41,41 @@ module Gamerom
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def games
|
44
|
+
def games(platform, options = {})
|
41
45
|
platform_database = "#{Gamerom::CACHE_DIR}/#{@name}/#{platform}.yml"
|
42
|
-
update_database platform unless File.
|
43
|
-
games = YAML.load_file(platform_database).map
|
46
|
+
update_database platform unless File.exist? platform_database
|
47
|
+
games = YAML.load_file(platform_database).map do |game|
|
44
48
|
Game.new(game.merge(platform: platform, repo: self))
|
45
|
-
|
49
|
+
end
|
46
50
|
|
47
|
-
|
48
|
-
games
|
49
|
-
|
50
|
-
|
51
|
+
unless options[:region].nil?
|
52
|
+
games.select! do |game|
|
53
|
+
game.region == options[:region]
|
54
|
+
end
|
51
55
|
end
|
52
56
|
|
53
|
-
|
54
|
-
games
|
57
|
+
unless options[:keyword].nil?
|
58
|
+
games.select! do |game|
|
55
59
|
game.name =~ /#{options[:keyword]}/i
|
56
|
-
|
60
|
+
end
|
57
61
|
end
|
58
62
|
|
59
63
|
games
|
60
64
|
end
|
61
65
|
|
62
|
-
def name
|
63
|
-
@name
|
64
|
-
end
|
65
|
-
|
66
66
|
def platforms
|
67
67
|
@repo.platforms
|
68
68
|
end
|
69
69
|
|
70
|
-
def regions
|
71
|
-
games(platform).map
|
70
|
+
def regions(platform)
|
71
|
+
games(platform).map(&:region).sort.uniq
|
72
72
|
end
|
73
73
|
|
74
74
|
def to_s
|
75
75
|
@name
|
76
76
|
end
|
77
77
|
|
78
|
-
def update_database
|
78
|
+
def update_database(platform)
|
79
79
|
games = @repo.games platform
|
80
80
|
FileUtils.mkdir_p("#{Gamerom::CACHE_DIR}/#{@name}")
|
81
81
|
File.write("#{Gamerom::CACHE_DIR}/#{@name}/#{platform}.yml", games.to_yaml)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'rest-client'
|
5
|
+
|
6
|
+
module Gamerom
|
7
|
+
# RepoAdapter - Common adapter methods
|
8
|
+
module RepoAdapter
|
9
|
+
def nokogiri_get(url)
|
10
|
+
Nokogiri::HTML(RestClient.get(url))
|
11
|
+
end
|
12
|
+
|
13
|
+
def games(platform)
|
14
|
+
games = []
|
15
|
+
progress_bar = ProgressBar.new(platform, sections.count)
|
16
|
+
|
17
|
+
extract_games(platform) do |section_games, section_index|
|
18
|
+
games.append(*section_games)
|
19
|
+
progress_bar.set(section_index + 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
progress_bar.finish
|
23
|
+
games
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -3,12 +3,13 @@
|
|
3
3
|
require 'mechanize'
|
4
4
|
require 'mechanize/progressbar'
|
5
5
|
require 'mechanizeprogress'
|
6
|
-
require 'nokogiri'
|
7
|
-
require 'rest-client'
|
8
6
|
|
9
7
|
module Gamerom
|
10
8
|
module RepoAdapters
|
9
|
+
# Coolrom - An adapter for the CoolROM repository website
|
11
10
|
class Coolrom
|
11
|
+
extend Gamerom::RepoAdapter
|
12
|
+
|
12
13
|
PLATFORM = {
|
13
14
|
'atari2600' => 'Atari 2600',
|
14
15
|
'atari5200' => 'Atari 5200',
|
@@ -33,32 +34,33 @@ module Gamerom
|
|
33
34
|
'psx' => 'Sony Playstation',
|
34
35
|
'ps2' => 'Sony Playstation 2',
|
35
36
|
'psp' => 'Sony Playstation Portable',
|
36
|
-
}
|
37
|
+
}.freeze
|
37
38
|
|
38
39
|
def self.platforms
|
39
40
|
PLATFORM
|
40
41
|
end
|
41
42
|
|
42
|
-
def self.
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
def self.sections
|
44
|
+
('a'..'z').to_a.unshift('0')
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.extract_games(platform)
|
46
48
|
sections.each_with_index do |section, index|
|
47
|
-
page =
|
48
|
-
regions = page.css('input.region').map { |i| i[
|
49
|
+
page = nokogiri_get("https://coolrom.com.au/roms/#{platform}/#{section}/")
|
50
|
+
regions = page.css('input.region').map { |i| i['name'] }
|
49
51
|
regions.each do |region|
|
50
|
-
|
51
|
-
|
52
|
-
id: game['href'].split('/')[3].to_i,
|
53
|
-
name: game.text,
|
54
|
-
region: region,
|
55
|
-
}
|
56
|
-
}
|
52
|
+
game_links = page.css("div.#{region} a")
|
53
|
+
yield game_links.map { |game_link| game(game_link, region) }, index
|
57
54
|
end
|
58
|
-
progress_bar.set(index+1)
|
59
55
|
end
|
60
|
-
|
61
|
-
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.game(game_link, region)
|
59
|
+
{
|
60
|
+
id: game_link['href'].split('/')[3].to_i,
|
61
|
+
name: game_link.text,
|
62
|
+
region: region,
|
63
|
+
}
|
62
64
|
end
|
63
65
|
|
64
66
|
def self.install(game)
|
@@ -67,15 +69,16 @@ module Gamerom
|
|
67
69
|
agent.user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'
|
68
70
|
|
69
71
|
response = nil
|
70
|
-
agent.progressbar
|
72
|
+
agent.progressbar do
|
71
73
|
response = agent.get("https://coolrom.com.au/downloader.php?id=#{game.id}")
|
72
|
-
}
|
73
|
-
if response.code.to_i == 200
|
74
|
-
filename = response.filename
|
75
|
-
FileUtils.mkdir_p(game.filepath)
|
76
|
-
response.save!("#{game.filepath}/#{filename}")
|
77
|
-
yield [filename]
|
78
74
|
end
|
75
|
+
|
76
|
+
return unless response.code.to_i == 200
|
77
|
+
|
78
|
+
filename = response.filename
|
79
|
+
FileUtils.mkdir_p(game.filepath)
|
80
|
+
response.save!("#{game.filepath}/#{filename}")
|
81
|
+
yield [filename]
|
79
82
|
end
|
80
83
|
end
|
81
84
|
end
|
@@ -4,12 +4,13 @@ require 'cgi'
|
|
4
4
|
require 'mechanize'
|
5
5
|
require 'mechanize/progressbar'
|
6
6
|
require 'mechanizeprogress'
|
7
|
-
require 'nokogiri'
|
8
|
-
require 'rest-client'
|
9
7
|
|
10
8
|
module Gamerom
|
11
9
|
module RepoAdapters
|
10
|
+
# Romnation - An adapter for the ROMNation repository website
|
12
11
|
class Romnation
|
12
|
+
extend Gamerom::RepoAdapter
|
13
|
+
|
13
14
|
PLATFORM = {
|
14
15
|
'amstrad' => 'Amstrad',
|
15
16
|
'atari2600' => 'Atari 2600',
|
@@ -44,36 +45,46 @@ module Gamerom
|
|
44
45
|
'virtualboy' => 'Virtual Boy',
|
45
46
|
'watara' => 'Watara Supervision',
|
46
47
|
'wonderswan' => 'WonderSwan',
|
47
|
-
}
|
48
|
+
}.freeze
|
48
49
|
|
49
50
|
def self.platforms
|
50
51
|
PLATFORM
|
51
52
|
end
|
52
53
|
|
53
|
-
def self.
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
def self.sections
|
55
|
+
('a'..'z').to_a.unshift('0')
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.extract_games(platform)
|
57
59
|
sections.each_with_index do |section, index|
|
58
|
-
|
59
|
-
pages
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
60
|
+
pages = extract_pages(platform, section)
|
61
|
+
yield extract_games_from_section_pages(platform, section, pages), index
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.extract_pages(platform, section)
|
66
|
+
page = nokogiri_get("https://www.romnation.net/srv/roms/#{platform}/#{section}/sort-title.html")
|
67
|
+
pages = ['1']
|
68
|
+
pages = page.css('.pagination').first.css('a').map(&:text).map(&:strip).reject(&:empty?) unless page.css('.pagination').empty?
|
69
|
+
pages
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.extract_games_from_section_pages(platform, section, pages)
|
73
|
+
pages.reduce([]) do |section_games, p|
|
74
|
+
page = nokogiri_get("https://www.romnation.net/srv/roms/#{platform}/#{section}/page-#{p}_sort-title.html")
|
75
|
+
games_links = page.css('table.listings td.title a')
|
76
|
+
section_games.append(*games_links.map { |game_link| game(game_link) })
|
74
77
|
end
|
75
|
-
|
76
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.game(game_link)
|
81
|
+
game_info = GameInfo.new(game_link.text)
|
82
|
+
{
|
83
|
+
id: game_link['href'].split('/')[3].to_i,
|
84
|
+
name: game_link.text,
|
85
|
+
region: game_info.region,
|
86
|
+
tags: game_info.tags,
|
87
|
+
}
|
77
88
|
end
|
78
89
|
|
79
90
|
def self.install(game)
|
@@ -83,15 +94,16 @@ module Gamerom
|
|
83
94
|
page = agent.get("https://www.romnation.net/download/rom/#{game.id}")
|
84
95
|
|
85
96
|
response = nil
|
86
|
-
agent.progressbar
|
87
|
-
response = page.link_with(:
|
88
|
-
}
|
89
|
-
if response.code.to_i == 200
|
90
|
-
filename = CGI.unescape(response.filename.split('_host=').first)
|
91
|
-
FileUtils.mkdir_p(game.filepath)
|
92
|
-
response.save!("#{game.filepath}/#{filename}")
|
93
|
-
yield [filename]
|
97
|
+
agent.progressbar do
|
98
|
+
response = page.link_with(text: 'Download This Rom').click
|
94
99
|
end
|
100
|
+
|
101
|
+
return unless response.code.to_i == 200
|
102
|
+
|
103
|
+
filename = CGI.unescape(response.filename.split('_host=').first)
|
104
|
+
FileUtils.mkdir_p(game.filepath)
|
105
|
+
response.save!("#{game.filepath}/#{filename}")
|
106
|
+
yield [filename]
|
95
107
|
end
|
96
108
|
end
|
97
109
|
end
|