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
|
Unknown_18 uint8 // unknown; 0x00 in all known games
|
||||||
|
|
||||||
BoodReadFileCode uint8
|
BootReadFileCode uint8
|
||||||
|
|
||||||
Unknown_1A [5]byte // unknown; 0xFF x5
|
Unknown_1A [5]byte // unknown; 0xFF x5
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,42 +37,18 @@ func run(args *Arguments) error {
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
//reader := bufio.NewReader(file)
|
blocks, err := fds.ReadRom(file, filepath.Ext(args.Input) == ".fds")
|
||||||
//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")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, block := range rom.Blocks {
|
rom, err := fds.RomFromBlocks(filepath.Base(args.Input), blocks)
|
||||||
fmt.Println(block)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(rom.Info())
|
||||||
|
|
||||||
return nil
|
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"
|
"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)
|
reader := bufio.NewReader(r)
|
||||||
magic, err := reader.Peek(4)
|
magic, err := reader.Peek(4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Unable to peek header: %w", err)
|
return nil, fmt.Errorf("Unable to peek header: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rom := &Rom{Header: &FdsHeader{}}
|
|
||||||
|
|
||||||
// "FDS" + EOF. header may be omitted.
|
// "FDS" + EOF. header may be omitted.
|
||||||
if bytes.Equal(magic, FdsHeaderMagic) {
|
if bytes.Equal(magic, FdsHeaderMagic) {
|
||||||
err = binary.Read(reader, binary.LittleEndian, rom.Header)
|
_, err = reader.Discard(binary.Size(&FdsHeader{}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error reading header: %w", err)
|
return nil, fmt.Errorf("Error reading header: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rom.Blocks, err = ReadBlocks(reader, IsFds)
|
return ReadBlocks(reader, IsFds)
|
||||||
return rom, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
|
func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
|
||||||
|
|
|
||||||
273
rom.go
273
rom.go
|
|
@ -1,8 +1,264 @@
|
||||||
package fds
|
package fds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/sigurn/crc16"
|
||||||
|
)
|
||||||
|
|
||||||
type Rom struct {
|
type Rom struct {
|
||||||
Header *FdsHeader
|
Name string // filename?
|
||||||
Blocks []DiskBlock
|
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 {
|
type FdsHeader struct {
|
||||||
|
|
@ -12,3 +268,16 @@ type FdsHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var FdsHeaderMagic []byte = []byte{0x46, 0x44, 0x53, 0x1A}
|
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