public static bool Export(string originalScriptFilename, string databaseFilename, string gracesJapaneseFilename, string newScriptFilename) { byte[] script = System.IO.File.ReadAllBytes(originalScriptFilename); uint sectionPointerLocation = BitConverter.ToUInt32(script, 0); // this is SUPER HACKY but should work using (var stream = new System.IO.FileStream(newScriptFilename, System.IO.FileMode.Create)) { var entries = GraceNoteDatabaseEntry.GetAllEntriesFromDatabase("Data Source=" + databaseFilename, "Data Source=" + gracesJapaneseFilename); // copy whole flie except for the section pointers for (int i = 0; i < sectionPointerLocation; ++i) { stream.WriteByte(script[i]); } long pos = stream.Position; // remove original strings from the file foreach (var entry in entries) { stream.Position = entry.PointerRef; RemoveString(stream, stream.ReadUInt32()); } stream.Position = pos; // now write the modified strings from the GN db at the end of the file foreach (var entry in entries) { uint stringLocation = Convert.ToUInt32(stream.Position); stream.Position = entry.PointerRef; stream.WriteUInt32(stringLocation); stream.Position = stringLocation; stream.Write(StringToBytesBlazeUnion(entry.TextEN)); stream.WriteByte(0); } // write the section pointers and replace position stream.Position = stream.Position.Align(4); uint newSectionPointerLocation = Convert.ToUInt32(stream.Position); for (uint i = sectionPointerLocation; i < script.Length; ++i) { stream.WriteByte(script[i]); } stream.Position = 0; stream.WriteUInt32(newSectionPointerLocation); } return(true); }
public static bool Export( string originalScriptFilename, string databaseFilename, string gracesJapaneseFilename, string newScriptFilename ) { byte[] script = System.IO.File.ReadAllBytes( originalScriptFilename ); uint sectionPointerLocation = BitConverter.ToUInt32( script, 0 ); // this is SUPER HACKY but should work using ( var stream = new System.IO.FileStream( newScriptFilename, System.IO.FileMode.Create ) ) { var entries = GraceNoteDatabaseEntry.GetAllEntriesFromDatabase( "Data Source=" + databaseFilename, "Data Source=" + gracesJapaneseFilename ); // copy whole flie except for the section pointers for ( int i = 0; i < sectionPointerLocation; ++i ) { stream.WriteByte( script[i] ); } long pos = stream.Position; // remove original strings from the file foreach ( var entry in entries ) { stream.Position = entry.PointerRef; RemoveString( stream, stream.ReadUInt32() ); } stream.Position = pos; // now write the modified strings from the GN db at the end of the file foreach ( var entry in entries ) { uint stringLocation = Convert.ToUInt32( stream.Position ); stream.Position = entry.PointerRef; stream.WriteUInt32( stringLocation ); stream.Position = stringLocation; stream.Write( StringToBytesBlazeUnion( entry.TextEN ) ); stream.WriteByte( 0 ); } // write the section pointers and replace position stream.Position = stream.Position.Align( 4 ); uint newSectionPointerLocation = Convert.ToUInt32( stream.Position ); for ( uint i = sectionPointerLocation; i < script.Length; ++i ) { stream.WriteByte( script[i] ); } stream.Position = 0; stream.WriteUInt32( newSectionPointerLocation ); } return true; }
public static int Execute( List<string> args ) { if ( args.Count == 0 ) { Console.WriteLine( "This is intended to help extracting skit audio from the Xbox 360 game files." ); Console.WriteLine( "Do the following in order:" ); Console.WriteLine( "-- unpack chat.svo (FPS4 archive, with HyoutaTools -> ToVfps4e)" ); Console.WriteLine( "-- decompress individual skit with xbdecompress" ); Console.WriteLine( "-- unpack skit (FPS4 archive, with HyoutaTools -> ToVfps4e)" ); Console.WriteLine( "-- cut SE3 header from audio file to get a nub archive" ); Console.WriteLine( " (file 0004, seems to be 0x800 bytes for skits but can be bigger, first four bytes of new file should be 0x00020100)" ); Console.WriteLine( "-- extract nub archive with NUBExt r12beta" ); Console.WriteLine( "-- this gives you an \"xma\" file that isn't actually an xma, run this tool on it" ); Console.WriteLine( "-- resulting file is a valid enough xma file that can be converted to WAV with \"toWav\"" ); return -1; } string filename = args[0]; using ( var source = new FileStream( filename, FileMode.Open ) ) { using ( var dest = new FileStream( filename + "-real.xma", FileMode.Create ) ) { source.Position = 0x100; int dataLength = (int)( source.Length - source.Position ); dest.WriteAscii( "RIFF" ); dest.WriteUInt32( (uint)dataLength + 0x34 ); dest.WriteAscii( "WAVE" ); dest.WriteAscii( "fmt " ); dest.WriteUInt32( 0x20 ); source.Position = 0xBC; dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteByte( (byte)source.ReadByte() ); dest.WriteByte( (byte)source.ReadByte() ); dest.WriteUInt32( source.ReadUInt32().SwapEndian() ); dest.WriteUInt32( source.ReadUInt32().SwapEndian() ); dest.WriteUInt32( source.ReadUInt32().SwapEndian() ); dest.WriteUInt32( source.ReadUInt32().SwapEndian() ); dest.WriteByte( (byte)source.ReadByte() ); dest.WriteByte( (byte)source.ReadByte() ); dest.WriteUInt16( source.ReadUInt16().SwapEndian() ); dest.WriteAscii( "data" ); dest.WriteUInt32( (uint)dataLength ); source.Position = 0x100; Util.CopyStream( source, dest, dataLength ); } } return 0; }
public void Pack( string[] files, string outFilename, string headerName = null, string metadata = null ) { FileCount = (uint)files.Length + 1; HeaderSize = 0x1C; EntrySize = 0; if ( ContainsStartPointers ) { EntrySize += 4; } if ( ContainsSectorSizes ) { EntrySize += 4; } if ( ContainsFileSizes ) { EntrySize += 4; } if ( ContainsFilenames ) { EntrySize += 0x20; } if ( ContainsFiletypes ) { EntrySize += 4; } if ( ContainsFileMetadata ) { EntrySize += 4; } bool headerToSeparateFile = false; if ( headerName != null ) { headerToSeparateFile = true; } using ( FileStream f = new FileStream( headerToSeparateFile ? headerName : outFilename, FileMode.Create ) ) { // header f.Write( Encoding.ASCII.GetBytes( "FPS4" ), 0, 4 ); f.WriteUInt32( FileCount.ToEndian( Endian ) ); f.WriteUInt32( HeaderSize.ToEndian( Endian ) ); f.WriteUInt32( 0 ); // start of first file goes here, will be filled in later f.WriteUInt16( EntrySize.ToEndian( Endian ) ); f.WriteUInt16( ContentBitmask.ToEndian( Endian ) ); f.WriteUInt32( Unknown2.ToEndian( Endian ) ); f.WriteUInt32( 0 ); // ArchiveNameLocation, will be filled in later // file list for ( int i = 0; i < files.Length; ++i ) { var fi = new System.IO.FileInfo( files[i] ); if ( ContainsStartPointers ) { f.WriteUInt32( 0 ); } // properly written later if ( ContainsSectorSizes ) { f.WriteUInt32( 0 ); } // properly written later if ( ContainsFileSizes ) { f.WriteUInt32( ( (uint)( fi.Length ) ).ToEndian( Endian ) ); } if ( ContainsFilenames ) { string filename = fi.Name.Truncate( 0x1F ); byte[] fnbytes = Util.ShiftJISEncoding.GetBytes( filename ); f.Write( fnbytes, 0, fnbytes.Length ); for ( int j = fnbytes.Length; j < 0x20; ++j ) { f.WriteByte( 0 ); } } if ( ContainsFiletypes ) { string extension = fi.Extension.TrimStart( '.' ).Truncate( 4 ); byte[] extbytes = Util.ShiftJISEncoding.GetBytes( extension ); f.Write( extbytes, 0, extbytes.Length ); for ( int j = extbytes.Length; j < 4; ++j ) { f.WriteByte( 0 ); } } if ( ContainsFileMetadata ) { if ( infile != null ) { // copy this from original file, very hacky when filenames/filecount/metadata changes infile.Position = f.Position; f.WriteUInt32( infile.ReadUInt32() ); } else { // place a null for now and go back to fix later f.WriteUInt32( 0 ); } } } // at the end of the file list, reserve space for a final entry pointing to the end of the container for ( int j = 0; j < EntrySize; ++j ) { f.WriteByte( 0 ); } // the idea is to write a pointer here // and at the target of the pointer you have a nullterminated string // with all the metadata in a param=data format separated by spaces // maybe including a filepath at the start without a param= // strings should be after the filelist block but before the actual files if ( ContainsFileMetadata && metadata != null ) { for ( int i = 0; i < files.Length; ++i ) { var fi = new System.IO.FileInfo( files[i] ); long ptrPos = 0x1C + ( ( i + 1 ) * EntrySize ) - 4; // remember pos uint oldPos = (uint)f.Position; // jump to metaptr f.Position = ptrPos; // write remembered pos f.WriteUInt32( oldPos.ToEndian( Endian ) ); // jump to remembered pos f.Position = oldPos; // write meta + nullterm if ( metadata.Contains( 'p' ) ) { string relativePath = GetRelativePath( f.Name, fi.FullName ); f.Write( Util.ShiftJISEncoding.GetBytes( relativePath ) ); } if ( metadata.Contains( 'n' ) ) { f.Write( Util.ShiftJISEncoding.GetBytes( "name=" + Path.GetFileNameWithoutExtension( fi.FullName ) ) ); } f.WriteByte( 0 ); } } // write original archive filepath if ( ArchiveName != null ) { // put the location into the header ArchiveNameLocation = Convert.ToUInt32( f.Position ); f.Position = 0x18; f.WriteUInt32( ArchiveNameLocation.ToEndian( Endian ) ); f.Position = ArchiveNameLocation; // and actually write it byte[] archiveNameBytes = Util.ShiftJISEncoding.GetBytes( ArchiveName ); f.Write( archiveNameBytes, 0, archiveNameBytes.Length ); f.WriteByte( 0 ); } // fix up file pointers { long pos = f.Position; // location of first file if ( headerToSeparateFile ) { FirstFileStart = 0; } else { if ( infile != null ) { // keep FirstFileStart from loaded file, this is a hack // will break if file metadata (names, count, etc.) changes! } else { FirstFileStart = ( (uint)( f.Position ) ).Align( Alignment ); } } f.Position = 0xC; f.WriteUInt32( FirstFileStart.ToEndian( Endian ) ); // file entries uint ptr = FirstFileStart; for ( int i = 0; i < files.Length; ++i ) { f.Position = 0x1C + ( i * EntrySize ); var fi = new System.IO.FileInfo( files[i] ); if ( ContainsStartPointers ) { f.WriteUInt32( ptr.ToEndian( Endian ) ); } if ( ContainsSectorSizes ) { f.WriteUInt32( ( (uint)( fi.Length.Align( (int)Alignment ) ) ).ToEndian( Endian ) ); } ptr += (uint)fi.Length.Align( (int)Alignment ); } f.Position = 0x1C + ( files.Length * EntrySize ); f.WriteUInt32( ptr.ToEndian( Endian ) ); f.Position = pos; } if ( !headerToSeparateFile ) { // pad until files if ( infile != null ) { infile.Position = f.Position; for ( long i = f.Length; i < FirstFileStart; ++i ) { f.WriteByte( (byte)infile.ReadByte() ); } } else { for ( long i = f.Length; i < FirstFileStart; ++i ) { f.WriteByte( 0 ); } } } // actually write files if ( headerToSeparateFile ) { using ( FileStream dataStream = new FileStream( outFilename, FileMode.Create ) ) { WriteFilesToFileStream( files, dataStream ); dataStream.Close(); } } else { WriteFilesToFileStream( files, f ); } } }
public void Write( string outFile, int align = 0x10 ) { var f = new FileStream( outFile, FileMode.Create ); // create space for header and filelist stuff for ( int i = 0; i < FilesOffset; ++i ) { f.WriteByte( 0 ); } // create dummy entry f.Write( new byte[] { 0x44, 0x55, 0x4D, 0x4D, 0x59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0x10 ); // write files for ( int i = 0; i < Entries.Count; ++i ) { if ( Entries[i].Data != null ) { // special case: if multiple files in a row are identical, only add them once and point the offsets at the same data // oddly enough for the two times it happens in the original files, it only does it once, so no idea what is actually // the proper way to handle it, faking it with that last condition if ( i >= 1 && Entries[i - 1].Data != null && Entries[i - 1].Data.Length == Entries[i].Data.Length && Entries[i].Data.Length > 0x30 ) { Entries[i - 1].Data.Position = 0; Entries[i].Data.Position = 0; if ( Entries[i - 1].Data.IsIdentical( Entries[i].Data, Entries[i].Data.Length ) ) { Entries[i].Offset = Entries[i - 1].Offset; continue; } } Entries[i].Offset = (uint)f.Position - FilesOffset; Entries[i].Data.Position = 0; Util.CopyStream( Entries[i].Data, f, (int)Entries[i].Data.Length ); while ( f.Length % align != 0 ) { f.WriteByte( 0 ); } } } Filesize = (uint)f.Length; // write header f.Position = 0; f.Write( Encoding.ASCII.GetBytes( Magic ), 0, 8 ); f.WriteUInt32( Filesize.SwapEndian() ); f.WriteUInt32( Unknown.SwapEndian() ); f.WriteUInt32( Filecount.SwapEndian() ); f.WriteUInt32( FilesOffset.SwapEndian() ); f.WriteUInt32( Filesize ); f.WriteUInt32( 0 ); // write file list for ( int i = 0; i < Entries.Count; ++i ) { f.Position = 0x20u + i * 0x20u; f.WriteUInt32( Entries[i].Offset.SwapEndian() ); f.WriteUInt32( Entries[i].FilesizeCompressed.SwapEndian() ); f.WriteUInt32( Entries[i].FilesizeUncompressed.SwapEndian() ); } f.Close(); }