mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 11:11:10 +02:00
Add manifest-driven required mod imports
This commit is contained in:
@@ -44,6 +44,23 @@ Every mod contains a root `manifest.json` defining its metadata, supported games
|
||||
"optional_dependencies": [
|
||||
"gen1_modern_ui"
|
||||
],
|
||||
"required_imports": [
|
||||
{
|
||||
"id": "stadium2",
|
||||
"name": "Pokemon Stadium 2 ROM",
|
||||
"file": "stadium2.z64",
|
||||
"format": "n64",
|
||||
"md5": ["00000000000000000000000000000000"]
|
||||
}
|
||||
],
|
||||
"optional_imports": [
|
||||
{
|
||||
"id": "bonus_source",
|
||||
"name": "Optional bonus source",
|
||||
"file": "bonus.bin",
|
||||
"md5": "00000000000000000000000000000000"
|
||||
}
|
||||
],
|
||||
"conflicts": [],
|
||||
"permissions": ["engine_internals"],
|
||||
"description": "A brief description of the mod.",
|
||||
@@ -67,6 +84,8 @@ Every mod contains a root `manifest.json` defining its metadata, supported games
|
||||
| `priority` | `integer` | Load priority order (lower numbers load earlier; dependencies always precede dependents regardless of priority). |
|
||||
| `dependencies` | `array` | Hard required dependencies. A mod will not load if a required dependency is missing or disabled for the active game. |
|
||||
| `optional_dependencies` | `array` | Soft dependencies. Guarantees that if the target mod is present and active, it loads *before* this mod without blocking load if absent. |
|
||||
| `required_imports` | `array` | User-supplied files required by this mod. The launcher validates and copies each file into this mod's `baseroms/` directory; the mod does not load while one is missing. |
|
||||
| `optional_imports` | `array` | User-supplied files that unlock optional mod functionality. They use the same validation and private-copy flow but never block the mod from loading. |
|
||||
| `conflicts` / `incompatible` | `array` | List of mod IDs that cannot run concurrently with this mod. |
|
||||
| `permissions` | `array` | Requested privileges (e.g. `["engine_internals"]`, `["network"]`, `["filesystem"]`). |
|
||||
| `github` | `string` | GitHub repository (`"owner/repo"`) used for update checks and dependency download links. |
|
||||
@@ -91,6 +110,28 @@ Dependencies in `dependencies` and `optional_dependencies` can be declared in se
|
||||
#### Version-Scoped Dependencies
|
||||
When a mod supports multiple games (`"games": ["gen1", "gen2"]`), a dependency can specify `"games": ["gen2"]` to indicate it is only required when booting Gen 2. When booting Gen 1, the engine will ignore the dependency, preventing unnecessary boot blocks on games that do not need it.
|
||||
|
||||
### Required user-supplied files
|
||||
|
||||
`required_imports` and `optional_imports` keep copyrighted or otherwise user-owned source material
|
||||
out of mod archives while giving every platform the same installation flow.
|
||||
Each object requires a stable `id`, a display `name`, a destination `file`
|
||||
(a filename, never a path), and one MD5 digest or an array of accepted MD5
|
||||
digests. `format` is either `"raw"` (the default) or `"n64"`.
|
||||
|
||||
For `"n64"`, the launcher recognizes `.z64`, `.v64`, and `.n64` byte orders,
|
||||
strips a recognized 512-byte copier header, converts the bytes to canonical
|
||||
big-endian `.z64` order, and then checks MD5. The canonical bytes are written
|
||||
to `mods/<mod-id>/baseroms/<file>`. Another installed mod with an overlapping
|
||||
accepted MD5 automatically supplies a copy, so the player only selects a ROM
|
||||
once. Mods read the result with their existing scoped `mod:read` API, for
|
||||
example `mod:read("baseroms/stadium2.z64")`; no host path or new filesystem
|
||||
permission is exposed. Missing `required_imports` block the mod before its
|
||||
entry chunk runs; missing `optional_imports` remain visible in the same
|
||||
launcher panel but do not block loading.
|
||||
|
||||
MD5 here identifies a known dump; it is not used as a security or authenticity
|
||||
guarantee. Mod archives must not include anything beneath `baseroms/`.
|
||||
|
||||
## Mods and Gold (Gen 2)
|
||||
|
||||
The mod API is one API across both generations, but Gold runs its own battle
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# RFC 0010 — Manifest-declared user imports for mods
|
||||
|
||||
## Status
|
||||
|
||||
Proposed. Engine: `Manifest.lua`, `RequiredImports.lua`, `Loader.lua`,
|
||||
`LauncherMods.lua`. Launcher: `RomImporter.lua`, `LauncherView.lua`. Native
|
||||
picker bridges: Android and iOS.
|
||||
|
||||
## Motivation
|
||||
|
||||
Some mods derive presentation data from another cartridge the player owns.
|
||||
Shipping those bytes in a mod is not acceptable, while asking each mod to
|
||||
escape the sandbox, implement native pickers, and maintain platform-specific
|
||||
paths defeats the sandbox's purpose. Pokemon Stadium and Pokemon Stadium 2 are
|
||||
the first concrete consumers, but the ownership/import problem is generic.
|
||||
|
||||
## The decision it extends
|
||||
|
||||
This extends the manifest as the engine-owned declaration of mod dependencies
|
||||
and preserves the sandbox rule in `src/mods/Sandbox.lua`: mods do not receive
|
||||
raw host filesystem access. It also extends the launcher's established ROM,
|
||||
save, and mod archive picker/inbox flows instead of introducing a second host
|
||||
integration.
|
||||
|
||||
## Exact manifest delta
|
||||
|
||||
Additive `required_imports` and `optional_imports` arrays are accepted:
|
||||
|
||||
```json
|
||||
{
|
||||
"required_imports": [{
|
||||
"id": "stadium2",
|
||||
"name": "Pokemon Stadium 2 ROM",
|
||||
"file": "stadium2.z64",
|
||||
"format": "n64",
|
||||
"md5": ["00000000000000000000000000000000"]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Both arrays use the same object schema. A missing `required_imports` entry
|
||||
blocks the mod; a missing `optional_imports` entry only leaves that bonus
|
||||
functionality unavailable.
|
||||
|
||||
- `id` is unique within the manifest and uses the mod-id vocabulary.
|
||||
- `name` is launcher-facing text.
|
||||
- `file` is one safe filename below the mod's `baseroms/` directory.
|
||||
- `md5` is one 32-digit hexadecimal digest or a non-empty array of accepted
|
||||
digests. MD5 is a known-dump identity convention, not a trust primitive.
|
||||
- `format` is `raw` by default. `n64` recognizes a canonical big-endian dump,
|
||||
pair-byte-swapped and little-endian-word dumps, with or without a recognized
|
||||
512-byte copier header. Validation and stored output use canonical big-endian
|
||||
bytes.
|
||||
|
||||
The launcher copies a validated file to
|
||||
`mods/<manifest.id>/baseroms/<file>`. Missing required imports make the launcher
|
||||
row need attention and make the loader refuse that enabled mod before its entry
|
||||
chunk runs. Missing optional imports remain selectable without changing load
|
||||
status. A matching validated import owned by another installed mod is
|
||||
copied automatically. Replace and remove remain explicit per-mod actions.
|
||||
|
||||
Desktop and UWP use their existing native/host picker routes. Android and iOS
|
||||
add a `required_import` picker kind which stages
|
||||
`picked_required_import.bin`. NX scans the engine-owned
|
||||
`imports/baseroms/` MTP inbox. All writes continue through `CacheFs`, preserving
|
||||
portable-mode placement.
|
||||
|
||||
`modkit validate` and `modkit pack` report MK307 for every file beneath a
|
||||
source mod's `baseroms/` directory, so the packaging path cannot accidentally
|
||||
distribute a file the launcher placed there. The installer also rejects a mod
|
||||
archive containing `baseroms/` files, covering packages built without modkit.
|
||||
|
||||
## Mod-facing API and sandbox statement
|
||||
|
||||
There is no new runtime API and no new permission. A mod reads its own copied
|
||||
file through the existing `mod:read("baseroms/<file>")` capability. It never
|
||||
learns the selected host path, cannot browse another mod's tree, and receives
|
||||
no raw `io`, `love.filesystem`, or platform-picker access.
|
||||
|
||||
## Migration and compatibility
|
||||
|
||||
Existing manifests omit both import arrays and behave exactly as before. The
|
||||
fields are additive for both manifest API levels. Mods currently maintaining
|
||||
their own cross-platform picker can declare the source file and remove that
|
||||
host integration; their processing code changes only to read the declared
|
||||
`baseroms/<file>` path.
|
||||
|
||||
## Parity tests
|
||||
|
||||
- Empty/absent `required_imports` leaves existing manifests and loader behavior
|
||||
unchanged.
|
||||
- Manifest validation refuses path traversal, duplicate ids/files, malformed
|
||||
MD5 values, and unknown normalization formats.
|
||||
- N64 byte orders and the recognized copier-header form produce identical
|
||||
canonical bytes before MD5 validation.
|
||||
- A mismatched selection is never written.
|
||||
- An enabled mod with a missing declared file never executes its entry chunk.
|
||||
- A matching import in another installed mod is copied into the requesting
|
||||
mod's own `baseroms/` directory.
|
||||
- The packaging gate refuses every `baseroms/` file.
|
||||
- The launcher refuses an archive that contains `baseroms/` files.
|
||||
|
||||
## Deprecation etiquette
|
||||
|
||||
Nothing deprecated or removed.
|
||||
Reference in New Issue
Block a user