diff --git a/pangya/iff/common.go b/pangya/iff/common.go index f8cdad9..af6cbc9 100644 --- a/pangya/iff/common.go +++ b/pangya/iff/common.go @@ -17,28 +17,14 @@ package iff -import "github.com/pangbox/server/pangya" - -type Header struct { - RecordCount uint16 - BindingID uint16 - Version uint32 +type File[T any] struct { + Header + Records []T `struct:"size=Header.RecordCount"` } -type Common struct { - Active uint32 - ID uint32 - Name string `struct:"[40]byte"` - Level byte - Icon string `struct:"[40]byte"` - Price uint32 - DiscountPrice uint32 - UsedPrice uint32 - ShopFlag byte - MoneyFlag byte - TimeFlag byte - TimeByte byte - Point uint32 - StartTime pangya.SystemTime - EndTime pangya.SystemTime +type Header struct { + /* 0x00 */ RecordCount uint16 + /* 0x02 */ BindingID uint16 + /* 0x04 */ Version Version + /* 0x08 */ } diff --git a/pangya/iff/course.go b/pangya/iff/course.go index 924fd8f..b175be1 100644 --- a/pangya/iff/course.go +++ b/pangya/iff/course.go @@ -17,12 +17,31 @@ package iff +import "github.com/pangbox/server/pangya" + type Course struct { - Common Common + Active bool + _ [3]byte + ID uint32 + Name string `struct:"[40]byte"` + Level byte + Icon string `struct:"[40]byte"` + _ [3]byte + Price uint32 + DiscountPrice uint32 + Condition uint32 + ShopFlag byte + MoneyFlag byte + TimeFlag byte + TimeByte byte + Point uint32 + Unknown [0x1C]byte + StartTime pangya.SystemTime + EndTime pangya.SystemTime ShortName string `struct:"[40]byte"` LocalizedName string `struct:"[40]byte"` CourseFlag byte PropertyFileName string `struct:"[40]byte"` - Unknown uint32 + Unknown2 uint32 CourseSequence string `struct:"[40]byte"` } diff --git a/pangya/iff/file.go b/pangya/iff/file.go index edcddc6..86adb3b 100644 --- a/pangya/iff/file.go +++ b/pangya/iff/file.go @@ -20,14 +20,18 @@ package iff import ( "archive/zip" "bytes" + "encoding/binary" "errors" "fmt" + "io" + "github.com/go-restruct/restruct" "github.com/pangbox/pangfiles/pak" log "github.com/sirupsen/logrus" ) type Archive struct { + ItemMap map[uint32]*Item } // Filenames to look for to find client IFF. @@ -50,14 +54,112 @@ func LoadFromPak(fs pak.FS) (*Archive, error) { } func Load(data []byte) (*Archive, error) { + archive := &Archive{} r, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { return nil, err } for _, f := range r.File { log.Debugf("Found IFF: %s", f.Name) + if f.Name == "Item.iff" { + r, err := f.Open() + if err != nil { + return nil, err + } + defer r.Close() + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + archive.loadItems(data) + } + } + return archive, nil +} + +func (a *Archive) loadItems(data []byte) error { + file, err := LoadItems(data) + if err != nil { + return err + } + a.ItemMap = make(map[uint32]*Item) + for _, item := range file.Records { + a.ItemMap[item.ID] = &item + } + return nil +} + +func LoadItems(data []byte) (*File[Item], error) { + recordCount := binary.LittleEndian.Uint16(data[:2]) + recordLength := (len(data) - 0x8) / int(recordCount) + version := Version(binary.LittleEndian.Uint32(data[4:8])) + + switch version { + case Version11: + switch recordLength { + case 0x78: + return loadItemVersion[ItemV11_78](data) + case 0x98: + return loadItemVersion[ItemV11_98](data) + case 0xB0: + return loadItemVersion[ItemV11_B0](data) + case 0xC0: + return loadItemVersion[ItemV11_C0](data) + case 0xC4: + return loadItemVersion[ItemV11_C4](data) + case 0xD8: + // JP4xx has the common times after the model name + // this is back to normal in JP5xx + var testItem ItemV11_D8_2 + restruct.Unpack(data[8:], binary.LittleEndian, &testItem) + if testItem.StartTime.IsZero() || testItem.StartTime.IsValid() { + return loadItemVersion[ItemV11_D8_2](data) + } else { + return loadItemVersion[ItemV11_D8_1](data) + } + default: + return nil, fmt.Errorf("unknown item iff v%d record size %d (please report)", version, recordLength) + } + case Version13: + switch recordLength { + case 0xE0: + return loadItemVersion[ItemV13_E0](data) + case 0xF8: + return loadItemVersion[ItemV13_F8](data) + default: + return nil, fmt.Errorf("unknown item iff v%d record size %d (please report)", version, recordLength) + } + default: + return nil, errors.New("unknown item iff version") + } +} + +func loadItemVersion[T itemGeneric](data []byte) (*File[Item], error) { + result := &File[Item]{} + f, err := LoadFile[T](data) + if err != nil { + return nil, err + } + result.Header = f.Header + for _, record := range f.Records { + result.Records = append(result.Records, record.Generic()) + } + size, err := restruct.SizeOf(f) + if err != nil { + return nil, err + } + if len(data) > size { + return nil, fmt.Errorf("short read: read %d of %d bytes", size, len(data)) + } + return result, nil +} + +func LoadFile[T any](data []byte) (*File[T], error) { + file := &File[T]{} + if err := restruct.Unpack(data, binary.LittleEndian, file); err != nil { + return nil, err } - return &Archive{}, nil + return file, nil } func findPangYaIFF(fs pak.FS) ([]byte, error) { diff --git a/pangya/iff/generic.go b/pangya/iff/generic.go new file mode 100644 index 0000000..158044b --- /dev/null +++ b/pangya/iff/generic.go @@ -0,0 +1,228 @@ +package iff + +import "github.com/pangbox/server/pangya" + +type Common struct { +} + +type Item struct { + Active bool + ID uint32 + Name string + MaxRank byte + Icon string + Price uint32 + DiscountPrice uint32 + Condition uint32 + ShopFlag byte + MoneyFlag byte + TimeFlag byte + TimeByte byte + Point uint32 + StartTime pangya.SystemTime + EndTime pangya.SystemTime + Common Common + Model string + Quantity uint16 + Unknown2 [4]uint16 +} + +type itemGeneric interface { + Generic() Item +} + +func (i ItemV11_78) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + Model: i.Icon, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_98) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + Model: i.Icon, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_B0) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Icon, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_C0) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_D8_1) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_D8_2) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV13_E0) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + Point: i.Point, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV11_C4) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} + +func (i ItemV13_F8) Generic() Item { + return Item{ + Active: i.Active, + ID: i.ID, + Name: i.Name, + MaxRank: i.MaxRank, + Icon: i.Icon, + Price: i.Price, + DiscountPrice: i.DiscountPrice, + Condition: i.Condition, + ShopFlag: i.ShopFlag, + MoneyFlag: i.MoneyFlag, + TimeFlag: i.TimeFlag, + TimeByte: i.TimeByte, + Point: i.Point, + StartTime: i.StartTime, + EndTime: i.EndTime, + Model: i.Model, + Quantity: i.Quantity, + Unknown2: i.Unknown2, + } +} diff --git a/pangya/iff/helpers_test.go b/pangya/iff/helpers_test.go new file mode 100644 index 0000000..de483b5 --- /dev/null +++ b/pangya/iff/helpers_test.go @@ -0,0 +1,11 @@ +package iff + +import "os" + +func mustLoad(filename string) []byte { + data, err := os.ReadFile(filename) + if err != nil { + panic(err) + } + return data +} diff --git a/pangya/iff/item.go b/pangya/iff/item.go new file mode 100644 index 0000000..1997e44 --- /dev/null +++ b/pangya/iff/item.go @@ -0,0 +1,222 @@ +package iff + +import ( + _ "github.com/pangbox/server/common" // for restruct.EnableExprBeta + "github.com/pangbox/server/pangya" +) + +type ItemV11_78 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[40]byte"` + /* 0x30 */ MaxRank byte + /* 0x31 */ Icon string `struct:"[40]byte"` + /* 0x59 */ _ [3]byte + /* 0x5C */ Price uint32 + /* 0x60 */ DiscountPrice uint32 + /* 0x64 */ Condition uint32 + /* 0x68 */ ShopFlag byte + /* 0x69 */ MoneyFlag byte + /* 0x6A */ TimeFlag byte + /* 0x6B */ TimeByte byte + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0x76 */ _ [2]byte + /* 0x78 */ +} + +type ItemV11_98 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[40]byte"` + /* 0x30 */ MaxRank byte + /* 0x31 */ Icon string `struct:"[40]byte"` + /* 0x59 */ _ [3]byte + /* 0x5C */ Price uint32 + /* 0x60 */ DiscountPrice uint32 + /* 0x64 */ Condition uint32 + /* 0x68 */ ShopFlag byte + /* 0x69 */ MoneyFlag byte + /* 0x6A */ TimeFlag byte + /* 0x6B */ TimeByte byte + /* 0x6C */ StartTime pangya.SystemTime + /* 0x7C */ EndTime pangya.SystemTime + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0x96 */ _ [2]byte + /* 0x98 */ +} + +type ItemV11_B0 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[64]byte"` + /* 0x48 */ MaxRank byte + /* 0x49 */ Icon string `struct:"[40]byte"` + /* 0x71 */ _ [3]byte + /* 0x74 */ Price uint32 + /* 0x78 */ DiscountPrice uint32 + /* 0x7C */ Condition uint32 + /* 0x80 */ ShopFlag byte + /* 0x81 */ MoneyFlag byte + /* 0x82 */ TimeFlag byte + /* 0x83 */ TimeByte byte + /* 0x84 */ StartTime pangya.SystemTime + /* 0x94 */ EndTime pangya.SystemTime + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xAE */ _ [2]byte + /* 0xB0 */ +} + +type ItemV11_C0 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[40]byte"` + /* 0x30 */ MaxRank byte + /* 0x31 */ Icon string `struct:"[40]byte"` + /* 0x59 */ _ [3]byte + /* 0x5C */ Price uint32 + /* 0x60 */ DiscountPrice uint32 + /* 0x64 */ Condition uint32 + /* 0x68 */ ShopFlag byte + /* 0x69 */ MoneyFlag byte + /* 0x6A */ TimeFlag byte + /* 0x6B */ TimeByte byte + /* 0x6C */ StartTime pangya.SystemTime + /* 0x7C */ EndTime pangya.SystemTime + /* 0x8C */ Model string `struct:"[40]byte"` + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xBE */ _ [2]byte + /* 0xC0 */ +} + +type ItemV11_D8_1 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[64]byte"` + /* 0x48 */ MaxRank byte + /* 0x49 */ Icon string `struct:"[40]byte"` + /* 0x71 */ _ [3]byte + /* 0x74 */ Price uint32 + /* 0x78 */ DiscountPrice uint32 + /* 0x7C */ Condition uint32 + /* 0x80 */ ShopFlag byte + /* 0x81 */ MoneyFlag byte + /* 0x82 */ TimeFlag byte + /* 0x83 */ TimeByte byte + /* 0x84 */ Model string `struct:"[40]byte"` + /* 0x84 */ StartTime pangya.SystemTime + /* 0x94 */ EndTime pangya.SystemTime + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xD6 */ _ [2]byte + /* 0xD8 */ +} + +type ItemV11_D8_2 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[64]byte"` + /* 0x48 */ MaxRank byte + /* 0x49 */ Icon string `struct:"[40]byte"` + /* 0x71 */ _ [3]byte + /* 0x74 */ Price uint32 + /* 0x78 */ DiscountPrice uint32 + /* 0x7C */ Condition uint32 + /* 0x80 */ ShopFlag byte + /* 0x81 */ MoneyFlag byte + /* 0x82 */ TimeFlag byte + /* 0x83 */ TimeByte byte + /* 0x** */ StartTime pangya.SystemTime + /* 0x** */ EndTime pangya.SystemTime + /* 0x** */ Model string `struct:"[40]byte"` + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xD6 */ _ [2]byte + /* 0xD8 */ +} + +type ItemV11_C4 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[40]byte"` + /* 0x30 */ MaxRank byte + /* 0x31 */ Icon string `struct:"[40]byte"` + /* 0x59 */ _ [3]byte + /* 0x5C */ Price uint32 + /* 0x60 */ DiscountPrice uint32 + /* 0x64 */ Condition uint32 + /* 0x68 */ ShopFlag byte + /* 0x69 */ MoneyFlag byte + /* 0x6A */ TimeFlag byte + /* 0x6B */ TimeByte byte + /* 0x6C */ Unknown uint32 + /* 0x70 */ StartTime pangya.SystemTime + /* 0x80 */ EndTime pangya.SystemTime + /* 0x90 */ Model string `struct:"[40]byte"` + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xC2 */ _ [2]byte + /* 0xC4 */ +} + +type ItemV13_E0 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[40]byte"` + /* 0x30 */ MaxRank byte + /* 0x31 */ Icon string `struct:"[40]byte"` + /* 0x59 */ _ [3]byte + /* 0x5C */ Price uint32 + /* 0x60 */ DiscountPrice uint32 + /* 0x64 */ Condition uint32 + /* 0x68 */ ShopFlag byte + /* 0x69 */ MoneyFlag byte + /* 0x6A */ TimeFlag byte + /* 0x6B */ TimeByte byte + /* 0x6C */ Point uint32 + /* 0x70 */ Unknown [0x1C]byte + /* 0x8C */ StartTime pangya.SystemTime + /* 0x9C */ EndTime pangya.SystemTime + /* 0xAC */ Model string `struct:"[40]byte"` + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xDE */ _ [2]byte + /* 0xE0 */ +} + +type ItemV13_F8 struct { + /* 0x00 */ Active bool + /* 0x01 */ _ [3]byte + /* 0x04 */ ID uint32 + /* 0x08 */ Name string `struct:"[64]byte"` + /* 0x48 */ MaxRank byte + /* 0x49 */ Icon string `struct:"[40]byte"` + /* 0x71 */ _ [3]byte + /* 0x74 */ Price uint32 + /* 0x78 */ DiscountPrice uint32 + /* 0x7C */ Condition uint32 + /* 0x80 */ ShopFlag byte + /* 0x81 */ MoneyFlag byte + /* 0x82 */ TimeFlag byte + /* 0x83 */ TimeByte byte + /* 0x84 */ Point uint32 + /* 0x88 */ Unknown [0x1C]byte + /* 0xA4 */ StartTime pangya.SystemTime + /* 0xB4 */ EndTime pangya.SystemTime + /* 0xC4 */ Model string `struct:"[40]byte"` + /* 0x6C */ Quantity uint16 + /* 0x6E */ Unknown2 [4]uint16 + /* 0xF6 */ _ [2]byte + /* 0xF8 */ +} diff --git a/pangya/iff/item_test.go b/pangya/iff/item_test.go new file mode 100644 index 0000000..ffde431 --- /dev/null +++ b/pangya/iff/item_test.go @@ -0,0 +1,33 @@ +package iff + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadItem(t *testing.T) { + testLoadItemVersion(t, "testdata/eu301/item.iff") + testLoadItemVersion(t, "testdata/eu500/item.iff") + testLoadItemVersion(t, "testdata/in212/item.iff") + testLoadItemVersion(t, "testdata/jp211/item.iff") + testLoadItemVersion(t, "testdata/jp401/item.iff") + testLoadItemVersion(t, "testdata/jp585/item.iff") + testLoadItemVersion(t, "testdata/jp977/item.iff") + testLoadItemVersion(t, "testdata/kr326/item.iff") + testLoadItemVersion(t, "testdata/sg216/item.iff") + testLoadItemVersion(t, "testdata/us431/item.iff") + testLoadItemVersion(t, "testdata/us500/item.iff") + testLoadItemVersion(t, "testdata/us852/item.iff") +} + +func testLoadItemVersion(t *testing.T, filename string) { + data := mustLoad(filename) + file, err := LoadItems(data) + if err != nil { + t.Fatalf("Loading %q failed: %v", filename, err) + } + assert.Equal(t, "Test Item", file.Records[0].Name) + assert.Equal(t, "item0_00", file.Records[0].Icon) + assert.Equal(t, "item0_00", file.Records[0].Model) +} diff --git a/pangya/iff/testdata/eu301/item.iff b/pangya/iff/testdata/eu301/item.iff new file mode 100644 index 0000000..4abaac6 Binary files /dev/null and b/pangya/iff/testdata/eu301/item.iff differ diff --git a/pangya/iff/testdata/eu500/item.iff b/pangya/iff/testdata/eu500/item.iff new file mode 100644 index 0000000..89e3698 Binary files /dev/null and b/pangya/iff/testdata/eu500/item.iff differ diff --git a/pangya/iff/testdata/in212/item.iff b/pangya/iff/testdata/in212/item.iff new file mode 100644 index 0000000..af7eecb Binary files /dev/null and b/pangya/iff/testdata/in212/item.iff differ diff --git a/pangya/iff/testdata/jp211/item.iff b/pangya/iff/testdata/jp211/item.iff new file mode 100644 index 0000000..5529bfb Binary files /dev/null and b/pangya/iff/testdata/jp211/item.iff differ diff --git a/pangya/iff/testdata/jp401/item.iff b/pangya/iff/testdata/jp401/item.iff new file mode 100644 index 0000000..41b9e3d Binary files /dev/null and b/pangya/iff/testdata/jp401/item.iff differ diff --git a/pangya/iff/testdata/jp585/item.iff b/pangya/iff/testdata/jp585/item.iff new file mode 100755 index 0000000..3c0b6c8 Binary files /dev/null and b/pangya/iff/testdata/jp585/item.iff differ diff --git a/pangya/iff/testdata/jp977/item.iff b/pangya/iff/testdata/jp977/item.iff new file mode 100644 index 0000000..261cbbe Binary files /dev/null and b/pangya/iff/testdata/jp977/item.iff differ diff --git a/pangya/iff/testdata/kr326/item.iff b/pangya/iff/testdata/kr326/item.iff new file mode 100644 index 0000000..7ae27dc Binary files /dev/null and b/pangya/iff/testdata/kr326/item.iff differ diff --git a/pangya/iff/testdata/sg216/item.iff b/pangya/iff/testdata/sg216/item.iff new file mode 100644 index 0000000..dc82194 Binary files /dev/null and b/pangya/iff/testdata/sg216/item.iff differ diff --git a/pangya/iff/testdata/us431/item.iff b/pangya/iff/testdata/us431/item.iff new file mode 100644 index 0000000..d557d54 Binary files /dev/null and b/pangya/iff/testdata/us431/item.iff differ diff --git a/pangya/iff/testdata/us500/item.iff b/pangya/iff/testdata/us500/item.iff new file mode 100644 index 0000000..73a8f5a Binary files /dev/null and b/pangya/iff/testdata/us500/item.iff differ diff --git a/pangya/iff/testdata/us852/item.iff b/pangya/iff/testdata/us852/item.iff new file mode 100644 index 0000000..a0e1ad2 Binary files /dev/null and b/pangya/iff/testdata/us852/item.iff differ diff --git a/pangya/iff/version.go b/pangya/iff/version.go new file mode 100644 index 0000000..fa8df1b --- /dev/null +++ b/pangya/iff/version.go @@ -0,0 +1,8 @@ +package iff + +type Version uint32 + +const ( + Version11 Version = 11 + Version13 Version = 13 // US 852, JP 977 +) diff --git a/pangya/systemtime.go b/pangya/systemtime.go index bc03c1c..b87a16f 100644 --- a/pangya/systemtime.go +++ b/pangya/systemtime.go @@ -18,6 +18,40 @@ package pangya type SystemTime struct { - Year, Month, DayOfWeek, Day uint16 - Hour, Minute, Second, Milliseconds uint16 + /* 0x00 */ Year, Month, DayOfWeek, Day uint16 + /* 0x08 */ Hour, Minute, Second, Milliseconds uint16 + /* 0x10 */ +} + +func (s SystemTime) IsZero() bool { + return (s.Year == 0 && s.Month == 0 && s.DayOfWeek == 0 && s.Day == 0 && + s.Hour == 0 && s.Minute == 0 && s.Second == 0 && s.Milliseconds == 0) +} + +func (s SystemTime) IsValid() bool { + if s.Year < 1601 || s.Year > 30827 { + return false + } + if s.Month < 1 || s.Month > 12 { + return false + } + if s.DayOfWeek > 6 { + return false + } + if s.Day < 1 || s.Day > 31 { + return false + } + if s.Hour > 23 { + return false + } + if s.Minute > 59 { + return false + } + if s.Second > 59 { + return false + } + if s.Milliseconds > 999 { + return false + } + return true }