Skip to content

Instantly share code, notes, and snippets.

@UserUnknownFactor
Last active September 1, 2022 07:17
Show Gist options
  • Save UserUnknownFactor/c4190242206f0677789db24fbaa48118 to your computer and use it in GitHub Desktop.
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
powershell -executionPolicy bypass -file "tyranocut.ps1" -Unpack -GameExeName %1
# 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
}
#!/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