From c3fbdf72e51f0eb551fd58504767531eb5c53837 Mon Sep 17 00:00:00 2001 From: Aleteoryx Date: Tue, 16 Jun 2026 03:11:08 -0400 Subject: [PATCH] basically done --- extract.tcl | 78 ++++++++++++++--- pvrt.tcl | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 314 insertions(+), 10 deletions(-) create mode 100644 pvrt.tcl diff --git a/extract.tcl b/extract.tcl index eff3b9b4cbf0adac33f0e9647c3c6e9f3099b27f..58ff3e05c9f1e022bcbb468d24c057d50743891b 100755 --- a/extract.tcl +++ b/extract.tcl @@ -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 } diff --git a/pvrt.tcl b/pvrt.tcl new file mode 100644 index 0000000000000000000000000000000000000000..05099f189a44dd5589fbb920e25354153a291c41 --- /dev/null +++ b/pvrt.tcl @@ -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 + } +}