Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting flash ID #3

Merged
merged 1 commit into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions blockmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@ func (m *blockMap) isValid() error {

return nil
}

func newBlockMap(updateCounter, freeBlocks uint16) blockMap {
return blockMap{
UpdateCounter: updateCounter,
FreeBlocks: freeBlocks,
LastAllocatedBlock: reservedBlocks - 1,
}
}
30 changes: 22 additions & 8 deletions dirent.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ type entry struct {
}

func (e *entry) isEmpty() bool {
e1 := bytes.Equal(e.GameCode[:], []byte{0xff, 0xff, 0xff, 0xff})
e2 := bytes.Equal(e.GameCode[:], []byte{0x00, 0x00, 0x00, 0x00})

return e1 || e2
return *e == newEntry()
}

func (e *entry) gameCode() string {
Expand Down Expand Up @@ -80,6 +77,14 @@ func (e *entry) MarshalBinary() ([]byte, error) {
return b, nil
}

func newEntry() entry {
e := entry{}

_ = binary.Read(plumbing.FillReader(0xff), binary.BigEndian, &e) //nolint:gomnd

return e
}

const (
maxEntries = 127
directoryReserved1Size = 0x003a
Expand All @@ -97,10 +102,7 @@ func (d *directory) MarshalBinary() ([]byte, error) {
buf.Grow(binary.Size(d))

for _, e := range d.Entries {
b, err := e.MarshalBinary()
if err != nil {
return nil, err
}
b, _ := e.MarshalBinary()

_, _ = buf.Write(b)
}
Expand Down Expand Up @@ -152,3 +154,15 @@ func (d *directory) isValid() error {

return nil
}

func newDirectory(updateCounter uint16) directory {
d := directory{}

for i := range d.Entries {
d.Entries[i] = newEntry()
}

d.UpdateCounter = updateCounter

return d
}
40 changes: 40 additions & 0 deletions flashid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gc

// Shamelessly lifted from GCMM

const (
serialLength = 12

multiply = 1103515245 // 0x41C64E6D
addition = 12345 // 0x3039

shiftWidth = 16

mask = 0x7fff
)

func extractFlashID(serial [serialLength]byte, rand uint64) [serialLength]byte {
var flashID [serialLength]byte

for i := 0; i < serialLength; i++ {
rand = (rand*multiply + addition) >> shiftWidth
flashID[i] = serial[i] - byte(rand) // subtraction
rand = (rand*multiply + addition) >> shiftWidth
rand &= mask
}

return flashID
}

func computeSerial(flashID [serialLength]byte, rand uint64) [serialLength]byte {
var serial [serialLength]byte

for i := 0; i < serialLength; i++ {
rand = (rand*multiply + addition) >> shiftWidth
serial[i] = flashID[i] + byte(rand) // addition
rand = (rand*multiply + addition) >> shiftWidth
rand &= mask
}

return serial
}
24 changes: 24 additions & 0 deletions flashid_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package gc

import (
"crypto/rand"
"testing"

"github.com/stretchr/testify/assert"
)

func TestExtractFlashID(t *testing.T) {
t.Parallel()

var flashID [serialLength]byte

if _, err := rand.Read(flashID[:]); err != nil {
t.Fatal(err)
}

t0 := now()

serial := computeSerial(flashID, t0)

assert.Equal(t, flashID, extractFlashID(serial, t0))
}
4 changes: 2 additions & 2 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ const (
//nolint:maligned
type header struct {
Serial [12]byte
FormatTime uint64
FormatTime uint64 // Ticks since GameCube epoch
CounterBias uint32
Lang uint32
_ [headerReserved1Size]byte
Unknown [headerReserved1Size]byte // Seems to be either 0 or 1 as a uint32
DeviceID uint16
CardSize uint16
Encoding uint16
Expand Down
66 changes: 45 additions & 21 deletions memcard.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ const (
)

var (
errInvalidCapacity = errors.New("not a valid capacity")
errTrailingBytes = errors.New("trailing bytes")
errInvalidBlockMapCounters = errors.New("invalid block allocation map update counters")
errInvalidCapacity = errors.New("not a valid capacity")
errInvalidDirectoryCounters = errors.New("invalid directory update counters")
errInvalidEncoding = errors.New("not a valid encoding")
errTrailingBytes = errors.New("trailing bytes")
)

type memoryCard struct {
Expand Down Expand Up @@ -123,6 +126,16 @@ func (mc *memoryCard) isValid() error {
}
}

diff := int(mc.directory[master].UpdateCounter) - int(mc.directory[backup].UpdateCounter)
if diff != 1 && diff != -1 {
return errInvalidDirectoryCounters
}

diff = int(mc.blockMap[master].UpdateCounter) - int(mc.blockMap[backup].UpdateCounter)
if diff != 1 && diff != -1 {
return errInvalidBlockMapCounters
}

return nil
}

Expand All @@ -142,6 +155,18 @@ func validateCardSize(capacity uint16) error {
return nil
}

func validateEncoding(encoding uint16) error {
switch encoding {
case EncodingANSI:
case EncodingSJIS:
break
default:
return errInvalidEncoding
}

return nil
}

func (mc *memoryCard) unmarshalBinary(r io.Reader) error {
if err := binary.Read(r, binary.BigEndian, &mc.header); err != nil {
return fmt.Errorf("unable to read header: %w", err)
Expand All @@ -151,6 +176,10 @@ func (mc *memoryCard) unmarshalBinary(r io.Reader) error {
return err
}

if err := validateEncoding(mc.header.Encoding); err != nil {
return err
}

if err := binary.Read(r, binary.BigEndian, &mc.directory); err != nil {
return fmt.Errorf("unable to read directory: %w", err)
}
Expand Down Expand Up @@ -220,39 +249,34 @@ func (mc *memoryCard) MarshalBinary() ([]byte, error) {
return buf.Bytes(), nil
}

func newMemoryCard(capacity, encoding uint16) (*memoryCard, error) {
func newMemoryCard(flashID [12]byte, formatTime uint64, capacity, encoding uint16) (*memoryCard, error) {
if err := validateCardSize(capacity); err != nil {
return nil, err
}

if err := validateEncoding(encoding); err != nil {
return nil, err
}

header := header{
CardSize: capacity,
Encoding: encoding,
Serial: computeSerial(flashID, formatTime),
FormatTime: formatTime,
CardSize: capacity,
Encoding: encoding,
UpdateCounter: 0xffff, //nolint:gomnd
}

freeBlocks := header.blocks() - reservedBlocks

mc := &memoryCard{
header: header,
directory: [copies]directory{
{
UpdateCounter: 1,
},
{
UpdateCounter: 1,
},
newDirectory(1),
newDirectory(0),
},
blockMap: [copies]blockMap{
{
UpdateCounter: 1,
FreeBlocks: uint16(freeBlocks),
LastAllocatedBlock: reservedBlocks - 1,
},
{
UpdateCounter: 1,
FreeBlocks: uint16(freeBlocks),
LastAllocatedBlock: reservedBlocks - 1,
},
newBlockMap(1, uint16(freeBlocks)),
newBlockMap(0, uint16(freeBlocks)),
},
}

Expand Down
3 changes: 3 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type Reader struct {

mc *memoryCard

FlashID [12]byte
CardSize uint16
Encoding uint16

Expand All @@ -158,6 +159,8 @@ func (r *Reader) init(nr io.Reader) error {
return err
}

r.FlashID = extractFlashID(r.mc.header.Serial, r.mc.header.FormatTime)

r.CardSize, r.Encoding = r.mc.header.CardSize, r.mc.header.Encoding

r.File = make([]*File, 0, r.mc.count())
Expand Down
Binary file modified testdata/blank.mcd
Binary file not shown.
Loading