PDA

View Full Version : Yume Miru Kusuri game data extraction


Unregistered
2007-06-10, 06:37
I couldn't find a Susie plugin for this - seen one?
The file format is extremely simple, so here is a 10 minute Perl script I knocked up...

First argument is the archive file name, optional second argument is a specific file.


#!/usr/bin/perl

use strict;
use warnings;
use bytes;
use Fcntl;

my($buf, $arc);

sysopen($arc, $ARGV[0]||die('filename plz'), O_RDONLY) || die($!);

# Header
sysread($arc, $buf, 16)||die($!);
my($file_ext)=substr($buf, 4, 4);
$file_ext=~s/\0+$//;
my($num_files, $idx_elen)=unpack('VV', substr($buf, 8, 8));
# In all archives, 'idx_elen' has been set to 16...
# All index entries seem to be 17 bytes. I can only assume this tells us the index length, and thereby the filename+padding length? Or perhaps that is fixed at 8 chars + null... who knows.
$idx_elen++;
my $fnlen=$idx_elen-8;
print("num files: $num_files\n");

# Index entries
my(%index);
while($num_files--) {
sysread($arc, $buf, $idx_elen)||die($!);
my $fn=substr($buf, 0, $fnlen);
$fn=~s/\0+$//;
$fn.='.'.$file_ext;
$index{$fn}={
filename => $fn,
length => unpack('V', substr($buf, -8, 4)),
offset => unpack('V', substr($buf, -4, 4))
};
}

# Grab a chunk out of the file
sub write_out($$$$)
{
my($src, $dst, $offset, $length)=@_;
seek($src, $offset, 0)||die("Failed to seek to file location. Truncated archive?");
my $n;
while($length && ($n=sysread($src, $buf, ($length>0x10000?0x10000:$length)))) {
print("Read $n bytes...\n");
$length-=$n;
syswrite($dst, $buf)||die("Failed to write to destination: $!");
}
if(!defined($n)) { die("Failed to read to end of file: $!"); }
elsif(!$n) { die("premature EOF"); }
return(0);
}

# Extract
my $file=$ARGV[1];
if($file) {
if(!$index{$file}) { die("$file is not in this archive (case-sensitive)"); }
print("Extracting $file...\n");
my $out;
sysopen($out, $file, O_CREAT|O_WRONLY)||die("Failed to open output file: $!");
write_out($arc, $out, $index{$file}{offset}, $index{$file}{length});
close($out);
}
else {
print("No filename supplied, extracting all...\n");
foreach $file (values(%index)) {
print("Extracting $$file{filename}...\n");
my $out;
sysopen($out, $$file{filename}, O_CREAT|O_WRONLY)||die("Failed to open output file: $!");
write_out($arc, $out, $$file{offset}, $$file{length});
close($out);
}
}

close($arc);
exit(0);

Unregistered
2007-06-10, 07:02
I only tested it on the Bgm.arc, since that was the only one I was interested in.
It seems it doesn't work on chip.arc for some reason. I'll fix that later if anyone cares.
Also, remove the 'print("Read $n bytes...\n");' line - it was for debugging purposes and obscures the normal output... :)

zalas
2007-06-10, 09:00
I only tested it on the Bgm.arc, since that was the only one I was interested in.
It seems it doesn't work on chip.arc for some reason. I'll fix that later if anyone cares.
Also, remove the 'print("Read $n bytes...\n");' line - it was for debugging purposes and obscures the normal output... :)
Not owning the game, my guess is that stuff other than Bgm.arc is probably compressed ;)

Unregistered
2007-06-10, 10:28
Ah, nothing as complicated as that :)
It seems the index is split up into multiple sections based on file extension. All the other archives have only a single file type in them, so I didn't account for it.

Having just got back home I fixed up the script, and it seems to handle them all now:


#!/usr/bin/perl

use strict;
use warnings;
use bytes;
use Fcntl;

my($buf, $arc);

sysopen($arc, $ARGV[0]||die('filename plz'), O_RDONLY) || die($!);

# Number of file types present
sysread($arc, $buf, 4)||die($!);
my($num_types)=unpack('V', $buf);

# Each file type gets it's own index block
my(%types);
while($num_types--) {
sysread($arc, $buf, 12)||die($!);
$_=substr($buf, 0, 4);
s/\0+$//;
$types{$_}={
extension => $_,
num_files => unpack('V', substr($buf, 4, 4)),
index_offset => unpack('V', substr($buf, 8, 4))
};
print("File set $_, $types{$_}{num_files} files\n");
}

# Read each index block
# Filename sans-extension seems fixed at 9 bytes (yay lets throw off alignment...)
my(%index);
foreach my $type (values(%types)) {
seek($arc, $$type{index_offset}, 0)||die("Failed to seek to index for $$type{extension}: $!");
while($$type{num_files}--) {
sysread($arc, $buf, 17);
my $fn=substr($buf, 0, 9);
$fn=~s/\0+$//;
$fn.='.'.$$type{extension};
$index{$fn}={
filename => $fn,
length => unpack('V', substr($buf, -8, 4)),
offset => unpack('V', substr($buf, -4, 4))
};
}
}

sub write_out($$$$)
{
my($src, $dst, $offset, $length)=@_;
seek($src, $offset, 0)||die("Failed to seek to file location. Truncated archive?");
my $n;
while($length && ($n=sysread($src, $buf, ($length>0x10000?0x10000:$length)))) {
$length-=$n;
syswrite($dst, $buf)||die("Failed to write to destination: $!");
}
if(!defined($n)) { die("Failed to read to end of file: $!"); }
elsif(!$n) { die("premature EOF"); }
return(0);
}

my $file=$ARGV[1];
if($file) {
if(!$index{$file}) { die("$file is not in this archive (case-sensitive)"); }
print("Extracting $file...\n");
my $out;
sysopen($out, $file, O_CREAT)||die("Failed to open output file: $!");
write_out($arc, $out, $index{$file}{offset}, $index{$file}{length});
close($out);
}
else {
print("No filename supplied, extracting all...\n");
foreach $file (values(%index)) {
print("Extracting $$file{filename}...\n");
my $out;
sysopen($out, $$file{filename}, O_CREAT|O_WRONLY)||die("Failed to open output file: $!");
write_out($arc, $out, $$file{offset}, $$file{length});
close($out);
}
}

close($arc);

exit(0);

Unregistered
2007-11-26, 01:43
I apologize for posting in a (possibly) deceased thread but I'm using ActivePerl 5.8.8 build 822 on Windows XP Professional and trying to extract the data from the Bgm.arc of Yume Miru Kusuri. I have moved both the Perl script as well as the arc file into the same folder and have changed the

$ARGV[0]||die('filename plz')
to
$ARGV[0]||die('Bgm.arc')

When I attempt to run the Perl script via command prompt there is nothing produced except one line of data saying "Bgm.arc at {location of perl script} line 10."

As I am incredibly inexperienced with Perl I would greatly appreciate any help informing me as to what I'm doing wrong. I love the music for this game and it would be incredibly helpful to be able to have it in a playable format.

fireshark
2007-11-26, 19:47
uh, that line isn't where you're supposed to put the filename, youre supposed to specify it when you run the program.
________
New Jersey Dispensary (http://newjersey.dispensaries.org/)

Asceai
2007-11-26, 23:07
Or replace that entire line with

sysopen($arc, "bgm.arc", O_RDONLY);

EDIT: Oh, and if you got the Yume Miru Kusuri DVD from JAST/Peach Princess/whoever's selling it, there should be a directory containing the game's music in the 'Yume Miru Extras' directory on the DVD.

Unregistered
2007-11-27, 04:14
It's expecting the filename as the first command line argument.

C:\yume_game_data_dir> perl script.pl bgm.arc

(Presuming activestate installs perl in your PATH, I have no idea whether it does or not...)

Asceai
2007-11-27, 04:23
Yeah, activestate does. I assumed he's double-clicking the .pl file or something, in which passing an argument might not be immediately obvious.