Initial commit
This commit is contained in:
parent
efe3fdf580
commit
ebd09c117d
8 changed files with 1912 additions and 39 deletions
211
.gitignore
vendored
211
.gitignore
vendored
|
@ -1,43 +1,188 @@
|
||||||
# Windows image file caches
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
Thumbs.db
|
## files generated by popular Visual Studio add-ons.
|
||||||
ehthumbs.db
|
|
||||||
|
|
||||||
# Folder config file
|
# User-specific files
|
||||||
Desktop.ini
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
# User-specific folders
|
||||||
$RECYCLE.BIN/
|
*.sln.ide/
|
||||||
|
|
||||||
# Windows Installer files
|
# Build results
|
||||||
*.cab
|
[Dd]ebug/
|
||||||
*.msi
|
[Dd]ebugPublic/
|
||||||
*.msm
|
[Rr]elease/
|
||||||
*.msp
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
# =========================
|
# Roslyn cache directories
|
||||||
# Operating System Files
|
*.ide/
|
||||||
# =========================
|
|
||||||
|
|
||||||
# OSX
|
# MSTest test Results
|
||||||
# =========================
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
.DS_Store
|
#NUNIT
|
||||||
.AppleDouble
|
*.VisualState.xml
|
||||||
.LSOverride
|
TestResult.xml
|
||||||
|
|
||||||
# Icon must end with two \r
|
# Build Results of an ATL Project
|
||||||
Icon
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
# Thumbnails
|
*_i.c
|
||||||
._*
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
# Files that might appear on external disk
|
# Chutzpah Test files
|
||||||
.Spotlight-V100
|
_Chutzpah*
|
||||||
.Trashes
|
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
# Visual C++ cache files
|
||||||
.AppleDB
|
ipch/
|
||||||
.AppleDesktop
|
*.aps
|
||||||
Network Trash Folder
|
*.ncb
|
||||||
Temporary Items
|
*.opensdf
|
||||||
.apdisk
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding addin-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# If using the old MSBuild-Integrated Package Restore, uncomment this:
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
sql/
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
6
App.config
Normal file
6
App.config
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
BIN
ICSharpCode.SharpZipLib.dll
Normal file
BIN
ICSharpCode.SharpZipLib.dll
Normal file
Binary file not shown.
1175
Options.cs
Normal file
1175
Options.cs
Normal file
File diff suppressed because it is too large
Load diff
450
Program.cs
Normal file
450
Program.cs
Normal file
|
@ -0,0 +1,450 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using ICSharpCode.SharpZipLib.BZip2;
|
||||||
|
using NDesk.Options;
|
||||||
|
|
||||||
|
namespace RSCacheTool
|
||||||
|
{
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
static string cacheDir = Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\jagexcache\runescape\LIVE\");
|
||||||
|
static string outDir = "cache\\";
|
||||||
|
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
bool help = false, extract = false, combine = false, combineOverwrite = false, combineMergeIncomplete = false;
|
||||||
|
int extractArchive = -1, combineArchive = 40, combineStartFile = 0;
|
||||||
|
|
||||||
|
OptionSet argsParser = new OptionSet() {
|
||||||
|
{ "h", "show this message", val => { help = true; } },
|
||||||
|
|
||||||
|
{ "e", "extract files from cache", val => { extract = true; } },
|
||||||
|
{ "a=", "single archive to extract, if not given all archives will be extracted", val => { extractArchive = Convert.ToInt32(val); } },
|
||||||
|
|
||||||
|
{ "c", "combine sound", val => { combine = true; } },
|
||||||
|
{ "s=", "archive to combine sounds of, defaults to 40", val => { combineArchive = Convert.ToInt32(val); } },
|
||||||
|
{ "o", "overwrite existing soundfiles", val => { combineOverwrite = true; } },
|
||||||
|
{ "i", "merge incomplete files (into special directory)", val => { combineMergeIncomplete = true; } },
|
||||||
|
};
|
||||||
|
|
||||||
|
List<string> otherArgs = argsParser.Parse(args);
|
||||||
|
|
||||||
|
for (int i = 0; i < otherArgs.Count; i++)
|
||||||
|
{
|
||||||
|
string parsedPath = otherArgs[i];
|
||||||
|
if (!parsedPath.EndsWith("\\"))
|
||||||
|
parsedPath += "\\";
|
||||||
|
|
||||||
|
parsedPath = Environment.ExpandEnvironmentVariables(parsedPath);
|
||||||
|
|
||||||
|
if (Directory.Exists(parsedPath))
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
outDir = parsedPath;
|
||||||
|
else if (i == 1)
|
||||||
|
cacheDir = parsedPath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("The directory: " + parsedPath + " is not valid.");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 0 || help)
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"Usage: rscachetools [options] outDir cacheDir\n" +
|
||||||
|
"Provides various tools for extracting and manipulating RuneScape's cache files.\n" +
|
||||||
|
"\n" +
|
||||||
|
"Arguments:\n" +
|
||||||
|
"outDir - The directory in which all files generated by this tool will be placed. Default: cache\\\n" +
|
||||||
|
"cacheDir - The directory that contains all cache files. Default: %USERPROFILE%\\jagexcache\\runescape\\LIVE\\.\n" +
|
||||||
|
"\n" +
|
||||||
|
"Options:"
|
||||||
|
);
|
||||||
|
|
||||||
|
argsParser.WriteOptionDescriptions(Console.Out);
|
||||||
|
}
|
||||||
|
else if (!error)
|
||||||
|
{
|
||||||
|
if (extract)
|
||||||
|
ExtractFiles(extractArchive);
|
||||||
|
|
||||||
|
if (combine)
|
||||||
|
CombineSounds(combineArchive, combineStartFile, combineOverwrite, combineMergeIncomplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rips all files from the cachefile and puts them (structured and given a fitting extension where possible) in the fileDir.
|
||||||
|
/// </summary>
|
||||||
|
static void ExtractFiles(int archive)
|
||||||
|
{
|
||||||
|
int startArchive = 0, endArchive = 255;
|
||||||
|
|
||||||
|
if (archive != -1)
|
||||||
|
{
|
||||||
|
startArchive = archive;
|
||||||
|
endArchive = archive;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream cacheFile = File.Open(cacheDir + "main_file_cache.dat2", FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
for (int archiveIndex = startArchive; archiveIndex <= endArchive; archiveIndex++)
|
||||||
|
{
|
||||||
|
string indexFileString = cacheDir + "main_file_cache.idx" + archiveIndex.ToString();
|
||||||
|
|
||||||
|
if (File.Exists(indexFileString))
|
||||||
|
{
|
||||||
|
FileStream indexFile = File.Open(indexFileString, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
int fileCount = (int)indexFile.Length / 6;
|
||||||
|
|
||||||
|
for (int fileIndex = 0; fileIndex < fileCount; fileIndex++)
|
||||||
|
{
|
||||||
|
bool fileError = false;
|
||||||
|
|
||||||
|
indexFile.Seek(fileIndex * 6, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
uint fileSize = indexFile.ReadBytes(3);
|
||||||
|
long startChunkOffset = indexFile.ReadBytes(3) * 520L;
|
||||||
|
|
||||||
|
//Console.WriteLine("New file: archive: {0} file: {1} offset: {3} size: {2}", archiveIndex, fileIndex, fileSize, startChunkOffset);
|
||||||
|
|
||||||
|
//filter teeny tiny files (<=1kB) because they gobble up time
|
||||||
|
if (fileSize > 1024 && startChunkOffset > 0 && startChunkOffset + fileSize <= cacheFile.Length)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[fileSize];
|
||||||
|
int writeOffset = 0;
|
||||||
|
long currentChunkOffset = startChunkOffset;
|
||||||
|
|
||||||
|
for (int chunkIndex = 0; writeOffset < fileSize && currentChunkOffset > 0; chunkIndex++)
|
||||||
|
{
|
||||||
|
cacheFile.Seek(currentChunkOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
int chunkSize;
|
||||||
|
int checksumFileIndex = 0;
|
||||||
|
|
||||||
|
if (fileIndex < 65536)
|
||||||
|
{
|
||||||
|
chunkSize = (int)Math.Min(512, fileSize - writeOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if file index exceeds 2 bytes, add 65536 and read 2(?) extra bytes
|
||||||
|
chunkSize = (int)Math.Min(510, fileSize - writeOffset);
|
||||||
|
|
||||||
|
cacheFile.ReadByte();
|
||||||
|
checksumFileIndex = (cacheFile.ReadByte() << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksumFileIndex += (int)cacheFile.ReadBytes(2);
|
||||||
|
int checksumChunkIndex = (int)cacheFile.ReadBytes(2);
|
||||||
|
long nextChunkOffset = cacheFile.ReadBytes(3) * 520L;
|
||||||
|
int checksumArchiveIndex = cacheFile.ReadByte();
|
||||||
|
|
||||||
|
//Console.WriteLine("Chunk {2}: archive: {3} file: {1} size: {0} nextoffset: {4}", chunkSize, checksumFileIndex, checksumChunkIndex, checksumArchiveIndex, nextChunkOffset);
|
||||||
|
|
||||||
|
if (checksumFileIndex == fileIndex && checksumChunkIndex == chunkIndex && checksumArchiveIndex == archiveIndex &&
|
||||||
|
nextChunkOffset >= 0 && nextChunkOffset < cacheFile.Length)
|
||||||
|
{
|
||||||
|
cacheFile.Read(buffer, writeOffset, chunkSize);
|
||||||
|
writeOffset += chunkSize;
|
||||||
|
currentChunkOffset = nextChunkOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Ignoring file because a chunk's checksum doesn't match, ideally should not happen.");
|
||||||
|
|
||||||
|
fileError = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileError)
|
||||||
|
{
|
||||||
|
//process file
|
||||||
|
string outFileDir = outDir + archiveIndex + "\\";
|
||||||
|
string outFileName = fileIndex.ToString();
|
||||||
|
byte[] tempBuffer;
|
||||||
|
|
||||||
|
//decompress gzip
|
||||||
|
if ((buffer[9] << 8) + buffer[10] == 0x1f8b) //gzip
|
||||||
|
{
|
||||||
|
//remove the first 9 bytes cause they seem to be descriptors of sorts (no idea what they do but they are not part of the file)
|
||||||
|
tempBuffer = new byte[fileSize - 9];
|
||||||
|
Array.Copy(buffer, 9, tempBuffer, 0, fileSize - 9);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
GZipStream decompressionStream = new GZipStream(new MemoryStream(buffer), CompressionMode.Decompress);
|
||||||
|
|
||||||
|
int readBytes;
|
||||||
|
tempBuffer = new byte[0];
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
byte[] readBuffer = new byte[100000];
|
||||||
|
readBytes = decompressionStream.Read(readBuffer, 0, 100000);
|
||||||
|
|
||||||
|
int storedBytes = tempBuffer.Length;
|
||||||
|
Array.Resize(ref tempBuffer, tempBuffer.Length + readBytes);
|
||||||
|
Array.Copy(readBuffer, 0, tempBuffer, storedBytes, readBytes);
|
||||||
|
}
|
||||||
|
while (readBytes == 100000);
|
||||||
|
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
Console.WriteLine("File decompressed as gzip.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//decompress bzip2
|
||||||
|
if (buffer[9] == 0x31 && buffer[10] == 0x41 && buffer[11] == 0x59 && buffer[12] == 0x26 && buffer[13] == 0x53 && buffer[14] == 0x59) //bzip2
|
||||||
|
{
|
||||||
|
//remove the first 9 bytes cause they seem to be descriptors of sorts (no idea what they do but they are not part of the file)
|
||||||
|
tempBuffer = new byte[fileSize - 9];
|
||||||
|
Array.Copy(buffer, 9, tempBuffer, 0, fileSize - 9);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
//prepend file header
|
||||||
|
byte[] magic = new byte[] {
|
||||||
|
0x42, 0x5a, //BZ (signature)
|
||||||
|
0x68, //h (version)
|
||||||
|
0x31 //*100kB block-size
|
||||||
|
};
|
||||||
|
|
||||||
|
tempBuffer = new byte[magic.Length + buffer.Length];
|
||||||
|
magic.CopyTo(tempBuffer, 0);
|
||||||
|
buffer.CopyTo(tempBuffer, magic.Length);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
BZip2InputStream decompressionStream = new BZip2InputStream(new MemoryStream(buffer));
|
||||||
|
|
||||||
|
int readBytes;
|
||||||
|
tempBuffer = new byte[0];
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
byte[] readBuffer = new byte[100000];
|
||||||
|
readBytes = decompressionStream.Read(readBuffer, 0, 100000);
|
||||||
|
|
||||||
|
int storedBytes = tempBuffer.Length;
|
||||||
|
Array.Resize(ref tempBuffer, tempBuffer.Length + readBytes);
|
||||||
|
Array.Copy(readBuffer, 0, tempBuffer, storedBytes, readBytes);
|
||||||
|
}
|
||||||
|
while (readBytes == 100000);
|
||||||
|
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
Console.WriteLine("File decompressed as bzip2.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//detect ogg: OggS
|
||||||
|
if ((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] == 0x4f676753)
|
||||||
|
outFileName += ".ogg";
|
||||||
|
|
||||||
|
//detect ogg: 5 bytes - OggS
|
||||||
|
if ((buffer[5] << 24) + (buffer[6] << 16) + (buffer[7] << 8) + buffer[8] == 0x4f676753)
|
||||||
|
{
|
||||||
|
tempBuffer = new byte[fileSize - 5];
|
||||||
|
Array.Copy(buffer, 5, tempBuffer, 0, fileSize - 5);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
outFileName += ".ogg";
|
||||||
|
}
|
||||||
|
|
||||||
|
//detect jag: JAGA
|
||||||
|
if ((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] == 0x4a414741)
|
||||||
|
outFileName += ".jaga";
|
||||||
|
|
||||||
|
//detect jag: 5 bytes - JAGA
|
||||||
|
if ((buffer[5] << 24) + (buffer[6] << 16) + (buffer[7] << 8) + buffer[8] == 0x4a414741)
|
||||||
|
{
|
||||||
|
tempBuffer = new byte[fileSize - 5];
|
||||||
|
Array.Copy(buffer, 5, tempBuffer, 0, fileSize - 5);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
outFileName += ".jaga";
|
||||||
|
}
|
||||||
|
|
||||||
|
//detect png: .PNG
|
||||||
|
if ((uint)(buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] == 0x89504e47)
|
||||||
|
outFileName += ".png";
|
||||||
|
|
||||||
|
//detect png: 5 bytes - .PNG
|
||||||
|
if ((uint)(buffer[5] << 24) + (buffer[6] << 16) + (buffer[7] << 8) + buffer[8] == 0x89504e47)
|
||||||
|
{
|
||||||
|
tempBuffer = new byte[fileSize - 5];
|
||||||
|
Array.Copy(buffer, 5, tempBuffer, 0, fileSize - 5);
|
||||||
|
buffer = tempBuffer;
|
||||||
|
|
||||||
|
outFileName += ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
//create and write file
|
||||||
|
if (!Directory.Exists(outFileDir))
|
||||||
|
Directory.CreateDirectory(outFileDir);
|
||||||
|
|
||||||
|
using (FileStream outFile = File.Open(outFileDir + outFileName, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
outFile.Write(buffer, 0, buffer.Length);
|
||||||
|
Console.WriteLine(outFileDir + outFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Ignoring file because of size or offset.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Done extracting files.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines the sound files (.jag & .ogg) in the specified archive (40 for the build it was made on), and puts them into the soundtracks directory.
|
||||||
|
/// </summary>
|
||||||
|
static void CombineSounds(int archive = 40, int startFile = 0, bool overwriteExisting = false, bool mergeIncomplete = false)
|
||||||
|
{
|
||||||
|
string archiveDir = outDir + archive + "\\";
|
||||||
|
string soundDir = outDir + "sound\\";
|
||||||
|
|
||||||
|
//gather all index files
|
||||||
|
string[] indexFiles = Directory.GetFiles(archiveDir, "*.jag", SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
|
//create directories
|
||||||
|
if (!Directory.Exists(soundDir + "incomplete\\"))
|
||||||
|
Directory.CreateDirectory(soundDir + "incomplete\\");
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
foreach (string indexFileString in indexFiles)
|
||||||
|
{
|
||||||
|
if (i < startFile)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool incomplete = false;
|
||||||
|
List<string> chunkFiles = new List<string>();
|
||||||
|
|
||||||
|
using (FileStream indexFileStream = File.Open(indexFileString, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
|
{
|
||||||
|
indexFileStream.Seek(32, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
while (indexFileStream.ReadBytes(4) != 0x4f676753)
|
||||||
|
{
|
||||||
|
uint fileId = indexFileStream.ReadBytes(4);
|
||||||
|
|
||||||
|
//check if the file exists and add it to the buffer if it does
|
||||||
|
if (File.Exists(archiveDir + fileId + ".ogg"))
|
||||||
|
chunkFiles.Add(archiveDir + fileId + ".ogg");
|
||||||
|
else
|
||||||
|
incomplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy the first chunk to a temp file so SoX can handle the combining
|
||||||
|
indexFileStream.Seek(-4, SeekOrigin.Current);
|
||||||
|
|
||||||
|
//wait till file is available
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (FileStream tempIndexFile = File.Open("~index.ogg", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
|
||||||
|
{
|
||||||
|
indexFileStream.CopyTo(tempIndexFile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!incomplete || incomplete && mergeIncomplete)
|
||||||
|
{
|
||||||
|
string outFile = soundDir + (incomplete ? "incomplete\\" : "") + i + ".ogg";
|
||||||
|
|
||||||
|
if (!overwriteExisting && File.Exists(outFile))
|
||||||
|
Console.WriteLine("Skipping track because it already exists.");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//combine the files with sox
|
||||||
|
Console.WriteLine("Running SoX to concatenate ogg audio chunks.");
|
||||||
|
|
||||||
|
Process soxProcess = new Process();
|
||||||
|
soxProcess.StartInfo.FileName = "sox.exe";
|
||||||
|
|
||||||
|
soxProcess.StartInfo.Arguments = "--combine concatenate ~index.ogg";
|
||||||
|
chunkFiles.ForEach((str) =>
|
||||||
|
{
|
||||||
|
soxProcess.StartInfo.Arguments += " " + str;
|
||||||
|
});
|
||||||
|
soxProcess.StartInfo.Arguments += " " + soundDir + "incomplete\\" + i + ".ogg ";
|
||||||
|
soxProcess.StartInfo.UseShellExecute = false;
|
||||||
|
|
||||||
|
soxProcess.Start();
|
||||||
|
soxProcess.WaitForExit();
|
||||||
|
|
||||||
|
if (soxProcess.ExitCode == 0)
|
||||||
|
{
|
||||||
|
if (!incomplete)
|
||||||
|
{
|
||||||
|
//clear space
|
||||||
|
if (File.Exists(soundDir + i + ".ogg"))
|
||||||
|
File.Delete(soundDir + i + ".ogg");
|
||||||
|
|
||||||
|
File.Move(soundDir + "incomplete\\" + i + ".ogg", soundDir + i + ".ogg");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(soundDir + (incomplete ? "incomplete\\" : "") + i + ".ogg");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Console.WriteLine("SoX encountered error code " + soxProcess.ExitCode + " and probably didn't finish processing the files.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Console.WriteLine("Skipping track because it's incomplete.");
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup on isle 4
|
||||||
|
File.Delete("~index.ogg");
|
||||||
|
|
||||||
|
Console.WriteLine("Done combining sound.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a given amount of bytes from the stream.
|
||||||
|
/// </summary>
|
||||||
|
public static uint ReadBytes(this Stream stream, byte bytes)
|
||||||
|
{
|
||||||
|
if (bytes == 0 || bytes > 4)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
uint result = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < bytes; i++)
|
||||||
|
result += (uint)stream.ReadByte() << (bytes - i - 1) * 8;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
Properties/AssemblyInfo.cs
Normal file
36
Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("RSCacheTool")]
|
||||||
|
[assembly: AssemblyDescription("Various tools for extracting and manipulating RuneScape's cache files.")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Villermen")]
|
||||||
|
[assembly: AssemblyProduct("RSCacheTool")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("bbc70dd4-1015-4395-8340-4838b6a23815")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
61
RSCacheTool.csproj
Normal file
61
RSCacheTool.csproj
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{722D3FF9-C3E9-40D9-B68A-3FA1C304A34C}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>RSCacheTool</RootNamespace>
|
||||||
|
<AssemblyName>RSCacheTool</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="ICSharpCode.SharpZipLib">
|
||||||
|
<HintPath>.\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Options.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="sox.exe">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
BIN
sox.exe
Normal file
BIN
sox.exe
Normal file
Binary file not shown.
Loading…
Reference in a new issue