Skip to content

Instantly share code, notes, and snippets.

@geophree
Last active June 14, 2018 16:42
Show Gist options
  • Save geophree/5bd74882d9dcea855e1c15ca3cce93df to your computer and use it in GitHub Desktop.
Save geophree/5bd74882d9dcea855e1c15ca3cce93df to your computer and use it in GitHub Desktop.
Build Graveyard Keeper directory structure from installer on linux
#!/usr/bin/env python3
# Big thanks to @TechnicianLP for the inspiration! See https://gist.github.com/TechnicianLP/5882de4075b6ed3700bba2bbc310e616
# This script builds the Graveyard Keeper directory/file tree using the .msi installer db.
# NOTE: Before running this script:
# Install wine (GK works for me with 3.9, probably needs at least 3.0)
# unzip the installer, then run:
# wine installer_filename.exe /extract
# This will give you the .cab and the .msi
# Extract the .cab with a archive program (7z probably works, Archive Manager in Ubuntu definitely does)
# Install msitools if you haven't already, this script needs msiinfo (https://wiki.gnome.org/msitools)
# Run this script in the same directory as the .msi file and all the files extracted from the .cab
# NOTE: To finish installing and running GK:
# Move the directory it created to wine
# mv "Graveyard Keeper Alpha" ~/".wine/drive_c/Program Files (x86)/"
# I run GK (windowed) with the following command line
# wine explorer /desktop=grave,1920x1080 Graveyard\ Keeper.exe
# The first time I ran it it downloaded a few things, but works fine.
# Enjoy a great game!
# NOTE: GK stores your savegames in a different place, so you can just replace the game directory completely and you won't lose anything
import subprocess
GAME_DIR = 'Graveyard Keeper Alpha'
MSI_FILENAME = 'Graveyard Keeper Alpha.msi'
def shellEscape(string):
return "'" + string.replace("'", """'"'"'""") + "'"
def runMsi(*command):
return subprocess.run("msiinfo " + shellEscape(command[0]) + " " + shellEscape(MSI_FILENAME) + " " + " ".join(map(shellEscape, command[1:])), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
def makeTable(tableName, transform=lambda x: x):
table = {}
rows = iter(runMsi("export", tableName).splitlines())
column_names = next(rows).split("\t")
next(rows) # Column types
next(rows) # Not sure what this line is.
for i in rows:
columns = i.split("\t")
entry = dict(zip(column_names, columns))
entry = transform(entry)
if not entry: continue
table[columns[0]] = entry
return table
# Strip weird old-dos filename prefixes
def fixFilename(filename):
delimIndex = filename.find('|')
if delimIndex == -1: return filename
return filename[delimIndex+1:]
def createTransformFilename(filenameField):
def tranformFilename(x):
x[filenameField] = fixFilename(x[filenameField])
return x
return tranformFilename
def makeFullDirectoryMap(directories):
map = {}
def getFullPath(key):
if key in map:
return map[key]
i = directories[key]
parent = i['Directory_Parent']
defaultDir = i['DefaultDir']
fullPath = defaultDir
if parent:
fullPath = getFullPath(parent) + '/' + fullPath
map[key] = fullPath
return fullPath
for i in directories.keys():
getFullPath(i)
return map
# 'web.config': {'Attributes': '0',
# 'Component_': 'DefaultWsdlHelpGenerator.aspx_1',
# 'File': 'web.config',
# 'FileName': 'web.config',
# 'FileSize': '11686',
# 'Language': '',
# 'Sequence': '10',
# 'Version': ''},
# 'websocketsharp.dll': {'Attributes': '0',
# 'Component': 'websocketsharp.dll',
# 'ComponentId': '{4ED20095-7B23-4B62-9168-D98B50BF28E7}',
# 'Condition': '',
# 'Directory_': 'Managed_Dir',
# 'KeyPath': 'websocketsharp.dll'}}
def makeFileMap(files, components, directoryMap):
map = {}
for src, i in files.items():
map[src] = directoryMap[components[i['Component_']]['Directory_']] + '/' + i['FileName']
return map
def makeFileAndDirectoryMap():
files = makeTable("File", createTransformFilename('FileName'))
components = makeTable("Component")
directories = makeTable("Directory", createTransformFilename('DefaultDir'))
directories['APPDIR']['DefaultDir'] = GAME_DIR
directories['APPDIR']['Directory_Parent'] = ''
directoryMap = makeFullDirectoryMap(directories)
fileMap = makeFileMap(files, components, directoryMap)
return fileMap, directoryMap
def main(argv):
import os
fileMap, directoryMap = makeFileAndDirectoryMap()
print('Making directories:')
for i in sorted(directoryMap.values()):
if i.startswith(GAME_DIR):
print(i)
os.makedirs(i, exist_ok=True)
print('Moving files:')
for src, dest in fileMap.items():
print(src + ' -> ' + dest)
os.rename(src, dest)
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment