Last active
September 1, 2022 07:17
-
-
Save UserUnknownFactor/c4190242206f0677789db24fbaa48118 to your computer and use it in GitHub Desktop.
PowerShell and Python utility to cut data file out of combined TyranoBuilder executable
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
powershell -executionPolicy bypass -file "tyranocut.ps1" -Unpack -GameExeName %1 |
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
# PowerShell utility to cut data file out of combined TyranoBuilder executable | |
# Run as: | |
# powershell -executionPolicy bypass -file "tyranocut.ps1" -GameExeName "game.exe" | |
# powershell -executionPolicy bypass -file "tyranocut.ps1" -Unpack -GameExeName "game.exe" | |
# | |
Param( | |
[parameter(Mandatory=$true, HelpMessage="Provide file name of game exe")][string]$GameExeName, | |
[switch]$Unpack = $false | |
) | |
Set-StrictMode -Version Latest | |
$ErrorActionPreference = "Stop" | |
$PSDefaultParameterValues['*:ErrorAction']='Stop' | |
function ConvertTo-BinaryString { | |
# Converts the bytes of a file to a string that has a 1-to-1 mapping back to the file's original bytes. | |
# Useful for performing binary regular expressions. | |
[OutputType([String])] | |
Param ( | |
[Parameter(Mandatory = $True, ValueFromPipeline = $True, Position = 0)] | |
[ValidateScript( { Test-Path $_ -PathType Leaf } )] | |
[String]$Path | |
) | |
$Stream = New-Object System.IO.FileStream -ArgumentList $Path, 'Open', 'Read' | |
$Encoding = [Text.Encoding]::GetEncoding(28591) | |
$StreamReader = New-Object System.IO.StreamReader -ArgumentList $Stream, $Encoding | |
$BinaryText = $StreamReader.ReadToEnd() | |
$StreamReader.Close() | |
$Stream.Close() | |
return $BinaryText | |
} | |
if (-Not (Test-Path -LiteralPath $GameExeName)) { | |
Write-Host "$GameExeName file not found or path contains brackets." | |
exit | |
} | |
$outputFile = $GameExeName | |
$inputFile = [System.IO.Path]::GetFileNameWithoutExtension($GameExeName) + '.old' | |
$outputData = 'data.zip' | |
# Signature of first merged .nw file inside .exe file | |
$re = [Regex]'\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00' | |
Write-Host "Processing $outputFile..." | |
if (Test-Path -LiteralPath $inputFile) { | |
Write-Host "Backup of game exe already exists." | |
exit | |
} | |
Rename-Item -Path $outputFile -NewName $inputFile | |
$fileBytes = [System.IO.File]::ReadAllBytes($inputFile) | |
$binString = ConvertTo-BinaryString -Path $inputFile | |
$ms = New-Object System.IO.MemoryStream | |
$mso = New-Object System.IO.MemoryStream | |
$pos = $lastlength = $replacements = 0 | |
foreach ($m in $re.Matches($binString)) { | |
$ms.Write($fileBytes, $pos, $m.Index) | |
$pos += ($m.Index + $m.Length) | |
$lastlength = $m.Length | |
$replacements++ | |
# We need only the first match | |
break | |
} | |
if ($replacements) { | |
# Write unmerged .nw file, $lastlength = 10 is length of the header | |
$mso.Write($fileBytes, $pos - $lastlength, $fileBytes.Count - $pos + $lastlength) | |
[System.IO.File]::WriteAllBytes($outputFile, $ms.ToArray()) | |
[System.IO.File]::WriteAllBytes($outputData, $mso.ToArray()) | |
} | |
$ms.Dispose() | |
$mso.Dispose() | |
if ($replacements) { | |
if ($Unpack) { | |
$outfolder = (Get-Item -Path ".\").FullName | |
Write-Host -NoNewline "Unpacking $outputData" | |
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null | |
$zipFile = [IO.Compression.ZipFile]::OpenRead($outputData) | |
foreach ($packed in $zipFile.Entries) { | |
$file = Join-Path -Path $outfolder -ChildPath $packed.FullName | |
if ($file.Substring($file.get_Length()-1) -eq "\") { | |
# This is directory, don't extract it as file and create if not exist | |
if (-Not (Test-Path $file)) { | |
New-Item -ItemType Directory -Path $file | Out-Null | |
} | |
continue | |
} | |
Write-Host -NoNewline "." | |
[IO.Compression.ZipFileExtensions]::ExtractToFile($packed, $file, $true) | |
} | |
$zipFile.Dispose() | |
Write-Host "" | |
Write-Host "$replacements replacement(s) made, $outputData written and unpacked." | |
} else { | |
Write-Host "$replacements replacement(s) made and $outputData written." | |
} | |
} else { | |
Write-Host "Byte sequence not found." | |
Rename-Item -Path $inputFile -NewName $outputFile | |
} |
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
#!/usr/bin/env python | |
import sys, zipfile, os, mmap | |
def main(): | |
if len(sys.argv) != 2: | |
print (f'Combined TyranoBuilder executable data extractor v1.0') | |
print (f'Usage: {os.path.basename(__file__)} game.exe') | |
return | |
filename = sys.argv[1] | |
if os.path.isfile(filename): | |
print (f'Extracting {filename}... ', end='', flush=True) | |
with open(filename, 'rb') as f: | |
try: | |
with zipfile.ZipFile(f) as zf: | |
zf.extractall() | |
print ('OK') | |
except Exception as e: | |
print (f'FAILED ({e})') | |
return | |
newname = os.path.join(os.path.dirname(filename), os.path.splitext(os.path.basename(filename))[0] + '_unpacked.exe') | |
print (f'Writing cleaned exe {newname}... ', end='', flush=True) | |
try: | |
with open(filename, "r+b") as f: | |
mm = mmap.mmap(f.fileno(), 0) | |
found = mm.find(b'\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00') | |
#print(found + " ", end='') | |
if found == -1: | |
print('FAILED (not found)') | |
return | |
mm.seek(0) | |
with open(newname, "wb") as new: | |
new.write(mm.read(found)) | |
print ('OK') | |
except Exception as e: | |
print (f'FAILED ({e})') | |
else: | |
print (f'{filename} is not found.') | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment