From c5e37d82f16d63dc9f17ea27347f1f22cc7304a5 Mon Sep 17 00:00:00 2001 From: John Chadwick Date: Fri, 9 Jun 2023 22:51:48 -0400 Subject: [PATCH] Initial item.iff loading support. --- pangya/iff/common.go | 30 +--- pangya/iff/course.go | 23 ++- pangya/iff/file.go | 104 ++++++++++++- pangya/iff/generic.go | 228 +++++++++++++++++++++++++++++ pangya/iff/helpers_test.go | 11 ++ pangya/iff/item.go | 222 ++++++++++++++++++++++++++++ pangya/iff/item_test.go | 33 +++++ pangya/iff/testdata/eu301/item.iff | Bin 0 -> 160 bytes pangya/iff/testdata/eu500/item.iff | Bin 0 -> 204 bytes pangya/iff/testdata/in212/item.iff | Bin 0 -> 128 bytes pangya/iff/testdata/jp211/item.iff | Bin 0 -> 184 bytes pangya/iff/testdata/jp401/item.iff | Bin 0 -> 224 bytes pangya/iff/testdata/jp585/item.iff | Bin 0 -> 224 bytes pangya/iff/testdata/jp977/item.iff | Bin 0 -> 256 bytes pangya/iff/testdata/kr326/item.iff | Bin 0 -> 160 bytes pangya/iff/testdata/sg216/item.iff | Bin 0 -> 160 bytes pangya/iff/testdata/us431/item.iff | Bin 0 -> 200 bytes pangya/iff/testdata/us500/item.iff | Bin 0 -> 204 bytes pangya/iff/testdata/us852/item.iff | Bin 0 -> 232 bytes pangya/iff/version.go | 8 + pangya/systemtime.go | 38 ++++- 21 files changed, 670 insertions(+), 27 deletions(-) create mode 100644 pangya/iff/generic.go create mode 100644 pangya/iff/helpers_test.go create mode 100644 pangya/iff/item.go create mode 100644 pangya/iff/item_test.go create mode 100644 pangya/iff/testdata/eu301/item.iff create mode 100644 pangya/iff/testdata/eu500/item.iff create mode 100644 pangya/iff/testdata/in212/item.iff create mode 100644 pangya/iff/testdata/jp211/item.iff create mode 100644 pangya/iff/testdata/jp401/item.iff create mode 100755 pangya/iff/testdata/jp585/item.iff create mode 100644 pangya/iff/testdata/jp977/item.iff create mode 100644 pangya/iff/testdata/kr326/item.iff create mode 100644 pangya/iff/testdata/sg216/item.iff create mode 100644 pangya/iff/testdata/us431/item.iff create mode 100644 pangya/iff/testdata/us500/item.iff create mode 100644 pangya/iff/testdata/us852/item.iff create mode 100644 pangya/iff/version.go 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 0000000000000000000000000000000000000000..4abaac621861decdcddec8ae6810463139fb57d5 GIT binary patch literal 160 zcmZQ%ICF-Zfq{YX%$YMlN+Kk+xJ1FTBsCW(jt?+o0@WDA8yFB!2Qs4qXfO;cV_*Q; JLnJtJ1^^5~K#>3d literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/eu500/item.iff b/pangya/iff/testdata/eu500/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..89e3698d1f193e215a7a9a53d8523ddd4816f580 GIT binary patch literal 204 zcmZQ%U|`?|Vn!ea0*R2+;t~bVlGI!XA2*o^RAUftV1Qc%T&4ofVqj20V&h|yU_9Iy E0JI+oO8@`> literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/in212/item.iff b/pangya/iff/testdata/in212/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..af7eecb16a209fdac9017e3660ed682459d2b9d7 GIT binary patch literal 128 zcmZQ%xL3~2z`(!=#6Tbsl3HA%;8~KI3*qA?Gl6Oh;tdRNtANW?z*!6oY)EVn69C=3 B2j&0( literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/jp211/item.iff b/pangya/iff/testdata/jp211/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..5529bfb537979d9414c4d16b8212b42ad5a64440 GIT binary patch literal 184 ycmZQ%xL3~2z`(!=#6Tbsl3HA%;8~KI3*nPaW&-sZ#2Xk8p|b)>4I2`h7$yKsiU;NZ literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/jp401/item.iff b/pangya/iff/testdata/jp401/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..41b9e3dc5a692cc525b5789865d7bf28df2cdd00 GIT binary patch literal 224 zcmZQ%xL?lAz`(!=#6Tbsl3HA%;8~KI3*nPaW&-sZ#2Xk8p|b)>jS`Sef>tD*_?ZA` CUJ9WA literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/jp585/item.iff b/pangya/iff/testdata/jp585/item.iff new file mode 100755 index 0000000000000000000000000000000000000000..3c0b6c82fcc43ed45e86fc652f404a8af44ce08b GIT binary patch literal 224 zcmZQ%ICF-Zfq{V$h=D*NB(=Ci!LuYa7s4l<%mnH+h&M1GLT3e%8YLt)E+z>^!;Js{ DO@#{M literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/jp977/item.iff b/pangya/iff/testdata/jp977/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..261cbbeb9626f41839d78121a81ca016b25cebdc GIT binary patch literal 256 zcmZQ%U|`?{Vn!ea0*R2+;t~bVlGI!XpL8-4sMjFgz<>yy6-a87kl0KNDM0tIg57ii NNt_Uq1gqgj0RTKB3W5Lt literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/kr326/item.iff b/pangya/iff/testdata/kr326/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..7ae27dc928c3e406a6f94fd67e1fb9e6e6148292 GIT binary patch literal 160 zcmZQ%xKPf`z`(!=#6Tbsl3HA%;8~KI3*qA?Gl6Oh;tdRNtANW?z*!6oY)EWkm;lk| B2iO1r literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/sg216/item.iff b/pangya/iff/testdata/sg216/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..dc8219443caf7bef356c297a10f9dae492c97219 GIT binary patch literal 160 zcmZQ%xL?lAz`(!=#6Tbsl3HA%;8~KI3*qA?Gl6Oh;tdRNtANW?z*!6oOh{~Em;l;o B2jc($ literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/us431/item.iff b/pangya/iff/testdata/us431/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..d557d544908c4e2f9547947a0eae76750747c725 GIT binary patch literal 200 zcmZQ%xKPf`z`(!=#6Tbsl3HA%;8~KI3*qA?Gl6Oh;tdRNtANW?z*!6oNl0v5OcIQS G8vy{T?F!NW literal 0 HcmV?d00001 diff --git a/pangya/iff/testdata/us500/item.iff b/pangya/iff/testdata/us500/item.iff new file mode 100644 index 0000000000000000000000000000000000000000..73a8f5ad20f6ea763a2bc72cdd3a671c6084fdc0 GIT binary patch literal 204 zcmZQ%U|`?|Vn!ea0*R2+;t~bVlGI!XA2*o^RAUftV1Qc%T&4ofVqi!@V&h|yU_9Iy E0Lvc 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 }