Skip to content

Instantly share code, notes, and snippets.

@gdkchan
Created July 20, 2017 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gdkchan/635187f50a18539b36860a53493f275f to your computer and use it in GitHub Desktop.
Save gdkchan/635187f50a18539b36860a53493f275f to your computer and use it in GitHub Desktop.
Switch IStorage extractor in C#
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