-
-
Save kekekeks/3115ea23ffce8bca0b362441e5a80a90 to your computer and use it in GitHub Desktop.
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.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