mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-21 13:09:54 +02:00
anbernic
This commit is contained in:
@@ -31,6 +31,40 @@ The packaged app contains neither a ROM nor pre-extracted game data. Music,
|
||||
sound effects, and cries are synthesized while the game runs from compact
|
||||
audio channel programs copied out of the verified ROM.
|
||||
|
||||
## Anbernic RG34XXSP (Stock OS 64-bit MOD)
|
||||
|
||||
Download `gen1recomp-*-rg34xxsp-stockos64-mod.zip` from
|
||||
[Releases](https://github.com/bryanthaboi/gen1recomp/releases). This build
|
||||
targets **Stock OS 64-bit MOD** on the RG34XXSP with PortMaster installed
|
||||
(TF1).
|
||||
|
||||
1. Unzip the release on your computer. You get `Gen1recomp.sh` and a
|
||||
`gen1recomp/` folder.
|
||||
2. Copy **both** onto the SD card under **`Roms/PORTS/`** so the layout is:
|
||||
|
||||
```
|
||||
Roms/PORTS/Gen1recomp.sh
|
||||
Roms/PORTS/gen1recomp/
|
||||
```
|
||||
|
||||
On the device that path is `/mnt/mmc/Roms/PORTS/`. Keep the launcher and
|
||||
the `gen1recomp/` folder as siblings — do not nest the `.sh` inside the
|
||||
folder.
|
||||
3. Put your legal US Red and/or Blue `.gb` files inside the game folder:
|
||||
|
||||
```
|
||||
Roms/PORTS/gen1recomp/lovegame/
|
||||
```
|
||||
|
||||
Example: `Roms/PORTS/gen1recomp/lovegame/Pokemon - Red Version.gb`
|
||||
4. Eject the card, boot the handheld, open **Ports → Gen1recomp**.
|
||||
5. On the launcher, move the cursor with the D-pad or left stick, press **A**
|
||||
to click. Choose the Red or Blue tab, then **Choose ROM** — with no file
|
||||
picker on stock OS, that scans `lovegame/` for the `.gb` you dropped in.
|
||||
|
||||
After import, saves and the ROM-derived cache stay next to the game on the
|
||||
SD card (`portable.txt`).
|
||||
|
||||
## Controls
|
||||
|
||||
|
||||
|
||||
+29
-16
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a PortMaster-style aarch64 port of gen1recomp for Anbernic RG34XXSP
|
||||
# stock OS (Allwinner H700, 64-bit Stock OS / Stock OS Mod + PortMaster).
|
||||
# on Stock OS 64-bit MOD (H700; cbepx-me StockOS MOD / official V1.0.6 base
|
||||
# + PortMaster).
|
||||
#
|
||||
# The stock Anbernic firmware resolves ports relative to the launch script
|
||||
# (roms/PORTS/...), not PortMaster's /$directory/ports/... path. This pack
|
||||
@@ -11,10 +12,10 @@
|
||||
# ./build-rg34xxsp.sh [--version X.Y.Z]
|
||||
#
|
||||
# Output:
|
||||
# dist/rg34xxsp/gen1recomp-rg34xxsp.zip
|
||||
# dist/rg34xxsp/gen1recomp-rg34xxsp-stockos64-mod.zip
|
||||
#
|
||||
# Install on device:
|
||||
# 1. Flash / run 64-bit Stock OS (or Stock OS Mod) with PortMaster installed.
|
||||
# 1. Flash / run Stock OS 64-bit MOD with PortMaster installed.
|
||||
# 2. Unzip into the SD card's roms/PORTS/ folder so you have:
|
||||
# roms/PORTS/Gen1recomp.sh
|
||||
# roms/PORTS/gen1recomp/...
|
||||
@@ -31,6 +32,10 @@ WORK="$HERE/work/rg34xxsp"
|
||||
DIST="$ROOT/dist/rg34xxsp"
|
||||
|
||||
APP_NAME="gen1recomp"
|
||||
# Artifact suffix matches the CFW this port targets (Anbernic H700 Stock OS
|
||||
# 64-bit MOD for RG34XXSP). Release uploads stage it as
|
||||
# gen1recomp-<ver>-rg34xxsp-stockos64-mod.zip.
|
||||
ARTIFACT_SUFFIX="rg34xxsp-stockos64-mod"
|
||||
PORT_DIR_NAME="gen1recomp"
|
||||
LAUNCHER_NAME="Gen1recomp.sh"
|
||||
LOVE_VERSION="11.5"
|
||||
@@ -235,7 +240,7 @@ cat > "$PORT_ROOT/port.json" <<EOF
|
||||
"attr": {
|
||||
"title": "gen1recomp",
|
||||
"desc": "Native LÖVE2D recreation of Pokemon Red and Blue. Supply your own legal US Red or Blue ROM.",
|
||||
"inst": "Copy a canonical US Red or Blue .gb into gen1recomp/lovegame/, then launch and press Choose ROM. Requires 64-bit Stock OS with PortMaster.",
|
||||
"inst": "Requires Anbernic RG34XXSP Stock OS 64-bit MOD with PortMaster. Copy a canonical US Red or Blue .gb into gen1recomp/lovegame/, then launch and press Choose ROM.",
|
||||
"genres": ["adventure", "rpg"],
|
||||
"porter": ["gen1recomp"],
|
||||
"image": {},
|
||||
@@ -263,18 +268,30 @@ cat > "$PORT_ROOT/gameinfo.xml" <<EOF
|
||||
EOF
|
||||
|
||||
cat > "$PORT_ROOT/README.md" <<'EOF'
|
||||
## gen1recomp (RG34XXSP / Anbernic stock OS)
|
||||
## gen1recomp (RG34XXSP / Stock OS 64-bit MOD)
|
||||
|
||||
Native LÖVE 11.5 port of gen1recomp for Anbernic RG34XXSP (H700) 64-bit stock OS
|
||||
with PortMaster.
|
||||
Native LÖVE 11.5 port of gen1recomp for Anbernic RG34XXSP on
|
||||
**Stock OS 64-bit MOD** (H700, PortMaster).
|
||||
|
||||
### Install
|
||||
|
||||
1. Use **64-bit Stock OS** (or Stock OS Mod) with PortMaster installed.
|
||||
1. Use **Stock OS 64-bit MOD** with PortMaster installed (TF1).
|
||||
2. Unzip so `Gen1recomp.sh` and the `gen1recomp/` folder sit in `roms/PORTS/`.
|
||||
3. Copy a legal US Pokemon Red or Blue `.gb` into `gen1recomp/lovegame/`.
|
||||
4. Refresh Ports / restart EmulationStation and launch **Gen1recomp**.
|
||||
|
||||
### Controls (launcher)
|
||||
|
||||
| Input | Action |
|
||||
|--|--|
|
||||
| D-pad / left stick | Move cursor |
|
||||
| A | Click |
|
||||
| L1 / R1 | Switch tabs |
|
||||
| Right stick | Scroll lists |
|
||||
| Start / Select | Play or Choose ROM |
|
||||
|
||||
In-game controls use the normal PortMaster / SDL pad map (rebind under OPTIONS → CONTROLS).
|
||||
|
||||
### First run
|
||||
|
||||
Stock OS has no zenity file picker. Put the `.gb` in `lovegame/`, then press
|
||||
@@ -286,23 +303,19 @@ Canonical SHA-1 (1 MiB US carts only):
|
||||
- Red: `ea9bcae617fdf159b045185467ae58b2e4a48b9a`
|
||||
- Blue: `d7037c83e1ae5b39bde3c30787637ba1d4c48ce2`
|
||||
|
||||
### Controls
|
||||
|
||||
Physical controls map through PortMaster / SDL. Rebind in-game under
|
||||
OPTIONS → CONTROLS.
|
||||
|
||||
### Thanks
|
||||
|
||||
LÖVE runtime binaries from [PortMaster](https://portmaster.games/).
|
||||
Stock OS 64-bit MOD: [cbepx-me](https://github.com/cbepx-me/Anbernic-H700-RG-xx-StockOS-Modification).
|
||||
EOF
|
||||
|
||||
# --------------------------------------------------------------- zip
|
||||
ZIP_OUT="$DIST/$APP_NAME-rg34xxsp.zip"
|
||||
rm -f "$ZIP_OUT"
|
||||
ZIP_OUT="$DIST/$APP_NAME-$ARTIFACT_SUFFIX.zip"
|
||||
rm -f "$ZIP_OUT" "$DIST/$APP_NAME-rg34xxsp.zip"
|
||||
say "packing $ZIP_OUT"
|
||||
(cd "$PORT_ROOT" && zip -q -9 -r "$ZIP_OUT" \
|
||||
"$LAUNCHER_NAME" "$PORT_DIR_NAME" port.json gameinfo.xml README.md)
|
||||
|
||||
say "done."
|
||||
say "artifact: $ZIP_OUT ($(du -h "$ZIP_OUT" | cut -f1))"
|
||||
say "copy onto the RG34XXSP SD card under roms/PORTS/, then drop your .gb into gen1recomp/lovegame/"
|
||||
say "copy onto the RG34XXSP SD card under roms/PORTS/ (Stock OS 64-bit MOD), then drop your .gb into gen1recomp/lovegame/"
|
||||
|
||||
@@ -315,19 +315,19 @@ end
|
||||
|
||||
function love.gamepadpressed(joystick, button)
|
||||
if editorMode then return end
|
||||
if Importer then return end
|
||||
if Importer then return Importer:gamepadpressed(joystick, button) end
|
||||
Game:gamepadpressed(joystick, button)
|
||||
end
|
||||
|
||||
function love.gamepadreleased(joystick, button)
|
||||
if editorMode then return end
|
||||
if Importer then return end
|
||||
if Importer then return Importer:gamepadreleased(joystick, button) end
|
||||
Game:gamepadreleased(joystick, button)
|
||||
end
|
||||
|
||||
function love.gamepadaxis(joystick, axis, value)
|
||||
if editorMode then return end
|
||||
if Importer then return end
|
||||
if Importer then return Importer:gamepadaxis(joystick, axis, value) end
|
||||
Game:gamepadaxis(joystick, axis, value)
|
||||
end
|
||||
|
||||
|
||||
+176
-5
@@ -519,6 +519,14 @@ function RomImporter.new(onComplete, opts)
|
||||
-- Android SAF create-document: which game's SAVE FILES card should show
|
||||
-- "Save exported." when export_done.flag appears on focus.
|
||||
androidPendingExportVersion = nil,
|
||||
-- Virtual pointer for handhelds / gamepads (Anbernic stock OS has no
|
||||
-- mouse). D-pad + left stick move it; A clicks; shoulders cycle tabs;
|
||||
-- right stick scrolls the save-slot / mods lists.
|
||||
_padCursor = { x = 0, y = 0 },
|
||||
_padCursorActive = false,
|
||||
_padAxis = { leftx = 0, lefty = 0, righty = 0 },
|
||||
_padDir = {},
|
||||
_padInited = false,
|
||||
}, RomImporter)
|
||||
|
||||
for _, version in ipairs(GameVersion.ORDER) do
|
||||
@@ -571,6 +579,15 @@ function RomImporter.new(onComplete, opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- On Linux handhelds a gamepad is usually already connected at boot; arm
|
||||
-- the virtual cursor immediately so the player does not have to press a
|
||||
-- button before seeing something move.
|
||||
if self.launcher and love.system.getOS() == "Linux"
|
||||
and love.joystick and love.joystick.getJoystickCount
|
||||
and love.joystick.getJoystickCount() > 0 then
|
||||
self:_activatePadCursor()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1004,6 +1021,7 @@ end
|
||||
|
||||
function RomImporter:update(dt)
|
||||
self.pulse = self.pulse + dt
|
||||
self:_updatePadCursor(dt)
|
||||
if self.workState ~= "working" or not self.worker then return end
|
||||
local started = love.timer.getTime()
|
||||
repeat
|
||||
@@ -1020,6 +1038,125 @@ function RomImporter:update(dt)
|
||||
until love.timer.getTime() - started >= 0.008
|
||||
end
|
||||
|
||||
-- ------- gamepad virtual cursor (handheld / PortMaster) --------------------
|
||||
local PAD_DEAD = 0.28
|
||||
local PAD_SPEED = 560 -- px/s at full stick deflection
|
||||
local PAD_DPAD_SPEED = 420
|
||||
|
||||
function RomImporter:_activatePadCursor()
|
||||
if self._padCursorActive then return end
|
||||
local w, h = love.graphics.getDimensions()
|
||||
if not self._padInited then
|
||||
self._padCursor.x = w * 0.5
|
||||
self._padCursor.y = h * 0.45
|
||||
self._padInited = true
|
||||
end
|
||||
self._padCursorActive = true
|
||||
end
|
||||
|
||||
function RomImporter:_cycleTab(delta)
|
||||
local order = { "red", "blue", "yellow", "mods" }
|
||||
local idx = 1
|
||||
for i, id in ipairs(order) do
|
||||
if id == self.tab then idx = i; break end
|
||||
end
|
||||
idx = ((idx - 1 + delta) % #order) + 1
|
||||
self.tab = order[idx]
|
||||
self._slotPress = nil
|
||||
self._modPress = nil
|
||||
end
|
||||
|
||||
function RomImporter:_updatePadCursor(dt)
|
||||
-- Real mouse motion yields the pad cursor so desktop users keep a normal
|
||||
-- pointer after bumping a stick once.
|
||||
local mx, my = love.mouse.getPosition()
|
||||
if self._lastMouseX and self._padCursorActive then
|
||||
if math.abs(mx - self._lastMouseX) > 3 or math.abs(my - self._lastMouseY) > 3 then
|
||||
self._padCursorActive = false
|
||||
end
|
||||
end
|
||||
self._lastMouseX, self._lastMouseY = mx, my
|
||||
|
||||
local ax = self._padAxis.leftx or 0
|
||||
local ay = self._padAxis.lefty or 0
|
||||
local dx, dy = 0, 0
|
||||
if math.abs(ax) > PAD_DEAD then dx = dx + ax end
|
||||
if math.abs(ay) > PAD_DEAD then dy = dy + ay end
|
||||
if self._padDir.dpleft then dx = dx - 1 end
|
||||
if self._padDir.dpright then dx = dx + 1 end
|
||||
if self._padDir.dpup then dy = dy - 1 end
|
||||
if self._padDir.dpdown then dy = dy + 1 end
|
||||
|
||||
if dx ~= 0 or dy ~= 0 then
|
||||
self:_activatePadCursor()
|
||||
local mag = math.sqrt(dx * dx + dy * dy)
|
||||
if mag > 1 then dx, dy = dx / mag, dy / mag end
|
||||
local speed = (math.abs(ax) > PAD_DEAD or math.abs(ay) > PAD_DEAD)
|
||||
and PAD_SPEED or PAD_DPAD_SPEED
|
||||
local w, h = love.graphics.getDimensions()
|
||||
local nx = self._padCursor.x + dx * speed * dt
|
||||
local ny = self._padCursor.y + dy * speed * dt
|
||||
self._padCursor.x = math.max(0, math.min(w, nx))
|
||||
self._padCursor.y = math.max(0, math.min(h, ny))
|
||||
end
|
||||
|
||||
-- Right stick scrolls the active list (save slots or mods).
|
||||
local ry = self._padAxis.righty or 0
|
||||
if math.abs(ry) > PAD_DEAD then
|
||||
self:_activatePadCursor()
|
||||
local step = -ry * 480 * dt
|
||||
if self.tab == "mods" then
|
||||
local maxS = self._modMax or 0
|
||||
if maxS > 0 then
|
||||
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
|
||||
local maxS = (self._slotMax and self._slotMax[self.tab]) or 0
|
||||
if maxS > 0 then
|
||||
local next = (self.slotScroll[self.tab] or 0) + step
|
||||
self.slotScroll[self.tab] = math.max(0, math.min(maxS, next))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RomImporter:gamepadpressed(_, button)
|
||||
self:_activatePadCursor()
|
||||
if button == "a" then
|
||||
-- Instant click at the virtual pointer (same path as a mouse/touch tap).
|
||||
self:mousepressed(self._padCursor.x, self._padCursor.y, 1)
|
||||
elseif button == "leftshoulder" then
|
||||
self:_cycleTab(-1)
|
||||
elseif button == "rightshoulder" then
|
||||
self:_cycleTab(1)
|
||||
elseif button == "dpup" or button == "dpdown"
|
||||
or button == "dpleft" or button == "dpright" then
|
||||
self._padDir[button] = true
|
||||
elseif button == "start" or button == "back" then
|
||||
-- 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 self.ready[version] then self:play(version) else self:choose(version) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RomImporter:gamepadreleased(_, button)
|
||||
if button == "dpup" or button == "dpdown"
|
||||
or button == "dpleft" or button == "dpright" then
|
||||
self._padDir[button] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function RomImporter:gamepadaxis(_, axis, value)
|
||||
if axis == "leftx" or axis == "lefty" or axis == "righty" then
|
||||
self._padAxis[axis] = value
|
||||
if math.abs(value) > PAD_DEAD then self:_activatePadCursor() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Player pressed Play on a game whose ROM is imported: hand off to boot.
|
||||
function RomImporter:play(version)
|
||||
if self.workState == "working" then return end
|
||||
@@ -1214,12 +1351,17 @@ function RomImporter:draw()
|
||||
local pulse = self.pulse
|
||||
self._s = s
|
||||
|
||||
-- Hover state (desktop only -- touch has no cursor). Panel methods read the
|
||||
-- pointer + set self._anyHover through self:_hover; the cursor is set at the
|
||||
-- end. Reset the per-frame hit rects so a tab with no controls (mods) cannot
|
||||
-- Hover state. Desktop mouse, or the gamepad virtual cursor on handhelds
|
||||
-- (Android stays touch-only -- no hover). Panel methods read the pointer +
|
||||
-- set self._anyHover through self:_hover; the cursor is set at the end.
|
||||
-- Reset the per-frame hit rects so a tab with no controls (mods) cannot
|
||||
-- inherit last frame's game-panel buttons.
|
||||
if self._padCursorActive then
|
||||
self._mx, self._my = self._padCursor.x, self._padCursor.y
|
||||
else
|
||||
self._mx, self._my = love.mouse.getPosition()
|
||||
self._hoverEnabled = not self.android
|
||||
end
|
||||
self._hoverEnabled = self._padCursorActive or not self.android
|
||||
self._anyHover = false
|
||||
self.romButtonRect = nil
|
||||
self.playButtonRect = nil
|
||||
@@ -1578,7 +1720,8 @@ function RomImporter:draw()
|
||||
self:_updateSlotDrag()
|
||||
|
||||
-- pointer cursor over any interactive element (desktop only)
|
||||
if self._hoverEnabled and love.mouse.isCursorSupported and love.mouse.isCursorSupported() then
|
||||
if self._hoverEnabled and not self._padCursorActive
|
||||
and love.mouse.isCursorSupported and love.mouse.isCursorSupported() then
|
||||
if self._anyHover then
|
||||
self.handCursor = self.handCursor or love.mouse.getSystemCursor("hand")
|
||||
love.mouse.setCursor(self.handCursor)
|
||||
@@ -1586,6 +1729,34 @@ function RomImporter:draw()
|
||||
resetPointerCursor(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- Gamepad virtual cursor (drawn last so it sits above the CRT overlay).
|
||||
if self._padCursorActive then
|
||||
local x, y = self._padCursor.x, self._padCursor.y
|
||||
local hot = self._anyHover
|
||||
love.graphics.push("all")
|
||||
love.graphics.origin()
|
||||
love.graphics.setLineWidth(1)
|
||||
-- Drop shadow
|
||||
love.graphics.setColor(0, 0, 0, 0.45)
|
||||
love.graphics.polygon("fill",
|
||||
x + 2, y + 2, x + 2, y + 22, x + 8, y + 16, x + 14, y + 26,
|
||||
x + 18, y + 24, x + 11, y + 14, x + 20, y + 14)
|
||||
-- Pointer body
|
||||
if hot then
|
||||
love.graphics.setColor(0.25, 0.95, 0.55, 1)
|
||||
else
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
love.graphics.polygon("fill",
|
||||
x, y, x, y + 20, x + 6, y + 14, x + 12, y + 24,
|
||||
x + 16, y + 22, x + 9, y + 12, x + 18, y + 12)
|
||||
love.graphics.setColor(0.05, 0.07, 0.12, 1)
|
||||
love.graphics.polygon("line",
|
||||
x, y, x, y + 20, x + 6, y + 14, x + 12, y + 24,
|
||||
x + 16, y + 22, x + 9, y + 12, x + 18, y + 12)
|
||||
love.graphics.pop()
|
||||
end
|
||||
end
|
||||
|
||||
local function inside(r, x, y)
|
||||
|
||||
Reference in New Issue
Block a user