Created
July 20, 2017 19:52
-
-
Save gdkchan/635187f50a18539b36860a53493f275f to your computer and use it in GitHub Desktop.
Switch IStorage extractor in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Text; | |
namespace UnStore | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Console.ForegroundColor = ConsoleColor.Cyan; | |
Console.WriteLine("UnStore - IStorage extractor by gdkchan"); | |
Console.WriteLine("Version 0.1.0\n"); | |
Console.ResetColor(); | |
if (args.Length == 1 && File.Exists(args[0])) | |
{ | |
using (FileStream FS = new FileStream(args[0], FileMode.Open)) | |
{ | |
DumpIStorage(FS, Path.GetFileNameWithoutExtension(args[0])); | |
} | |
} | |
else | |
{ | |
Console.WriteLine("Please pass the *.istorage file name as an argument to this tool!"); | |
} | |
} | |
struct IStorage | |
{ | |
public Stream Data; | |
public BinaryReader Reader; | |
public long HeaderLength; | |
public long DirHashAddress; | |
public long DirHashLength; | |
public long DirInfoAddress; | |
public long DirInfoLength; | |
public long FileHashAddress; | |
public long FileHashLength; | |
public long FileInfoAddress; | |
public long FileInfoLength; | |
public long DataAddress; | |
} | |
static void DumpIStorage(Stream Data, string RootPath) | |
{ | |
BinaryReader Reader = new BinaryReader(Data); | |
IStorage IS = new IStorage() | |
{ | |
Data = Data, | |
Reader = Reader, | |
HeaderLength = Reader.ReadInt64(), | |
DirHashAddress = Reader.ReadInt64(), | |
DirHashLength = Reader.ReadInt64(), | |
DirInfoAddress = Reader.ReadInt64(), | |
DirInfoLength = Reader.ReadInt64(), | |
FileHashAddress = Reader.ReadInt64(), | |
FileHashLength = Reader.ReadInt64(), | |
FileInfoAddress = Reader.ReadInt64(), | |
FileInfoLength = Reader.ReadInt64(), | |
DataAddress = Reader.ReadInt64() | |
}; | |
Directory.CreateDirectory(RootPath); | |
Data.Seek(IS.DirInfoAddress, SeekOrigin.Begin); | |
DumpDirectory(IS, RootPath); | |
} | |
static void DumpDirectory(IStorage IS, string RootPath) | |
{ | |
int ParentDirOffset = IS.Reader.ReadInt32(); | |
int SiblingDirOffset = IS.Reader.ReadInt32(); | |
int ChildDirOffset = IS.Reader.ReadInt32(); | |
int FileOffset = IS.Reader.ReadInt32(); | |
int NextDirOffset = IS.Reader.ReadInt32(); | |
string Name = ReadName(IS.Reader); | |
string FullPath = Path.Combine(RootPath, Name); | |
Directory.CreateDirectory(FullPath); | |
DumpFile(IS, FullPath, FileOffset); | |
if (ChildDirOffset != -1) | |
{ | |
IS.Data.Seek(IS.DirInfoAddress + ChildDirOffset, SeekOrigin.Begin); | |
DumpDirectory(IS, FullPath); | |
} | |
if (SiblingDirOffset != -1) | |
{ | |
IS.Data.Seek(IS.DirInfoAddress + SiblingDirOffset, SeekOrigin.Begin); | |
DumpDirectory(IS, RootPath); | |
} | |
} | |
static void DumpFile(IStorage IS, string RootPath, int Offset) | |
{ | |
while (Offset != -1) | |
{ | |
IS.Data.Seek(IS.FileInfoAddress + Offset, SeekOrigin.Begin); | |
int ParentDirOffset = IS.Reader.ReadInt32(); | |
int SiblingFileOffset = IS.Reader.ReadInt32(); | |
long FileDataOffset = IS.Reader.ReadInt64(); | |
long FileDataLength = IS.Reader.ReadInt64(); | |
int NextFileOffset = IS.Reader.ReadInt32(); | |
string Name = ReadName(IS.Reader); | |
string FullPath = Path.Combine(RootPath, Name); | |
Console.WriteLine("Extracting " + FullPath + "..."); | |
IS.Data.Seek(IS.DataAddress + FileDataOffset, SeekOrigin.Begin); | |
using (FileStream Output = new FileStream(FullPath, FileMode.Create)) | |
{ | |
byte[] Buffer = new byte[0x10000]; | |
long Missing = FileDataLength; | |
while (Missing > 0) | |
{ | |
int ReadAmount = (int)Math.Min((long)Buffer.Length, Missing); | |
int BlkLength = IS.Reader.Read(Buffer, 0, ReadAmount); | |
Missing -= BlkLength; | |
Output.Write(Buffer, 0, BlkLength); | |
} | |
} | |
Offset = SiblingFileOffset; | |
} | |
} | |
static string ReadName(BinaryReader Reader) | |
{ | |
int NameLength = Reader.ReadInt32(); | |
byte[] NameBuffer = Reader.ReadBytes(NameLength); | |
string Name = Encoding.UTF8.GetString(NameBuffer); | |
while ((Reader.BaseStream.Position & 3) != 0) | |
{ | |
Reader.ReadByte(); | |
} | |
return Name; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment