badline 0.7.0 → 0.8.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.
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module Media
5
+ # The disks of a set a disk image belongs to, found by name in its
6
+ # folder: images whose names differ only in their disk, side or part
7
+ # markers, such as "Disk 1", "Side B", "d2", "(Disk 1 of 3)", ordered
8
+ # by those markers. A name with no marker can end in a number or a
9
+ # letter, as in game_1.d64 and game_2.d64, which counts only when the
10
+ # folder has the first of the set too.
11
+ module DiskSet
12
+ KINDS = %w[disk disc side part].freeze
13
+ EXTENSIONS = %w[.d64 .d71 .d81 .g64].freeze
14
+
15
+ # The paths of the set's disks in order, or just `path` when it
16
+ # belongs to none.
17
+ def self.around(path)
18
+ extension = File.extname(path).downcase
19
+ return [path] unless EXTENSIONS.include?(extension)
20
+
21
+ marked = !parse(stem(path))[1].negative?
22
+ base = key(stem(path), marked)[0]
23
+ found = siblings(File.dirname(path), extension, base, marked)
24
+ return [path] if found.size < 2 || (!marked && found.none? { |pair| pair[0] == 1 })
25
+
26
+ found.sort_by { |pair| pair[0] }.map { |pair| File.join(File.dirname(path), pair[1]) }
27
+ end
28
+
29
+ # The name's words without their markers, joined, and the order the
30
+ # markers give it, -1 for none.
31
+ def self.parse(stem)
32
+ words = words(stem)
33
+ base = []
34
+ values = []
35
+ index = 0
36
+ index = take(words, index, base, values) while index < words.size
37
+ [base.join(" "), order(values)]
38
+ end
39
+
40
+ # The words before a trailing number or letter, joined, and its
41
+ # order, -1 for a name that doesn't end in one.
42
+ def self.trailing(stem)
43
+ words = words(stem)
44
+ last = words.last.to_s
45
+ return ["", -1] if words.size < 2 || !value?(last) || last.length > 2
46
+
47
+ [words[0, words.size - 1].join(" "), value(last)]
48
+ end
49
+
50
+ def self.words(stem) = stem.downcase.split(/[^a-z0-9]+/).reject(&:empty?)
51
+
52
+ def self.stem(path) = File.basename(path, File.extname(path))
53
+
54
+ def self.key(stem, marked) = marked ? parse(stem) : trailing(stem)
55
+
56
+ # The order and name of each image in the folder in the set.
57
+ def self.siblings(directory, extension, base, marked)
58
+ found = []
59
+ children(directory).each do |name|
60
+ next unless File.extname(name).downcase == extension
61
+
62
+ name_key = key(stem(name), marked)
63
+ found << [name_key[1], name] if name_key[0] == base && !name_key[1].negative?
64
+ end
65
+ found
66
+ end
67
+
68
+ # Adds the word at `index` to the base, or its marker's value to the
69
+ # values, and returns the index of the next word.
70
+ def self.take(words, index, base, values)
71
+ word = words[index]
72
+ following = words[index + 1].to_s
73
+ if KINDS.include?(word) && value?(following)
74
+ values << value(following)
75
+ index += 2
76
+ index += 2 if words[index] == "of" && words[index + 1].to_s.match?(/\A\d+\z/)
77
+ index
78
+ elsif marker?(word)
79
+ values << value(word.sub(/\A(disk|disc|side|part|d|s)/, ""))
80
+ index + 1
81
+ else
82
+ base << word
83
+ index + 1
84
+ end
85
+ end
86
+
87
+ def self.marker?(word) = word.match?(/\A(disk|disc|side|part)(\d+|[a-z])\z/) || word.match?(/\A[ds]\d+\z/)
88
+
89
+ def self.value?(word) = word.match?(/\A(\d+|[a-z])\z/)
90
+
91
+ # A number counts as itself and a letter by its place in the
92
+ # alphabet, so side A is 1.
93
+ def self.value(word) = word.match?(/\A\d+\z/) ? word.to_i : word.ord - 96
94
+
95
+ def self.order(values)
96
+ return -1 if values.empty?
97
+
98
+ values.reduce(0) { |order, value| (order * 1000) + value }
99
+ end
100
+
101
+ def self.children(directory)
102
+ Dir.children(directory)
103
+ rescue SystemCallError
104
+ []
105
+ end
106
+ end
107
+ end
108
+ end
data/lib/badline/media.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "badline/media/true_drive"
4
4
  require "badline/media/queue"
5
+ require "badline/media/disk_set"
5
6
 
6
7
  module Badline
7
8
  module Media
@@ -16,7 +16,7 @@ module Badline
16
16
  Option.new("--version", "Show the version and what built it", section: "Options", build: :native),
17
17
  Option.new("--no-autostart", "Boot to READY. instead of running the program",
18
18
  section: "Window options", needs: :window),
19
- Option.new("--read-only", "Mount a disk image write-protected, leaving its file unchanged",
19
+ Option.new("--writable", "Let the machine write to disk images, changing their files",
20
20
  section: "Window options", needs: :window),
21
21
  Option.new("--sound", "Play the SID through the host's audio device (F10 mutes)",
22
22
  section: "Window options", needs: :window),
@@ -61,7 +61,7 @@ module Badline
61
61
  @sid_model = nil
62
62
  @reu = nil
63
63
  @autostart = true
64
- @read_only = false
64
+ @writable = false
65
65
  @sound = native
66
66
  @frames = 0
67
67
  @paced = true
@@ -135,7 +135,7 @@ module Badline
135
135
 
136
136
  def autostart? = @autostart
137
137
 
138
- def read_only? = @read_only
138
+ def writable? = @writable
139
139
 
140
140
  def sound? = @sound
141
141
 
@@ -238,7 +238,7 @@ module Badline
238
238
  def switch(name)
239
239
  case name
240
240
  when "--no-autostart" then @autostart = false
241
- when "--read-only" then @read_only = true
241
+ when "--writable" then @writable = true
242
242
  when "--sound" then @sound = true
243
243
  when "--no-sound" then @sound = false
244
244
  when "--no-vsync" then @vsync = false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Badline
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: badline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -41,7 +41,11 @@ files:
41
41
  - MIT-LICENSE
42
42
  - README.md
43
43
  - SECURITY.md
44
+ - doc/library.md
45
+ - doc/media.md
44
46
  - doc/pinned-behaviour.md
47
+ - doc/sid-player.md
48
+ - doc/snapshots.md
45
49
  - exe/badline-ruby
46
50
  - lib/badline.rb
47
51
  - lib/badline/address_bus.rb
@@ -137,16 +141,23 @@ files:
137
141
  - lib/badline/frontend/audio_sink.rb
138
142
  - lib/badline/frontend/boot.rb
139
143
  - lib/badline/frontend/buttons.rb
144
+ - lib/badline/frontend/confirmation.rb
140
145
  - lib/badline/frontend/controls.rb
141
146
  - lib/badline/frontend/drive_led.rb
147
+ - lib/badline/frontend/file_browser.rb
142
148
  - lib/badline/frontend/filter_view.rb
143
149
  - lib/badline/frontend/frame_rate.rb
150
+ - lib/badline/frontend/frame_report.rb
144
151
  - lib/badline/frontend/gamepads.rb
145
152
  - lib/badline/frontend/info_view.rb
146
153
  - lib/badline/frontend/keys.rb
154
+ - lib/badline/frontend/menu_dialogs.rb
155
+ - lib/badline/frontend/menu_media.rb
156
+ - lib/badline/frontend/menu_pages.rb
147
157
  - lib/badline/frontend/pacer.rb
148
158
  - lib/badline/frontend/pad_port.rb
149
159
  - lib/badline/frontend/painter.rb
160
+ - lib/badline/frontend/pause_menu.rb
150
161
  - lib/badline/frontend/player_commands.rb
151
162
  - lib/badline/frontend/player_footer.rb
152
163
  - lib/badline/frontend/player_header.rb
@@ -197,6 +208,7 @@ files:
197
208
  - lib/badline/keyboard.rb
198
209
  - lib/badline/keyboard_buffer.rb
199
210
  - lib/badline/media.rb
211
+ - lib/badline/media/disk_set.rb
200
212
  - lib/badline/media/queue.rb
201
213
  - lib/badline/media/true_drive.rb
202
214
  - lib/badline/memory.rb