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
+ }
}
}