diff --git a/AmlResImg.go b/AmlResImg.go new file mode 100644 index 0000000..941a4fe --- /dev/null +++ b/AmlResImg.go @@ -0,0 +1,119 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + + "github.com/hzyitc/AmlImg/AmlResImg" +) + +func res_unpack(filePath, extractPath string) error { + img, err := AmlResImg.NewReader(filePath, true) + if err != nil { + return errors.New("NewReader error: " + err.Error()) + } + defer img.Close() + + listfile, err := os.Create(extractPath + "/list.txt") + if err != nil { + return errors.New("Create error: " + err.Error()) + } + defer listfile.Close() + + for i := 0; i < int(img.Header.ItemCount); i++ { + item := img.Items[i] + + filename := fmt.Sprintf("%d.%s", item.Index, item.Name) + if item.Type == 0x090000 { + filename += ".bmp" + } + + println("Extracting ", extractPath+"/"+filename) + + fmt.Fprintf(listfile, "%08X:%s:%s\n", item.Type, item.Name, filename) + + file, err := os.Create(extractPath + "/" + filename) + if err != nil { + return errors.New("Create error:" + err.Error()) + } + + err = img.Seek(uint32(i), 0) + if err != nil { + file.Close() + return errors.New("Seek error:" + err.Error()) + } + + _, err = io.Copy(file, img) + if err != nil { + file.Close() + return errors.New("Copy error:" + err.Error()) + } + + file.Close() + } + + return nil +} + +func res_pack(filePath, dirPath string) error { + img, err := AmlResImg.NewWriter() + if err != nil { + return errors.New("NewWriter error: " + err.Error()) + } + + listfile, err := os.Open(dirPath + "/list.txt") + if err != nil { + return errors.New("Open error: " + err.Error()) + } + defer listfile.Close() + + scanner := bufio.NewScanner(listfile) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + txt := scanner.Text() + if txt == "" { + continue + } else if strings.HasPrefix(txt, "#") { + continue + } + + c := strings.SplitN(txt, ":", 3) + Name := c[1] + filename := c[2] + + Type, err := strconv.ParseInt(c[0], 16, 24) + if err != nil { + return errors.New("ParseInt error: " + err.Error()) + } + + img.Add(uint32(Type), Name, func(w io.Writer) error { + println("Packing ", filename) + + file, err := os.Open(dirPath + "/" + filename) + if err != nil { + return errors.New("Open error: " + err.Error()) + } + + _, err = io.Copy(w, file) + return err + }) + } + + err = scanner.Err() + if err != nil { + return errors.New("scanner error: " + err.Error()) + } + + err = img.Write(filePath, 2) + if err != nil { + return errors.New("Write error: " + err.Error()) + } + + return nil +} diff --git a/AmlResImg/header.go b/AmlResImg/header.go new file mode 100644 index 0000000..acb24a2 --- /dev/null +++ b/AmlResImg/header.go @@ -0,0 +1,30 @@ +package AmlResImg + +import ( + "encoding/binary" + "io" +) + +const ( + Header_Magic = uint64(0x215345525F4C4D41) // "AML_RES!" +) + +type Header struct { + CRC uint32 + Version uint32 + Magic uint64 + Size uint32 + ItemCount uint32 + AlignSize uint32 + Reserved [36]byte +} + +func Header_Unpack(reader io.Reader) (*Header, error) { + header := Header{} + err := binary.Read(reader, binary.LittleEndian, &header) + return &header, err +} + +func (header *Header) Pack(writer io.Writer) error { + return binary.Write(writer, binary.LittleEndian, header) +} diff --git a/AmlResImg/item.go b/AmlResImg/item.go new file mode 100644 index 0000000..da74899 --- /dev/null +++ b/AmlResImg/item.go @@ -0,0 +1,102 @@ +package AmlResImg + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +const ( + Item_Magic = uint32(0x27051956) +) + +type Item_v2 struct { + Magic uint32 + Header_CRC uint32 + Size uint32 + DataOffset uint32 + Entry uint32 + NextItemOffset uint32 + Data_CRC uint32 + Index uint8 + Type1 uint8 + Type2 uint8 + Type3 uint8 + Name [32]byte +} + +type Item struct { + Magic uint32 + Header_CRC uint32 + Size uint32 + DataOffset uint32 + Entry uint32 + NextItemOffset uint32 + Data_CRC uint32 + Index uint8 + Type uint32 + Name string +} + +func Item_Unpack(reader io.Reader, version uint32) (*Item, error) { + if version == 2 { + d := Item_v2{} + + buf := make([]byte, binary.Size(&d)) + _, err := io.ReadFull(reader, buf) + if err != nil { + return nil, err + } + + err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &d) + if err != nil { + return nil, err + } + + // crc := uint32(0xffffffff) + // crc = AmlImg.AmlCRC(crc, buf[:4]) + // crc = AmlImg.AmlCRC(crc, buf[8:]) + // if d.Header_CRC != crc { + // return nil, fmt.Errorf("incorrect crc: should %08X but is %08X", d.Header_CRC, crc) + // } + + return &Item{ + d.Magic, + d.Header_CRC, + d.Size, + d.DataOffset, + d.Entry, + d.NextItemOffset, + d.Data_CRC, + d.Index, + uint32(d.Type1)<<16 | uint32(d.Type2)<<8 | uint32(d.Type3)<<0, + string(bytes.TrimRight(d.Name[:], "\x00")), + }, nil + } else { + return nil, fmt.Errorf("unsupport version: %d", version) + } +} + +func (item *Item) Pack(writer io.Writer, version uint32) error { + if version == 2 { + d := Item_v2{ + item.Magic, + item.Header_CRC, + item.Size, + item.DataOffset, + item.Entry, + item.NextItemOffset, + item.Data_CRC, + item.Index, + uint8((item.Type >> 16) & 0xFF), + uint8((item.Type >> 8) & 0xFF), + uint8((item.Type >> 0) & 0xFF), + [32]byte{}, + } + copy(d.Name[:], []byte(item.Name)) + return binary.Write(writer, binary.LittleEndian, d) + } else { + return fmt.Errorf("unsupport version: %d", version) + } +} diff --git a/AmlResImg/reader.go b/AmlResImg/reader.go new file mode 100644 index 0000000..395d331 --- /dev/null +++ b/AmlResImg/reader.go @@ -0,0 +1,112 @@ +package AmlResImg + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "os" + + "github.com/hzyitc/AmlImg/AmlCRC" +) + +type ImageReader struct { + file *os.File + + Header *Header + Items []*Item + + remain uint64 +} + +func NewReader(path string, check bool) (*ImageReader, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + + header, err := Header_Unpack(file) + if err != nil { + return nil, err + } + + if header.Magic != Header_Magic { + return nil, fmt.Errorf("incorrect magic: should %08X but is %08X", Header_Magic, header.Magic) + } + + if check { + _, err = file.Seek(4, io.SeekStart) + if err != nil { + return nil, err + } + + crc := uint32(0xffffffff) + var buf [4096]byte + for { + n, err := file.Read(buf[:]) + crc = AmlCRC.AmlCRC(crc, buf[:n]) + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return nil, err + } + } + + if header.CRC != crc { + return nil, fmt.Errorf("incorrect crc: should %08X but is %08X", header.CRC, crc) + } + } + + next := int64(binary.Size(Header{})) + items := make([]*Item, header.ItemCount) + for i := 0; i < int(header.ItemCount); i++ { + _, err = file.Seek(next, io.SeekStart) + if err != nil { + return nil, err + } + + items[i], err = Item_Unpack(file, header.Version) + if err != nil { + return nil, err + } + + if items[i].Magic != Item_Magic { + return nil, fmt.Errorf("item[%d]: incorrect magic: should %08X but is %08X", i, Header_Magic, header.Magic) + } + + next = int64(items[i].NextItemOffset) + } + + return &ImageReader{ + file, + header, + items, + 0, + }, nil +} + +func (r *ImageReader) Seek(id uint32, offset uint64) error { + item := r.Items[id] + _, err := r.file.Seek(int64(item.DataOffset)+int64(offset), io.SeekStart) + r.remain = uint64(item.Size) - offset + return err +} + +func (r *ImageReader) Read(b []byte) (int, error) { + if r.remain == 0 { + return 0, io.EOF + } + + size := cap(b) + if size > int(r.remain) { + size = int(r.remain) + } + + n, err := r.file.Read(b[:size]) + r.remain -= uint64(n) + return n, err +} + +func (r *ImageReader) Close() { + r.file.Close() +} diff --git a/AmlResImg/writer.go b/AmlResImg/writer.go new file mode 100644 index 0000000..d5a036e --- /dev/null +++ b/AmlResImg/writer.go @@ -0,0 +1,153 @@ +package AmlResImg + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "os" + + "github.com/hzyitc/AmlImg/AmlCRC" +) + +type ImageWriter struct { + items []*imageWriter_Item +} + +type imageWriter_Item struct { + Type uint32 + Name string + callback func(w io.Writer) error +} + +func NewWriter() (*ImageWriter, error) { + return &ImageWriter{ + make([]*imageWriter_Item, 0), + }, nil +} + +func (w *ImageWriter) Add(Type uint32, Name string, callback func(w io.Writer) error) { + w.items = append(w.items, &imageWriter_Item{ + Type, + Name, + callback, + }) +} + +func (w *ImageWriter) Write(path string, version uint32) error { + file, err := os.Create(path) + if err != nil { + return err + } + + items_current := 0 + int64(binary.Size(Header{})) + item_size := 0 + if version == 2 { + item_size = binary.Size(Item_v2{}) + } else { + return fmt.Errorf("unsupport version: %d", version) + } + data_current := items_current + int64(item_size*len(w.items)) + + for i, item := range w.items { + _, err := file.Seek(data_current, io.SeekStart) + if err != nil { + return err + } + + err = item.callback(file) + if err != nil { + return err + } + + current, err := file.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + size := current - data_current + + if current%16 != 0 { + n, err := file.Write(make([]byte, 16-(current%16))) + if err != nil { + return err + } + current += int64(n) + } + + _, err = file.Seek(items_current, io.SeekStart) + if err != nil { + return err + } + + next := uint32(items_current) + uint32(item_size) + if i == len(w.items)-1 { + next = 0 + } + + err = (&Item{ + Magic: Item_Magic, + Header_CRC: 0, + Size: uint32(size), + DataOffset: uint32(data_current), + Entry: 0, + NextItemOffset: next, + Data_CRC: 0, + Index: uint8(i), + Type: item.Type, + Name: item.Name, + }).Pack(file, version) + if err != nil { + return err + } + + items_current += int64(item_size) + data_current = current + } + + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return err + } + + header := &Header{ + CRC: 0, + Version: version, + Magic: Header_Magic, + Size: uint32(data_current), + ItemCount: uint32(len(w.items)), + AlignSize: 16, + } + err = header.Pack(file) + if err != nil { + return err + } + + _, err = file.Seek(4, io.SeekStart) + if err != nil { + return err + } + + crc := uint32(0xffffffff) + var buf [4096]byte + for { + n, err := file.Read(buf[:]) + crc = AmlCRC.AmlCRC(crc, buf[:n]) + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return err + } + } + + header.CRC = crc + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return err + } + err = header.Pack(file) + if err != nil { + return err + } + + return nil +} diff --git a/main.go b/main.go index 11b7af0..cc4c774 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,8 @@ func usage() { print("Usage:\n") print(" " + os.Args[0] + " unpack \n") print(" " + os.Args[0] + " pack \n") + print(" " + os.Args[0] + " res_unpack \n") + print(" " + os.Args[0] + " res_pack \n") } func main() { @@ -35,5 +37,20 @@ func main() { println(err.Error()) return } + case "res_unpack": + os.MkdirAll(os.Args[3], 0755) + + err := res_unpack(os.Args[2], os.Args[3]) + if err != nil { + println(err.Error()) + return + } + + case "res_pack": + err := res_pack(os.Args[2], os.Args[3]) + if err != nil { + println(err.Error()) + return + } } }