Rework Rom struct; Print more info
- Reworked the Rom struct to make more sense in an emulation environment. Currently, cannot convert this to a .fds file, but maybe later. - Print info using the reworked Rom struct. More info is printed.
This commit is contained in:
parent
93c056db49
commit
4ab973c6b2
|
|
@ -61,7 +61,7 @@ type BlockInfo struct {
|
|||
|
||||
Unknown_18 uint8 // unknown; 0x00 in all known games
|
||||
|
||||
BoodReadFileCode uint8
|
||||
BootReadFileCode uint8
|
||||
|
||||
Unknown_1A [5]byte // unknown; 0xFF x5
|
||||
|
||||
|
|
|
|||
|
|
@ -37,42 +37,18 @@ func run(args *Arguments) error {
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
//reader := bufio.NewReader(file)
|
||||
//magic, err := reader.Peek(4)
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("Unable to peek header: %w", err)
|
||||
//}
|
||||
|
||||
//header := &fds.FdsHeader{}
|
||||
|
||||
//// "FDS" + EOF. header may be omitted.
|
||||
//if bytes.Equal(magic, []byte{0x46, 0x44, 0x53, 0x1A}) {
|
||||
// err = binary.Read(reader, binary.LittleEndian, header)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("Error reading header: %w", err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//if header != nil {
|
||||
// fmt.Println("Header found. sides:", header.Sides)
|
||||
//} else {
|
||||
// fmt.Println("No header")
|
||||
//}
|
||||
|
||||
//blocks, err := fds.ReadBlocks(reader, true)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
rom, err := fds.ReadRom(file, filepath.Ext(args.Input) == ".fds")
|
||||
blocks, err := fds.ReadRom(file, filepath.Ext(args.Input) == ".fds")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, block := range rom.Blocks {
|
||||
fmt.Println(block)
|
||||
rom, err := fds.RomFromBlocks(filepath.Base(args.Input), blocks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(rom.Info())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package fds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type FdsDate struct {
|
||||
Year int
|
||||
Month int
|
||||
Day int
|
||||
}
|
||||
|
||||
func NewFdsDate(raw []byte) (*FdsDate, error) {
|
||||
if len(raw) != 3 {
|
||||
return nil, fmt.Errorf("Must be three bytes")
|
||||
}
|
||||
|
||||
if raw[1] == 0x00 || raw[1] > 0x12 || (raw[1] > 0x09 && raw[1] < 0x10) {
|
||||
return nil, fmt.Errorf("Invalid month: %02X", raw[1])
|
||||
}
|
||||
|
||||
if raw[2] == 0x00 || raw[2] > 0x31 ||
|
||||
(raw[2] > 0x09 && raw[2] < 0x10) ||
|
||||
(raw[2] > 0x19 && raw[2] < 0x20) ||
|
||||
(raw[2] > 0x29 && raw[2] < 0x30) {
|
||||
|
||||
return nil, fmt.Errorf("Invalid day: %02X", raw[1])
|
||||
}
|
||||
|
||||
return &FdsDate{
|
||||
Year: bcdToDec(raw[0]),
|
||||
Month: bcdToDec(raw[1]),
|
||||
Day: bcdToDec(raw[2]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func bcdToDec(val byte) int {
|
||||
v := int(val)
|
||||
return (v>>4)*10 + (v&0x0F)
|
||||
}
|
||||
|
||||
func (date *FdsDate) String() string {
|
||||
return fmt.Sprintf("Years: %04d/%04d/%04d Month: %d Day: %d",
|
||||
date.Year+1926,
|
||||
date.Year+1989,
|
||||
date.Year+1900,
|
||||
date.Month,
|
||||
date.Day,
|
||||
)
|
||||
}
|
||||
|
||||
//func (date *FdsDate) Raw() []byte {
|
||||
//}
|
||||
|
|
@ -11,25 +11,22 @@ import (
|
|||
"github.com/sigurn/crc16"
|
||||
)
|
||||
|
||||
func ReadRom(r io.Reader, IsFds bool) (*Rom, error) {
|
||||
func ReadRom(r io.Reader, IsFds bool) ([]DiskBlock, error) {
|
||||
reader := bufio.NewReader(r)
|
||||
magic, err := reader.Peek(4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to peek header: %w", err)
|
||||
}
|
||||
|
||||
rom := &Rom{Header: &FdsHeader{}}
|
||||
|
||||
// "FDS" + EOF. header may be omitted.
|
||||
if bytes.Equal(magic, FdsHeaderMagic) {
|
||||
err = binary.Read(reader, binary.LittleEndian, rom.Header)
|
||||
_, err = reader.Discard(binary.Size(&FdsHeader{}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading header: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
rom.Blocks, err = ReadBlocks(reader, IsFds)
|
||||
return rom, err
|
||||
return ReadBlocks(reader, IsFds)
|
||||
}
|
||||
|
||||
func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
|
||||
|
|
|
|||
273
rom.go
273
rom.go
|
|
@ -1,8 +1,264 @@
|
|||
package fds
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
|
||||
"github.com/sigurn/crc16"
|
||||
)
|
||||
|
||||
type Rom struct {
|
||||
Header *FdsHeader
|
||||
Blocks []DiskBlock
|
||||
Name string // filename?
|
||||
Sides []*RomSide
|
||||
}
|
||||
|
||||
func (rom *Rom) Info() string {
|
||||
builder := &strings.Builder{}
|
||||
fmt.Fprintf(builder, "Name: %s", rom.Name)
|
||||
|
||||
//sides := []string{}
|
||||
for _, side := range rom.Sides {
|
||||
builder.WriteString("\n\n")
|
||||
builder.WriteString(side.Info())
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// In the event that a disk has two headers on one physical side of disk,
|
||||
// split the side into two.
|
||||
type RomSide struct {
|
||||
Header *RomHeader
|
||||
FileCount int // more than this number are "hidden" files
|
||||
Files []*RomFile
|
||||
}
|
||||
|
||||
func (side *RomSide) Info() string {
|
||||
builder := &strings.Builder{}
|
||||
|
||||
fmt.Fprintln(builder, side.Header.Info())
|
||||
fmt.Fprintf(builder, "FileCount: listed:%d total:%d\n", side.FileCount, len(side.Files))
|
||||
|
||||
sideAB := "?"
|
||||
if side.Header.PhysicalSide == 0x00 {
|
||||
sideAB = "A"
|
||||
} else if side.Header.PhysicalSide == 0x01 {
|
||||
sideAB = "B"
|
||||
}
|
||||
|
||||
fmt.Fprintf(builder, "Side%s\n", sideAB)
|
||||
|
||||
files := []string{}
|
||||
for _, file := range side.Files {
|
||||
files = append(files, file.Info())
|
||||
}
|
||||
builder.WriteString(strings.Join(files, "\n"))
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type RomHeader struct {
|
||||
Licensee uint8
|
||||
GameName string
|
||||
|
||||
GameType uint8
|
||||
|
||||
GameVersion int
|
||||
SideNumber int
|
||||
DiskNumber int
|
||||
|
||||
DiskType uint8
|
||||
BootFileId int
|
||||
|
||||
ManufacturingDate *FdsDate
|
||||
RewrittenDate *FdsDate
|
||||
|
||||
CountryCode uint8 // 0x49 = japan
|
||||
|
||||
DiskWriterSerial uint16
|
||||
|
||||
RewriteCount int
|
||||
PhysicalSide uint8
|
||||
|
||||
DiskTypeOther uint8
|
||||
DiskVersion uint8
|
||||
}
|
||||
|
||||
func (header *RomHeader) Info() string {
|
||||
return fmt.Sprintf("Name: %3s Licensee: $%02X - %s\nMFG Date: %s",
|
||||
header.GameName,
|
||||
header.Licensee,
|
||||
LicenseeEnName(header.Licensee),
|
||||
header.ManufacturingDate,
|
||||
)
|
||||
}
|
||||
|
||||
func RomFromBlocks(name string, blocks []DiskBlock) (*Rom, error) {
|
||||
rom := &Rom{
|
||||
Name: name,
|
||||
Sides: []*RomSide{},
|
||||
}
|
||||
|
||||
var side *RomSide
|
||||
var file *RomFile
|
||||
|
||||
for _, block := range blocks {
|
||||
switch block.Type() {
|
||||
case BtInfo:
|
||||
if file != nil {
|
||||
return nil, fmt.Errorf("Expected BlockFileData, found BlockInfo")
|
||||
}
|
||||
|
||||
blk := block.(*BlockInfo)
|
||||
side = &RomSide{
|
||||
Header: &RomHeader{
|
||||
Licensee: blk.Licensee,
|
||||
GameName: string(blk.GameName[:]),
|
||||
GameType: blk.GameType,
|
||||
GameVersion: int(blk.GameVersion),
|
||||
SideNumber: int(blk.SideNumber),
|
||||
DiskNumber: int(blk.DiskNumber),
|
||||
DiskType: blk.DiskType,
|
||||
BootFileId: int(blk.BootReadFileCode),
|
||||
ManufacturingDate: &FdsDate{
|
||||
Year: bcdToDec(blk.ManufacturingDate[0]),
|
||||
Month: bcdToDec(blk.ManufacturingDate[1]),
|
||||
Day: bcdToDec(blk.ManufacturingDate[2]),
|
||||
},
|
||||
RewrittenDate: &FdsDate{
|
||||
Year: bcdToDec(blk.RewrittenDate[0]),
|
||||
Month: bcdToDec(blk.RewrittenDate[1]),
|
||||
Day: bcdToDec(blk.RewrittenDate[2]),
|
||||
},
|
||||
CountryCode: blk.CountryCode,
|
||||
DiskWriterSerial: blk.DiskWriterSerial,
|
||||
RewriteCount: bcdToDec(blk.RewriteCount),
|
||||
PhysicalSide: blk.ActualDiskSide,
|
||||
DiskTypeOther: blk.DiskTypeOther,
|
||||
DiskVersion: blk.DiskVersion,
|
||||
},
|
||||
Files: []*RomFile{},
|
||||
}
|
||||
rom.Sides = append(rom.Sides, side)
|
||||
|
||||
case BtFileAmount:
|
||||
if side == nil {
|
||||
return nil, fmt.Errorf("Expected BlockInfo, found BlockFileAmount")
|
||||
}
|
||||
|
||||
side.FileCount = int((block.(*BlockFileAmount)).FileAmount)
|
||||
|
||||
case BtFileHeader:
|
||||
if side == nil {
|
||||
return nil, fmt.Errorf("Expected BlockInfo, found BlockFileHeader")
|
||||
}
|
||||
|
||||
if file != nil {
|
||||
return nil, fmt.Errorf("Expected BlockFileData, found BlockFileHeader")
|
||||
}
|
||||
|
||||
blk := block.(*BlockFileHeader)
|
||||
file = &RomFile{
|
||||
Name: BytesToAscii(blk.FileName[:]),
|
||||
Number: int(blk.FileNumber),
|
||||
Id: int(blk.FileId),
|
||||
Type: RomFileType(blk.FileType),
|
||||
Size: int(blk.FileSize),
|
||||
Address: blk.FileAddress,
|
||||
Hidden: (int(blk.FileNumber) >= side.FileCount),
|
||||
}
|
||||
|
||||
side.Files = append(side.Files, file)
|
||||
|
||||
case BtFileData:
|
||||
if side == nil {
|
||||
return nil, fmt.Errorf("Expected BlockInfo, found BlockFileData")
|
||||
}
|
||||
|
||||
if file == nil {
|
||||
return nil, fmt.Errorf("Expected BlockFileHeader, found BlockFileData")
|
||||
}
|
||||
|
||||
blk := block.(*BlockFileData)
|
||||
file.Data = blk.Data
|
||||
file.CRC = blk.CRC
|
||||
|
||||
file = nil
|
||||
}
|
||||
}
|
||||
|
||||
return rom, nil
|
||||
}
|
||||
|
||||
type RomFileType int
|
||||
|
||||
const (
|
||||
FT_Program RomFileType = iota
|
||||
FT_Character
|
||||
FT_Nametable
|
||||
)
|
||||
|
||||
func (ft RomFileType) String() string {
|
||||
switch ft {
|
||||
case FT_Program:
|
||||
return "FT_Program"
|
||||
case FT_Character:
|
||||
return "FT_Character"
|
||||
case FT_Nametable:
|
||||
return "FT_Nametable"
|
||||
}
|
||||
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func (ft RomFileType) Str() string {
|
||||
switch ft {
|
||||
case FT_Program:
|
||||
return "PRG"
|
||||
case FT_Character:
|
||||
return "CHR"
|
||||
case FT_Nametable:
|
||||
return "NT "
|
||||
}
|
||||
|
||||
return "UNK"
|
||||
}
|
||||
|
||||
type RomFile struct {
|
||||
Name string
|
||||
|
||||
Number int
|
||||
Id int
|
||||
|
||||
Type RomFileType
|
||||
Size int
|
||||
|
||||
Address uint16
|
||||
Hidden bool
|
||||
|
||||
Data []byte
|
||||
CRC uint16
|
||||
}
|
||||
|
||||
func (file *RomFile) Info() string {
|
||||
crcStatus := "PASS"
|
||||
// block type is included in the CRC
|
||||
crc := crc16.Checksum(append([]byte{0x04}, file.Data...), crc16.MakeTable(crc16.CRC16_KERMIT))
|
||||
if crc != file.CRC {
|
||||
crcStatus = fmt.Sprintf("FAIL [$%04X]", crc)
|
||||
}
|
||||
return fmt.Sprintf("%02d $%02X %q %s %5d $%04X-$%04X Hidden:%t CRC:$%04X (%s)",
|
||||
file.Number,
|
||||
file.Id,
|
||||
file.Name,
|
||||
file.Type.Str(),
|
||||
file.Size,
|
||||
file.Address,
|
||||
file.Address+uint16(file.Size),
|
||||
file.Hidden,
|
||||
file.CRC,
|
||||
crcStatus,
|
||||
)
|
||||
}
|
||||
|
||||
type FdsHeader struct {
|
||||
|
|
@ -12,3 +268,16 @@ type FdsHeader struct {
|
|||
}
|
||||
|
||||
var FdsHeaderMagic []byte = []byte{0x46, 0x44, 0x53, 0x1A}
|
||||
|
||||
func BytesToAscii(input []byte) string {
|
||||
builder := &strings.Builder{}
|
||||
for _, b := range input {
|
||||
if b >= ' ' && b <= '~' {
|
||||
builder.WriteByte(b)
|
||||
} else {
|
||||
builder.WriteString(fmt.Sprintf("\\x%02X", b))
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue