Last active
November 12, 2017 10:42
-
-
Save hidez8891/aa296f4f9538782b305ccfd7f27ff513 to your computer and use it in GitHub Desktop.
itc2 (iTunes Artworks Cache Format) extractor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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