Skip to content

Instantly share code, notes, and snippets.

@hidez8891
Last active November 12, 2017 10:42
Show Gist options
  • Save hidez8891/aa296f4f9538782b305ccfd7f27ff513 to your computer and use it in GitHub Desktop.
Save hidez8891/aa296f4f9538782b305ccfd7f27ff513 to your computer and use it in GitHub Desktop.
itc2 (iTunes Artworks Cache Format) extractor
package main
import (
"fmt"
"image"
"image/color"
"image/jpeg"
"io"
"os"
"github.com/hidez8891/bitio"
)
const (
tagItchBlock = "itch"
tagArtwBlock = "artw"
tagItemBlock = "item"
tagDataBlock = "data"
)
type SectionSize struct {
Size int `byte:"4" endian:"big"`
}
type Itch struct {
Tag string `byte:"4"`
Unknown1 []byte `byte:"1" len:"16"`
}
type Artw struct {
Tag string `byte:"4"`
// many unknown filed ...
}
type Item struct {
Tag string `byte:"4"`
Unknown1 []byte `byte:"1" len:"40"`
ImgFormat string `byte:"4"`
Unknown2 []byte `byte:"1" len:"4"`
ImgWidth int `byte:"4" endian:"big"`
ImgHeight int `byte:"4" endian:"big"`
Unknown3 []byte `byte:"1" len:"128"`
}
type Data struct {
Tag string `byte:"4"`
// raw data field ...
}
func main() {
if len(os.Args) < 2 {
fmt.Printf("usage: %s file_path.itc2\n", os.Args[0])
os.Exit(1)
}
path := os.Args[1]
r, err := os.Open(path)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer r.Close()
// skip itch & artw field
ssize := &SectionSize{}
br := bitio.NewBitFieldReader(r)
if _, err := br.ReadStruct(ssize); err != nil {
fmt.Println(err)
os.Exit(1)
}
r.Seek(int64(ssize.Size), os.SEEK_SET)
// output files
extractImages(r)
}
func extractImages(r io.Reader) {
br := bitio.NewBitFieldReader(r)
for {
ssize := &SectionSize{}
if n, err := br.ReadStruct(ssize); err != nil || n == 0 {
return
}
item := &Item{}
if _, err := br.ReadStruct(item); err != nil {
fmt.Println(err)
return
}
if item.Tag != tagItemBlock {
fmt.Println("Error: not found item field")
return
}
data := &Data{}
if _, err := br.ReadStruct(data); err != nil {
fmt.Println(err)
return
}
if data.Tag != tagDataBlock {
fmt.Println("Error: not found data field", []byte(data.Tag))
return
}
switch item.ImgFormat {
case "bGRA":
if err := extractBGRA(br, item.ImgWidth, item.ImgHeight); err != nil {
fmt.Println(err)
return
}
default:
fmt.Println("Error: un-support format %v", item.ImgFormat)
if err := skip(br, ssize.Size-196-4); err != nil {
fmt.Println(err)
return
}
}
}
}
func skip(r *bitio.BitFieldReader, size int) error {
blockSize := 1024
for size > 0 {
if size < blockSize {
blockSize = size
}
size -= blockSize
block := make([]byte, blockSize)
if _, err := r.Read(block); err != nil {
return err
}
}
return nil
}
func extractBGRA(r *bitio.BitFieldReader, w, h int) error {
img := image.NewRGBA(image.Rect(0, 0, w, h))
pix := make([]byte, 4)
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
if _, err := r.Read(pix); err != nil {
return err
}
// bGRA -> RGBA
img.SetRGBA(x, y, color.RGBA{pix[2], pix[1], pix[0], pix[3]})
}
}
name := fmt.Sprintf("artw_%03dx%03d.jpg", w, h)
out, err := os.Create(name)
if err != nil {
return err
}
defer out.Close()
return jpeg.Encode(out, img, &jpeg.Options{80})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment