Skip to content

Instantly share code, notes, and snippets.

@zpmorgan
Forked from strycore/setup.sh
Last active December 20, 2015 07:59
Show Gist options
  • Save zpmorgan/6096906 to your computer and use it in GitHub Desktop.
Save zpmorgan/6096906 to your computer and use it in GitHub Desktop.
cd "Wave Bank"
find . -name "*.wav" -exec ffmpeg -i {} -acodec pcm_u8 {}.conv.wav \;
#find . -name "*.m4a" -exec faad {} \;
#mmv -v -c "Wave Bank_0000*.xwma.m4a.wav.wma" "Music_#1"
#mmv -v -c "Wave Bank_0001*.xwma.m4a.wav.wma" "Music_1#1"
mmv -v -c "Wave Bank_0000*.wav.conv.wav" "Music_#1"
mmv -v -c "Wave Bank_0001*.wav.conv.wav" "Music_1#1"
import struct
import os
import glob
def main(outFileName, files, name="Wave Bank"):
with open(outFileName, "wb") as outFile:
outFile.write("WBND")
outFile.write(struct.pack("II",
46, #major version
44 #minor version
))
wmas = sorted(glob.glob('Wave Bank/Music_*'), key=lambda x:int(x.split('_')[1]))
for wma in wmas:
print wma
bankData = struct.pack("II64sIIIIII",
0, #flags
len(wmas),
name,
24, #entry meta data size
64, #entry name size
0, #alignment
0, #format data for compact format
0, #unkn?
0, #unkn?
)
metaDatas = []
playRegionCur = 0
for wma in wmas:
codec = 4
chans = 2
rate = 44100
align = 99
bits = 0
format = (
(codec & 0b11) |
((chans & 0b111) << 2) |
((rate & 0x3ffff) << (2+3)) |
((align & 0xff) << (2+3+18)) |
((bits & 1) << (2+3+18+8))
)
playDataSize = os.path.getsize(wma)
metaData = struct.pack("IIIIII",
0, #flags and duration...?
format,
playRegionCur, #play region offset
playDataSize, #play region length
0, #loop region offset
0 #loop region length
)
metaDatas.append(metaData)
playRegionCur += playDataSize
metaDatas = ''.join(metaDatas)
seekTable = ""
unknRegion = ""
headerFormat = "IIIIIIIIII"
headerEndOffset = outFile.tell()+struct.calcsize(headerFormat)
outFile.write(struct.pack(headerFormat,
headerEndOffset, #bank data offset
len(bankData), #bank data len
headerEndOffset+len(bankData), #entry metadata offset
len(metaDatas), #entry metadata len
headerEndOffset+len(bankData)+len(metaDatas), #seek table offset
len(seekTable), #seek table len
headerEndOffset+len(bankData)+len(metaDatas)+len(seekTable), #unkn region offset
len(unknRegion),
headerEndOffset+len(bankData)+len(metaDatas)+len(seekTable)+len(unknRegion), #play region offset
playRegionCur
))
assert outFile.tell() == headerEndOffset
outFile.write(bankData)
outFile.write(metaDatas)
outFile.write(seekTable)
outFile.write(unknRegion)
for wma in wmas:
outFile.write(open(wma, "rb").read())
if __name__ == "__main__":
from sys import argv
main(argv[1], argv[2:])
#!/bin/bash
# installing dependencies
dependencies="libmonogame-cil ffmpeg mmv libopenal1 zenity"
install_deps=0
for package in $dependencies; do
echo $package
dpkg -s $package | grep installed
if [ $? == 0 ]; then
install_deps=1
fi
done
if [ install_deps == 1 ]; then
gksu "apt-get install -y $dependencies"
fi
# libopenal symlinking
libopenal_path=$(find /usr/lib -maxdepth 2 -name libopenal.so.1)
if [ ! -f $libopenal_path ]; then
echo "libopenal.so.1 missing"
exit 2
fi
local_libopenal=/usr/local/lib/libopenal.so.0
if [ ! -f $local_libopenal ]; then
gksu cp $libopenal_path $local_libopenal
gksu ldconfig
fi
# get Terraria data
steam_path=$(zenity --file-selection --directory --title "Please select your Windows Steam directory")
terraria_path=$(find "$steam_path" -iname terraria -type d)
if [ ! -d "$terraria_path" ]; then
echo "Terraria data not found"
exit 2
fi
cp -r "$terraria_path/Content" .
# convert audio
cp sound-convert/* Content/
cd Content
perl xactxtract2.pl -x "Wave Bank.xwb"
./convertmusic.sh
python makeXwb.py "Wave Bank.xwb"
rm xa*.pm xa*.pl convertmusic.sh makeXwb.py
rm -r "Wave Bank"
cd ..
# setup launcher and icon
mkdir -p ~/.icons/
cp terraria.png ~/.icons
desktop_file="$HOME/.local/share/applications/Terraria.desktop"
rm -f $desktop_file
sed -e "s#%GAMEDIR%#"$PWD"#" Terraria.desktop > $desktop_file
zenity --question --text "Terraria setup is complete, launch game?"
if [ $? == 0 ]; then
mono Terraria.exe
fi
use Modern::Perl;
our $skriptName = 'xactxtract';
our $skriptVersion = 0.97;
use Math::BigInt;
use lib ('./t');
use xaWma;
use xaXwb;
use xaXsb;
use constant ('IS_SCRIPT', 0);
use constant ('DEBUG', 0);
use constant ('DINFO1', 0);
use constant ('XAXT_HELP', 0);
use constant ('XAXT_CHECKSUM', 1);
use constant ('XAXT_INJECT', 2);
use constant ('XAXT_XTRACT', 3);
use constant ('XAXT_INFO', 4);
use constant ('XAXT_SAFE', 1);
use constant ('XAXT_XMODE_INFO', 1);
use constant ('XAXT_XMODE_CONV', 2);
use constant ('XAXT_IS_XWB', 2);
use constant ('XAXT_IS_XSB', 4);
use constant ('RIFF_WAVE_PCM', 1);
use constant ('RIFF_WAVE_XBOX_ADPCM', 105);
use constant ('WAVEBANKMINIFORMAT_TAG_PCM', 0);
use constant ('WAVEBANKMINIFORMAT_TAG_XMA', 1);
use constant ('WAVEBANKMINIFORMAT_TAG_ADPCM', 2);
use constant ('WAVEBANKMINIFORMAT_TAG_WMA', 3);
use feature 'say';
use feature 'state';
use feature 'switch';
binmode(STDOUT, ':encoding(cp850)');
my $xaxt_mode = 0;
my $xtractmode = 3;
my $xaxt_safemode = 0;
my @infiles;
my ($myWavName, $myXsbName, $myXwbName, $myCueName, $filename);
# my $myvol = 90;
(my $arg = join(' ', @ARGV));
if (($arg =~ /-safe/)) {
shift(@ARGV);
($xaxt_safemode = 1);
}
do {
given ($ARGV[0]) {
when (/^-x/) {
($xaxt_mode = 3);
#($xtractmode = substr($ARGV[0], 2, 1));
$xtractmode = 2 if /-x2/;
(@infiles = @ARGV[1 .. $#ARGV]);
($filename = $ARGV[1]);
break;
}
when ('-c') {
($xaxt_mode = 1);
(@infiles = @ARGV[1 .. $#ARGV]);
($filename = $ARGV[1]);
break;
}
default {
if (($#ARGV > (-1))) {
(@infiles = @ARGV);
($xaxt_mode = 4);
($filename = $ARGV[0]);
}
else {
say("\n", uc($skriptName), " v$skriptVersion by Nachgrimm (aka Liandril)");
say('Tool zum Auslesen von XACT Soundbanks(.xsb) und Extrahieren von Wavebanks(.xwb)');
say("Aufrufm\366glichkeiten:");
say("$skriptName <FILENAME>.xsb/xwb ....liefert Infos zur Soundbank/Wavebank");
say("$skriptName -c <FILENAME>.xsb ....korrigiert Checksumme der Soundbank");
say("$skriptName -x <FILENAME>.xwb ....extrahiert .xWMAs aus Wavebank");
say("$skriptName -x2 <FILENAME>.xwb ....extrahiert u. konvertiert in .wav");
say("F\374r die Option -x2 muss sich das Programm xWMAEncode.exe (Teil des");
say('Microsoft DirectX SDK) im selben Verzeichnis befinden.');
say(q[Sollte extrahieren einer .xwb mit 'Out of Memory' error scheitern, dann:]);
say("$skriptName -safe -x2 <FILENAME>.xwb ..exrahiere+konvertiere im 'Safe Mode'");
exit;
}
}
}
};
my($infileTyp);
if ($filename =~ /\.xsb$/) {
$infileTyp = 4;
}
elsif ($filename =~ /\.xwb$/) {
$infileTyp ||= 2;
}
if ((($xaxt_mode == 3) and ($xtractmode & 2))) {
if ((not -e('xWMAEncode.exe'))) {
my $xWMAEncodeError =
"Das zum Konvertieren der .xwma Dateien in .wav ".
"Dateien benoetigte Programm \nxWMAEncode.exe ".
"konnte im akt. Verzeichnis nicht gefunden werden. ".
"xWMAEncode\nist Teil des frei erhaeltlichen ".
"Microsoft DirectX SDK(Version Maerz 2009).\n\n".
"The tool xWMAEncode.exe (necessary to convert" .
"the .xwma files to .wav) wasn't\nfound in the ".
"current directory. xWMAEncode is part of the ".
"freely obtainable\nMicrosoft DirectX SDK (Version: March 2009).\n";
die($xWMAEncodeError);
}
}
my $hasWildcard = 0;
do {
$hasWildcard = ($#infiles > 0) ? 1 : 0;
@infiles = grep(/.x(w|s)b$/, @infiles);
};
my($aktfile);
if (($xaxt_mode == 2)) {
print("Leider ist das Einf\374gen von Wavedateien (noch) nicht m\366glich, da die Hashfunktion von XSB unbekannt ist\n");
exit;
}
my ($wbNum, $sbNum, $wmaExtracted) = (0,0,0);
my(@res);
foreach $aktfile (@infiles) {
if (($infileTyp == 2)) {
(++$wbNum);
(($xaxt_mode == 3) and ($wmaExtracted += xtractXwb($aktfile)));
if (($xaxt_mode == 4)) {
(my $xwb = 'xaXwb'->new($aktfile));
$xwb->readHeader;
(my(@tmp) = $xwb->info);
(@res = map({($res[$_] + $tmp[$_]);} (0 .. $#tmp)));
$xwb->close;
}
(($xaxt_mode == 1) and print(($aktfile . " hat keine Checksumme(Option nur f\374r .xsb Dateien sinnvoll)\n")));
}
elsif (($infileTyp == 4)) {
(my $xsb = 'xaXsb'->new($aktfile));
$xsb->readAll;
if (($xaxt_mode == 4)) {
(++$sbNum);
(my(@tmp) = $xsb->info);
(@res = map({($res[$_] + $tmp[$_]);} (0 .. $#tmp)));
}
if (($xaxt_mode == 1)) {
(my $changed = $xsb->recalcChecksum);
if ($changed) {
$xsb->renameWriteAndClose('_org');
print(((' ' . ($aktfile . '_org')) . "=Backup der Orginaldatei\n"));
}
}
(($xaxt_mode == 3) and print(($aktfile . ": Extrahieren aus Soundbanks nicht m\366glich\n")));
$xsb->close;
}
else {
print("$aktfile ist weder eine .xsb noch eine .xwb Datei ?!?\n");
}
}
if (($hasWildcard and ($xaxt_mode == 4))) {
if (($infileTyp == 2)) {
print("Insgesamt: $wbNum Wavebank(s) (davon $res[0] streaming); WAVES: ");
say("$res[1] PCMs, $res[2] XMAs, $res[3] ADPCMs, $res[4] WMAs");
}
elsif (($infileTyp == 4)) {
print("Insgesamt: $sbNum Soundbanks mit $res[0] Standard-Cues, $res[1] ExtraCues und $res[2] Sounds\n");
}
}
elsif ((($hasWildcard and ($xaxt_mode == 3)) and ($infileTyp == 2))) {
print("Insgesamt: $wmaExtracted (x)WMA Dateien aus $wbNum Wavebanks extrahiert.\n");
}
exit;
my $wdTyp;
sub xtractXwb {
(my $infileName = shift(@_));
($infileName =~ /\.xwb$/);
(my $dirName = $`);
(my $outfileName = ($dirName . '_'));
($dirName = ('./' . $dirName));
(&mk_subdirs($dirName, 511) and die("Konnte Verzeichnis $dirName nicht anlegen"));
($dirName .= '/');
(my $xwb = 'xaXwb'->new($infileName, $xaxt_safemode));
(my $numWaves = $xwb->readHeader);
($| = 1);
print("$infileName : ");
($| = 0);
$xwb->readRest;
my ($wdFlags, $wdDur, $wdChan, $wdRate, $wdAlign, $wdPCM16, $wdOff, $wdLen);
my$waveData;
my@outWMAfiles;
my$outNonWMAFiles = 0;
(my $infoStep = int(($numWaves / 10)));
($infoStep = (($infoStep < 1) ? 1 : $infoStep));
(my(@infoXtractText) = split(//, 'Extracted ', 0));
(my(@infoConvText) = split(//, 'Converted ', 0));
(($xtractmode & 1) and print("$numWaves Waves. Writing:\n"));
my($infoFileName);
for my $i (0..$numWaves-1){
($wdFlags, $wdDur, $wdTyp, $wdChan, $wdRate, $wdAlign, $wdPCM16, $wdOff, $wdLen)
= $xwb->readWaveInfo($i);
($waveData = $xwb->readWaveData($wdOff, $wdLen));
($infoFileName = ($outfileName . sprintf('%05d', $i)));
(my $outfileNameFull = ($dirName . $infoFileName));
if (($wdTyp == 3)) {
(my $wma = 'xaWma'->new(($outfileNameFull . '.xwma')));
($waveData = $xwb->readWaveData($wdOff, $wdLen));
$wma->writeXWma($i, ($infoFileName . '.xwma'), $wdFlags, $wdDur, $wdTyp, $wdChan, $wdRate, $wdAlign, $wdPCM16, $wdOff, $wdLen, (\$waveData), ($xtractmode & 1));
$wma->flush;
$wma->close;
push(@outWMAfiles, $outfileNameFull);
}
elsif (($wdTyp == 0)) {
(my $wma = 'xaWma'->new(($outfileNameFull . '.wav')));
($waveData = $xwb->readWaveData($wdOff, $wdLen));
$wma->writePCM($i, ($infoFileName . '.xwma'), $wdFlags, $wdDur, $wdTyp, $wdChan, $wdRate, $wdAlign, $wdPCM16, $wdOff, $wdLen, (\$waveData), ($xtractmode & 1));
$wma->flush;
$wma->close;
$outNonWMAFiles++;
}
else {
();
}
if ((not (($xtractmode & 1) || ($i % $infoStep)))) {
print($infoXtractText[int(($i / $infoStep))]);
}
}
my $extractedFiles = ($#outWMAfiles + 1);
($infoStep = int(($extractedFiles / 10)));
($infoStep = (($infoStep < 1) ? 1 : $infoStep));
if (($xtractmode & 2)) {
my($encodeParas);
if ((not ($xtractmode & 1))) {
print('& ');
}
for ((my $j = 0); ($j < $extractedFiles); (++$j)) {
($encodeParas = (((('"' . $outWMAfiles[$j]) . '.xwma" "') . $outWMAfiles[$j]) . '.wav"'));
`wine xWMAEncode.exe $encodeParas`;
(unlink(($outWMAfiles[$j] . '.xwma')) or print((('Datei ' . $outWMAfiles[$j]) . ".xwma konnte nicht gel\366scht werden: $!\n")));
if (($xtractmode & 1)) {
print((('CONVERT ' . $outWMAfiles[$j]) . ".xwma to .wav\n"));
}
else {
if ((not ($j % $infoStep))) {
print($infoConvText[int(($j / $infoStep))]);
}
}
}
}
if ((not ($xtractmode & 1))) {
print((((' ' . $extractedFiles) + $outNonWMAFiles) . " Files\n"));
}
$xwb->close;
return(($extractedFiles + $outNonWMAFiles));
}
sub typ2String {
(my $typ = shift(@_));
given ($wdTyp) {
when ((($_ < 0) or ($_ > 3))) {
print("UNBEKANNTER AUDIOTYP $_\n");
}
when (($_ == 0)) {
return('PCM ');
}
when (($_ == 1)) {
return('XMA ');
}
when (($_ == 2)) {
return('ADPCM');
}
when (($_ == 3)) {
return('WMA');
}
default {
return("UNBEKANNTES FORMAT($wdTyp)?!?");
}
}
}
sub mk_subdirs {
(my $anzahl = @_);
my($dir, $rights);
if (($anzahl == 1)) {
($dir = shift(@_));
($$rights = 511);
}
elsif (($anzahl == 2)) {
(($dir, $rights) = @_);
}
(my(@dirs) = split(m[/], $dir, 0));
(my $akdir = '');
($dir =~ s/^\s+//);
($dir =~ s/\s+$//);
($dir =~ s[^/][]);
($dir =~ s[/$][]);
foreach $_ (@dirs) {
($akdir .= $_);
if ((not -e($akdir))) {
(my $res = mkdir($akdir, $rights));
(($res != 1) and return(1));
}
($akdir .= '/');
}
return(0);
}
#line 1 "xaWma.pm"
package xaWma;
my $skriptName= "xaWma.pm";
my $skriptVersion= 0.97;
use 5.010;
use Math::BigInt;
use constant XWB_HEADERSIZE => 52;
use constant WAVEBANK_TYPE_BUFFER => 0x00000000;
use constant WAVEBANK_TYPE_STREAMING => 0x00000001;
use constant WAVEBANK_TYPE_MASK => 0x00000001;
use constant WAVEBANKMINIFORMAT_TAG_PCM => 0x0;
use constant WAVEBANKMINIFORMAT_TAG_XMA => 0x1;
use constant WAVEBANKMINIFORMAT_TAG_ADPCM => 0x2;
use constant WAVEBANKMINIFORMAT_TAG_WMA => 0x3;
@aWMAAvgBytesPerSec = (12000, 24000, 4000, 6000, 8000, 20000);
@aWMABlockAlign =(929, 1487, 1280, 2230, 8917, 8192, 4459, 5945, 2304,1536, 1485, 1008, 2731, 4096,6827, 5462);
sub new {
my ($class, $ofilename)=@_;
my $this = {};
$this->{CNAME}=$class;
$this->{DATA}= undef;
$this->{OFNAME} = $ofilename;
$this->{HD} = ();
open (OFH,'>' ,$ofilename) || die "$class: Kann Datei $filename nicht zum Schreiben oeffnen: $!";
binmode (OFH);
$this->{OFHANDLE} = OFH;
bless($this, $class);
return $this;
}
sub getCName {
my $this=shift;
return $this->{CNAME};
}
sub getOFName {
my $this=shift;
return $this->{OFNAME};
}
sub getDSize {
my $this = shift;
return (length($this->{DATA}));
}
sub close {
my $this=shift;
$this->{HD}= undef;
$this->{DATA}= undef;
if ($this->{OFHANDLE}) { close $this->{OFHANDLE}; }
}
sub flush {
my $this=shift;
my $fh = $this->{OFHANDLE};
print $fh $this->{DATA};
}
sub getHeader {
my $this = shift;
return $this->{HD};
}
sub getHD {
my ($this, $key)=@_;
return ($this->{HD}->{$key});
}
sub setHD {
my ($this, $key, $value)=@_;
if (exists ($this->{HD}{$key})) {
if ($DEBUG) { say "addHD: lösche $key"; }
delete $this->{HD}{$key}
}
$this->{HD}->{$key}=$value;
}
sub getHDnum {
my $this=shift;
my $hashref=$this->getHeader();
return (keys %$hashref);
}
sub printBufferHex {
my $buffer=shift;
my $newline=16;
my $counter=0;
my $result="";
my @resBuffer=unpack('C*',$buffer);
foreach (@resBuffer) {
$result=$result.sprintf("%02x ",$_);
if ($counter < $newline) { $counter++; }
else {
$result=$result."\n";
$counter=0;
}
}
return $result;
}
sub writeXWma {
my ($this, $wNum, $shortFileName, $dFlags, $dDur, $dTyp, $dChan, $dRate, $dAlign, $dPCM16, $dOff, $dLength, $wdref, $printInfo)=@_;
if ($dTyp != WAVEBANKMINIFORMAT_TAG_WMA) {
if (DEBUG) { print "Wave $wNum ist kein WMA(?)\n"; }
return 0;
}
print "$shortFileName:" if ($printInfo);
print "Dur:$dDur,".$dChan."ch,$dRate\Hz,Align:$dAlign," if ($printInfo);
$this->writeString(0,"RIFF");
my $odRIchunkSize=$this->getDSize();
$this->writeLong(0);
$this->writeString(8,"XWMAfmt ");
$this->writeLong(18);
$this->writeWord(0x161);
$this->writeWord($dChan);
$this->writeLong($dRate);
my $dFMavgBytesPerSec=($dAlign > $#aWMAAvgBytesPerSec ? $aWMAAvgBytesPerSec[$dAlign >> 5] : $aWMAAvgBytesPerSec[$dAlign]);
my $dFMblockAlign=($dAlign > $#aWMABlockAlign ? $aWMABlockAlign[$dAlign & 0xf] :$aWMABlockAlign[$dAlign]);
$this->writeLong($dFMavgBytesPerSec);
$this->writeWord($dFMblockAlign);
$this->writeWord(16);
$this->writeWord(0);
$this->writeString($this->getDSize(),"dpds");
my $packetLength=$dFMblockAlign;
if ($dLength % $packetLength) { die $this->getCName."xWMAWrite:Paketgröße($packetLength) teilt Datenblockgröße($dLength) nicht restfrei";}
my $packetNum=$dLength / $packetLength;
$this->writeLong($packetNum * 4);
my $fullsize=round4096($dDur * 2);
my $allBlocks=int($fullsize/4096);
my $avgBlocksPerPacket=int($allBlocks/$packetNum);
my $spareBlocks=$allBlocks - ($avgBlocksPerPacket * $packetNum);
print "Pakete: $packetNum\n" if ($printInfo);
my $accu=0;
for(my $i=0; $i < $packetNum; $i++) {
$accu+=$avgBlocksPerPacket * 4096;
if ($spareBlocks) {
$accu+=4096;
$spareBlocks--;
}
$this->writeLong($accu);
}
$this->writeString($this->getDSize(),"data");
$this->writeLong($dLength);
$this->writeString($this->getDSize(),$$wdref);
$this->writeLong($odRIchunkSize,$this->getDSize() - 8);
return 0;
}
sub round4096 {
my $value=shift;
return ($value % 4096 ? (1+ int($value /4096))*4096 : $value);
}
sub writePCM {
my ($this, $wNum, $shortFileName, $dFlags, $dDur, $dTyp, $dChan, $dRate, $dAlign, $dPCM16, $dOff, $dLength, $wdref, $printInfo)=@_;
if ($dTyp != WAVEBANKMINIFORMAT_TAG_PCM) {
if (DEBUG) { print "Wave $wNum ist kein PCM(?)\n"; }
return 0;
}
print "$shortFileName:" if ($printInfo);
print "Dur:$dDur,".$dChan."ch,$dRate\Hz,Align:$dAlign," if ($printInfo);
$this->writeString(0,"RIFF");
my $odRIchunkSize=$this->getDSize();
$this->writeLong(0);
$this->writeString(8,"WAVEfmt ");
$this->writeLong(16);
$this->writeWord(0x0001);
$this->writeWord($dChan);
$this->writeLong($dRate);
my $wFMbitsPerSample = ($dPCM16 ? 16 : 8);
my $dFMblockAlign = $dChan * roundInt(($wFMbitsPerSample + 7) / 8);
my $dFMavgBytesPerSec=$dRate * $dFMblockAlign;
$this->writeLong($dFMavgBytesPerSec);
$this->writeWord($dFMblockAlign);
$this->writeWord($wFMbitsPerSample);
$this->writeString($this->getDSize(),"data");
$this->writeLong($dLength);
$this->writeString($this->getDSize(),$$wdref);
$this->writeLong($odRIchunkSize,$this->getDSize() - 8);
return 0;
}
sub roundInt {
my $value=shift;
my $intvalue=int($value);
return ( ($value - $intvalue < 0.5) ? $intvalue : $intvalue + 1);
}
sub ADPCM2PCM {
my ($this, $wdref, $dLength)=@_;
my $adpcm=$$wdref;
my $pcm="";
}
sub dhexStr {
my $value=shift;
return "$value(\$".hexStr($value).")";
}
sub hexStr {
my $value=shift;
return sprintf("%x",$value);
}
sub isInFile {
my ($this, $pos)=@_;
if (($pos > length($this->{DATA})) || ($pos < 0)) {
say $this->{CNAME}.":isInFile: Position $pos liegt ausserhalb des Buffers der Datei $this->{IFNAME} (Groesse: ",length($this->{DATA}),")\n";
return 0;
} else {
return ($pos ? $pos : 1);
}
}
sub readByte {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('C',substr($this->{DATA},$position,1));
}
sub readWord {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('S',substr($this->{DATA},$position,2));
}
sub readLong {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('L',substr($this->{DATA},$position,4));
}
sub readString {
my ($this, $position, $len)=@_;
if ($DEBUG) { $this->isInFile($position); }
return substr($this->{DATA},$position,$len);
}
sub writeQLong {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeLong: zu viele ($anzahl) Parameter: @_"; }
my $hex= new Math::BigInt $value;
my $hexLo=Math::BigInt->new(0);
my $hexHi=Math::BigInt->new(0);
$hexLo=$hex->copy();
$hexHi=$hex->copy();
print "hex:",$hex->as_hex();
$hexLo=$hexLo->band(Math::BigInt->new(0xFFFFFFFF));
$hexHi=$hexHi->brsft(32);
my $low=$hexLo->as_int();
my $high=$hexHi->as_int();
print ",hex-hi: $high, hex-lo: $low,";
print "HEX: $value -> $hex\n";
$this->writeLong($pos,$low);
$this->writeLong($pos+4,$high);
}
sub writeLong {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeLong: zu viele ($anzahl) Parameter: @_"; }
my $byteString=pack('V',$value);
substr($this->{DATA},$pos,4,$byteString);
}
sub writeWord {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeWord: zu viele ($anzahl) Parameter: @_"; }
my $byteString=pack('v',$value);
substr($this->{DATA},$pos,2,$byteString);
}
sub writeBigEndianLong {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeLong: zu viele ($anzahl) Parameter: @_"; }
my $byteString=pack('N',$value);
substr($this->{DATA},$pos,4,$byteString);
}
sub writeBigEndianWord {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeLong: zu viele ($anzahl) Parameter: @_"; }
my $byteString=pack('n',$value);
substr($this->{DATA},$pos,2,$byteString);
}
sub writeByte {
my $this=shift;
my $pos, $value;
my $anzahl=@_;
if ($anzahl == 1) {
$value=shift;
$pos=length($this->{DATA});
} elsif ($anzahl == 2) {
($pos, $value)=@_;
} else { die "xaWMA:writeWord: zu viele ($anzahl) Parameter: @_"; }
$this->writeString($pos, pack("C", $value), 1);
}
sub writeString {
my ($this, $pos, $value, $laenge)=@_;
my $byteString=$value;
my $length= $laenge ? $laenge : length($byteString);
if ($DEBUG) { $this->isInFile($pos); }
substr($this->{DATA},$pos,$length,$byteString);
}
sub writeBytes {
my ($this, $pos, $value, $laenge)=@_;
my $byteString=pack ("C*",$value);
my $length= $laenge ? $laenge : length($byteString);
$this->writeString($pos,$byteString,$length);
}
sub denull {
my $string = shift;
$string =~ s/\0//g if defined $string;
return $string;
}
sub checkByteValue {
my $myVal= shift;
return (($myVal > -1) && ($myVal < 256)) ? 1 : 0;
}
1;
#line 1 "xaX_b.pm"
package xaX_b;
use warnings;
use strict;
my $skriptVersion= 0.97;
use 5.010;
use Math::BigInt;
my $DEBUG = 0;
sub new {
my ($class, $ifilename)=@_;
my $this = {
CNAME => $class,
DATA => undef,
IFNAME => $ifilename,
IFSIZE => -s $ifilename,
IFGELESEN => 0,
HD => [],
};
my $IFH;
open ($IFH,'<' ,$ifilename) || die "$class: Kann Datei $ifilename nicht oeffnen: $!";
binmode ($IFH);
$this->{IFHANDLE} = $IFH;
print "$class:Konstruktor: $ifilename\n";
bless($this, $class);
return $this;
}
sub getCName {
my $this=shift;
return $this->{CNAME};
}
sub getAll {
my $this=shift;
say "INFO ANFANG";
print "CNAME: ".$this->{CNAME}.", getName(".$this->getCName()."),IFNAME: $this->{IFNAME}";
say "INFO ENDE";
}
sub getIFH {
my $this = shift;
if ($DEBUG) {
if (! defined ($this->{IFHANDLE})) { die "$this: Inputfilehandle für ".($this->{IFNAME})." existiert nicht"; }
}
return $this->{IFHANDLE};
}
sub get {
my ($this, $key)=@_;
return ($this->{HD}->{$key});
}
sub set {
if ($#_ % 2) { die "x_b:set: hat eine gerade Anzahl von Parametern @_"; }
my $this = shift;
my ($key, $value);
while (@_) {
$key=shift;
$value=shift;
if (exists ($this->{HD}{$key})) {
if ($DEBUG) { say "addHD: lösche $key"; }
delete $this->{HD}{$key}
}
$this->{HD}->{$key}=$value;
}
}
sub inject {
my ($this, $pos, $content, $debugstring) = @_;
my $len=length($content);
my $flen=$this->getDSize();
if (! $this->isInFile($pos)) { die $this->{CNAME}.":inject: Einfügeposition $pos liegt ausserhalb Datei"; }
my $pre=substr($this->{DATA},0,$pos);
my $post=substr($this->{DATA},$pos,$flen - $pos);
$this->{DATA}=$pre.$content.$post;
say $this->getCName()."inject $debugstring : Pos: $pos, Len: $len in Buffer (vorher $flen , nun ".$this->getDSize().")";
my @arrPos=@{$this->{HD}->{'arrPos'} };
my @arrLen=@{$this->{HD}->{'arrLen'} };
my $insPos=0;
for (0 .. $#arrPos) {
if ($pos > $arrPos[$_]) { $insPos++; }
}
splice @arrPos, $insPos, 0, $pos;
splice @arrLen, $insPos, 0, $len;
$this->{HD}->{'arrPos'}=\@arrPos;
$this->{HD}->{'arrLen'}=\@arrLen;
}
sub infoInject {
my $this = shift;
my $dSize=$this->getDSize();
my @arrPos=@{$this->{HD}->{'arrPos'} };
my @arrLen=@{$this->{HD}->{'arrLen'} };
my $total=0;
print "Info nach ".($#arrPos +1)." inserts: ";
for (0 .. $#arrPos) {
$total+=$arrLen[$_];
}
}
sub corrOffsetAfter {
my ($this, $oldPos) = @_;
my @arrPos=@{$this->{HD}->{'arrPos'} };
my @arrLen=@{$this->{HD}->{'arrLen'} };
my $delta=0;
for (0 .. $#arrPos) {
if ($oldPos < $arrPos[$_]) { last; }
else { $delta+=$arrLen[$_]; }
}
if (! $this->isInFile($oldPos+$delta)) { die $this->{CNAME}."corrOffset: Offset $oldPos um $delta verschoben liegt nicht mehr im File?!?"; }
return $oldPos+$delta;
}
sub corrOffsetBefore {
my ($this, $oldPos) = @_;
my @arrPos=@{$this->{HD}->{'arrPos'} };
my @arrLen=@{$this->{HD}->{'arrLen'} };
my $delta=0;
for (0 .. $#arrPos) {
if ($oldPos <= $arrPos[$_]) { last; }
else { $delta+=$arrLen[$_]; }
}
if (! $this->isInFile($oldPos+$delta)) { die $this->{CNAME}."corrOffset: Offset $oldPos um $delta verschoben liegt nicht mehr im File?!?"; }
return $oldPos+$delta;
}
sub corrPointer {
my ($this, $pos,$debugstring) = @_;
my $offset=$this->readLong($this->corrOffsetAfter($pos));
print "[corrPtr $debugstring:";
print "readPtr $pos->(".($this->corrOffsetAfter($pos)).")=";
if (! $this->isInFile($offset)) { die $this->{CNAME}."corrPointer: $pos => $offset liegt nicht in Datei"; }
say "val $offset->".($this->corrOffsetAfter($offset))."]";
$offset=$this->corrOffsetAfter($offset);
$this->writeLong($this->corrOffsetAfter($pos), $offset);
}
sub getIFSize {
my $this = shift;
print "$this: getIFSize".$this->{IFSIZE}."\n";
return $this->{IFSIZE};
}
sub getDSize {
my $this = shift;
return (length($this->{DATA}));
}
sub getDataString {
my $this = shift;
return ($this->{DATA});
}
sub readIF {
my ($this, $size) = @_;
my $buffer;
# some sections are apparently empty. Don't do any reading if that's the case.
my $readBytes = $size // ($this->{IFSIZE});
if ($readBytes > 0){
if ($DEBUG) { print $this->getCName().": Lese $readBytes Byte aus ".($this->{IFNAME}) };
my $handle = $this->getIFH;
my $nbytes = read($handle,$buffer,$readBytes);
if ($nbytes < $readBytes) {
say "Daten aus ".($this->{IFNAME})." konnten nicht gelesen werden";
say "uuh. Tried to read $readBytes. Actually read $nbytes.";
die;
};
$this->{IFGELESEN} += $nbytes;
$this->{DATA} .= $buffer;
}
if ($DEBUG) { say "=>insges. ".$this->{IFGELESEN}." Byte gelesen. Buffergröße:".$this->getDSize()};
return $this->getDSize();
}
sub readRest {
my $this = shift;
return $this->readIF( $this->getIFSize() - $this->getDSize());
}
sub directReadIF {
my ($this, $startpos, $size) = @_;
my $buffer;
seek($this->getIFH(), $startpos, 0);
if (read($this->getIFH(),$buffer,$size) < $size) { die "Daten aus ".($this->{IFNAME})." konnten nicht gelesen werden"};
return $buffer;
}
sub openIF {
my ($this, $ifilename)=@_;
if ($this->{IFHANDLE}) { $this->closeIF(); }
$this->{DATA}= undef;
$this->{HD}= undef;
$this->{IFNAME} = $ifilename;
$this->{IFSIZE} =-s $ifilename;
$this->{IFGELESEN}=0;
my $IFH;
open ($IFH,'<' ,$ifilename) || die $this->getCName()." Kann Datei $ifilename nicht oeffnen: $!";
binmode ($IFH);
$this->{IFHANDLE} = $IFH;
}
sub closeIF {
my $this = shift;
$this->{IFGELESEN}=0;
$this->{DATA}= undef;
$this->{HD}= undef;
if ($this->{IFHANDLE}) { close $this->{IFHANDLE}; }
}
sub printBufferHex {
my ($this,$buffer)=@_;
my $newline=16;
my $counter=0;
my $result="";
my @resBuffer=unpack('C*',$buffer);
foreach (@resBuffer) {
$result=$result.sprintf("%02x ",$_);
if ($counter < $newline) { $counter++; }
else {
$result=$result."\n";
$counter=0;
}
}
return $result;
}
sub printIFileHex {
my ($this, $pos, $size) = @_;
my $buffer;
if ($pos) { $buffer=substr($this->{DATA},$pos,$size); }
else { $buffer=$this->{DATA}; }
if ($DEBUG) { print "$this:printIFileHex: drucke Buffer mit Länge ".length($buffer)."\n"; }
print $this->printBufferHex($buffer)."\n";
}
sub IFBackup {
my ($this, $suffix)=@_;
$suffix ||= "_backup";
my $ifname= ($this->{IFNAME}=~/(\w+).x(w|s)b$/) ? $1 : die $this->{IFNAME}." hat nicht die Endung .xsb/.xwb";
if ($DEBUG) { print $this->getCName().": Filebackup $ifname.xsb -> $ifname.xsb$suffix\n"; }
system("copy $ifname.xsb $ifname.xsb$suffix")==0 or die "Fehler beim Anlegen der Backupdatei $ifname.xsb$suffix : $?";
}
sub IFRenameWriteAndClose {
my ($this, $suffix)=@_;
$suffix ||= "_backup";
my $ifname= $this->{IFNAME};
if ($DEBUG) { print $this->getCName().": Filebackup/rename $ifname -> $ifname$suffix\n"; }
close $this->{IFHANDLE};
rename($ifname,$ifname.$suffix) || die "IFRenameAndWrite: Fehler beim Umbenennen von $ifname : $!";
open (IFH,'>' ,$ifname) || die $this->getCName()." Kann Datei $ifname nicht oeffnen: $!";
binmode (IFH);
print IFH $this->{DATA};
close IFH;
$this->closeIF();
}
sub isInFile {
my ($this, $pos)=@_;
if (($pos > length($this->{DATA})) || ($pos < 0)) {
return 0;
} else {
return ($pos ? $pos : 1);
}
}
sub readByte {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('C',substr($this->{DATA},$position,1));
}
sub readWord {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('S',substr($this->{DATA},$position,2));
}
sub readLong {
my ($this, $position)=@_;
if ($DEBUG) { $this->isInFile($position); }
return unpack('L',substr($this->{DATA},$position,4));
}
sub readString {
my ($this, $position, $len)=@_;
if ($DEBUG) { $this->isInFile($position); }
return substr($this->{DATA},$position,$len);
}
sub readBytes {
my ($this, $position, $len)=@_;
return unpack ("C*",substr($this->{DATA},$position,$len));
}
sub writeLong {
my ($this, $pos, $value)=@_;
my $byteString=pack('V',$value);
substr($this->{DATA},$pos,4,$byteString);
}
sub writeWord {
my ($this, $pos, $value)=@_;
my $byteString=pack('v',$value);
substr($this->{DATA},$pos,2,$byteString);
}
sub writeByte {
my ($this, $pos, $value)=@_;
$this->writeString($pos, pack("C", $value), 1);
}
sub writeString {
my ($this, $pos, $value, $laenge)=@_;
my $byteString=$value;
my $length= $laenge ? $laenge : length($byteString);
if ($DEBUG) { $this->isInFile($pos); }
say "writeString: Pos $pos, val $value, byteString $byteString, len $length";
substr($this->{DATA},$pos,$length,$byteString);
}
sub writeBytes {
my ($this, $pos, $value, $laenge)=@_;
my $byteString=pack ("C*",$value);
my $length= $laenge ? $laenge : length($byteString);
$this->writeString($pos,$byteString,$length);
}
sub dhexStr {
my $value=shift;
return "$value(\$".hexStr($value).")";
}
sub hexStr {
my $value=shift;
return sprintf("%x",$value);
}
1;
#line 1 "xaXsb.pm"
package xaXsb;
my $skriptName= "xactXtract(xaXsb)";
my $skriptVersion= 0.97;
use 5.010;
use Math::BigInt;
use lib "./t";
use xaX_b;
@ISA=(xaX_b);
use constant XSB_HEADERSIZE => 0x008a;
use constant XSB_TOOLVERSION => 45;
use constant XSB_FORMATVERSION => 43;
use constant XTRA_WARNUNG => "WARNUNG! .xsb-Datei enthält unbekannte Strukturen. Ausgabe-Infos und Einfügen eventuell fehlerhaft\n";
my $DEBUG = 0;
my $WORD = 2;
my $DWORD = 4;
my $QWORD = 8;
my $GUID = 16;
sub new {
my ($class, $iXsbname)=@_;
my $this = {};
$this->{HD} = ();
$this->{CNAME}=$class;
$this->{XSBFNAME}=$iXsbname;
$this->{XSBFSIZE}=-s $iXsbname;
bless($this, $class);
$this->SUPER::openIF($iXsbname);
return $this;
}
sub get {
my ($this, $key)=@_;
return ($this->{HD}->{$key});
}
sub set {
if ($#_ % 2) { die $this->getCName."set: hat eine gerade Anzahl von Parametern @_"; }
my $this = shift;
my $key, $value;
while (@_) {
$key=shift;
$value=shift;
if (exists ($this->{HD}{$key})) {
if ($DEBUG) { say "addHD: lösche $key"; }
delete $this->{HD}{$key}
}
$this->{HD}->{$key}=$value;
}
}
sub getNum {
my $hashref=$_[0]->getHeader();
return (keys %$hashref);
}
sub getCName { return $_[0]->{CNAME}; }
sub getIFName { return $_[0]->{XSBFNAME}; }
sub getIFSize { return $_[0]->{XSBFSIZE}; }
sub open {
my ($this, $iXsbname)=@_;
$this->{XSBFNAME} = $iXsbname;
$this->{XSBFSIZE} =-s $iXsbname;
$this->{HD} = [];
$this->SUPER::openIF($iXsbname);
return $this;
}
sub close {
$_[0]->{HD}= undef;
$_[0]->SUPER::closeIF();
}
sub readIF { return $_[0]->SUPER::readIF(); }
sub readAll {
my $this=shift;
my $xsbSize=$this->readIF();
if ($xsbSize < XSB_HEADERSIZE) { die $this->getCName().":".$this->getIFName()." ist zu klein für eine .xsb-Datei"; }
my $dwSignature=$this->readStr(0,4);
if ($dwSignature ne 'SDBK') { die $this->getIFName()."(Signatur: $dwSignature ) ist keine XACT-Soundbank"; }
my $wVersion=$this->readW(4);
my $wHeaderVersion=$this->readW(6);
if (($wVersion != XSB_TOOLVERSION) and ($wHeaderVersion != XSB_FORMATVERSION)) {
print "WARNUNG: ".$this->getIFName()." hat Toolversion $wVersion und Formatversion $wHeaderVersion.\n";
print "$skriptName unterstuetzt nur die Versionen ",XSB_TOOLVERSION," und ",XSB_FORMATVERSION,"\n";
}
my $xtraChunkOff=$this->readL(0x32);
my $xtraChunk2Off=$this->readL(0x36);
my $soundChunkOff=$this->readL(0x46);
my $cueChunkOff=$this->readL(0x22);
my $unknownChunkOff=$this->readL(0x3e);
my $cueNamTabOff=$this->readL(0x42);
my $cueNamOff=$this->readL(0x2a);
my $wbNamChunkOff=$this->readL(0x3a);
if ($wbNamChunkOff != XSB_HEADERSIZE) { say "Warnung! Wavebank-Namenstabelle an unerwarteter Position: $wbNamChunkOff" };
my $hasSoundChunk=$this->chunkExists($soundChunkOff);
my $hasCueChunk=$this->chunkExists($cueChunkOff);
my $hasCueNamTab=$this->chunkExists($cueNamTabOff);
my $hasCueNamChunk=$this->chunkExists($cueNamOff);
my $hasUnknownChunk=$this->chunkExists($unknownChunkOff);
my $hasXtraChunk=$this->chunkExists($xtraChunkOff);
my $haxXtraChunk2=$this->chunkExists($xtraChunk2Off);
die $this->getIFName()." fehlt Cue Chunk oder Sound Chunk(?!?)" if ( (! $hasSoundChunk) || (! $hasCueChunk ));
if ($xtraChunkOff != 0xffffffff) {
if ($DEBUG) { print "Extra-Chunk gefunden(".hexStr($xtraChunkOff).")".XTRA_WARNUNG; }
}
else { $xtraChunkOff=0; }
if ($xtraChunk2Off != 0xffffffff) {
if ($DEBUG) { print "Extra-Chunk2 gefunden(".hexStr($xtraChunk2Off).")".XTRA_WARNUNG; }
}
else { $xtraChunk2Off=0; }
my $wNumStdCues=$this->readW(0x13);
my $wNumXtraCues=$this->readW(0x15);
my $numSounds=$this->readW(0x1c);
my $numWBs=unpack('C',$this->readB(0x1b));
my $wCueSum=$this->readW(0x19);
my $cueNamLen=$this->readW(0x1e);
$this->{HD} = {
dwVersion => $wVersion,
dwHeaderVersion => $wHeaderVersion,
numStdCues => $wNumStdCues,
numXtraCues => $wNumXtraCues,
realCueSum => ($this->readW(0x13) + $this->readW(0x15)),
cueSum => $wCueSum,
numSounds => $numSounds,
numWBs => $numWBs,
wbNamChunkOff => $this->readL(0x3a),
xtraChunkOff => $xtraChunkOff,
xtraChunk2Off => $xtraChunk2Off,
soundChunkOff => $soundChunkOff,
cueChunkOff => $cueChunkOff,
unknownChunkOff => $unknownChunkOff,
cueNamTabOff => $cueNamTabOff,
cueNamOff => $cueNamOff,
cueNamLen => $cueNamLen,
};
if ($DEBUG) { say "0x13: $wNumStdCues, 0x15: $wNumXtraCues, 0x19: $wCueSum, Sounds: $numSounds, WaveBanks: $numWBs"; }
my @wbNames;
for(my $i=0; $i < $numWBs; $i++) {
$wbNames[$i]=$this->denull($this->readStr($wbNamChunkOff+ $i * 64,64));
}
$this->{HD}->{'arrWbNames'}=\@wbNames;
if ($DEBUG) { say "Wavebanks: @wbNames"; }
my @cOff=( $soundChunkOff , $cueChunkOff, $unknownChunkOff, $cueNamTabOff, $cueNamOff);
foreach (@cOff) {
$this->SUPER::isInFile($_);
}
my $soundChunkLen=( $cueChunkOff > $soundChunkOff ? $cueChunkOff - $soundChunkOff : die $this->getIFName()." hat unbek. Struktur: CueChunk vor SoundChunk");
my $nextChunkOff=
($xtraChunkOff) ? $xtraChunkOff:
($xtraChunk2Off) ? $xtraChunk2Off: $unknownChunkOff;
if ($nextChunkOff == 0xffffffff) {
if ((! $hasCueNamChunk) && (! $hasCueNamChunk)) {
say "WARNUNG: ".$this->getIFName()." hat keinen CueName Chunk";
} else {
die $this->getIFName()." hat CueNamTable oder CueNameChunk, aber keinen IndexChunk -> Datei nicht interpretierbar";
}
$nextChunkOff=$this->getIFSize();
}
my $cueChunkLen=$nextChunkOff - $cueChunkOff;
my $cueChunkLen2=$wNumStdCues * 5 + $wNumXtraCues * 15;
if ($cueChunkLen != $cueChunkLen2) {
print "WARNUNG! Die cueChunkgroesse von ".$this->getIFName()." ist $cueChunkLen, sollte aber $wNumStdCues * 5 + $wNumXtraCues * 15=$cueChunkLen2 sein\n";
}
my $xtraChunkLen=
($xtraChunkOff==0) ? 0 :
($xtraChunk2Off) ? $xtraChunk2Off-$xtraChunkOff : $unknownChunkOff-$xtraChunkOff;
my $xtraChunk2Len=($xtraChunk2Off==0) ? 0 : $unknownChunkOff-$xtraChunk2Off;
my $unknownChunkLen=( $cueNamTabOff > $unknownChunkOff ? $cueNamTabOff - $unknownChunkOff : 0);
my $cueNamTabLen=( $cueNamOff > $cueNamTabOff ? $cueNamOff - $cueNamTabOff : 0);
my $soundChunk=$this->readChunk($soundChunkOff,$soundChunkLen);
my $cueChunk=$this->readChunk($cueChunkOff, $cueChunkLen);
my $xtraChunk= $hasXtraChunk ? $this->readChunk($xtraChunkOff, $xtraChunkLen) : "";
my $xtraChunk2= $hasXtraChunk2 ? $this->readChunk($xtraChunk2Off, $xtraChunk2Len) : "";
my $unknownChunk=$hasUnknownChunk ? $this->readChunk($unknownChunkOff, $unknownChunkLen) : "";
my $cueNamTab=$hasCueNamTab ? $this->readChunk($cueNamTabOff,$cueNamTabLen) : "";
my $cueNam=$hasCueNamChunk ? $this->readChunk($cueNamOff,$cueNamLen) : "";
if ($DEBUG) {
print "soundChunkOff $soundChunkOff, soundChunkLen $soundChunkLen";
print "cueChunkOff $cueChunkOff, cueChunkLen $cueChunkLen\n";
print "xtraChunkOff $xtraChunkOff, xtraChunkLen $xtraChunkLen";
print "xtraChunk2Off $xtraChunk2Off, xtraChunk2Len $xtraChunk2Len\n";
print "unknownChunkOff $unknownChunkOff, unknownChunkLen $unknownChunkLen";
print "cueNamTabOff $cueNamTabOff, cueNamTabLen $cueNamTabLen\n";
print "cueNamOff $cueNamOff, cueNamLen $cueNamLen\n";
}
if ($DEBUG) { print "unknownChunk:\n",printBufferHex($unknownChunk); }
my @cueNames=split /\x00/,$cueNam;
$this->set('soundChunkLen',$soundChunkLen, 'cueChunkLen',$cueChunkLen, 'xtraChunkLen', $xtraChunkLen,
'xtraChunk2Len',$xtraChunk2Len, 'unknownChunkLen',$unknownChunkLen, 'cueNamTabLen',$cueNamTabLen);
$this->{HD}->{'arrCueNames'}=\@cueNames;
}
sub injectCue {
my ($this,$myCueName,$myXwb,$myWavIndex,$myVolOptional)=@_;
my @wbNames=@{$this->{HD}->{'arrWbNames'} };
if (! checkByteValue($myVol)) { die "Volume muss zwischen 0 und 255 liegen. Eingabe war $myVol"; }
my $myXwbIndex=-1;
for (my $i=0; $i < @wbNames; $i++) {
if ($myXwb ~~ $wbNames[$i]) {
$myXwbIndex=$i;
last;
}
}
if ($myXwbIndex==-1) { die $this->getIFName()." referenziert NICHT die Wavebank $myXwb, nur @wbNames"; }
say "Wavebank $myXwb hat in ".$this->getIFName()." den Index $myXwbIndex";
if (! $this->checkByteValue($myXwbIndex)) { die "Die angegebene Wavebank $myXwb konnte im WavebankChunk von $inputfilename nicht gefunden werden"; }
my $cueNamOff=$this->get('cueNamOff');
my $cueNamLen=$this->get('cueNamLen');
my $myCueName=$myCueName.pack('C',0);
my $myCueNameLen=length($myCueName);
my $myCueNameOff=$cueNamOff+$cueNamLen;
$this->inject($myCueNameOff,$myCueName,"cueName");
my $myCueNameTabEntry=pack "C*", unpack('C4',pack('V',$myCueNameOff)), 0xff, 0xff;
$this->inject($cueNamOff,$myCueNameTabEntry,"CueNameTabEntry");
my $indexChunkOff=$this->get('unknownChunkOff');
my $indexChunkLen=$this->get('unknownChunkLen');
my $tmpCueSum=$this->get('cueSum');
if ($tmpCueSum != ($indexChunkLen / 2)) { die "injectCue: indexChunk ist nicht doppelt so lang ($indexChunkLen) wie CueSumme $tmpCueSum\n"; }
my $numXtraCues=$this->readW(0x15);
my $numStdCues=$this->get('numStdCues');
my $myStdCueIndex= $numStdCues;
my @tmpIndexChunk;
for(my $i=0; $i < $tmpCueSum; $i++) {
$tmpIndexChunk[$i]=$this->readW($indexChunkOff+ (2 * $i));
}
say "indexChunk: @tmpIndexChunk";
my $myStdCueIndexPos=-1;
for (my $i=0; $i < @tmpIndexChunk; $i++) {
if ($myStdCueIndex - 1 == $tmpIndexChunk[$i]) {
$myStdCueIndexPos=$i;
last;
}
}
$myStdCueIndexPos= ($myStdCueIndexPos == -1) ? $indexChunkLen : ( ($myStdCueIndexPos + 1) *2);
my $myIndexEntry=pack "C*", unpack('C2',pack('v',$myStdCueIndex));
my $cueChunkOff=$this->get('cueChunkOff');
my $myCueOff=$cueChunkOff + 5*$numStdCues;
my $myCue=pack "C*",0x04, unpack('C4',pack('V',$cueChunkOff));
$this->inject($myCueOff,$myCue,"Cue");
$myVol=$myVolOptional ? $myVolOptional : 0x5a;
print "myVol: $myVol , ".pack('C',$myVol)." myXwbIndex: $myWavIndex\n";
my $mySound=pack "C*",0x00, 0x01, 0x00, $myVol, 0x00, 0x00, 0x00, 0x0C, 0x00, unpack('C2',pack('v',$myWavIndex)), $myXwbIndex;
$this->inject($cueChunkOff, $mySound,"Sound");
$numStdCues++;
$this->writeW(0x13,$numStdCues);
my $cueSum=$numStdCues+$numXtraCues > 16 ? ($numStdCues+$numXtraCues ) : 16;
$numSounds=1 + $this->readW(0x1c);
$cueNamLen += $myCueNameLen;
$this->writeW(0x19, $cueSum);
$this->writeW(0x1c, $numSounds);
$this->writeW(0x1e, $cueNamLen);
$this->set('numStdCues',$numStdCues, 'cueSum',$cueSum, 'numSounds', $numSounds,
'cueNamLen',$cueNamLen, 'realCueSum',($numStdCues+$numXtraCues));
say "Korrigiere Offsets....";
$this->corrPointer(0x22,"cueChunkOffset");
if ($this->get('xtraChunkOff')) { $this->corrPointer(0x32,"xtraChunkOffset"); }
if ($this->get('xtraChunk2Off')) { $this->corrPointer(0x36,"xtraChunk2Offset"); }
$this->corrPointer(0x3e,"unknownChunkOffset");
$this->corrPointer(0x42,"cueNamTabOffset");
$this->corrPointer(0x2a,"cueNamOffset");
$this->set('cueChunkOff',$this->readL(0x22), 'unknownChunkOff',$this->readL(0x3e),
'cueNamTabOff',$this->readL(0x42), 'cueNamOff',$this->readL(0x2a));
my $cueNamTabOff=$this->get('cueNamTabOff');
my $offset;
print "Korrigiere CueNamTab:";
for (my $i=0; $i < ($numStdCues+$numXtraCues -1); $i++) {
$offset=$this->readL($cueNamTabOff + $i*6);
print "(Entry $i: pos(".($cueNamTabOff + $i*6).")= $offset->";
$offset=$this->corrOffsetAfter($offset);
print "$offset )";
$this->writeL($cueNamTabOff + $i*6,$offset);
}
$offset=$this->corrOffsetBefore($this->readL($cueNamTabOff + ($numStdCues+$numXtraCues -1)*6));
$this->writeL($cueNamTabOff + ($numStdCues+$numXtraCues -1)*6,$offset);
print "\n";
$this->recalcChecksum();
}
sub recalcChecksum {
my $this = shift;
my $checksum=$this->calcFCS16old();
my $oldChecksum=$this->readW(0x08);
if ($checksum == $oldChecksum) {
print $this->getIFName()." : Checksumme $oldChecksum ist bereits korrekt\n";
return 0;
} else {
print $this->getIFName()." : Ändere Checksumme: $oldChecksum -> $checksum\n";
$this->writeW(0x08,$checksum);
return 1;
}
}
sub info {
my $this = shift;
my $fcs=$this->calcFCS16();
if ($fcs != $this->readW(8)) { die "Checksumme falsch. Berechnet ".dhexStr($fcs).", in Datei: ".$this->getIFName(); }
my @res=$this->info4Release();
print "\n";
return @res;
}
sub info4Debug {
my $this = shift;
print "\XSB-INFO";
print "\n".$this->getIFName()." : Version (".$this->get('dwVersion').",".$this->get('dwHeaderVersion')."), ";
print $this->get('numStdCues')." Std-Cues,";
say $this->get('numXtraCues')." ExtraCues, angegeb.Cue-Summe: ".$this->get('cueSum');
my @wbNames=@{$this->{HD}->{'arrWbNames'} };
print "Nutzt ".$this->get('numWBs')." Wavebank(s): @wbNames\n";
my @cueNames=@{$this->{HD}->{'arrCueNames'} };
print "".($#cueNames +1)." Cues: @cueNames\n";
my $hashdataref=($this->{HD}) ? $this->{HD} : return 0;
my %hd=%$hashdataref;
if ($DEBUG) { print map { "$_ => $hd{$_}\n" } keys %hd; }
return 1;
}
sub info4Release {
my $this = shift;
my $numStdCues=$this->get('numStdCues');
my @wbNames=@{$this->{HD}->{'arrWbNames'} };
print "\nINFO ".$this->getIFName();
print ": Version (".$this->get('dwVersion').",".$this->get('dwHeaderVersion')."), ";
say "$numStdCues StdCues,".$this->get('numXtraCues')." ExtraCues, ".$this->get('numSounds')." Sounds";
say $this->get('numWBs')." Wavebank(s): @wbNames";
my $xtraChunks= ($this->get('xtraChunkLen')) ? 1 : 0;
$xtraChunks+=($this->get('xtraChunk2Len')) ? 1 : 0;
my $realCueSum=$this->get('realCueSum');
my $cueSum=$this->get('cueSum');
my $warning=0;
if ($xtraChunks) {
say "Extra-Chunk(s) gefunden ($xtraChunks)";
$warning+=$xtraChunks;
}
my $cueChunkOff=$this->get('cueChunkOff');
my $cueChunkLen=$this->get('cueChunkLen');
my $cueChunk=$this->readChunk($cueChunkOff, $cueChunkLen);
my @cueNames=@{$this->{HD}->{'arrCueNames'} };
my $cue, $sound, $soundOff, $soundLen, $wbNam, $wavIndex;
say "Cue-Name |Soundtyp | Wavebankname+Index";
say "------------------------------|---------|----------------------------------";
for (my $i=0; $i<$numStdCues; $i++) {
printf "%-30s",$cueNames[$i];
$cue=$this->readStr($cueChunkOff + $i * 5, 5);
if ($DEBUG) { say "Cue: ".printBufferHex($cue); }
($sound, $soundOff, $soundLen)=$this->getSoundFromStdCue($cue);
if ($soundLen != 12) { say " NonStd,$soundLen\B"; }
else {
($wbNam,$wavIndex)=$this->getWBFromStdSoundPos($soundOff);
printf "%-11s %-30s\n", "->Std->", $wbNam."_".$wavIndex;
}
}
return ($numStdCues, $this->get('numXtraCues'), $this->get('numSounds'));
}
sub info4Table {
my $this = shift;
my $numStdCues=$this->get('numStdCues');
print $this->getIFName()."\t";
print "$numStdCues \t".$this->get('numXtraCues')."\t";
print $this->get('cueSum')."\t".$this->get('numSounds')."\t".$this->get('numWBs');
print "\t".$this->get('soundChunkOff')."\t".$this->get('soundChunkLen');
print "\t".$this->get('cueChunkOff')."\t".$this->get('cueChunkLen');
print "\t".$this->get('xtraChunkOff')."\t".$this->get('xtraChunkLen');
print "\t".$this->get('xtraChunk2Off')."\t".$this->get('xtraChunk2Len');
print "\t".$this->get('unknownChunkOff')."\t".$this->get('unknownChunkLen');
print "\t".$this->get('cueNamTabOff')."\t".$this->get('cueNamTabLen');
print "\t".$this->get('cueNamOff')."\t".$this->get('cueNamLen');
my $xtraChunks= ($this->get('xtraChunkLen')) ? 2 : 0;
$xtraChunks+=($this->get('xtraChunk2Len')) ? 4 : 0;
my $realCueSum=$this->get('realCueSum');
my $cueSum=$this->get('cueSum');
my $warning=0;
if ($cueSum != $realCueSum) {
$warning+=1;
}
if ($xtraChunks) {
$warning+=$xtraChunks;
}
print "\t".$this->readW(0x10)."\t".$this->readBVal(0x12);
print "\t".$this->readW(0x17)."\t".$this->readW(0x20);
my $unOff=$this->readL(0x26);
my $unOff2=$this->readL(0x2e);
print "\t".hexStr($unOff)."\t".hexStr($unOff2);
print "\n";
my $cueChunkOff=$this->get('cueChunkOff');
my $cueChunkLen=$this->get('cueChunkLen');
my $cueChunk=$this->readChunk($cueChunkOff, $cueChunkLen);
my @cueNames=@{$this->{HD}->{'arrCueNames'} };
my $cue, $sound, $soundOff, $soundLen, $wbNam, $wavIndex;
for (my $i=0; $i<$numStdCues; $i++) {
$cue=$this->readStr($cueChunkOff + $i * 5, 5);
if ($DEBUG) { say "Cue: ".printBufferHex($cue); }
($sound, $soundOff, $soundLen)=$this->getSoundFromStdCue($cue);
if ($soundLen != 12) {
}
else {
($wbNam,$wavIndex)=$this->getWBFromStdSoundPos($soundOff);
}
}
}
sub getSoundFromStdCue {
my ($this,$cue)=@_;
if (length($cue) != 5) { die "getSoundFromStdCue: StdCue $cue hat falsche Länge ".length($cue); }
my $soundPos=unpack('L',substr($cue,1,4));
return $this->getSoundFromPos($soundPos);
}
sub getSoundFromPos {
my ($this,$soundPos)=@_;
if ($DEBUG) { print "Soundpos: $soundPos,"; }
my $soundLength=$this->readBVal($soundPos + 7);
my $sound=$this->readStr($soundPos,$soundLength);
if ($DEBUG) { print "SoundLen: $soundLength,"; }
if ($soundLength != 12) {
if ($DEBUG) { print "kein StdSound"; }
}
if ($DEBUG) { say "\nSound: ".printBufferHex($sound); }
return ($sound, $soundPos, $soundLength);
}
sub getWBFromStdSoundPos {
my ($this,$soundPos)=@_;
if ($this->readBVal($soundPos + 7) != 12) { die "getWBFromStdSound: StdSound ($soundPos) hat falsche Länge"; }
my $wavIndex=$this->readW($soundPos + 9);
my $wbNum=$this->readBVal($soundPos + 11);
my @wbNames=@{$this->{HD}->{'arrWbNames'} };
my $wb=$wbNames[$wbNum];
if ($DEBUG) { say "soundPos: $soundPos, wavIndex: $wavIndex, wbNum $wbNum, wbName $wb"; }
return ($wb, $wavIndex);
}
sub readCue {
my ($this,$cIndex)=@_;
my $realCueSum=$this->get('realCueSum'), $numStdCues=$this->get('numStdCues');
my $myCue, @myCueOff;
if ($cIndex+1 > $realCueSum) { die "readCue mit Index. $cIndex aufgerufen, aber nur $realCueSum Cues vorhanden"; }
if ($cIndex < $numStdCues) {
$myCue=$this->readStr($this->get('cueChunkOff') + 5*$cIndex, 5);
$myCueOff[0]=$this->readL($this->get('cueChunkOff') + 5*$cIndex +1);
return ($myCue, @myCueOff);
}
print "Non-Standard Cues können noch nicht gelesen werden(CueIndex $cIndex)";
return ($myCue, @myCueOff);
}
sub readChunk {
my ($this,$position, $len)=@_;
return $this->readStr($position,$len);
}
sub chunkExists {
my ($this,$offs)=@_;
if ( ($offs == 0) || ($offs == 0xffffffff) ) { return 0; }
return ($this->SUPER::isInFile($offs) ? 1 : 0);
}
sub calcFCS16old {
my $this = shift;
my $dataLen=$this->getDSize();
if ($dataLen < XSB_HEADERSIZE) { die "calcFCS16: ".$this->getIFName()." Pufferinhalt zu klein: $dataLen"; }
my @arrFCS16 = ( 0x0, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48,
0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
0x1081, 0x108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9,
0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
0x2102, 0x308B, 0x210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A,
0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
0x3183, 0x200A, 0x1291, 0x318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB,
0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
0x4204, 0x538D, 0x6116, 0x709F, 0x420, 0x15A9, 0x2732, 0x36BB, 0xCE4C,
0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x528, 0x37B3, 0x263A, 0xDECD,
0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x630, 0x17B9, 0xEF4E,
0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x738, 0xFFCF,
0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x840,
0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
0x18C1, 0x948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
0x2942, 0x38CB, 0xA50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
0x39C3, 0x284A, 0x1AD1, 0xB58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0xC60, 0x1DE9, 0x2F72, 0x3EFB,
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0xD68, 0x3FF3, 0x2E7A,
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0xE70, 0x1FF9,
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0xF78);
my $fcs=65535;
my $arrPointer, $arrElement, $actDatabyte;
for (my $i=18; $i < $dataLen; $i++) {
$actDatabyte=$this->readBVal($i);
$arrPointer=($fcs ^ $actDatabyte) & 0xff;
$arrElement=$arrFCS16[$arrPointer];
$fcs=int($fcs / 256);
$fcs=$fcs ^ $arrElement;
}
my $fcsComplement= $fcs ^ 65535;
my $fcsHigh=$fcsComplement & 0xff;
$fcsComplement=int($fcsComplement/256);
my $fcsLow=$fcsComplement & 0xff;
$fcs=$fcsLow * 256 + $fcsHigh;
return $fcs;
}
sub calcFCS16 {
my $this = shift;
my $dataLen=$this->getDSize() -18;
my $data=$this->readStr(18,$dataLen);
if ($dataLen < XSB_HEADERSIZE) { die "calcFCS16: ".$this->getIFName()." Pufferinhalt zu klein: $dataLen"; }
return $this->calcFCS16FromByteString($data);
}
sub calcFCS16FromByteString {
my ($this,$myString)=@_;
my $dataLen=length($myString);
my @arrFCS16 = ( 0x0, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48,
0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
0x1081, 0x108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9,
0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
0x2102, 0x308B, 0x210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A,
0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
0x3183, 0x200A, 0x1291, 0x318, 0x77A7, 0x662E, 0x54B5, 0x453C, 0xBDCB,
0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
0x4204, 0x538D, 0x6116, 0x709F, 0x420, 0x15A9, 0x2732, 0x36BB, 0xCE4C,
0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x528, 0x37B3, 0x263A, 0xDECD,
0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x630, 0x17B9, 0xEF4E,
0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x738, 0xFFCF,
0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, 0x840,
0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
0x18C1, 0x948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
0x2942, 0x38CB, 0xA50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
0x39C3, 0x284A, 0x1AD1, 0xB58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0xC60, 0x1DE9, 0x2F72, 0x3EFB,
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0xD68, 0x3FF3, 0x2E7A,
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0xE70, 0x1FF9,
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0xF78);
my $fcs=65535;
my $arrPointer, $arrElement, $actDatabyte;
for (my $i=0; $i < $dataLen; $i++) {
$actDatabyte=unpack('C',substr($myString,$i,1));
$arrPointer=($fcs ^ $actDatabyte) & 0xff;
$arrElement=$arrFCS16[$arrPointer];
$fcs=int($fcs / 256);
$fcs=$fcs ^ $arrElement;
}
my $fcsComplement= $fcs ^ 65535;
my $fcsHigh=$fcsComplement & 0xff;
$fcsComplement=int($fcsComplement/256);
my $fcsLow=$fcsComplement & 0xff;
$fcs=$fcsLow * 256 + $fcsHigh;
return $fcs;
}
sub dhexStr {
my $value=shift;
return "$value(\$".hexStr($value).")";
}
sub hexStr {
my $value=shift;
return sprintf("%x",$value);
}
sub printBufferHex {
my $buffer=shift;
my $newline=16;
my $counter=0;
my $result="";
my @resBuffer=unpack('C*',$buffer);
foreach (@resBuffer) {
$result=$result.sprintf("%02x ",$_);
if ($counter < $newline) { $counter++; }
else {
$result=$result."\n";
$counter=0;
}
}
return $result;
}
sub denull {
my ($this,$string)=@_;
$string =~ s/\0//g if defined $string;
return $string;
}
sub checkByteValue {
my ($this,$myVal)=@_;
return (($myVal > -1) && ($myVal < 256)) ? 1 : 0;
}
sub readB {
return $_[0]->SUPER::readString($_[1], 1); }
sub readBVal {
$_[0]->SUPER::readByte($_[1]); }
sub readW {
return $_[0]->SUPER::readWord($_[1]); }
sub readL {
return $_[0]->SUPER::readLong($_[1]); }
sub readStr {
return $_[0]->SUPER::readString($_[1],$_[2]); }
sub getDSize {
return $_[0]->SUPER::getDSize(); }
sub writeB {
$_[0]->SUPER::writeByte($_[1],$_[2]); }
sub writeW {
$_[0]->SUPER::writeWord($_[1],$_[2]); }
sub writeL {
$_[0]->SUPER::writeLong($_[1],$_[2]); }
sub writeStr {
$_[0]->SUPER::writeString($_[1],$_[2],$_[3]); }
sub writeBytes {
$_[0]->SUPER::writeBytes($_[1],$_[2],$_[3]); }
sub inject {
$_[0]->SUPER::inject($_[1],$_[2],$_[3]); }
sub infoInject {
$_[0]->SUPER::infoInject(); }
sub corrOffsetAfter {
return $_[0]->SUPER::corrOffsetAfter($_[1]); }
sub corrOffsetBefore {
return $_[0]->SUPER::corrOffsetBefore($_[1]); }
sub corrPointer {
return $_[0]->SUPER::corrPointer($_[1],$_[2]); }
sub renameWriteAndClose {
return $_[0]->SUPER::IFRenameWriteAndClose($_[1]); }
1;
#line 1 "xaXwb.pm"
package xaXwb;
use warnings;
use strict;
use feature "say";
use feature "switch";
my $skriptName= "xaXwb.pm";
my $skriptVersion= 0.97;
my $DEBUG = 1;
use Math::BigInt;
use lib "./t";
use parent 'xaX_b';
use constant XWB_HEADERSIZE => 52;
use constant WAVEBANK_TYPE_BUFFER => 0x00000000;
use constant WAVEBANK_TYPE_STREAMING => 0x00000001;
use constant WAVEBANK_TYPE_MASK => 0x00000001;
use constant WAVEBANKMINIFORMAT_TAG_PCM => 0x0;
use constant WAVEBANKMINIFORMAT_TAG_XMA => 0x1;
use constant WAVEBANKMINIFORMAT_TAG_ADPCM => 0x2;
use constant WAVEBANKMINIFORMAT_TAG_WMA => 0x3;
sub new {
my ($class, $iXwbname, $safeMode)=@_;
my $this = {};
$this->{HD} = ();
$this->{CNAME}=$class;
$this->{XWBFNAME}=$iXwbname;
$this->{XWBFSIZE}=-s $iXwbname;
$this->{SAFEMODE}=$safeMode;
bless($this, $class);
$this->SUPER::openIF($iXwbname);
return $this;
}
sub getCName {
my $this=shift;
return $this->{CNAME};
}
sub getSafeMode {
my $this=shift;
return $this->{SAFEMODE};
}
sub getIFName {
my $this=shift;
return $this->{XWBFNAME};
}
sub getIFSize {
my $this=shift;
return $this->{XWBFSIZE};
}
sub open {
my ($this, $iXwbname)=@_;
$this->{XWBFNAME} = $iXwbname;
$this->{XWBFSIZE} =-s $iXwbname;
$this->{HD} = [];
$this->SUPER::openIF($iXwbname);
return $this;
}
sub close {
my $this=shift;
$this->SUPER::closeIF();
$this->{HD}= undef;
}
sub injectWav {
my $self = shift;
die $self->{CNAME} . "::injectWav: TODO";
}
sub info {
my $this=shift;
print "---WAVEBANK ".$this->getIFName().": ";
my $headerref=($this->{HD}) ? $this->{HD} : return 0;
my %header=%$headerref;
print "Version (".$this->getHD('dwVersion').",".$this->getHD('dwHeaderVersion')."), Typ: ";
my $isStreamingWB=$this->getHD('wbTyp');
if ($isStreamingWB == WAVEBANK_TYPE_STREAMING) { print "Streaming"; }
else { print "In-Memory"; }
my $waves = $this->getHD('dwEntryCount');
say ", $waves Waves";
my @wdFlags=@{$this->{HD}->{'arrWdFlags'} };
my @wdDur= @{$this->{HD}->{'arrWdDur'} };
my @wdTyp= @{$this->{HD}->{'arrWdTyp'} };
my @wdChan= @{$this->{HD}->{'arrWdChan'} };
my @wdRate= @{$this->{HD}->{'arrWdRate'} };
my @wdAlign=@{$this->{HD}->{'arrWdAlign'} };
my @wdPCM16=@{$this->{HD}->{'arrWdPCM16'} };
my @wdOff= @{$this->{HD}->{'arrWdOff'} };
my @wdLen= @{$this->{HD}->{'arrWdLen'} };
my ($pcmNum, $xmaNum,$adpcmNum, $wmaNum) = (0,0,0,0);
print "Wave| | | | Chan-| | |16bit|in WAVEDATASEGMENT\n";
print "Nr. |Flags|Duration| Typ| nels|SampleRate|Align| PCM?| Offset Length\n";
print "--------------------------------------------------------------------------\n";
for my $i (0..$waves-1) {
printf "%4d %4d %9d", $i,$wdFlags[$i],$wdDur[$i];
given($wdTyp[$i]) {
when (($_ < 0) or ($_ > 3)) { print "UNBEKANNTER AUDIOTYP $_\n"; }
when($_ == WAVEBANKMINIFORMAT_TAG_PCM) { print " PCM"; $pcmNum++; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_XMA) { print " XMA"; $xmaNum++; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_ADPCM) { print " ADPCM"; $adpcmNum++; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_WMA) { print " WMA"; $wmaNum++; continue;}
default { printf "%6d %10d %5d %5d %9d %9d\n", $wdChan[$i], $wdRate[$i], $wdAlign[$i], $wdPCM16[$i], $wdOff[$i], $wdLen[$i];}
}
}
if ($DEBUG) { print map { "$_ => $header{$_}\n" } keys %header; }
print "Insges.: ";
print "$pcmNum PCM," if $pcmNum;
print "$xmaNum XMA," if $xmaNum;
print "$adpcmNum ADPCM," if $adpcmNum;
print "$wmaNum WMA" if $wmaNum;
print "\n";
return ($isStreamingWB,$pcmNum, $xmaNum,$adpcmNum, $wmaNum);
}
sub getHeader {
my $this = shift;
return $this->{HD};
}
sub getHD {
my ($this, $key)=@_;
return ($this->{HD}->{$key});
}
sub setHD {
my ($this, $key, $value)=@_;
if (exists ($this->{HD}{$key})) {
if ($DEBUG) { say "addHD: lösche $key"; }
delete $this->{HD}{$key}
}
$this->{HD}->{$key}=$value;
}
sub getHDnum {
my $this=shift;
my $hashref=$this->getHeader();
return (keys %$hashref);
}
sub readHeader {
my $this=shift;
$this->SUPER::readIF(XWB_HEADERSIZE);
my $dwSignature=$this->readStr(0,4);
if (($dwSignature ne 'DNBW') && ($dwSignature ne 'WBND')) {
die "Fehler: $this->{XWBFNAME} (Signatur: $dwSignature ) ist keine XACT-Wavebank";
}
my $dwVersion=$this->readL(4);
my $dwHeaderVersion=$this->readL(8);
#if (($dwVersion != 45) or ($dwHeaderVersion != 43)) {
# print "WARNUNG: $this->{XWBFNAME} hat Toolversion $dwVersion und Formatversion $dwHeaderVersion.\n";
# print "$skriptName unterstuetzt nur die Versionen 45 und 43\n";
#}
if ($DEBUG) { say "Version: $dwVersion,$dwHeaderVersion"};
$this->{HD} = {
dwVersion => $dwVersion,
dwHeaderVersion => $dwHeaderVersion,
segBankDataOff => $this->readL(12),
segBankDataLen => $this->readL(16),
segEntryMetaOff => $this->readL(20),
segEntryMetaLen => $this->readL(24),
segSeekTabOff => $this->readL(28),
segSeekTabLen => $this->readL(32),
anzRegions => 5,
};
say ($this->getHD('segBankDataOff'));
given($this->getHD('segBankDataOff')) {
when($_ == 52) {
if ($DEBUG) {print "WAVEBANKHEADER hat korrekte Laenge\n";}
$this->setHD('segWaveDatOff',$this->readL(44));
$this->setHD('segWaveDatLen',$this->readL(48));
}
when($_ < 52) { print "WARNUNG: WAVEBANKHEADER zu klein\n"; continue}
when($_ > 43) {
$this->setHD('anzRegions',4);
$this->setHD('segWaveDatOff',$this->readL(36));
$this->setHD('segWaveDatLen',$this->readL(40));
}
default { die "WAVEBANKHEADER von $this->{XWBFNAME} scheint defekt\n"}
}
my $segWaveDatOff=$this->getHD('segWaveDatOff');
my $segWaveDatLen=$this->getHD('segWaveDatLen');
if ($segWaveDatOff == 0 || $segWaveDatLen==0) {
#die "FEHLER: WAVEDATA Segment: Offset: $segWaveDatOff ,Laenge: $segWaveDatLen";
}
if ($DEBUG) {
say "WAVEBANKREGION 1: WAVEBANKDATA. Offset ".$this->getHD('segBankDataOff').", Laenge ".$this->getHD('segBankDataLen');
say "WAVEBANKREGION 2: ENTRYMETADATA. Offset ".$this->getHD('segEntryMetaOff').", Laenge ".$this->getHD('segEntryMetaLen');
say "WAVEBANKREGION 3: SEEKTABLES. Offset ".$this->getHD('segSeekTabOff').", Laenge ".$this->getHD('segSeekTabLen');
say "WAVEBANKREGION ".$this->getHD('anzRegions')." : WAVEDATA. Offset ".$this->getHD('segWaveDatOff').", Laenge ".$this->getHD('segWaveDatLen');
}
my $infoSegSize=$this->getHD('segBankDataLen') + $this->getHD('segEntryMetaLen') + $this->getHD('segSeekTabLen');
if ( (XWB_HEADERSIZE + $infoSegSize + $segWaveDatLen) > $this->getIFSize()) {
die "$this->{CNAME}:readHeader: Längenangaben im Header von $this->{XWBFNAME} fehlerhaft; Datei zu kurz\n";
}
$this->SUPER::readIF($infoSegSize);
my $pos=$this->getHD('segBankDataOff');
if ($DEBUG) { print "WAVEBANKDATA:\n"; }
my $wbd_dwFlags=$this->readL($pos);
$this->setHD('wbTyp', $wbd_dwFlags && WAVEBANK_TYPE_MASK);
my $szBankName=$this->readStr($pos+8,64);
if ($DEBUG) { print "Wavebank Name: ",$szBankName,"\n"; }
$this->setHD('szBankName',$szBankName);
my $dwEntryCount=$this->readL($pos+4);
$this->setHD('dwEntryCount',$dwEntryCount);
if ($DEBUG) { print "Anzahl Entries/Wavs: $dwEntryCount\n"; }
my $dwEntryMetaDataElementSize=$this->readL($pos+72);
my $dwEntryNameElementSize=$this->readL($pos+76);
if ($DEBUG) {
print "Laenge eines WAVEBANKENTRY in ENTRYMETADATA: $dwEntryMetaDataElementSize \n";
print "Laenge eines WAVENAMENS in ENTRYNAMES: $dwEntryNameElementSize \n";
}
$pos=$this->getHD('segEntryMetaOff');
my @wdFlags=[];
my @wdDur=[];
my @wdTyp=[];
my @wdChan=[];
my @wdRate=[];
my @wdAlign=[];
my @wdPCM16=[];
my @wdOff=[];
my @wdLen=[];
if ($DEBUG) {
print "Wave| | | | Chan-| | |16bit|in WAVEDATASEGMENT\n";
print "Nr. |Flags|Duration| Typ| nels|SampleRate|Align| PCM?| Offset Length\n";
print "--------------------------------------------------------------------------\n";
}
my $wdTotalLength=0;
for(my $i=0; $i<$dwEntryCount; $i++) {
($wdFlags[$i], $wdDur[$i])=$this->readENTRYdwFlags($pos);
($wdTyp[$i],$wdChan[$i],$wdRate[$i],$wdAlign[$i],$wdPCM16[$i])=$this->readMINIWAVEFORMAT($pos+4);
$wdOff[$i]=$this->readL($pos+8);
$wdLen[$i]=$this->readL($pos+12);
$pos+=$dwEntryMetaDataElementSize;
if ($DEBUG) { printf "%4d %4d %9d", $i,$wdFlags[$i],$wdDur[$i]; }
if ($DEBUG) {
given($wdTyp[$i]) {
when (($_ < 0) or ($_ > 3)) { print "UNBEKANNTER AUDIOTYP $_\n"; }
when($_ == WAVEBANKMINIFORMAT_TAG_PCM) { print " PCM"; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_XMA) { print " XMA"; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_ADPCM) { print " ADPCM"; continue;}
when($_ == WAVEBANKMINIFORMAT_TAG_WMA) { print " WMA"; continue;}
default { printf "%6d %10d %5d %5d %9d %9d\n", $wdChan[$i], $wdRate[$i], $wdAlign[$i], $wdPCM16[$i], $wdOff[$i], $wdLen[$i]; }
}
}
$wdTotalLength+=$wdLen[$i];
if ( $wdTotalLength > $segWaveDatLen) {
die "Fehler in ".$this->getIFName()." : Entrymetadata-Segment zu kurz (?!?)\n";
}
if ($wdLen[$i] < 4) {
die "Fehler in ".$this->getIFName()." : Wave-Datei Nr. $i zu kurz (",$wdLen[$i]," Byte)\n";
}
}
$this->{HD}->{'arrWdFlags'}=\@wdFlags;
$this->{HD}->{'arrWdDur'}=\@wdDur;
$this->{HD}->{'arrWdTyp'}=\@wdTyp;
$this->{HD}->{'arrWdChan'}=\@wdChan;
$this->{HD}->{'arrWdRate'}=\@wdRate;
$this->{HD}->{'arrWdAlign'}=\@wdAlign;
$this->{HD}->{'arrWdPCM16'}=\@wdPCM16;
$this->{HD}->{'arrWdOff'}=\@wdOff;
$this->{HD}->{'arrWdLen'}=\@wdLen;
return $dwEntryCount;
}
sub readWaveInfo {
my ($this, $i)=@_;
my $numWaves= $this->getHD('dwEntryCount');
my @wdFlags=@{$this->{HD}->{'arrWdFlags'} };
my @wdDur= @{$this->{HD}->{'arrWdDur'} };
my @wdTyp= @{$this->{HD}->{'arrWdTyp'} };
my @wdChan= @{$this->{HD}->{'arrWdChan'} };
my @wdRate= @{$this->{HD}->{'arrWdRate'} };
my @wdAlign=@{$this->{HD}->{'arrWdAlign'} };
my @wdPCM16=@{$this->{HD}->{'arrWdPCM16'} };
my @wdOff= @{$this->{HD}->{'arrWdOff'} };
my @wdLen= @{$this->{HD}->{'arrWdLen'} };
if ($i < 0 || $i >= $numWaves) { die "xwb:readWave: WaveIndex $i nicht vorhanden (Anz.Waves=$numWaves)"; }
return ($wdFlags[$i],$wdDur[$i],$wdTyp[$i],$wdChan[$i],$wdRate[$i],$wdAlign[$i],$wdPCM16[$i],$wdOff[$i],$wdLen[$i]);
}
sub readWaveData {
my ($this, $wOff,$wLen)=@_;
if ($this->getSafeMode()) {
return $this->readWaveDataSafe($wOff,$wLen);
} else {
return $this->readWaveData2($wOff,$wLen);
}
}
sub readWaveData2 {
my ($this, $wOff,$wLen)=@_;
my $segWaveDatOff=$this->getHD('segWaveDatOff');
my $segWaveDatLen=$this->getHD('segWaveDatLen');
die "xwb:readWaveData2: Wave($wOff,$wLen) liegt ausserhalb WaveDataSegment(Länge: $segWaveDatLen)" if ($wOff+$wLen > $segWaveDatLen);
return $this->readStr($segWaveDatOff + $wOff, $wLen);
}
sub readWaveDataSafe {
my ($this, $wOff,$wLen)=@_;
my $segWaveDatOff=$this->getHD('segWaveDatOff');
my $segWaveDatLen=$this->getHD('segWaveDatLen');
die "xwb:readWaveDataSafe: Wave($wOff,$wLen) liegt ausserhalb WaveDataSegment(Länge: $segWaveDatLen)" if ($wOff+$wLen > $segWaveDatLen);
return $this->SUPER::directReadIF($segWaveDatOff + $wOff, $wLen);
}
sub printBufferHex {
my $buffer=shift;
my $newline=16;
my $counter=0;
my $result="";
my @resBuffer=unpack('C*',$buffer);
foreach (@resBuffer) {
$result=$result.sprintf("%02x ",$_);
if ($counter < $newline) { $counter++; }
else {
$result=$result."\n";
$counter=0;
}
}
return $result;
}
sub dhexStr {
my $value=shift;
return "$value(\$".hexStr($value).")";
}
sub hexStr {
my $value=shift;
return sprintf("%x",$value);
}
sub writeQLong {
my ($OUTHANDLE, $value)=@_;
my $hex= new Math::BigInt '0x'.$value;
my $hexLo=Math::BigInt->new(0);
my $hexHi=Math::BigInt->new(0);
$hexLo=$value->copy();
$hexHi=$value->copy();
print "hex:",$hex->as_hex();
$hexLo=$hexLo->band(Math::BigInt->new(0xFFFFFFFF));
$hexHi=$hexHi->brsft(32);
my $low=$hexLo->as_int();
my $high=$hexHi->as_int();
print ",hex-hi: $high, hex-lo: $low,";
print "HEX: $value -> $hex\n";
}
sub writeLong {
my ($OUTHANDLE, $value)=@_;
print $OUTHANDLE pack('L',$value);
}
sub writeWord {
my ($OUTHANDLE, $value)=@_;
print $OUTHANDLE pack('S',$value);
}
sub writeBigEndianLong {
my ($OUTHANDLE, $value)=@_;
print $OUTHANDLE pack('N',$value);
}
sub writeBigEndianWord {
my ($OUTHANDLE, $value)=@_;
print $OUTHANDLE pack('n',$value);
}
sub writeBytes {
my ($OUTHANDLE, $value)=@_;
print "WRITING ".printBufferHex($value)."\n";
print $OUTHANDLE $value;
}
sub denull {
my $string = shift;
$string =~ s/\0//g if defined $string;
return $string;
}
sub checkByteValue {
my $myVal= shift;
return (($myVal > -1) && ($myVal < 256)) ? 1 : 0;
}
sub readENTRYdwFlags {
my ($this,$position)=@_;
my @res= $this->readMaskedLong($position,4,28);
return @res;
}
sub readMINIWAVEFORMAT {
my ($this,$position)=@_;
my @res= $this->readMaskedLong($position,2,3,18,8,1);
return @res;
}
sub readMaskedLong {
my ($this,$position, @bitlen)=@_;
my $num=$#bitlen + 1;
my @ch;
for(my $i=0; $i < 4; $i++) {
$ch[$i]=$this->readStr($position+$i,1);
$ch[$i]=unpack('b8',$ch[$i]);
}
my $binary=join("",@ch);
my @result;
my $bitpos=0;
for (my $i=0; $i<$num; $i++) {
$result[$i]=substr($binary,$bitpos, $bitlen[$i]);
$bitpos+=$bitlen[$i];
$result[$i]=reverse($result[$i]);
my $bh= new Math::BigInt '0b'.$result[$i];
$result[$i]=$bh->as_int;
}
return @result;
}
sub readB {
$_[0]->SUPER::readByte($_[1]); }
sub readW {
$_[0]->SUPER::readWord($_[1]); }
sub readL {
$_[0]->SUPER::readLong($_[1]); }
sub readStr {
$_[0]->SUPER::readString($_[1],$_[2]); }
sub readRest {
$_[0]->getSafeMode() ? return 0 : return $_[0]->SUPER::readRest(); }
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment