Skip to content

Instantly share code, notes, and snippets.

@kekekeks
Created April 12, 2016 17:08
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 kekekeks/3115ea23ffce8bca0b362441e5a80a90 to your computer and use it in GitHub Desktop.
Save kekekeks/3115ea23ffce8bca0b362441e5a80a90 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Extractor
{
class MainClass
{
static HashSet<string> KnownFiles = new HashSet<string>{
"GameplayData/GalaxyGenerationTables/ModelledGalaxyData.yaml","GameplayData/GalaxyGenerationTables/GalaxyAges.yaml","GameplayData/GalaxyGenerationTables/GameStartPoints.yaml","GameplayData/Difficulties.yaml","GameplayData/GalaxyGenerationTables/BiomeChances.yaml","GameplayData/GalaxyGenerationTables/PlanetSizeChances.yaml","GameplayData/StarTypes.yaml","GameplayData/PlanetaryResourceTypes.yaml","GameplayData/GalaxyGenerationTables/TradeableResourceChances.yaml","GameplayData/ShipData/TacticalScenarioDesign.yaml","GameplayData/ShipData/TacticalShipDesign.yaml","GameplayData/ShipData/FormationTypes.yaml","GameplayData/TacticalObstacleTypes.yaml","GameplayData/PlanetData/PlanetSizeTypes.yaml","GameplayData/PlanetData/PlanetBiomeTypes.yaml","GameplayData/PlanetData/PlanetMineralRichnessTypes.yaml","GameplayData/GalaxyGenerationTables/GravityTable.yaml","GameplayData/ShipData/ShipHullTypes.yaml","GameplayData/ShipData/ShipHullModels.yaml","GameplayData/ShipData/ShipSlots.yaml","GameplayData/ShipData/ShipModuleModTypes.yaml","GameplayData/ShipData/WeaponTypes.yaml","GameplayData/ShipData/ShipModules.yaml","GameplayData/ShipData/ShipBlueprintTemplates.yaml","GameplayData/RacialPerks.yaml","GameplayData/RaceTypes.yaml","GameplayData/ColonyStructureTypes.yaml","GameplayData/ColonyProjects.yaml","GameplayData/PlanetDefenses.yaml","GameplayData/Miniaturizations.yaml","GameplayData/TechAchievements.yaml","GameplayData/TechApps.yaml","GameplayData/GovernmentTypes.yaml","GameplayData/TechTree.yaml","GameplayData/EspionageMissions.yaml","GameplayData/RandomEvents.yaml","GameplayData/GNNEvent.yaml","GameplayData/GNNRankings.yaml","GameplayData/NotificationType.yaml","GameplayData/AudienceEvent.yaml","GameplayData/AudienceEventPlaylist.yaml","GameplayData/Tutorials.yaml","GameplayData/TriggerableActions.yaml","GameplayData/MentatResearchEvent.yaml","GameplayData/AI/PlanetRating.yaml",
};
class FileDesc
{
public string Path;
public BoyerMooreBinarySearch Search;
public byte[] Data;
public string StringData;
public FileDesc(string path)
{
Path = path.Replace('/', System.IO.Path.DirectorySeparatorChar);
var search = path.Split('/').Last().Split('.')[0];
Search = new BoyerMooreBinarySearch(Encoding.ASCII.GetBytes("\0\0"+search));
}
}
static int Find(byte[] array, byte b, int offset)
{
for(int c=offset; c<array.Length && c<offset +0x100000; c++)
{
if (array[c] == b)
return c;
}
return -1;
}
public static void Main(string[] args)
{
try
{
RealMain();
Console.WriteLine("Completed!");
}
catch(Exception e)
{
Console.Error.WriteLine(e.ToString());
}
Console.WriteLine("Press any key");
Console.ReadKey();
}
static void RealMain()
{
if (Debugger.IsAttached)
{
Directory.SetCurrentDirectory("/tmp/orion");
}
var files = KnownFiles.Select(f => new FileDesc(f)).ToList();
var buffer = new byte[0x8000000];
using (var file = new FileStream("resources.assets", FileMode.Open))
{
file.Seek(0x7f000000, SeekOrigin.Begin);
while (true)
{
Util.Memset(buffer, 0, buffer.Length);
var fileOffset = file.Position;
Console.WriteLine("Reading {0} bytes from offset 0x{1}", buffer.Length, file.Position.ToString("x8"));
if (file.Read(buffer, 0, buffer.Length) <= 0)
break;
foreach(var f in files.Where(x=>x.Data==null))
{
foreach(var match in f.Search.GetMatchIndexes(buffer))
{
var currentOffset = fileOffset + match;
var startIndex = Find(buffer, 0x0A, match);
if (startIndex == -1)
continue;
startIndex -= 4;
if (buffer[startIndex] != '-')
continue;
var endIndex = Find(buffer, 0, startIndex);
if (endIndex == -1)
continue;
endIndex-=3;
f.Data = new byte[endIndex - startIndex];
Buffer.BlockCopy(buffer, startIndex, f.Data, 0, f.Data.Length);
f.StringData = Encoding.UTF8.GetString(f.Data);
Console.WriteLine("Saving {0} to StreamingAssets", f.Path);
var path = Path.Combine("StreamingAssets", f.Path);
Directory.CreateDirectory(Path.GetDirectoryName(path));
File.WriteAllBytes(path, f.Data);
break;
}
}
}
}
foreach(var f in files)
{
if (f.Data == null)
Console.WriteLine("Unable to find resource " + f.Path);
}
}
}
public static class Util
{
static Util()
{
var dynamicMethod = new DynamicMethod("Memset", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard,
null, new [] { typeof(IntPtr), typeof(byte), typeof(int) }, typeof(Util), true);
var generator = dynamicMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Initblk);
generator.Emit(OpCodes.Ret);
MemsetDelegate = (Action<IntPtr, byte, int>)dynamicMethod.CreateDelegate(typeof(Action<IntPtr, byte, int>));
}
public static void Memset(byte[] array, byte what, int length)
{
var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, length);
gcHandle.Free();
}
public static void ForMemset(byte[] array, byte what, int length)
{
for(var i = 0; i < length; i++)
{
array[i] = what;
}
}
private static Action<IntPtr, byte, int> MemsetDelegate;
}
/// <summary>
/// Uses the Boyer-Moore algorithm to perform binary searches.
/// </summary>
/// <remarks>
/// The core implementation of the Boyer-Moore algorithm used here was taken from an article on the Code Project website
/// located here: http://www.codeproject.com/Articles/12781/Boyer-Moore-and-related-exact-string-matching-algo
/// and is covered by the Code Project Open License (CPOL) 1.02 located here: http://www.codeproject.com/info/cpol10.aspx
/// </remarks>
public class BoyerMooreBinarySearch
{
private readonly int[] _badCharacterShift;
private readonly int[] _goodSuffixShift;
private readonly byte[] _searchPattern;
/// <summary>
/// Constructor
/// </summary>
/// <param name="searchPattern">Pattern for search</param>
public BoyerMooreBinarySearch(byte[] searchPattern)
{
if ((searchPattern == null) ||
!searchPattern.Any())
{
throw new ArgumentNullException("searchPattern");
}
/* Preprocessing */
_searchPattern = searchPattern;
_badCharacterShift = BuildBadCharacterShift(searchPattern);
var suffixes = FindSuffixes(searchPattern);
_goodSuffixShift = BuildGoodSuffixShift(searchPattern, suffixes);
}
/// <summary>
/// Build the bad byte shift array.
/// </summary>
/// <param name="pattern">Pattern for search</param>
/// <returns>Bad byte shift array</returns>
private int[] BuildBadCharacterShift(byte[] pattern)
{
var badCharacterShift = new int[256];
int patternLength = pattern.Length;
for (int c = 0; c < Convert.ToInt64(badCharacterShift.Length); ++c)
{
badCharacterShift[c] = patternLength;
}
for (int i = 0; i < patternLength - 1; ++i)
{
badCharacterShift[pattern[i]] = patternLength - i - 1;
}
return badCharacterShift;
}
/// <summary>
/// Find suffixes in the pattern
/// </summary>
/// <param name="pattern">Pattern for search</param>
/// <returns>Suffix array</returns>
private int[] FindSuffixes(byte[] pattern)
{
int f = 0;
var patternLength = pattern.Length;
var suffixes = new int[pattern.Length + 1];
suffixes[patternLength - 1] = patternLength;
int g = patternLength - 1;
for (int i = patternLength - 2; i >= 0; --i)
{
if (i > g && suffixes[i + patternLength - 1 - f] < i - g)
{
suffixes[i] = suffixes[i + patternLength - 1 - f];
}
else
{
if (i < g)
{
g = i;
}
f = i;
while (g >= 0 && (pattern[g] == pattern[g + patternLength - 1 - f]))
{
--g;
}
suffixes[i] = f - g;
}
}
return suffixes;
}
/// <summary>
/// Build the good suffix array.
/// </summary>
/// <param name="pattern">Pattern for search</param>
/// <param name="suff">Suffixes in the pattern</param>
/// <returns>Good suffix shift array</returns>
private int[] BuildGoodSuffixShift(byte[] pattern, int[] suff)
{
var patternLength = pattern.Length;
var goodSuffixShift = new int[pattern.Length + 1];
for (int i = 0; i < patternLength; ++i)
{
goodSuffixShift[i] = patternLength;
}
int j = 0;
for (int i = patternLength - 1; i >= -1; --i)
{
if (i == -1 || suff[i] == i + 1)
{
for (; j < patternLength - 1 - i; ++j)
{
if (goodSuffixShift[j] == patternLength)
{
goodSuffixShift[j] = patternLength - 1 - i;
}
}
}
}
for (int i = 0; i <= patternLength - 2; ++i)
{
goodSuffixShift[patternLength - 1 - suff[i]] = patternLength - 1 - i;
}
return goodSuffixShift;
}
/// <summary>
/// Return all matches of the pattern in specified data using the Boyer-Moore algorithm
/// </summary>
/// <param name="dataToSearch">The data to be searched</param>
/// <returns>List which returns the indexes of pattern matches</returns>
public IEnumerable<int> GetMatchIndexes(byte[] dataToSearch)
{
//var matchIndexes = new List<int>();
if (dataToSearch == null)
{
throw new ArgumentNullException("dataToSearch");
}
var patternLength = _searchPattern.Length;
var textLength = dataToSearch.Length;
/* Searching */
var index = 0;
while (index <= (textLength - patternLength))
{
int unmatched = 0;
for (unmatched = patternLength - 1; unmatched >= 0 && (_searchPattern[unmatched] == dataToSearch[unmatched + index]); --unmatched)
{
// empty - do nothing
}
if (unmatched < 0)
{
yield return index;
index += _goodSuffixShift[0];
}
else
{
index += Math.Max(_goodSuffixShift[unmatched], _badCharacterShift[dataToSearch[unmatched + index]] - patternLength + 1 + unmatched);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment