~aleteoryx/mtg-pac

c3fbdf72e51f0eb551fd58504767531eb5c53837 — Aleteoryx 3 months ago 168eaf2
basically done
2 files changed, 314 insertions(+), 10 deletions(-)

M extract.tcl
A pvrt.tcl
M extract.tcl => extract.tcl +68 -10
@@ 1,5 1,7 @@
#!/bin/env tclsh

#proc pvr2png

proc fatal {num reason} {
	global argv0
	puts stderr "fatal: $argv0: $reason"


@@ 17,31 19,87 @@ set fp [open $file rb]
binary scan [read $fp 0xc] {iii} magic numents startpos

if {$magic != 0x00434150} {
	die 2 "unknown magic: $magic"
} else {
	puts "magic = $magic, OK"
	die 2 "${file}: unknown magic: [format %08x $magic]"
}

set hdrlen [expr {0xc + ($numents * 4)}]
puts "header length: $hdrlen ($numents entries)"
puts "data offset: $startpos"
puts "${file}: header length: $hdrlen ($numents entries)"
puts "${file}: record start: $startpos"

puts "reading end table..."
set endhdr [read $fp [expr {$numents * 4}]]
binary scan $endhdr "i$numents" endtab
puts $endtab
puts "${file}: record end table: $endtab"

seek $fp $startpos

file mkdir $dir

set pos $startpos
set i 0
foreach endpt $endtab {
	set id [format %04d $i]
	set filebase $dir/$id

	set length [expr {$endpt - $pos}]
	set data [read $fp $length]
	set fp2 [open $dir/[format %08x $endpt].bin wb]
	puts $fp2 $data
	close $fp2

	set type [string range $data 0 3]
	switch -- $type {
	NZIP {
		puts "${file}: record $id: Deflate Compressed"

		set data [string range $data 8 end]
		set decom [zlib inflate $data]

		set magic [string range $decom 0 3]
		if {$magic in {GBIX PVRT}} {
			set ext pvr
			puts "${file}: record $id (decompressed): PVRT Image Data"
		} else {
			set ext bin
			binary scan $type i tynum
			puts "${file}: record $id (decompressed): Unknown (magic 0x[format %08x $tynum])"
		}

		set fp2 [open ${filebase}.${ext} wb]
		puts $fp2 $decom
		close $fp2

		if {$ext == "pvr"} {
			pvrt2tga::convert ${filebase}.${ext} ${filebase}.tga
			exec -- convert ${filebase}.tga ${filebase}.png
			file delete ${filebase}.tga
			puts "${file}: record $id (decompressed): Converted to PNG"
		}
	}
	NJCM {
		puts "${file}: record $id: Ninja Chunk Model"
		set fp2 [open ${filebase}.njcm wb]
		puts $fp2 $data
		close $fp2
	}
	NJTL {
		puts "${file}: record $id: Ninja Texture List"
		puts "${filebase}.njcm"
		set fp2 [open ${filebase}.njtl wb]
		puts $fp2 $data
		close $fp2
	}
	NMDM {
		puts "${file}: record $id: Ninja Motion"
		set fp2 [open ${filebase}.nmdm wb]
		puts $fp2 $data
		close $fp2
	}
	default {
		binary scan $type i tynum
		puts "${file}: record $id: Unknown (magic 0x[format %08x $tynum])"
		set fp2 [open ${filebase}.rec wb]
		puts $fp2 $data
		close $fp2
	}
	}

	set pos $endpt
	incr i
}

A pvrt.tcl => pvrt.tcl +246 -0
@@ 0,0 1,246 @@
proc bindex {buf idx} {
	binary scan $buf x${idx}c byte
	expr {$byte & 0xff}
}

namespace eval ::pvrt2tga {}
namespace eval ::pvrt2tga::pfmt {}
namespace eval ::pvrt2tga::dfmt {
	variable tmap {0 1}
}

# src = raw buffer, dst = []BGRA
proc ::pvrt2tga::pfmt::lookup {code} {
	switch -- $code {
	0 { return ::pvrt2tga::pfmt::argb1555 }
	1 { return ::pvrt2tga::pfmt::rgb565 }
	2 { return ::pvrt2tga::pfmt::argb4444 }
	default { return "" }
	}
}
proc ::pvrt2tga::pfmt::argb1555 {srcvar srcidx dstvar dstidx} {
	upvar $srcvar srcbuf $dstvar dstbuf

	set srcidx [expr {$srcidx * 2}]
	binary scan $srcbuf x${srcidx}s pixel

	set dstidx [expr {$dstidx * 4}]
	lset dstbuf $dstidx+0 [expr {($pixel        & 0x1f) * 0xff / 0x1f}]
	lset dstbuf $dstidx+1 [expr {($pixel >> 0x5 & 0x1f) * 0xff / 0x1f}]
	lset dstbuf $dstidx+2 [expr {($pixel >> 0xa & 0x1f) * 0xff / 0x1f}]
	lset dstbuf $dstidx+3 [expr {($pixel >> 0xf & 0x01) * 0xff       }]
}
proc ::pvrt2tga::pfmt::rgb565 {srcvar srcidx dstvar dstidx} {
	upvar $srcvar srcbuf $dstvar dstbuf

	set srcidx [expr {$srcidx * 2}]
	binary scan $srcbuf x${srcidx}s pixel

	set dstidx [expr {$dstidx * 4}]
	lset dstbuf $dstidx+0 [expr {($pixel        & 0x1f) * 0xff / 0x1f}]
	lset dstbuf $dstidx+1 [expr {($pixel >> 0x5 & 0x3f) * 0xff / 0x3f}]
	lset dstbuf $dstidx+2 [expr {($pixel >> 0xb & 0x1f) * 0xff / 0x1f}]
	lset dstbuf $dstidx+3 255
}
proc ::pvrt2tga::pfmt::argb4444 {srcvar srcidx dstvar dstidx} {
	upvar $srcvar srcbuf $dstvar dstbuf

	set srcidx [expr {$srcidx * 2}]
	binary scan $srcbuf x${srcidx}s pixel

	set dstidx [expr {$dstidx * 4}]
	lset dstbuf $dstidx+0 [expr {($pixel        & 0xf) * 0x11}]
	lset dstbuf $dstidx+1 [expr {($pixel >> 0x4 & 0xf) * 0x11}]
	lset dstbuf $dstidx+2 [expr {($pixel >> 0x8 & 0xf) * 0x11}]
	lset dstbuf $dstidx+3 [expr {($pixel >> 0xc & 0xf) * 0x11}]
}

proc ::pvrt2tga::dfmt::extend_tmap {len} {
	variable tmap
	for {set i [llength $tmap]} {$i < $len} {incr i} {
		lappend tmap [expr {(([lindex $tmap $i-1] | 0xaaaaaaaa) + 1) & 0x55555555}]
	}
}

# src = raw buffer, dst = []BGRA
proc ::pvrt2tga::dfmt::lookup {code} {
	switch -- $code {
	1  { return ::pvrt2tga::dfmt::square_twid }
	3  { return ::pvrt2tga::dfmt::vq }
	4  { return ::pvrt2tga::dfmt::vq_mipmap }
	9  { return ::pvrt2tga::dfmt::rect }
	13 { return ::pvrt2tga::dfmt::rect_twid }
	16 { return ::pvrt2tga::dfmt::small_vq }
	default { return "" }
	}
}
proc ::pvrt2tga::dfmt::square_twid {pfmt srcvar dstvar w h} {
	variable tmap
	upvar $srcvar srcbuf $dstvar dstbuf

	extend_tmap $w

	set dstidx 0
	for {set y 0} {$y < $h} {incr y} {
		for {set x 0} {$x < $w} {incr x} {
			set srcidx [expr {[lindex $tmap $x] << 1 | [lindex $tmap $y]}]
			$pfmt srcbuf $srcidx dstbuf $dstidx
			incr dstidx
		}
	}
}
proc ::pvrt2tga::dfmt::vq {pfmt srcvar dstvar w h {mipmaps 0} {small 0}} {
	variable tmap
	upvar $srcvar srcbuf $dstvar dstbuf

	extend_tmap $w

	if {$small} {
		if {$w <= 16} {
			set pallen 64
		} elseif {$w <= 32} {
			set pallen 128
		} elseif {$w <= 64} {
			set pallen 512
		} else {
			set pallen 1024
		}
	} else {
		set pallen 1024
	}

	set palbuf [lrepeat [expr {$pallen * 4}] 0]
	for {set i 0} {$i < $pallen} {incr i} {
		$pfmt srcbuf $i palbuf $i
	}
	set srcbuf [string range $srcbuf [expr {$pallen * 2}] end]

	if {$mipmaps} {
		set w2 [expr {int(pow(2, int(log($w) / log(2))))}]
		set mipmaplen [lindex $tmap [expr {($w2 >> 1) - 1}]]
		set srcbuf [string range $srcbuf $mipmaplen+1 end]
	}

	set dstidx 0
	for {set y 0} {$y < $h / 2} {incr y} {
		for {set x 0} {$x < $w / 2} {incr x} {
			set srcidx [expr {[lindex $tmap $x] << 1 | [lindex $tmap $y]}]
			set palidx [expr {[bindex $srcbuf $srcidx] * 16}]

			for {set x2 0} {$x2 < 2} {incr x2} {
				for {set y2 0} {$y2 < 2} {incr y2} {
					set dstidx [expr {(($y * 2 + $y2) * $w + ($x * 2) + $x2) * 4}]

					# puts "$w $h $pallen $palidx [llength $dstbuf] $dstidx"

					lset dstbuf $dstidx+0 [lindex $palbuf $palidx+0]
					lset dstbuf $dstidx+1 [lindex $palbuf $palidx+1]
					lset dstbuf $dstidx+2 [lindex $palbuf $palidx+2]
					lset dstbuf $dstidx+3 [lindex $palbuf $palidx+3]

					incr palidx 4
				}
			}
		}
	}
}
proc ::pvrt2tga::dfmt::vq_mipmap {pfmt srcvar dstvar w h} {
	upvar $srcvar srcbuf $dstvar dstbuf
	vq $pfmt srcbuf dstbuf $w $h 1

}
proc ::pvrt2tga::dfmt::small_vq {pfmt srcvar dstvar w h} {
	upvar $srcvar srcbuf $dstvar dstbuf
	vq $pfmt srcbuf dstbuf $w $h 0 1

}
proc ::pvrt2tga::dfmt::rect {pfmt srcvar dstvar w h} {
	variable tmap
	upvar $srcvar srcbuf $dstvar dstbuf

	extend_tmap $w

	set i 0
	for {set y 0} {$y < $h} {incr y} {
		for {set x 0} {$x < $w} {incr x} {
			$pfmt srcbuf $i dstbuf $i
			incr i
		}
	}
}
proc ::pvrt2tga::dfmt::rect_twid {pfmt srcvar dstvar w h} {
	variable tmap
	upvar $srcvar srcbuf $dstvar dstbuf

	set size [expr {min($w, $h)}]
	extend_tmap $size

	set nblk [expr {($w / $size) * ($h / $size)}]

	set dstidx 0
	for {set blk 0} {$blk < $nblk} {incr blk} {
		if {$size != $w} {
			set dstidx [expr {$blk * $size}]
		}

		for {set y 0} {$y < $size} {incr y} {
			for {set x 0} {$x < $size} {incr x} {
				set srcidx [expr {($size*$size*$blk) + ([lindex $tmap $x] << 1 | [lindex $tmap $y])}]
				$pfmt srcbuf $srcidx dstbuf $dstidx
				incr dstidx
			}
			if {$size != $w} {
				incr dstidx [expr {$size * ($nblk - 1)}]
			}
		}
	}
}


proc ::pvrt2tga::convert {src dst} {
	set sfp [open $src rb]

	if {[read $sfp 4] == "GBIX"} {
		seek $sfp 16
	}
	if {[read $sfp 4] != "PVRT"} {
		puts "$src not PVRT"
		close $sfp
		return
	}

	binary scan [read $sfp 12] iccsss len pfmt dfmt _ w h
	set srcbuf [read $sfp [expr {$len - 8}]]
	set dstbuf [lrepeat [expr {$w * $h * 4}] 0]

	if {$dfmt != 4} {
		close $sfp
		return
	}

	set pdec [pfmt::lookup $pfmt]
	set ddec [dfmt::lookup $dfmt]

	puts "$src $dst $pdec $ddec $w $h"

	$ddec $pdec srcbuf dstbuf $w $h

	set tga [binary format cccsscsssscbc* 0 0 2 0 0 0 0 0 $w $h 32 00100000 $dstbuf]
	set dfp [open $dst wb]
	puts $dfp $tga
	close $dfp

	close $sfp
}

# pvrt2tga::convert ./unpacs/GAME/NOWLOAD/0000.pvr ./unpacs/GAME/NOWLOAD/0000.tga

set dirs .
while {$dirs != ""} {
	set dirs [lassign $dirs dir]
	set dirs [concat $dirs [glob -nocomplain -directory $dir */]]

	foreach file [glob -nocomplain -directory $dir *.pvr] {
		pvrt2tga::convert $file [file rootname $file].tga
	}
}