-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
58 changed files
with
6,234 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# This workflow will build a golang project | ||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go | ||
|
||
name: Go | ||
|
||
on: | ||
push: | ||
branches: [ "main" ] | ||
pull_request: | ||
branches: [ "main" ] | ||
|
||
jobs: | ||
|
||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: '1.23' | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Test | ||
run: go test -v ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,6 @@ go.work.sum | |
|
||
# env file | ||
.env | ||
/pocket-library-editor.iml | ||
/vendor/ | ||
/.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,50 @@ | ||
# pocket-library-editor | ||
# pocket-library-editor | ||
|
||
A rough-and-ready way to edit your Pocket's library. | ||
|
||
## General Warning | ||
|
||
This product is provided as-is. While the chance that it breaks your Pocket is extremely low, it is very much | ||
experimental software that is doing something Analogue was not expecting users to do. Use at your own risk. | ||
|
||
## But Why? | ||
|
||
Because I can get remarkably anal about these things. | ||
|
||
First off: 95% of Pocket users won't need or even want this. | ||
|
||
This software is for the users who are annoyed that their library shows `Famicom Mini 01 - Super Mario Bros.` but also | ||
`Famicom Mini 22: Nazo no Murasame Jou`, that it's `The Lion King` but `NewZealand Story, The`. It's for those users who | ||
have one of the small number of carts that the Pocket misidentifies & who'd rather it appeared in their library under | ||
the correct name. | ||
|
||
## Limitations | ||
|
||
The library info screen for a given cart is stored in the Pocket's internal memory. Even if your library | ||
now shows "Sagaia" instead of "Mani 4 in 1 - Taito", clicking into it or loading the cart will still show you the | ||
original info. | ||
|
||
Additionally, if you have two different entries with the same cart signature, it's likely that only the playtime for the | ||
first will get updated. | ||
|
||
## Troubleshooting | ||
|
||
_"My Pocket freezes or starts crash looping when trying to access the new library"_ | ||
|
||
Something's probably gone wrong with the new library if this happens. From my experience, the solution is to power down | ||
your Pocket (hold the power button until it turns off) and then replace the list.bin and playtimes.bin files on the SD | ||
card with the backup copies you made. | ||
|
||
You did make backups, right? If not, just delete the two files but you'll have to rebuild | ||
your library from scratch after. | ||
|
||
Simply removing the SD card won't solve this problem and powering down is the only way to restore operation. | ||
|
||
## Technical Details | ||
|
||
Analogue has not made the file format for list.bin, playtimes.bin, or *_thumbs.bin available in their developer docs. | ||
The following is what I've managed to decipher & reverse engineer from my copies. | ||
|
||
* [list.bin](./docs/list.md) | ||
* [playtimes.bin](./docs/playtimes.md) | ||
* [*_thumbs.bin](./docs/thumbs.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
- error handling | ||
- can we do the text entry such that it checks each character & cancels if ESC is pressed? (+ Ctrl-C on Unix) | ||
- can we allow left + right arrow keys? Or will that cause trouble with delete? | ||
- can we update _thumbs.bin with a new image if we're adding & it doesn't exist? | ||
- delete the record from _thumbs.bin when we remove? | ||
- check a cart signature for Atari & PCE and see if I can generate it to check for conflicts. (Find unheadered ROMs?) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package main | ||
|
||
import ( | ||
"embed" | ||
"encoding/json" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"slices" | ||
"strings" | ||
|
||
"github.com/g026r/pocket-library-editor/pkg" | ||
"github.com/g026r/pocket-library-editor/pkg/model" | ||
"github.com/g026r/pocket-library-editor/pkg/util" | ||
) | ||
|
||
//go:embed resources/*.json | ||
var jsons embed.FS | ||
|
||
func main() { | ||
var err error | ||
var arg string | ||
switch len(os.Args) { | ||
case 1: | ||
if arg, err = os.Executable(); err != nil { // TODO: Would it be better to use cwd instead? | ||
fatal(err) | ||
} | ||
case 2: | ||
arg = os.Args[1] | ||
default: | ||
printUsage() | ||
os.Exit(2) | ||
} | ||
|
||
app, err := loadPocketDir(arg) | ||
if err != nil { | ||
fatal(err) | ||
} | ||
|
||
if c, err := model.LoadConfig(); err == nil { | ||
app.Config = c | ||
} | ||
|
||
if app.ShowAdd { // Only need to load these for the add UI | ||
library, err := loadInternal() | ||
if err != nil { | ||
fatal(err) | ||
} | ||
app.Internal = library | ||
} | ||
|
||
if err := app.Run(); err != nil { | ||
fatal(err) | ||
} | ||
} | ||
|
||
func fatal(err error) { | ||
fmt.Printf("\nFATAL ERROR: %v\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
func printUsage() { | ||
fmt.Println("Usage: place in the root of your Pocket's SD card & run. Or run & pass it the path to the SD card root as an argument.") | ||
fmt.Println("Outputs files in the current working directory.") | ||
} | ||
|
||
func loadPocketDir(d string) (pkg.Application, error) { | ||
d, err := filepath.Abs(d) | ||
if err != nil { | ||
return pkg.Application{}, err | ||
} | ||
fi, err := os.Stat(d) | ||
if err != nil { | ||
return pkg.Application{}, err | ||
} else if !fi.IsDir() { | ||
return pkg.Application{}, fmt.Errorf("%s is not a directory", d) | ||
} | ||
|
||
root := os.DirFS(d) | ||
|
||
pg, err := fs.Sub(root, "System/Played Games") | ||
if err != nil { | ||
return pkg.Application{}, nil | ||
} | ||
entries, err := model.ReadEntries(pg) | ||
if err != nil { | ||
return pkg.Application{}, err | ||
} | ||
|
||
playtimes, err := model.ReadPlayTimes(pg) | ||
if err != nil { | ||
return pkg.Application{}, err | ||
} | ||
if len(playtimes) != len(entries) { | ||
return pkg.Application{}, fmt.Errorf("entry count mismatch between list.bin [%d] & playtimes.bin [%d]", len(entries), len(playtimes)) | ||
} | ||
|
||
tb, err := fs.Sub(root, "System/Library/Images") | ||
if err != nil { | ||
return pkg.Application{}, nil | ||
} | ||
thumbs, err := model.LoadThumbnails(tb) | ||
if err != nil { | ||
return pkg.Application{}, err | ||
} | ||
|
||
return pkg.Application{ | ||
RootDir: root, | ||
Entries: entries, | ||
PlayTimes: playtimes, | ||
Thumbs: thumbs, | ||
Config: model.Config{ | ||
RemoveImages: true, | ||
AdvancedEditing: false, | ||
ShowAdd: true, | ||
}, | ||
}, nil | ||
} | ||
|
||
func loadInternal() (map[util.System][]model.Entry, error) { | ||
dir, err := jsons.ReadDir("resources") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
library := make(map[util.System][]model.Entry) | ||
for _, d := range dir { | ||
f, err := jsons.ReadFile(fmt.Sprintf("resources/%s", d.Name())) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var x []jsonEntry | ||
if err := json.Unmarshal(f, &x); err != nil { | ||
return nil, err | ||
} | ||
sys, err := util.Parse(strings.TrimSuffix(d.Name(), ".json")) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Oh, for a native map function | ||
e := make([]model.Entry, len(x)) | ||
for i := range x { | ||
e[i] = x[i].Entry() | ||
} | ||
|
||
slices.SortFunc(e, model.EntrySort) | ||
library[sys] = e | ||
} | ||
|
||
return library, nil | ||
} | ||
|
||
type jsonEntry struct { | ||
util.System `json:"system"` | ||
Crc32 string `json:"crc"` | ||
Sig string `json:"sig"` | ||
Magic string `json:"magic"` // TODO: Work out all possible mappings for this | ||
Name string `json:"name"` | ||
} | ||
|
||
func (j jsonEntry) Entry() model.Entry { | ||
e := model.Entry{ | ||
Name: j.Name, | ||
System: j.System, | ||
} | ||
e.Sig, _ = util.HexStringTransform(j.Sig) | ||
e.Magic, _ = util.HexStringTransform(j.Magic) | ||
e.Crc32, _ = util.HexStringTransform(j.Crc32) | ||
return e | ||
} |
Oops, something went wrong.