gamerom 0.2.0 → 0.4.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.
@@ -1,4 +1,4 @@
1
- # 'frozen_string_literal' => true
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("~/.gamerom")
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,50 +1,56 @@
1
- # 'frozen_string_literal' => true
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(self.state_filename).map do |filename|
11
- "#{self.filepath}/#{filename}"
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}/#{self.repo.name}/#{self.platform}/#{self.region}"
17
+ "#{Gamerom::GAME_DIR}/#{repo.name}/#{platform}/#{region}"
17
18
  end
18
19
 
19
20
  def install
20
- self.repo.install self do |filenames|
21
- self.update_state filenames
21
+ repo.install self do |filenames|
22
+ update_state filenames
22
23
  end
23
24
  end
24
25
 
25
26
  def installed?
26
- File.exists? self.state_filename
27
+ File.exist? state_filename
27
28
  end
28
29
 
29
30
  def state_filename
30
- "#{Gamerom::STATE_DIR}/#{self.repo.name}/#{self.platform}/#{self.region}/#{self.id}"
31
+ "#{Gamerom::STATE_DIR}/#{repo.name}/#{platform}/#{region}/#{id}"
31
32
  end
32
33
 
33
34
  def to_s
34
- "#{self.id} - #{self.name} - #{self.region}#{self.installed? ? " (#{shell.set_color 'installed', :green})" : ''}"
35
+ install_status = ''
36
+ install_status = " (#{shell.set_color "installed", :green})" if installed?
37
+ tags = ''
38
+ tags = " - tags: #{tags.join(", ")}" if respond_to?(:tags) && !tags.empty?
39
+ "#{id} - #{name} - #{region}#{install_status}#{tags}"
35
40
  end
36
41
 
37
42
  def uninstall
38
- FileUtils.rm_rf self.filenames
39
- FileUtils.rm_rf self.state_filename
43
+ FileUtils.rm_rf filenames
44
+ FileUtils.rm_rf state_filename
40
45
  end
41
46
 
42
- def update_state filenames
43
- FileUtils.mkdir_p("#{Gamerom::STATE_DIR}/#{self.repo.name}/#{self.platform}/#{self.region}")
44
- File.write(self.state_filename, filenames.to_yaml)
47
+ def update_state(filenames)
48
+ FileUtils.mkdir_p("#{Gamerom::STATE_DIR}/#{repo.name}/#{platform}/#{region}")
49
+ File.write(state_filename, filenames.to_yaml)
45
50
  end
46
51
 
47
52
  private
53
+
48
54
  def shell
49
55
  @shell ||= Thor::Shell::Color.new
50
56
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamerom
4
+ # GameInfo - Extracts region and tags from game name
5
+ class GameInfo
6
+ REGIONS = {
7
+ '1' => 'Japan & Korea',
8
+ '4' => 'USA & Brazil - NTSC',
9
+ '5' => 'NTSC',
10
+ '8' => 'PAL',
11
+ 'A' => 'Australia',
12
+ 'As' => 'Asia',
13
+ 'B' => 'Brazil',
14
+ 'C' => 'Canada',
15
+ 'Ch' => 'China',
16
+ 'D' => 'Netherlands (Dutch)',
17
+ 'E' => 'Europe',
18
+ 'F' => 'France',
19
+ 'FC' => 'French Canadian',
20
+ 'FN' => 'Finland',
21
+ 'G' => 'Germany',
22
+ 'GR' => 'Greece',
23
+ 'H' => 'Holland',
24
+ 'HK' => 'Hong Kong',
25
+ 'I' => 'Italy',
26
+ 'J' => 'Japan',
27
+ 'JUE' => 'Japan & USA & Europe',
28
+ 'K' => 'Korea',
29
+ 'Nl' => 'Netherlands',
30
+ 'NL' => 'Netherlands',
31
+ 'No' => 'Norway',
32
+ 'PD' => 'Public Domain',
33
+ 'R' => 'Russia',
34
+ 'S' => 'Spain',
35
+ 'Sw' => 'Sweden',
36
+ 'SW' => 'Sweden',
37
+ 'U' => 'USA',
38
+ 'UK' => 'England',
39
+ 'Unk' => 'Unknown Country',
40
+ 'Unl' => 'Unlicensed',
41
+ 'PAL' => 'PAL regions (Australia, Europe)',
42
+ 'NTSC' => 'NTSC regions (Japan, USA, Latin America)',
43
+ }.freeze
44
+
45
+ TAGS = {
46
+ '!' => :good,
47
+ '!p' => :pending,
48
+ 'a' => :alternate,
49
+ 'b' => :bad,
50
+ 'BF' => :bung,
51
+ 'c' => :checksum,
52
+ 'C' => :color,
53
+ 'f' => :fixed,
54
+ 'h' => :hack,
55
+ 'J' => :japanese_translation,
56
+ 'o' => :overdump,
57
+ 'p' => :pirate,
58
+ 'PC10' => :pc10,
59
+ 'S' => :super,
60
+ 'T-' => :old_translation,
61
+ 't' => :trained,
62
+ 'T+' => :newer_translation,
63
+ 'VS' => :vs,
64
+ 'x' => :bad_checksum,
65
+ }.freeze
66
+
67
+ attr_reader :name
68
+
69
+ def initialize(name)
70
+ @name = name
71
+ end
72
+
73
+ def region
74
+ identifiers = @name.scan(/\((?<region>[A-Za-z0-9]+)\)/).flatten
75
+ region_id = identifiers.find { |i| REGIONS.include? i }
76
+ if region_id
77
+ REGIONS[region_id]
78
+ else
79
+ 'USA'
80
+ end
81
+ end
82
+
83
+ def tags
84
+ tags = []
85
+ codes = @name.scan(/\[(?<code>[^\]]+)\]/).flatten
86
+ codes.each do |code|
87
+ code = Regexp.last_match(1) if code.match /^(?<code>[abcfhop])[0-9]*/
88
+ tags << TAGS[code] if TAGS.include?(code)
89
+ end
90
+ tags
91
+ end
92
+ end
93
+ end
data/lib/gamerom/repo.rb CHANGED
@@ -1,4 +1,4 @@
1
- # 'frozen_string_literal' => true
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
- self.new repo
21
+ new repo
18
22
  end
19
23
  end
20
24
 
21
- def initialize name
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 game, &block
30
+ def install(game, &block)
27
31
  @repo.install game, &block
28
32
  end
29
33
 
30
- def find platform, game_identifier
34
+ def find(platform, game_identifier)
31
35
  games(platform).find do |game|
32
- if Float(game_identifier, exception: false)
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,47 +41,42 @@ module Gamerom
37
41
  end
38
42
  end
39
43
 
40
- def games platform, options={}
44
+ def games(platform, options = {})
41
45
  platform_database = "#{Gamerom::CACHE_DIR}/#{@name}/#{platform}.yml"
42
- update_database platform unless File.exists? platform_database
43
- games = YAML.load_file(platform_database).map { |game|
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
- if !options[:region].nil?
48
- games = games.select { |game|
49
- options[:region].nil? || game.region == options[:region]
50
- }
51
+ unless options[:region].nil?
52
+ games.select! do |game|
53
+ game.region == options[:region]
54
+ end
51
55
  end
52
56
 
53
- if !options[:keyword].nil?
54
- games = games.select { |game|
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 platform
71
- games(platform).map { |game| game.region }.sort.uniq
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 platform
78
+ def update_database(platform)
79
79
  games = @repo.games platform
80
- puts
81
80
  FileUtils.mkdir_p("#{Gamerom::CACHE_DIR}/#{@name}")
82
81
  File.write("#{Gamerom::CACHE_DIR}/#{@name}/#{platform}.yml", games.to_yaml)
83
82
  end
@@ -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,31 +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.games(platform)
43
- games = []
44
- sections = ('a'..'z').to_a.unshift("0")
43
+ def self.sections
44
+ ('a'..'z').to_a.unshift('0')
45
+ end
45
46
 
46
- sections.each do |section|
47
- print "#{section} "
48
- page = Nokogiri::HTML(RestClient.get("https://coolrom.com.au/roms/#{platform}/#{section}/"))
49
- regions = page.css('input.region').map { |i| i["name"] }
47
+ def self.extract_games(platform)
48
+ sections.each_with_index do |section, index|
49
+ page = nokogiri_get("https://coolrom.com.au/roms/#{platform}/#{section}/")
50
+ regions = page.css('input.region').map { |i| i['name'] }
50
51
  regions.each do |region|
51
- games.append *page.css("div.#{region} a").map { |game|
52
- {
53
- id: game['href'].split('/')[3].to_i,
54
- name: game.text,
55
- region: region,
56
- }
57
- }
52
+ game_links = page.css("div.#{region} a")
53
+ yield game_links.map { |game_link| game(game_link, region) }, index
58
54
  end
59
55
  end
60
- games
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
+ }
61
64
  end
62
65
 
63
66
  def self.install(game)
@@ -66,15 +69,16 @@ module Gamerom
66
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'
67
70
 
68
71
  response = nil
69
- agent.progressbar{
72
+ agent.progressbar do
70
73
  response = agent.get("https://coolrom.com.au/downloader.php?id=#{game.id}")
71
- }
72
- if response.code.to_i == 200
73
- filename = response.filename
74
- FileUtils.mkdir_p(game.filepath)
75
- response.save!("#{game.filepath}/#{filename}")
76
- yield [filename]
77
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]
78
82
  end
79
83
  end
80
84
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'mechanize'
5
+ require 'mechanize/progressbar'
6
+ require 'mechanizeprogress'
7
+
8
+ module Gamerom
9
+ module RepoAdapters
10
+ # Romnation - An adapter for the ROMNation repository website
11
+ class Romnation
12
+ extend Gamerom::RepoAdapter
13
+
14
+ PLATFORM = {
15
+ 'amstrad' => 'Amstrad',
16
+ 'atari2600' => 'Atari 2600',
17
+ 'atari5200' => 'Atari 5200',
18
+ 'atari7800' => 'Atari 7800',
19
+ 'atarijaguar' => 'Atari Jaguar',
20
+ 'atarilynx' => 'Atari Lynx',
21
+ 'colecovision' => 'ColecoVision',
22
+ 'commodore64' => 'Commodore 64',
23
+ 'gamegear' => 'Game Gear',
24
+ 'gb' => 'Game Boy',
25
+ 'gbc' => 'Game Boy Color',
26
+ 'gcdvectrex' => 'Vectrex',
27
+ 'genesis' => 'Genesis',
28
+ 'intellivision' => 'Intellivision',
29
+ 'mame' => 'MAME',
30
+ 'msx1' => 'MSX',
31
+ 'msx2' => 'MSX2',
32
+ 'mtx' => 'MTX',
33
+ 'n64' => 'N64',
34
+ 'neogeocd' => 'Neo Geo CD',
35
+ 'neogeopocket' => 'Neo Geo Pocket',
36
+ 'nes' => 'NES',
37
+ 'oric' => 'Oric',
38
+ 'pce' => 'PC Engine',
39
+ 'radioshackcolorcomputer' => 'TRS-80',
40
+ 'samcoupe' => 'SAM Coupé',
41
+ 'segacd' => 'Sega CD',
42
+ 'segamastersystem' => 'Master System',
43
+ 'snes' => 'SNES',
44
+ 'thompsonmo5' => 'Thomson MO5',
45
+ 'virtualboy' => 'Virtual Boy',
46
+ 'watara' => 'Watara Supervision',
47
+ 'wonderswan' => 'WonderSwan',
48
+ }.freeze
49
+
50
+ def self.platforms
51
+ PLATFORM
52
+ end
53
+
54
+ def self.sections
55
+ ('a'..'z').to_a.unshift('0')
56
+ end
57
+
58
+ def self.extract_games(platform)
59
+ sections.each_with_index do |section, index|
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) })
77
+ end
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
+ }
88
+ end
89
+
90
+ def self.install(game)
91
+ agent = Mechanize.new
92
+ agent.pluggable_parser.default = Mechanize::Download
93
+ 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'
94
+ page = agent.get("https://www.romnation.net/download/rom/#{game.id}")
95
+
96
+ response = nil
97
+ agent.progressbar do
98
+ response = page.link_with(text: 'Download This Rom').click
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]
107
+ end
108
+ end
109
+ end
110
+ end