mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-16 16:21:30 +02:00
yellow alpha
This commit is contained in:
+64
-53
@@ -37,8 +37,8 @@ local REQUIRED_FILES = {
|
||||
|
||||
-- "Split-screen ROM selector" first-run palette (matches FirstRun.dc.html from
|
||||
-- the Claude Design project): a dark neon arcade panel, one column per game.
|
||||
-- Red is live; Blue and Yellow are lit placeholders until those games are
|
||||
-- supported. Values are 0-255 RGB; alpha is applied per draw.
|
||||
-- Red, Blue, and Yellow share the same importer flow once listed in
|
||||
-- GameVersion.VERSIONS. Values are 0-255 RGB; alpha is applied per draw.
|
||||
local PAL = {
|
||||
-- radial background gradient (bright navy at top-centre -> near black)
|
||||
bgTop = { 22, 34, 74 }, -- #16224a
|
||||
@@ -302,13 +302,13 @@ end
|
||||
-- it directly through love.filesystem -- already mounted at the physfs
|
||||
-- root, so no io.* absolute-path handling is needed.
|
||||
--
|
||||
-- Only a .gb whose SHA maps to a version that is not yet ready counts as
|
||||
-- Only a .gb/.gbc whose SHA maps to a version that is not yet ready counts as
|
||||
-- pending. GameActivity always writes the SAF pick to picked_rom.gb, so a
|
||||
-- naive "first .gb wins" scan would re-import Red when the player tries to
|
||||
-- add Blue (issue #167).
|
||||
-- naive "first ROM wins" scan would re-import Red when the player tries to
|
||||
-- add Blue (issue #167). Yellow carts are typically .gbc.
|
||||
local function findPendingRom(ready)
|
||||
for _, name in ipairs(love.filesystem.getDirectoryItems("")) do
|
||||
if name:lower():match("%.gb$") and love.filesystem.getInfo(name, "file") then
|
||||
if name:lower():match("%.gbc?$") and love.filesystem.getInfo(name, "file") then
|
||||
local data = love.filesystem.read(name)
|
||||
if type(data) == "string" and #data == 1024 * 1024 then
|
||||
local version = GameVersion.forSha1(sha1(data))
|
||||
@@ -360,14 +360,14 @@ local function chooseRom(promptName)
|
||||
local platform = love.system.getOS()
|
||||
if platform == "OS X" then
|
||||
return commandOutput(
|
||||
([[osascript -e 'POSIX path of (choose file with prompt "%s" of type {"gb"})' 2>/dev/null]])
|
||||
([[osascript -e 'POSIX path of (choose file with prompt "%s" of type {"gb", "gbc"})' 2>/dev/null]])
|
||||
:format(prompt))
|
||||
elseif platform == "Windows" then
|
||||
local script = table.concat({
|
||||
"Add-Type -AssemblyName System.Windows.Forms;",
|
||||
"$d=New-Object System.Windows.Forms.OpenFileDialog;",
|
||||
"$d.Title='" .. prompt .. "';",
|
||||
"$d.Filter='Game Boy ROM (*.gb)|*.gb|All files (*.*)|*.*';",
|
||||
"$d.Filter='Game Boy ROM (*.gb;*.gbc)|*.gb;*.gbc|All files (*.*)|*.*';",
|
||||
-- write the pick as UTF-8: the console's OEM codepage would mangle
|
||||
-- non-ASCII names (Pokémon -> Pok\x82mon) and crash any text draw
|
||||
-- that shows them (#325)
|
||||
@@ -377,11 +377,11 @@ local function chooseRom(promptName)
|
||||
'powershell -NoProfile -STA -Command "' .. script .. '"')
|
||||
elseif platform == "Linux" then
|
||||
local path = commandOutput(
|
||||
([[zenity --file-selection --title="%s" --file-filter="Game Boy ROM | *.gb" 2>/dev/null]])
|
||||
([[zenity --file-selection --title="%s" --file-filter="Game Boy ROM | *.gb *.gbc" 2>/dev/null]])
|
||||
:format(prompt))
|
||||
if path then return path end
|
||||
return commandOutput(
|
||||
[[kdialog --getopenfilename "$HOME" "*.gb|Game Boy ROM" 2>/dev/null]])
|
||||
[[kdialog --getopenfilename "$HOME" "*.gb *.gbc|Game Boy ROM" 2>/dev/null]])
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@@ -470,10 +470,11 @@ local function updaterAllowed()
|
||||
return true
|
||||
end
|
||||
|
||||
-- The launcher runs Red and Blue as two independent columns. Each dropped or
|
||||
-- The launcher runs each GameVersion as an independent tab. Each dropped or
|
||||
-- chosen ROM is routed to its version by SHA-1, extracted into that version's
|
||||
-- own cache (Red at the root, Blue under blue/), so both can be imported and
|
||||
-- played side by side. onComplete(version) hands the chosen game off to boot.
|
||||
-- own cache (Red at the root, Blue under blue/, Yellow under yellow/), so all
|
||||
-- can be imported and played side by side. onComplete(version) hands the
|
||||
-- chosen game off to boot.
|
||||
-- opts: launcher (a fresh import stays on the launcher instead of auto-booting),
|
||||
-- forceImport (treat every version as not-yet-imported, so re-import is forced),
|
||||
-- onEditSave(version, slotId) (host handler for the Edit affordance on a save
|
||||
@@ -542,13 +543,18 @@ function RomImporter.new(onComplete, opts)
|
||||
CacheFs.prefix = saved
|
||||
self.returning[version] =
|
||||
(not ready) and marker ~= nil and marker ~= markerFor(version)
|
||||
self.romName[version] = "pokemon_" .. info.id .. ".gb"
|
||||
self.romName[version] = "pokemon_" .. info.id
|
||||
.. (info.id == "yellow" and ".gbc" or ".gb")
|
||||
end
|
||||
|
||||
-- Android: import a save-dir .gb that is not yet ready (USB drop or a
|
||||
-- Android: import a save-dir .gb/.gbc that is not yet ready (USB drop or a
|
||||
-- leftover SAF pick), routed by SHA-1. Already-imported carts are skipped
|
||||
-- so a stale picked_rom.gb cannot block the opposite version.
|
||||
if android and not (self.ready.red and self.ready.blue) then
|
||||
-- so a stale picked_rom.gb cannot block another version.
|
||||
local needRom = false
|
||||
for _, version in ipairs(GameVersion.ORDER) do
|
||||
if not self.ready[version] then needRom = true; break end
|
||||
end
|
||||
if android and needRom then
|
||||
local name, data = findPendingRom(self.ready)
|
||||
if name then self:startData(data, name) end
|
||||
end
|
||||
@@ -608,7 +614,7 @@ function RomImporter:focus(f)
|
||||
local version = self.androidPendingExportVersion or self:_savedropTarget()
|
||||
self.androidPendingExportVersion = nil
|
||||
self.saveNotice[version] = { ok = true, text = "Save exported." }
|
||||
if self.tab == "mods" or self.tab == "yellow" then self.tab = version end
|
||||
if self.tab == "mods" then self.tab = version end
|
||||
return
|
||||
end
|
||||
local modName = findPendingMod(false)
|
||||
@@ -629,9 +635,13 @@ function RomImporter:focus(f)
|
||||
end
|
||||
return
|
||||
end
|
||||
if self.ready.red and self.ready.blue then return end
|
||||
local name, data = findPendingRom(self.ready)
|
||||
if name then self:startData(data, name) end
|
||||
for _, v in ipairs(GameVersion.ORDER) do
|
||||
if not self.ready[v] then
|
||||
local name, data = findPendingRom(self.ready)
|
||||
if name then self:startData(data, name) end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RomImporter:setError(message, version)
|
||||
@@ -660,7 +670,8 @@ local function resetPointerCursor(self)
|
||||
end
|
||||
|
||||
-- Verify + extract a ROM. The version is decided by the ROM's own SHA-1, so
|
||||
-- dropping a Red or Blue cart into either column always lands in the right one.
|
||||
-- dropping a Red, Blue, or Yellow cart into any column always lands in the
|
||||
-- right one.
|
||||
function RomImporter:startData(data, displayName)
|
||||
if self.workState == "working" then return end
|
||||
if type(data) ~= "string" then
|
||||
@@ -676,14 +687,14 @@ function RomImporter:startData(data, displayName)
|
||||
local version = GameVersion.forSha1(actualHash)
|
||||
if not version then
|
||||
self:setError(("Unsupported ROM (SHA-1 %s). Use an unmodified US Pokemon "
|
||||
.. "Red or Blue ROM."):format(actualHash))
|
||||
.. "Red, Blue, or Yellow ROM."):format(actualHash))
|
||||
return
|
||||
end
|
||||
local info = GameVersion.info(version)
|
||||
|
||||
-- Bring the launcher to this version's tab so its progress bar is on screen
|
||||
-- (a dropped cart is routed by SHA-1 regardless of which tab was showing).
|
||||
if self.tab == "red" or self.tab == "blue" or self.tab == "yellow" then
|
||||
if GameVersion.VERSIONS[self.tab] then
|
||||
self.tab = version
|
||||
end
|
||||
self.importing = version
|
||||
@@ -731,7 +742,7 @@ function RomImporter:startData(data, displayName)
|
||||
self.returning[version] = false
|
||||
self.romName[version] = (displayName
|
||||
and (displayName:match("[^/\\]+$") or displayName)) or self.romName[version]
|
||||
-- Android: drop the consumed save-dir .gb (picked_rom.gb or a USB copy)
|
||||
-- Android: drop the consumed save-dir .gb/.gbc (picked_rom.gb or a USB copy)
|
||||
-- so the next Choose / focus cannot treat it as a fresh pending ROM.
|
||||
if self.android and type(displayName) == "string"
|
||||
and not displayName:find("[/\\]") then
|
||||
@@ -845,12 +856,12 @@ function RomImporter:chooseMod()
|
||||
end
|
||||
|
||||
-- Which game a dropped .sav imports into: a .sav has no version signature of
|
||||
-- its own, so it lands on the active game tab. When a non-game tab (mods, or
|
||||
-- the locked yellow placeholder) is showing, default to red -- the always-
|
||||
-- present first game -- rather than guess.
|
||||
-- its own, so it lands on the active game tab. When a non-game tab (mods) is
|
||||
-- showing, default to red -- the always-present first game -- rather than
|
||||
-- guess.
|
||||
function RomImporter:_savedropTarget()
|
||||
local v = self.tab
|
||||
if v == "red" or v == "blue" then return v end
|
||||
if GameVersion.VERSIONS[v] then return v end
|
||||
return "red"
|
||||
end
|
||||
|
||||
@@ -861,8 +872,7 @@ end
|
||||
-- playable with its game's data present.
|
||||
function RomImporter:_importSave(version, source)
|
||||
if self.workState == "working" then return end
|
||||
if self.tab == "red" or self.tab == "blue" or self.tab == "mods"
|
||||
or self.tab == "yellow" then
|
||||
if GameVersion.VERSIONS[self.tab] or self.tab == "mods" then
|
||||
self.tab = version
|
||||
end
|
||||
if not self.ready[version] then
|
||||
@@ -971,8 +981,8 @@ function RomImporter:choose(version)
|
||||
if self.workState == "working" then return end
|
||||
self.chooseVersion = version or "red"
|
||||
if self.android then
|
||||
-- Prefer a not-yet-imported .gb already in the save dir (USB copy, or a
|
||||
-- fresh SAF pick). Never reuse an already-imported cart's file -- that
|
||||
-- Prefer a not-yet-imported .gb/.gbc already in the save dir (USB copy, or
|
||||
-- a fresh SAF pick). Never reuse an already-imported cart's file -- that
|
||||
-- was the #167 failure mode (second Choose just re-extracted Red).
|
||||
local name, data = findPendingRom(self.ready)
|
||||
if name then
|
||||
@@ -995,8 +1005,8 @@ function RomImporter:choose(version)
|
||||
return
|
||||
end
|
||||
-- Handheld Linux (Anbernic stock OS / PortMaster) rarely has zenity or
|
||||
-- kdialog. Fall back to the same "drop a .gb next to the game" scan used
|
||||
-- on Android, which works when the game is launched as an unpacked
|
||||
-- kdialog. Fall back to the same "drop a .gb/.gbc next to the game" scan
|
||||
-- used on Android, which works when the game is launched as an unpacked
|
||||
-- directory (see build-rg34xxsp.sh).
|
||||
local name, data = findPendingRom(self.ready)
|
||||
if name then
|
||||
@@ -1010,13 +1020,13 @@ function RomImporter:choose(version)
|
||||
or "the game folder"
|
||||
self.notice = {
|
||||
version = self.chooseVersion,
|
||||
status = "No file picker. Copy your .gb into:",
|
||||
status = "No file picker. Copy your .gb/.gbc into:",
|
||||
detail = where,
|
||||
}
|
||||
return
|
||||
end
|
||||
if love.system.getOS() ~= "OS X" and love.system.getOS() ~= "Windows" then
|
||||
self:setError("File selection is unavailable here. Drop the .gb file onto the window.")
|
||||
self:setError("File selection is unavailable here. Drop the .gb/.gbc file onto the window.")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1112,7 +1122,7 @@ function RomImporter:_updatePadCursor(dt)
|
||||
local next = (self.modScroll or 0) + step
|
||||
self.modScroll = math.max(0, math.min(maxS, next))
|
||||
end
|
||||
elseif self.tab == "red" or self.tab == "blue" then
|
||||
elseif GameVersion.VERSIONS[self.tab] then
|
||||
local maxS = (self._slotMax and self._slotMax[self.tab]) or 0
|
||||
if maxS > 0 then
|
||||
local next = (self.slotScroll[self.tab] or 0) + step
|
||||
@@ -1138,7 +1148,7 @@ function RomImporter:gamepadpressed(_, button)
|
||||
-- Start / Select: Play if ready, else Choose ROM on the active game tab.
|
||||
if self.workState == "working" then return end
|
||||
local version = self.tab
|
||||
if version == "red" or version == "blue" then
|
||||
if GameVersion.VERSIONS[version] then
|
||||
if self.ready[version] then self:play(version) else self:choose(version) end
|
||||
end
|
||||
end
|
||||
@@ -1972,9 +1982,9 @@ function RomImporter:keypressed(key)
|
||||
if self.workState == "working" then return end
|
||||
if key == "return" or key == "space" or key == "kpenter" then
|
||||
-- Enter acts on the visible game tab: Play if its ROM is ready, otherwise
|
||||
-- open its picker. The mods / placeholder tabs have no keyboard action.
|
||||
-- open its picker. The mods tab has no keyboard action.
|
||||
local version = self.tab
|
||||
if version == "red" or version == "blue" then
|
||||
if GameVersion.VERSIONS[version] then
|
||||
if self.ready[version] then self:play(version) else self:choose(version) end
|
||||
end
|
||||
end
|
||||
@@ -2132,7 +2142,7 @@ function RomImporter:_drawTabBar(x, y, w, h, chip)
|
||||
end
|
||||
cursorX = segEnd + gap
|
||||
end
|
||||
-- "N of 3 ready" (Red + Blue count; Yellow never ready), hidden if no room
|
||||
-- "N of 3 ready" (Red + Blue + Yellow once in GameVersion.ORDER)
|
||||
local ready = 0
|
||||
for _, v in ipairs(GameVersion.ORDER) do if self.ready[v] then ready = ready + 1 end end
|
||||
love.graphics.setFont(self.readyFont)
|
||||
@@ -2152,9 +2162,12 @@ end
|
||||
function RomImporter:_drawGamePanel(version, x, y, w, h)
|
||||
local s, pulse = self._s, self.pulse
|
||||
self.panelVersion = version
|
||||
local locked = version == "yellow"
|
||||
local info = (not locked) and GameVersion.info(version) or nil
|
||||
local gameName = locked and "Pokemon Yellow" or info.displayName
|
||||
-- Defensive: only lock when the version is absent from GameVersion (never
|
||||
-- solely because id == "yellow").
|
||||
local info = GameVersion.info(version)
|
||||
local locked = info == nil
|
||||
local gameName = info and (info.launcherName or info.displayName)
|
||||
or tostring(version)
|
||||
local ready = (not locked) and self.ready[version] or false
|
||||
|
||||
-- header: name + status pill
|
||||
@@ -2191,12 +2204,13 @@ function RomImporter:_drawGamePanel(version, x, y, w, h)
|
||||
local rightX = twoCol and (x + colW + colGap) or x
|
||||
|
||||
-- ROM card contents by state (rehomes the existing import flow)
|
||||
local dropHint = self.android and "Copy the .gb via USB."
|
||||
or Strings("Or drop the .gb file here.")
|
||||
local accent = locked and PAL.gold or (version == "red" and PAL.red or PAL.blue)
|
||||
local dropHint = self.android and "Copy the .gb/.gbc via USB."
|
||||
or Strings("Or drop the .gb/.gbc file here.")
|
||||
local accent = version == "yellow" and PAL.gold
|
||||
or (version == "red" and PAL.red or PAL.blue)
|
||||
local romState, romDetail, romBtnLabel, romBtnEnabled, romProgress
|
||||
if locked then
|
||||
romState, romDetail = "Not supported yet", "Yellow support is on the way."
|
||||
romState, romDetail = "Not supported yet", "Support for this game is on the way."
|
||||
romBtnLabel, romBtnEnabled = "Import unavailable", false
|
||||
else
|
||||
local importing = self.importing == version
|
||||
@@ -2245,7 +2259,6 @@ function RomImporter:_drawGamePanel(version, x, y, w, h)
|
||||
|
||||
-- SAVE FILES card: Import save is live once the ROM is imported (playable);
|
||||
-- Export save is live only when the active slot actually holds a save. The
|
||||
-- locked yellow placeholder has no save backend, so both stay disabled. The
|
||||
-- hint line doubles as the last import/export outcome (green ok / red error).
|
||||
local sfImportEnabled, sfExportEnabled = false, false
|
||||
if not locked then
|
||||
@@ -2358,9 +2371,7 @@ function RomImporter:_drawGamePanel(version, x, y, w, h)
|
||||
self:_playButton(leftX, playY, colW, playH, gameName, ready, locked)
|
||||
|
||||
-- SAVE SLOT card (right column, or stacked below Play when single-column).
|
||||
-- The locked Yellow placeholder has no save backend (no GameVersion entry, so
|
||||
-- no slots can exist); skip the panel entirely rather than draw an empty,
|
||||
-- non-functional "+ New save slot" on a COMING SOON game.
|
||||
-- Skip only when the version is absent from GameVersion (no save backend).
|
||||
if not locked then
|
||||
if twoCol then
|
||||
self:_drawSaveSlotPanel(version, rightX, bodyTop, colW, bodyH)
|
||||
|
||||
Reference in New Issue
Block a user