Skip to content

Commit

Permalink
Add F-Zero and Phantasy Star Online patches
Browse files Browse the repository at this point in the history
Based on code in GCMM.

Fixes #2
  • Loading branch information
bodgit committed Jan 9, 2023
1 parent 46915b8 commit 22d7342
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 1 deletion.
12 changes: 12 additions & 0 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ func (h *header) blocks() int {
return int(h.CardSize) << 4 //nolint:gomnd
}

func (h *header) serialNumbers() (uint32, uint32) {
buf := new(bytes.Buffer)
buf.Grow(binary.Size(h))

_ = binary.Write(buf, binary.BigEndian, h)

serial := make([]uint32, 8) //nolint:gomnd
_ = binary.Read(buf, binary.BigEndian, &serial)

return serial[0] ^ serial[2] ^ serial[4] ^ serial[6], serial[1] ^ serial[3] ^ serial[5] ^ serial[7]
}

func (h *header) generateChecksums() ([]byte, []byte, error) {
b, err := h.MarshalBinary()
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions memcard.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ func (mc *memoryCard) count() int {
return count
}

func (mc *memoryCard) serialNumbers() (uint32, uint32) {
return mc.header.serialNumbers()
}

func (mc *memoryCard) checksum() error {
if err := mc.header.checksum(); err != nil {
return err
Expand Down
119 changes: 119 additions & 0 deletions patches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package gc

import (
"bytes"
"fmt"
"io"
)

const (
lengthFZero = 0x8000
lengthPSO = 0x6000
)

//nolint:gomnd
func patchFZero(r io.Reader, mc *memoryCard) (io.Reader, error) {
serial1, serial2 := mc.serialNumbers()

b, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("unable to read save data: %w", err)
}

if len(b) < lengthFZero {
return nil, errInvalidLength
}

b[0x2066] = byte(serial1 >> 24)
b[0x2067] = byte(serial1 >> 16)
b[0x7580] = byte(serial2 >> 24)
b[0x7581] = byte(serial2 >> 16)
b[0x2060] = byte(serial1 >> 8)
b[0x2061] = byte(serial1)
b[0x2200] = byte(serial2 >> 8)
b[0x2201] = byte(serial2)

var chksum uint16 = 0xffff

for i := 2; i < 0x8000; i++ {
chksum ^= uint16(b[i])
for j := 8; j > 0; j-- {
if chksum&1 == 1 {
chksum = (chksum >> 1) ^ 0x8408
} else {
chksum >>= 1
}
}
}

b[0x0000] = byte(^chksum >> 8)
b[0x0001] = byte(^chksum)

return bytes.NewReader(b), nil
}

const (
offsetPSO12 = 0x00
offsetPSO3 = 0x10
)

func patchPSO12(r io.Reader, mc *memoryCard) (io.Reader, error) {
return patchPSO(r, mc, offsetPSO12)
}

func patchPSO3(r io.Reader, mc *memoryCard) (io.Reader, error) {
return patchPSO(r, mc, offsetPSO3)
}

//nolint:gomnd
func patchPSO(r io.Reader, mc *memoryCard, offset int) (io.Reader, error) {
serial1, serial2 := mc.serialNumbers()

b, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("unable to read save data: %w", err)
}

if len(b) < lengthPSO {
return nil, errInvalidLength
}

b[0x2158] = byte(serial1 >> 24)
b[0x2159] = byte(serial1 >> 16)
b[0x215a] = byte(serial1 >> 8)
b[0x215b] = byte(serial1)
b[0x215c] = byte(serial2 >> 24)
b[0x215d] = byte(serial2 >> 16)
b[0x215e] = byte(serial2 >> 8)
b[0x215f] = byte(serial2)

lut := make([]uint32, 256)

for i := 0; i < 256; i++ {
chksum := uint32(i)
for j := 8; j > 0; j-- {
if chksum&1 == 1 {
chksum = (chksum >> 1) ^ 0xedb88320
} else {
chksum >>= 1
}
}

lut[i] = chksum
}

var chksum uint32 = 0xdebb20e3

for i := 0x204c; i < 0x2164+offset; i++ {
chksum = (chksum>>8)&0xffffff ^ lut[byte(chksum)^b[i]]
}

chksum ^= 0xffffffff

b[0x2048] = byte(chksum >> 24)
b[0x2049] = byte(chksum >> 16)
b[0x204a] = byte(chksum >> 8)
b[0x204b] = byte(chksum)

return bytes.NewReader(b), nil
}
21 changes: 20 additions & 1 deletion writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ func (w *fileWriter) Write(p []byte) (int, error) {
return w.buf.Write(p) //nolint:wrapcheck
}

var gamePatches = map[string]func(io.Reader, *memoryCard) (io.Reader, error){
"f_zero.dat": patchFZero,
"PSO_SYSTEM": patchPSO12,
"PSO3_SYSTEM": patchPSO3,
}

//nolint:cyclop,funlen
func (w *fileWriter) Close() error {
w.w.mu.Lock()
defer w.w.mu.Unlock()
Expand Down Expand Up @@ -67,13 +74,25 @@ func (w *fileWriter) Close() error {
return errNoFreeSpace
}

var (
r io.Reader = w.buf
err error
)

patchFunc, ok := gamePatches[e.filename()]
if ok {
if r, err = patchFunc(w.buf, mc); err != nil {
return err
}
}

// Set e.FirstBlock to the correct location
e.FirstBlock = mc.blockMap[mc.activeBlockMap()].LastAllocatedBlock + 1

// Write out the blocks
lastBlock := e.FirstBlock + e.FileLength - reservedBlocks
for i := e.FirstBlock - reservedBlocks; i < lastBlock; i++ {
_, _ = w.buf.Read(mc.blocks[i][:])
_, _ = r.Read(mc.blocks[i][:])

if i+1 < lastBlock {
mc.blockMap[mc.activeBlockMap()].Blocks[i] = i + reservedBlocks + 1
Expand Down

0 comments on commit 22d7342

Please sign in to comment.