10-20-2014, 10:36 AM
According by this upload of SFX http://www.sounds-resource.com/wii/superpapermario/,i want to ask,how he did that,because one user(Phaze) wrote:
But .samp(samples) file is contains in Wii game images!
UP I found unpacker script .samp files(by Nisto) here its source code and .php file(PHP):
.php file(at mega.co.nz)
Quote:So in the basics as people figured, there are sound effects in the pmario.samp file located in the TTYD ISO's /sound/proj/ folder. I've confirmed this by importing the pmario.samp file as Raw Data in Audacity. The only thing is that when I've imported the data, there is this awful LOUD, SCREECHY NOISE PERMEATING THE WHOLE FILE SO IT'S NOT USABLE FOR ANYTHING BUT DAMAGING YOUR EARS. On the plus side though, I did noticeably hear Bowser/Mario/Peach's voice clips past the mid point in the noise! Based on other docs I've read, the format might be a custom-ish 4-bit ADPCM format used for other things in the 'cube like streamed audio. Makes sense..
But .samp(samples) file is contains in Wii game images!
UP I found unpacker script .samp files(by Nisto) here its source code and .php file(PHP):
.php file(at mega.co.nz)
PHP Code:
?php
/*
*
* MusyX sample extraction tool by Nisto
* Last revision: May 16, 2014
*
* Minimum required PHP version: 4.1.0
*
*/
if($argc!=3)
{
echo 'Usage: php <script> <sound_dir> <format> ' . PHP_EOL;
echo PHP_EOL;
echo 'script ' . PHP_EOL;
echo ' ' . $argv[0] . PHP_EOL;
echo PHP_EOL;
echo 'sound_dir ' . PHP_EOL;
echo ' path to the sound directory ' . PHP_EOL;
echo PHP_EOL;
echo 'format ' . PHP_EOL;
echo ' 0 - standard MusyX .sdir format ' . PHP_EOL;
echo ' 1 - .sdir within a .arc file (e.g. Resident Evil 0) ' . PHP_EOL;
echo ' 2 - .snd format (e.g. Resident Evil) ' . PHP_EOL;
echo PHP_EOL;
echo PHP_EOL;
echo PHP_EOL;
echo 'The script has only been tested with the following GameCube games:' . PHP_EOL;
echo ' * Biohazard (Resident Evil) ' . PHP_EOL;
echo ' * Biohazard 0 (Resident Evil 0) ' . PHP_EOL;
echo ' * Paper Mario: The Thousand-Year Door ' . PHP_EOL;
echo ' * Star Fox Adventures ' . PHP_EOL;
echo PHP_EOL;
echo 'It may or may not work with other games utilizing MusyX. ' . PHP_EOL;
exit();
}
if(!is_dir($argv[1]))
{
exit('The directory path specified does not appear to be valid.');
}
if($argv[2]!=='0' && $argv[2]!=='1' && $argv[2]!=='2')
{
exit('Invalid format specified.');
}
define('SOUND_DIR', rtrim(realpath($argv[1]), DIRECTORY_SEPARATOR));
define('OUT_DIR', SOUND_DIR . DIRECTORY_SEPARATOR . 'samples');
define('FORMAT', $argv[2]);
define('NULL_64BIT', pack('x8'));
define('HEADER_PAD', pack('x36'));
if(($Dir = opendir(SOUND_DIR)) === FALSE)
{
exit('ERROR: Could not open input directory! Terminating script.');
}
if(!is_dir(OUT_DIR) && mkdir(OUT_DIR) === FALSE)
{
exit('ERROR: Could not create output directory! Terminating script.');
}
$sdirExts = array('sdir','sdi','arc','snd');
$Done = array();
while(($sdirName = readdir($Dir)) !== FALSE)
{
/*
* skip irrelevant and already-done files
*/
$sdirPath = SOUND_DIR . DIRECTORY_SEPARATOR . $sdirName;
if (!is_file($sdirPath))
{
continue;
}
$Ext = strtolower(pathinfo($sdirName, PATHINFO_EXTENSION));
if (!in_array($Ext, $sdirExts))
{
continue;
}
$Basename = strtolower(basename($sdirName, '.'.$Ext));
if (in_array($Basename, $Done))
{
continue;
}
/*
* parse files according to the format specified
* if the extension does not match with the selected format,
* skip the file
*
* because there may be errors before finishing parsing,
* we need to add to $Done[] individually
*/
if (($sdirFile = fopen($sdirPath, 'rb')) === FALSE)
{
err_skip('Could not open sdir %s for reading.', $sdirName);
continue;
}
if(FORMAT=='0' && ($Ext=='sdir'||$Ext=='sdi'))
{
// standard sdir
$Done[] = $Basename;
$sdirOffset = 0;
$sdirSize = filesize($sdirPath);
$sampPath = SOUND_DIR . DIRECTORY_SEPARATOR . $Basename . '.sam';
if(!file_exists($sampPath) && !file_exists($sampPath .= 'p'))
{
err_skip($sdirFile, 'Could not find the sample file for %s.', $sdirName);
continue;
}
$sampName = basename($sampPath);
$sampOffset = 0;
$sampSize = filesize($sampPath);
}
elseif(FORMAT=='1' && $Ext=='arc')
{
// standard sdir in arc
$Done[] = $Basename;
fseek($sdirFile, 0x04);
$arcFileCount = fread($sdirFile, 4);
$arcIndexOffset = fread($sdirFile, 4);
if(strlen($arcFileCount)!=4 || strlen($arcIndexOffset)!=4)
{
err_skip($sdirFile, 'Failed reading arc header values in %s.', $sdirName);
continue;
}
$arcSize = filesize($sdirPath);
$arcFileCount = unpack('N', $arcFileCount)[1];
$arcIndexOffset = unpack('N', $arcIndexOffset)[1];
if ($arcFileCount < 1 || $arcIndexOffset+($arcFileCount*32) > $arcSize)
{
err_skip($sdirFile, '%s does not appear to be a valid .arc file.', $sdirName);
continue;
}
fseek($sdirFile, $arcIndexOffset);
for($i=1; $i<=$arcFileCount; ++$i)
{
$sdirOffset = fread($sdirFile, 4);
$sdirSize = fread($sdirFile, 4);
fseek($sdirFile, 8, SEEK_CUR);
$InnerExt = fread($sdirFile, 4);
fseek($sdirFile, 12, SEEK_CUR);
if (strlen($sdirOffset)!=4 || strlen($sdirSize)!=4 || strlen($InnerExt)!=4)
{
err_skip($sdirFile, 'Failed reading data from arc file %s.', $sdirName);
continue 2;
}
if ($InnerExt=='sdir')
{
break;
}
}
if($InnerExt!='sdir')
{
err_skip($sdirFile, 'Could not locate sdir file within %s.', $sdirName);
continue;
}
$sdirOffset = unpack('N',$sdirOffset)[1];
$sdirSize = unpack('N',$sdirSize)[1];
if($sdirOffset+$sdirSize > $arcSize)
{
err_skip($sdirFile, 'The sdir table in %s is out of range based on header values.', $sdirName);
continue;
}
$sampPath = SOUND_DIR . DIRECTORY_SEPARATOR . $Basename . '.sam';
if(!file_exists($sampPath) && !file_exists($sampPath .= 'p'))
{
err_skip($sdirFile, 'Could not find the sample file for %s.', $sdirName);
continue;
}
$sampName = basename($sampPath);
$sampOffset = 0;
$sampSize = filesize($sampPath);
}
elseif(FORMAT=='2' && $Ext=='snd')
{
// .snd
$Done[] = $Basename;
$sampFile = $sdirFile;
$sampPath = $sdirPath;
$sampName = $sdirName;
fseek($sdirFile, 0x10);
$sdirOffset = fread($sdirFile, 4);
$sdirSize = fread($sdirFile, 4);
fseek($sdirFile, 0x28);
$sampOffset = fread($sdirFile, 4);
$sampSize = fread($sdirFile, 4);
if(strlen($sdirOffset)!=4 || strlen($sdirSize)!=4 || strlen($sampOffset)!=4 || strlen($sampSize)!=4)
{
err_skip($sdirFile, 'Failed to read snd header values in %s.', $sdirName);
continue;
}
$sndSize = filesize($sdirPath);
$sdirOffset = unpack('N',$sdirOffset)[1];
$sdirSize = unpack('N',$sdirSize)[1];
$sampOffset = unpack('N',$sampOffset)[1];
$sampSize = unpack('N',$sampSize)[1];
if($sdirOffset+$sdirSize > $sndSize)
{
err_skip($sdirFile, 'The sdir table in %s is out of range based on header values.', $sdirName);
continue;
}
if($sampOffset+$sampSize > $sndSize)
{
err_skip($sdirFile, 'The sample chunk in %s is out of range based on header values.', $sdirName);
continue;
}
}
else
{
continue;
}
/*
* get offset, sample rate, raw sample count, coefficients
* (and maybe, eventually, some other data)
*
* 4 - 0xFF FF FF FF (end of block)
* 32 - block 1 entry size
* 40 - block 2 entry size (that I've ever seen..)
*/
$SampleArr = array();
$Entries = ($sdirSize-4) / (32+40);
if(($sdirSize-4) % $Entries)
{
err_skip($sdirFile, 'Unexpected entry size in sdir table 2 in %s.', $sdirName);
continue;
}
fseek($sdirFile, $sdirOffset);
for($i=1; $i<=$Entries; ++$i)
{
fseek($sdirFile, 4, SEEK_CUR);
$Offset = fread($sdirFile, 4);
fseek($sdirFile, 6, SEEK_CUR);
$Rate = fread($sdirFile, 2);
$RawSamples = fread($sdirFile, 4);
fseek($sdirFile, 12, SEEK_CUR);
//$LoopStart = fread($sdirFile, 4);
//$LoopEnd = fread($sdirFile, 4);
//$DecDataOffset = fread($sdirFile, 4);
if(strlen($Offset)!=4 || strlen($Rate)!=2 || strlen($RawSamples)!=4)
{
err_skip($sdirFile, 'Failed to read attributes from sdir table in %s.', $sdirName);
continue 2;
}
$SampleArr[$i]['start_offset'] = $sampOffset + unpack('N',$Offset)[1];
$SampleArr[$i]['rate'] = unpack('n',$Rate)[1];
$SampleArr[$i]['raw_samples'] = unpack('N',$RawSamples)[1];
//$SampleArr[$i]['loop_start'] = unpack('N',$LoopStart)[1];
//$SampleArr[$i]['loop_end'] = unpack('N',$LoopEnd)[1];
//$SampleArr[$i]['dec_data_offset'] = unpack('N',$DecDataOffset)[1];
if ($i>1)
{
$SampleArr[$i-1]['end_offset'] = $SampleArr[$i]['start_offset'];
}
if ($i==$Entries)
{
$SampleArr[$i]['end_offset'] = $sampOffset + $sampSize;
}
}
fseek($sdirFile, 4, SEEK_CUR);
for($i=1; $i<=$Entries; ++$i)
{
fseek($sdirFile, 8, SEEK_CUR);
$Coeffs = fread($sdirFile, 32);
if(strlen($Coeffs)!=32)
{
err_skip($sdirFile, 'Failed to read sample %d coefficients from the sdir table in %s.', $n, $Filename);
continue 2;
}
$SampleArr[$i]['coeffs'] = $Coeffs;
}
/*
* get sample data, make header, and output to DSP files
*/
echo 'Extracting samples from ' . $sampName . '... ';
if($sdirPath != $sampPath)
{
fclose($sdirFile);
if(($sampFile = fopen($sampPath,'rb')) === FALSE)
{
err_skip('Could not open file for reading.');
continue;
}
}
$dspDirPath = OUT_DIR . DIRECTORY_SEPARATOR . $Basename;
if(!is_dir($dspDirPath) && mkdir($dspDirPath) === FALSE)
{
err_skip($sampFile, 'Could not create DSP output directory.');
continue;
}
fseek($sampFile, $sampOffset);
foreach($SampleArr as $n => $Sample)
{
if($Sample['start_offset'] >= $sampSize)
{
err_skip($sampFile, 'Sample %d is out of range based on the offsets from the sdir table.', $n);
continue 2;
}
$SampleData = '';
$p = ftell($sampFile);
while ($p < $Sample['end_offset'])
{
$Bytes = fread($sampFile, 16);
$p = ftell($sampFile);
if($p < $sampSize && strlen($Bytes)!=16)
{
err_skip($sampFile, 'Failed to read sample data at 0x%x.', ftell($sampFile));
continue 3;
}
$SampleData .= $Bytes;
}
// fixes some (not all) files that vgmstream won't play
if(substr($SampleData,0,8)!=NULL_64BIT)
{
$SampleData = NULL_64BIT . $SampleData;
}
$SampleDataLen = strlen($SampleData);
$RawSamples = $Sample['raw_samples'];
$Nibbles = $SampleDataLen * 2;
$Rate = $Sample['rate'];
$LoopFlag = 0;//($Sample['loop_start'] || $Sample['loop_end']) ? 1 : 0;
$LoopStart = 2;//$Sample['loop_start'];
$LoopEnd = 0;//$Sample['loop_end'];
$Coeffs = $Sample['coeffs'];
$Header = pack('N',$RawSamples); // 0x00 raw samples
$Header.= pack('N',$Nibbles); // 0x04 nibbles
$Header.= pack('N',$Rate); // 0x08 sample rate
$Header.= pack('n',$LoopFlag); // 0x0C loop flag
$Header.= pack('n',00000000); // 0x0E format (always zero - ADPCM)
$Header.= pack('N',$LoopStart); // 0x10 loop start address (in nibbles)
$Header.= pack('N',$LoopEnd); // 0x14 loop end address (in nibbles)
$Header.= pack('N',00000002); // 0x18 initial offset value (in nibbles)
$Header.= $Coeffs; // 0x1C
$Header.= HEADER_PAD;
//$Header.= pack('n',...); // 0x3C gain
//$Header.= pack('n',...); // 0x3E predictor/scale
//$Header.= pack('n',...); // 0x40 sample history
//$Header.= pack('n',...); // 0x42 sample history
//$Header.= pack('n',...); // 0x44 predictor/scale for loop context
//$Header.= pack('n',...); // 0x46 sample history for loop context
//$Header.= pack('n',...); // 0x48 sample history for loop context
//$Header.= pack('x22'); // 0x4A pad (reserved)
$dspName = str_pad($n,2,'0',STR_PAD_LEFT) . '.dsp';
$dspPath = $dspDirPath . DIRECTORY_SEPARATOR . $dspName;
if(($dspFile = fopen($dspPath, 'wb')) === FALSE)
{
err_skip($sampFile, 'Could not open sample %d output file for writing.', $n);
continue 2;
}
if(fwrite($dspFile, $Header.$SampleData) != 0x60+$SampleDataLen)
{
err_skip($sampFile, $dspFile, 'Failed writing data to %s.', $dspName);
continue 2;
}
fclose($dspFile);
}
fclose($sampFile);
echo 'Done.' . PHP_EOL;
}
echo 'No more files to process.' . PHP_EOL;
closedir($Dir);
function err_skip()
{
$Args = func_get_args();
$Arg1 = array_shift($Args);
while (is_resource($Arg1) && get_resource_type($Arg1)=='stream')
{
fclose($Arg1);
$Arg1 = array_shift($Args);
}
$Err = vsprintf($Arg1, $Args);
echo 'ERROR: ' . $Err . ' Skipping the input file.' . PHP_EOL;
}
?>