package fds import ( "fmt" "strings" ) type BlockType byte const ( BtInfo BlockType = 0x01 BtFileAmount BlockType = 0x02 BtFileHeader BlockType = 0x03 BtFileData BlockType = 0x04 BtTest BlockType = 0x05 ) func (bt BlockType) String() string { switch bt { case BtInfo: return "BlockInfo" case BtFileAmount: return "BlockAmount" case BtFileHeader: return "BlockFileHeader" case BtFileData: return "BlockFileData" } return "Unknown block" } type DiskBlock interface { Type() BlockType String() string } // Block 1 type BlockInfo struct { // always 0x01 BlockCode byte // literal ASCII: "*NINTENDO-HVC*" DiskVerification [14]byte Licensee uint8 // 3 byte ASCII code GameName [3]byte // 0x20: " " normal disk // 0x45: "E" Event // 0x4A: "J" Unknown // 0x52: "R" Reduction in price via advert (lmao what?) GameType uint8 GameVersion uint8 SideNumber uint8 DiskNumber uint8 // 0x01 for FMC blue-disk // 0x00 otherwise DiskType uint8 Unknown_18 uint8 // unknown; 0x00 in all known games BootReadFileCode uint8 Unknown_1A [5]byte // unknown; 0xFF x5 // BCD YMD, but 1-indexed from Japanese calendar era (fuck you, lmao). ManufacturingDate [3]byte CountryCode uint8 // 0x49 = japan Unknown_23 uint8 // region code? Unknown_24 uint8 // location/site? Unknown_25 [2]byte // raw: 0x00 0x22 Unknown_27 [5]byte // game info representation? // BCD YMD (same as above). Disk Writer kiosk write date? RewrittenDate [3]byte Unknown_2F uint8 Unknown_30 uint8 // raw: 0x80 DiskWriterSerial uint16 Unknown_33 uint8 // raw: 0x07 // BCD format. 0x00 == original RewriteCount byte // 0x00 = SideA; 0x01 = SideB ActualDiskSide uint8 // 0x00 yellow // 0xFF blue // 0xFE prototype/internal (white or pink) DiskTypeOther uint8 DiskVersion uint8 // CRC-16/KERMIT. apparently this will work: github.com/sigurn/crc16 CRC uint16 // omitted from FDS files (fucking whyyyyyyy) } func (b *BlockInfo) Type() BlockType { return BtInfo } func (b *BlockInfo) String() string { return fmt.Sprintf("FdsBlockInfo Licensee:%02X GameName:%q GameType:%c", b.Licensee, b.GameName, b.GameType, ) } const Template_FdsBlockInfo string = `Licensee: {{.Licensee}} {{.LicEn}} {{.LicJp}} Name: {{.GameName}} Version: {{.GameVersion}} Type: {{.GameType}}` func (b *BlockInfo) Info() string { return "yup" } //func (b *BlockInfo) Info() string { // return fmt.Sprintf("Licensee:%02X\n %s\n %s\nGameName: %s", // b.Licensee, // LicenseeEnName(b.Licensee), // LicenseeJpName(b.Licensee), // ) //} type BlockFileAmount struct { BlockCode byte // raw: 0x02 FileAmount uint8 CRC uint16 // omitted from FDS files (fucking whyyyyyyy) } func (b *BlockFileAmount) Type() BlockType { return BtFileAmount } func (b *BlockFileAmount) String() string { return fmt.Sprintf("BlockFileAmount FileAmount:%d", b.FileAmount) } type BlockFileHeader struct { BlockCode byte // raw: 0x03 FileNumber uint8 FileId uint8 FileName [8]byte FileAddress uint16 // destination when loading FileSize uint16 // 0x00: Program (PRAM) // 0x01: Character (CRAM) // 0x02: Nametable (VRAM) FileType uint8 CRC uint16 // omitted from FDS files (fucking whyyyyyyy) } func (b *BlockFileHeader) Type() BlockType { return BtFileHeader } func (b *BlockFileHeader) String() string { return fmt.Sprintf("BlockFileHeader FileNumber:%d FileId:%d FileName:%q FileAddress:$%04X FileSize:%d FileType:%d", b.FileNumber, b.FileId, string(b.FileName[:]), b.FileAddress, b.FileSize, b.FileType, ) } type BlockFileData struct { BlockCode byte // raw: 0x04 // Disk data (FdsFileHeader.FileSize length) Data []byte CRC uint16 // omitted from FDS files (fucking whyyyyyyy) } func (b *BlockFileData) Type() BlockType { return BtFileData } func (b *BlockFileData) String() string { return "BlockFileData" } type BlockTest struct { Raw []byte } func (b *BlockTest) Type() BlockType { return BtTest } func (b *BlockTest) String() string { bytes := []string{} for _, byt := range b.Raw { bytes = append(bytes, fmt.Sprintf("$%02X", byt)) } return fmt.Sprintf("BlockTest Raw:[%s]", strings.Join(bytes, " ")) }