Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- # created using python 3.6
- import copy
- from functools import partial
- import math
- import os
- import re
- from struct import pack, unpack, calcsize
- import subprocess
- import sys
- import tempfile
- from zipfile import ZipFile
- ImageMagicConvert = '/usr/bin/convert'
- SCMAPMAGIC = b'\x4d\x61\x70\x1a'
- DDSMAGIC = b'DDS '
- decalDebug = False
- debugPrintEnabled = False
- #mirrorMode = "Diagonal Top-Left to Bottom-Right"
- mirrorMode = "Left-Right"
- #mirrorEmbeddedImages = "from left"
- mirrorEmbeddedImages = "from right"
- if mirrorMode == "Diagonal Top-Left to Bottom-Right":
- def translatePosition( position, mapSize ):
- return ( position[2], position[1], position[0] )
- def rotateDecal(rotation):
- return ( rotation[2], -rotation[1], -rotation[0] )
- # not really posible to mirror the mesh by rotation...
- def rotateProp( rotationX, rotationY, rotationZ ):
- rad = math.acos( rotationX[0] ) + math.pi
- newRotationX = ( math.cos(rad), rotationX[1], math.sin(rad) )
- newRotationY = ( rotationY[0], rotationY[1], rotationY[2] )
- newRotationZ = ( -math.sin(rad), rotationZ[1], math.cos(rad) )
- return ( newRotationX, newRotationY, newRotationZ )
- elif mirrorMode == "Left-Right":
- unitPositionFix = {
- 'xec8012': (-1, 0, 1), # -|
- 'xec8004': ( 1, 0, 0), # ---
- 'xec8008': (-1, 0, 1), # |-
- 'xec8003': ( 0, 0,-1), # |
- }
- def translatePosition( position, mapSize ):
- return ( ( mapSize[0] - position[0] ), position[1], position[2] )
- def translateUnitPosition( unitType, position, mapSize ):
- position = ( ( mapSize[0] - position[0] ), position[1], position[2] )
- if unitType in list(unitPositionFix):
- fix = unitPositionFix[unitType]
- position = ( position[0]+fix[0], position[1]+fix[1], position[2]+fix[2] )
- return position
- def rotateDecal(rotation):
- return ( rotation[2], math.pi/2 - rotation[1], -rotation[0] )
- def rotateProp( rotationX, rotationY, rotationZ ):
- rad = math.acos( rotationX[0] ) + math.pi
- newRotationX = ( math.cos(rad), rotationX[1], math.sin(rad) )
- newRotationY = ( rotationY[0], rotationY[1], rotationY[2] )
- newRotationZ = ( -math.sin(rad), rotationZ[1], math.cos(rad) )
- return ( newRotationX, newRotationY, newRotationZ )
- def dummyUnitRotation( unitType, rotation ):
- return ( rotation[0], rotation[1], rotation[2] )
- unitTypeTranslation = {
- 'xec8001': 'xec8003',
- 'xec8002': 'xec8004',
- 'xec8005': 'xec8008',
- 'xec8006': 'xec8007',
- 'xec8009': 'xec8012',
- 'xec8010': 'xec8011',
- 'xec8013': 'xec8018',
- 'xec8014': 'xec8017',
- 'xec8015': 'xec8016',
- 'xec8019': 'xec8020',
- }
- def translateUnitType( unitType ):
- if unitType in list(unitTypeTranslation):
- return unitTypeTranslation[unitType]
- else:
- return unitType
- def mirrorProps( mapInfos ):
- newPropsList = []
- for prop in mapInfos['propsList']:
- (blueprintPath,position,rotationX,rotationY,rotationZ,scale) = prop
- # create mirrored parameters
- newPosition = translatePosition( position, mapInfos['mapSize'] )
- newRotation = rotateProp(rotationX,rotationY,rotationZ)
- # add version with mirrored parameters to props list
- newPropsList.append( (blueprintPath,newPosition,*newRotation,scale) )
- mapInfos['propsList'] += newPropsList
- def mirrorDecals( mapInfos, decalsArchivePath, newMapDirectory, newMapFolderNameWithVersion ):
- def generateMirroredDecal( decalsArchive, decalToMirror, isNormalMap ):
- if not decalToMirror:
- return b''
- newDecalPath = newMapDirectory + '/mflop' + decalToMirror
- newDecalIngamePath = '/maps/{}/mflop{}'.format( newMapFolderNameWithVersion, decalToMirror )
- if os.path.exists( newDecalPath ):
- return newDecalIngamePath.encode()
- print("Converting {}".format(newDecalPath))
- with decalsArchive.open(decalsArchive.decalsCaseInsensitiveLookup[decalToMirror[1:].lower()]) as decalTexture:
- with tempfile.NamedTemporaryFile() as originalDecalTempFile:
- originalDecalTempFile.write( decalTexture.read() )
- os.makedirs( os.path.dirname( newDecalPath ), exist_ok = True )
- cmd = [ImageMagicConvert,
- originalDecalTempFile.name,
- "-flop","-rotate","-90",
- # fix rotation in normals
- *(( "-channel", "rgba",
- "-separate", "-swap", "1,3",
- "-combine" )
- if isNormalMap else ()),
- newDecalPath]
- print("running {}".format(' '.join(cmd)))
- subprocess.run(cmd)
- return newDecalIngamePath.encode()
- newDecals = []
- decalsCount = len(mapInfos['decalsList'])
- with ZipFile( decalsArchivePath, 'r' ) as decalsArchive:
- decalsArchive.decalsCaseInsensitiveLookup = { s.lower():s for s in decalsArchive.namelist() }
- for decal in mapInfos['decalsList']:
- (
- decalId,decalType,unknown15,
- decalsTexture1Path,decalsTexture2Path,
- scale,position,rotation,
- cutOffLOD,nearCutOffLOD,removeTick
- ) = decal
- newPosition = translatePosition( position, mapInfos['mapSize'] )
- newRotation = rotateDecal( rotation )
- isNormalMap = ( decalType == 2 )
- if decalDebug:
- # switch normals to albedo for debugging
- decalType = 1
- decal[1] = 1
- # place theta bridges at decal position with decal rotation
- mapInfos['propsList'] += [(
- b'/env/redrocks/props/thetabridge01_prop.bp',
- position,
- (-math.cos(rotation[1]),0,-math.sin(rotation[1])),
- (0,1,0),
- (math.sin(rotation[1]),0,-math.cos(rotation[1])),
- (1,1,1))]
- mapInfos['propsList'] += [(
- b'/env/redrocks/props/thetabridge01_prop.bp',
- newPosition,
- (-math.cos(newRotation[1]),0,-math.sin(newRotation[1])),
- (0,1,0),
- (math.sin(newRotation[1]),0,-math.cos(newRotation[1])),
- (1,1,1))]
- newDecalsTexture1Path = generateMirroredDecal( decalsArchive, decalsTexture1Path.decode(), isNormalMap )
- newDecalsTexture2Path = generateMirroredDecal( decalsArchive, decalsTexture2Path.decode(), isNormalMap )
- newDecals.append([
- decalsCount+decalId,decalType,unknown15,
- newDecalsTexture1Path,newDecalsTexture2Path,
- scale,newPosition,newRotation,
- cutOffLOD,nearCutOffLOD,removeTick
- ])
- mapInfos['decalsList'] += newDecals
- def main():
- if len(sys.argv) < 3:
- print("Usage: {} <infile> <outfile>".format(sys.argv[0]))
- sys.exit(1)
- pathToOldScmap = sys.argv[1]
- oldScmapName, oldScmapExtension = os.path.splitext(os.path.basename( pathToOldScmap ))
- pathToOldScmapSaveLua = os.path.join( os.path.dirname( pathToOldScmap ), "{}_save.lua".format(oldScmapName) )
- pathToNewScmap = sys.argv[2]
- newScmapName, newScmapExtension = os.path.splitext(os.path.basename( pathToNewScmap ))
- newMapDirectory = os.path.dirname( pathToNewScmap )
- newMapFolderNameWithVersion = os.path.basename( newMapDirectory )
- pathToNewScmapSaveLua = os.path.join( os.path.dirname( pathToNewScmap ), "{}_save.lua".format(newScmapName) )
- mapInfos = readMap( pathToOldScmap )
- # TODO FIXME add as env or commandline parameter:
- decalsArchivePath = '/home/b2ag/PlayOnLinux virtual drives/Supreme_Commander/drive_c/Program Files/THQ/Gas Powered Games/Supreme Commander - Forged Alliance/gamedata/env.scd'
- mirrorProps( mapInfos )
- mirrorDecals( mapInfos, decalsArchivePath, newMapDirectory, newMapFolderNameWithVersion )
- if mirrorEmbeddedImages:
- with tempfile.NamedTemporaryFile(suffix='.gray') as heightMap:
- with tempfile.NamedTemporaryFile(suffix='.gray') as mirroredHeightMap:
- heightMap.write(mapInfos['heightMapData'])
- size = [ int(x) for x in mapInfos['mapSize'] ]
- cmd = [ImageMagicConvert,
- '-size',"{}x{}".format(size[0]+1,size[1]+1),
- '-depth', '16',
- heightMap.name,
- '-repage', '-{}x{}+0+0'.format(int(size[0]/2+1),size[1]+1),
- '-crop', '{}x{}+0+0'.format(int(size[0]/2+1),size[1]+1),
- '-background', 'none',
- '-flop',
- heightMap.name,
- '-crop', "{}x{}+{}+0".format(int(size[0]/2+1),size[1]+1,int(size[0]/2)),
- '+append', mirroredHeightMap.name,
- ]
- print('running {}'.format(' '.join(cmd)))
- if subprocess.run(cmd):
- mapInfos['heightMapData'] = open(mirroredHeightMap.name,'rb').read()
- open('/tmp/debugMe.gray','wb').write(mapInfos['heightMapData'])
- writeMirroredMap( pathToOldScmap, pathToNewScmap, mapInfos )
- scenario = mirrorStuffInSaveLua( pathToOldScmapSaveLua, pathToNewScmapSaveLua, mapInfos )
- with open(pathToNewScmapSaveLua,'w') as newSaveLua:
- writeSaveLua( newSaveLua, scenario, first=True )
- def mirrorStuffInSaveLua( pathToOldScmapSaveLua, pathToNewScmapSaveLua, mapInfos ):
- if not os.path.exists( pathToOldScmapSaveLua ):
- print("Couldn't find \"{}\".".format(pathToOldScmapSaveLua))
- sys.exit(1)
- pathToOldScmapSaveModule, _ = os.path.splitext( pathToOldScmapSaveLua )
- # import map_save.lua with lupa
- import lupa
- lua = lupa.LuaRuntime(unpack_returned_tuples=False)
- lua.execute('FLOAT=function(x) return string.format("FLOAT( %.6f )",x) end')
- lua.execute('BOOLEAN=function(x) return string.format("BOOLEAN( %s )",x) end')
- lua.execute('STRING=function(x) return string.format("STRING( \'%s\' )",x) end')
- lua.execute('VECTOR3=function(x,y,z) return "VECTOR3( "..x..", "..y..", "..z.." )" end')
- lua.execute('GROUP=function(x) return function() return x end end')
- lua.require( pathToOldScmapSaveModule )
- scenario = lua.table_from({'Scenario': lua.eval('Scenario') })
- # mirror Mexes
- changeValueByPathRegex(
- re.compile("/Scenario/MasterChain/[^/]*/Markers"),
- partial(
- partial(duplicateMirrorAndRotate,re.compile("/Mass [^/]*")),
- partial(getMaxFromPrefix,'Mass '),
- dummySetNewMaxIdFunc,
- newIdAndKeyFunc,
- partial(translateUnitPosition,mapSize=mapInfos['mapSize']),
- dummyUnitRotation,
- translateUnitType,
- ), scenario )
- # mirror some armies
- changeValueByPathRegex(
- re.compile("/Scenario/Armies/[^/]*/[^/]*/Units/[^/]*/Units"),
- partial(
- partial(duplicateMirrorAndRotate,re.compile("/[^/]*")),
- partial(getMaxFromScenario,'next_unit_id'),
- partial(setNewMaxToScenario,'next_unit_id'),
- newIdAndKeyFunc,
- partial(translateUnitPosition,mapSize=mapInfos['mapSize']),
- dummyUnitRotation,
- translateUnitType,
- ), scenario )
- return scenario
- # duplicate 'position' and 'Position' values for tables matched by regular expression
- def duplicateMirrorAndRotate(regEx,getMaxIdFunc,setNewMaxIdFunc,newIdAndKeyFunc,positionFunc,rotateFunc,translateUnitTypeFunc,k,v,rootTable):
- oldTables = getTablesByPathRegex( regEx, v )
- oldMaxId = getMaxIdFunc(oldTables,rootTable)
- newTable = {}
- newMaxId = oldMaxId
- for key in oldTables:
- ( newCurrentId, newKey ) = newIdAndKeyFunc(key,newMaxId,oldMaxId)
- if newCurrentId > newMaxId:
- newMaxId = newCurrentId
- newParams = {}
- newParams.update( oldTables[key] )
- if 'type' in newParams:
- unitType = newParams['type'] = translateUnitTypeFunc( newParams['type'] )
- elif 'resource' in newParams:
- unitType = 'mass'
- else:
- unitType = 'unknown'
- if 'position' in newParams:
- newParams['position'] = mapSaveLuaVector( partial(positionFunc,unitType), newParams['position'] )
- elif 'Position' in newParams:
- newParams['Position'] = mapSaveLuaVector( partial(positionFunc,unitType), newParams['Position'] )
- if 'orientation' in newParams:
- newParams['orientation'] = mapSaveLuaVector( partial(rotateFunc,unitType), newParams['orientation'] )
- elif 'Orientation' in newParams:
- newParams['Orientation'] = mapSaveLuaVector( partial(rotateFunc,unitType), newParams['Orientation'] )
- newTable.update( {newKey: newParams} )
- for k2 in newTable:
- v[k2] = newTable[k2]
- setNewMaxIdFunc(newMaxId,newTable,rootTable)
- return v
- # write the output _save.lua
- def writeSaveLua( oStream, luaTable, path='', first=False ):
- import lupa
- indent = " "*(path.count('/'))
- # try to make output look more like original
- keys = orderedSaveLuaKeys( list(luaTable), path )
- # iterate over lua table keys
- for k in keys:
- newPath = '/'.join([path,str(k)])
- printPathDecorator(oStream,indent,newPath)
- if keyIsWrittenAlternativly(newPath):
- oStream.write("{}['{}'] = ".format(indent,k))
- else:
- oStream.write("{}{} = ".format(indent,k))
- v = luaTable[k]
- if lupa.lua_type(v) == 'table' or type(v) is dict:
- if list(v) == [1,2,3]:
- oStream.write("{{ {0:.6f}, {1:.6f}, {2:.6f} }}".format(v[1],v[2],v[3]))
- else:
- oStream.write("{\n")
- writeSaveLua( oStream, v, newPath )
- oStream.write("{}}}".format(indent))
- elif type(v) is str:
- if v[:5] in ['FLOAT','BOOLE','STRIN','VECTO']:
- oStream.write("{}".format(v))
- else:
- oStream.write("'{}'".format(v))
- elif type(v) is int:
- oStream.write("{}".format(v))
- elif lupa.lua_type(v) == 'function':
- oStream.write("GROUP {\n")
- writeSaveLua( oStream, v(), newPath )
- oStream.write("{}}}".format(indent))
- elif type(v) is tuple:
- oStream.write("{{ {:.6f}, {:.6f}, {:.6f} }}".format(*v))
- else:
- raise Exception("Unknown format {} at {} ".format(type(v),newPath))
- if not first:
- oStream.write(",\n")
- # try to make output look more like original
- # by enforcing some predefined order
- def orderedSaveLuaKeys( keys, path ):
- pathDepth = path.count('/')
- oldKeys = copy.copy(sorted(keys))
- newKeys = []
- if path == '/Scenario':
- for k in [
- 'next_area_id','Props','Areas',
- 'MasterChain','Chains',
- 'next_queue_id','Orders',
- 'next_platoon_id', 'Platoons',
- 'next_army_id','next_group_id','next_unit_id',
- 'Armies']:
- if k in oldKeys:
- oldKeys.remove(k)
- newKeys.append(k)
- newKeys += oldKeys
- return newKeys
- elif path.startswith('/Scenario/MasterChain/'):
- for k in [
- 'size', 'resource', 'amount', 'color', 'editorIcon',
- 'type','prop',
- 'orientation','position']:
- if k in oldKeys:
- oldKeys.remove(k)
- newKeys.append(k)
- newKeys += oldKeys
- return newKeys
- elif path.startswith('/Scenario/Armies/'):
- for k in [
- 'mass', 'energy',
- 'personality', 'plans', 'color', 'faction', 'Economy', 'Alliances',
- 'type', 'orders','platoon',
- 'Units','Position','Orientation',
- 'PlatoonBuilders', 'next_platoon_builder_id']:
- if k in oldKeys:
- oldKeys.remove(k)
- newKeys.append(k)
- newKeys += oldKeys
- return newKeys
- else:
- return oldKeys
- # try to make output look more like original
- # by adding theses huge comment sections
- def printPathDecorator( oStream, indent, path ):
- fmt = "{0:}--[["+" "*75+"]]--\n{0:}--[[ {1: <73}]]--\n{0:}--[["+" "*75+"]]--\n"
- s = ''
- if path == '/Scenario':
- s += fmt.format( indent, "Automatically generated code (do not edit)" )
- s += fmt.format( indent, "Scenario" )
- elif path == '/Scenario/Props':
- s += fmt.format( indent, "Props" )
- elif path == '/Scenario/Areas':
- s += fmt.format( indent, "Areas" )
- elif path == '/Scenario/MasterChain':
- s += fmt.format( indent, "Markers" )
- elif path == '/Scenario/next_queue_id':
- s += fmt.format( indent, "Orders" )
- elif path == '/Scenario/next_platoon_id':
- s += fmt.format( indent, "Platoons" )
- elif path == '/Scenario/next_army_id':
- s += fmt.format( indent, "Armies" )
- elif path.startswith('/Scenario/Armies/') and path.count('/') == 3:
- s += fmt.format( indent, "Army" )
- if s:
- oStream.write(s)
- # try to make output look more like original
- # by alternating writings e.g. Units and ['Units']
- def keyIsWrittenAlternativly(path):
- pathDepth = path.count('/')
- if path.startswith('/Scenario/MasterChain/'):
- if pathDepth != 4:
- return True
- if path.startswith('/Scenario/Chains/'):
- if pathDepth != 4:
- return True
- if path.startswith('/Scenario/Armies/'):
- if pathDepth == 3:
- return True
- if pathDepth == 4 and path.endswith('/Units'):
- return True
- if pathDepth == 6:
- return True
- if pathDepth == 8:
- return True
- return False
- def mapSaveLuaVector( mirrorFunc, value ):
- import lupa
- if lupa.lua_type(value) == 'table':
- if list(value) == [1,2,3]:
- value = mirrorFunc((value[1],value[2],value[3]))
- elif value.startswith('VECTOR3'):
- value = eval(value[7:])
- value = mirrorFunc(value)
- return "VECTOR3( {}, {}, {} )".format(*value)
- return value
- # find max i for a prefix e.g. 'Mass <i>' for all table keys
- def getMaxFromPrefix(prefix,table,rootTable):
- maxId = 0
- for key in table:
- currentId = int(key[len(prefix):])
- if currentId > maxId:
- maxId = currentId
- return maxId
- # get max by reading some Scenario.x lua variable
- def getMaxFromScenario(maxVarname,table,rootTable):
- return int(rootTable['Scenario'][maxVarname]) - 1
- # update some Scenario.x lua variable with current max id
- def setNewMaxToScenario(maxVarname,newMaxId,table,rootTable):
- rootTable['Scenario'][maxVarname] = str( newMaxId + 1 )
- # just a dummy
- def dummySetNewMaxIdFunc(newMaxId,table,rootTable):
- pass
- # return a new unique id based on current key
- def newIdAndKeyFunc( key, newMaxId, oldMaxId ):
- m = re.match( '(.*[^0-9])([0-9]*)', key ).groups(0)
- prefix = m[0]
- newId = newMaxId+1
- if m[1]:
- newId = max( int(m[1])+oldMaxId, newId )
- else:
- prefix += '_'
- newKey = '{}{}'.format( prefix, newId )
- return ( newId, newKey )
- # helper function to traverse lua data by regular expression
- def getTablesByPathRegex( regEx, luaTable, path='' ):
- import lupa
- ret = {}
- for key in list(luaTable):
- v = luaTable[key]
- newPath = '/'.join([path,str(key)])
- if regEx.match( newPath ):
- ret.update({key:v})
- elif lupa.lua_type(v) == 'table':
- ret.update(getTablesByPathRegex( regEx, v, newPath ))
- return ret
- # update values for tables matched by regular expression
- def changeValueByPathRegex( regEx, func, luaTable, rootTable=None, path='' ):
- import lupa
- if not rootTable: rootTable = luaTable
- for key in list(luaTable):
- v = luaTable[key]
- newPath = '/'.join([path,str(key)])
- if regEx.match( newPath ):
- luaTable[key] = func( key, v, rootTable )
- elif lupa.lua_type(v) == 'table':
- changeValueByPathRegex( regEx, func, v, rootTable, newPath )
- elif lupa.lua_type(v) == 'function':
- changeValueByPathRegex( regEx, func, v(), rootTable, newPath )
- class MapParsingException(Exception):
- def __init__( self, subject, fileObject ):
- self.offset = fileObject.tell()
- self.message = "Couldn't parse {} before offset {} ".format( subject, self.offset )
- super(Exception, self).__init__( self.message )
- def readcstr(f):
- buf = b''
- while True:
- b = f.read(1)
- if b is None or b == b'\0':
- return buf
- else:
- buf += b
- def getOffsetsFromMap( pathToScmap, offsets ):
- if type( offsets ) is not dict:
- raise Exception("Offsets parameter needs to have type of dict.")
- def debugPrint( label, text ):
- if debugPrintEnabled:
- print("{}: {}".format(label,text))
- def readMap( pathToScmap ):
- infos = {}
- listOfDebugProps = []
- with open(pathToScmap,'rb') as scmap:
- scmapMagic = scmap.read(4)
- if scmapMagic != SCMAPMAGIC:
- raise MapParsingException( "file magic", scmap )
- fileVersionMajor = unpack('I', scmap.read(4) )[0]
- if not fileVersionMajor:
- raise MapParsingException( "file major version", scmap )
- debugPrint( "fileVersionMajor", fileVersionMajor )
- # always 0xbeeffeed and other always 2
- (unknown3,unknown4) = ( scmap.read(4), scmap.read(4) )
- debugPrint( "unknown3", unknown3 )
- debugPrint( "unknown4", unknown4 )
- (scaledMapWidth,scaledMapHeight) = unpack('ff', scmap.read(calcsize('ff')) )
- if not scaledMapWidth or not scaledMapHeight:
- raise MapParsingException( "scaled map size", scmap )
- debugPrint( "scaledMapWidth", scaledMapWidth )
- debugPrint( "scaledMapHeight", scaledMapHeight )
- # always 0
- (unknown5,unknown6) = ( scmap.read(4), scmap.read(2) )
- debugPrint( "unknown5", unknown5 )
- debugPrint( "unknown6", unknown6 )
- #######################################################################
- ### Preview Image
- #######################################################################
- previewImageDataLength = unpack('I', scmap.read(4) )[0]
- if not previewImageDataLength:
- raise MapParsingException( "preview image data length", scmap )
- previewImageData = scmap.read(previewImageDataLength)
- if len(previewImageData) != previewImageDataLength:
- raise MapParsingException( "preview image data ({} bytes)".format(previewImageDataLength), scmap )
- debugPrint( "previewImageDataLength", "{} bytes".format(previewImageDataLength) )
- debugPrint( "previewImageDataMagic", previewImageData[0:4].decode( ))
- #if previewImageData[0:4] == DDSMAGIC:
- #previewDataPath = "{}_preview.dds".format( scmap.name )
- #with open(previewDataPath,'wb') as previewDumpFile:
- #previewDumpFile.write( previewImageData )
- #print("preview image dumped to \"{}\"".format(previewDataPath))
- fileVersionMinor = unpack('I', scmap.read(4) )[0]
- if not fileVersionMinor:
- raise MapParsingException( "file minor version", scmap )
- debugPrint( "fileVersionMinor", fileVersionMinor )
- if fileVersionMinor not in [60, 59, 56, 53]:
- raise MapParsingException( "unsupported file minor version", scmap )
- #######################################################################
- mapSize = (mapWidth,mapHeight) = unpack('II', scmap.read(8) )
- if not mapWidth or not mapHeight:
- raise MapParsingException( "map size", scmap )
- debugPrint( "mapWidth", mapWidth )
- debugPrint( "mapHeight", mapHeight )
- infos['mapSize'] = mapSize
- #######################################################################
- ### Height Map
- #######################################################################
- # Height Scale, usually 1/128
- heightScale = unpack('f', scmap.read(4) )[0]
- debugPrint( "heightScale", heightScale )
- heightMapDataLength = ( mapHeight + 1 ) * ( mapWidth + 1 ) * calcsize('h')
- infos['heightMapStartOffset'] = scmap.tell()
- heightMapData = scmap.read(heightMapDataLength)
- infos['heightMapEndOffset'] = scmap.tell()
- infos['heightMapData'] = heightMapData
- #######################################################################
- ### Some Shader
- #######################################################################
- if fileVersionMinor >= 56:
- unknown7 = readcstr(scmap)
- debugPrint( "unknown7", unknown7 )
- terrain = readcstr(scmap)
- debugPrint( "terrain", terrain )
- texPathBackground = readcstr(scmap)
- debugPrint( "texPathBackground", texPathBackground )
- texPathSkyCubemap = readcstr(scmap)
- debugPrint( "texPathSkyCubemap", texPathSkyCubemap )
- if fileVersionMinor < 56:
- texPathEnvCubemap = readcstr(scmap)
- debugPrint( "texPathEnvCubemap", texPathEnvCubemap )
- elif fileVersionMinor >= 56:
- environmentLookupTexturesCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "environmentLookupTexturesCount", environmentLookupTexturesCount )
- for i in range(environmentLookupTexturesCount):
- environmentLookupTexturesLabel = readcstr(scmap)
- debugPrint( "environmentLookupTexturesLabel", environmentLookupTexturesLabel )
- environmentLookupTexturesFile = readcstr(scmap)
- debugPrint( "environmentLookupTexturesFile", environmentLookupTexturesFile )
- #######################################################################
- ### Render Settings
- #######################################################################
- lightingMultiplier = unpack('f', scmap.read(4) )[0]
- debugPrint( "lightingMultiplier", lightingMultiplier )
- lightDirection = unpack('fff', scmap.read(12) )
- debugPrint( "lightDirection", lightDirection )
- ambienceLightColor = unpack('fff', scmap.read(12) )
- debugPrint( "ambienceLightColor", ambienceLightColor )
- lightColor = unpack('fff', scmap.read(12) )
- debugPrint( "lightColor", lightColor )
- shadowFillColor = unpack('fff', scmap.read(12) )
- debugPrint( "shadowFillColor", shadowFillColor )
- specularColor = unpack('ffff', scmap.read(16) )
- debugPrint( "specularColor", specularColor )
- bloom = unpack('f', scmap.read(4) )[0]
- debugPrint( "bloom", bloom )
- fogColor = unpack('fff', scmap.read(12) )
- debugPrint( "fogColor", fogColor )
- fogStart = unpack('f', scmap.read(4) )[0]
- debugPrint( "fogStart", fogStart )
- fogEnd = unpack('f', scmap.read(4) )[0]
- debugPrint( "fogEnd", fogEnd )
- hasWater = unpack('c', scmap.read(1) )[0]
- debugPrint( "hasWater", hasWater )
- waterElevation = unpack('f', scmap.read(4) )[0]
- debugPrint( "waterElevation", waterElevation )
- waterElevationDeep = unpack('f', scmap.read(4) )[0]
- debugPrint( "waterElevationDeep", waterElevationDeep )
- waterElevationAbyss = unpack('f', scmap.read(4) )[0]
- debugPrint( "waterElevationAbyss", waterElevationAbyss )
- surfaceColor = unpack('fff', scmap.read(12) )
- debugPrint( "surfaceColor", surfaceColor )
- colorLerpMin = unpack('f', scmap.read(4) )[0]
- debugPrint( "colorLerpMin", colorLerpMin )
- colorLerpMax = unpack('f', scmap.read(4) )[0]
- debugPrint( "colorLerpMax", colorLerpMax )
- refraction = unpack('f', scmap.read(4) )[0]
- debugPrint( "refraction", refraction )
- fresnelBias = unpack('f', scmap.read(4) )[0]
- debugPrint( "fresnelBias", fresnelBias )
- fresnelPower = unpack('f', scmap.read(4) )[0]
- debugPrint( "fresnelPower", fresnelPower )
- reflectionUnit = unpack('f', scmap.read(4) )[0]
- debugPrint( "reflectionUnit", reflectionUnit )
- reflectionSky = unpack('f', scmap.read(4) )[0]
- debugPrint( "reflectionSky", reflectionSky )
- sunShininess = unpack('f', scmap.read(4) )[0]
- debugPrint( "sunShininess", sunShininess )
- sunStrength = unpack('f', scmap.read(4) )[0]
- debugPrint( "sunStrength", sunStrength )
- sunGlow = unpack('f', scmap.read(4) )[0]
- debugPrint( "sunGlow", sunGlow )
- unknown8 = unpack('f', scmap.read(4) )[0]
- debugPrint( "unknown8", unknown8 )
- unknown9 = unpack('f', scmap.read(4) )[0]
- debugPrint( "unknown9", unknown9 )
- sunColor = unpack('fff', scmap.read(12) )
- debugPrint( "sunColor", sunColor )
- reflectionSun = unpack('f', scmap.read(4) )[0]
- debugPrint( "reflectionSun", reflectionSun )
- unknown10 = unpack('f', scmap.read(4) )[0]
- debugPrint( "unknown10", unknown10 )
- ### Texture Maps
- texPathWaterCubemap = readcstr(scmap)
- debugPrint( "texPathWaterCubemap", texPathWaterCubemap )
- texPathWaterRamp = readcstr(scmap)
- debugPrint( "texPathWaterRamp", texPathWaterRamp )
- for i in range(4):
- debugPrint( "waveTexture", i )
- normalsFrequency = unpack('f', scmap.read(4) )[0]
- debugPrint( "normalsFrequency", normalsFrequency )
- for i in range(4):
- debugPrint( "waveTexture", i )
- waveTextureScaleX = unpack('f', scmap.read(4) )[0]
- debugPrint( "waveTextureScaleX", waveTextureScaleX )
- waveTextureScaleY = unpack('f', scmap.read(4) )[0]
- debugPrint( "waveTextureScaleY", waveTextureScaleY )
- waveTexturePath = readcstr(scmap)
- debugPrint( "waveTexturePath", waveTexturePath )
- waveGeneratorCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "waveGeneratorCount", waveGeneratorCount )
- for i in range(waveGeneratorCount):
- debugPrint( "waveGenerator", i )
- textureName = readcstr(scmap)
- debugPrint( "textureName", textureName )
- rampName = readcstr(scmap)
- debugPrint( "rampName", rampName )
- position = unpack('fff', scmap.read(12) )
- debugPrint( "position", position )
- rotation = unpack('f', scmap.read(4) )[0]
- debugPrint( "rotation", rotation )
- velocity = unpack('fff', scmap.read(12) )
- debugPrint( "velocity", velocity )
- lifetimeFirst = unpack('f', scmap.read(4) )[0]
- debugPrint( "lifetimeFirst", lifetimeFirst )
- lifetimeSecond = unpack('f', scmap.read(4) )[0]
- debugPrint( "lifetimeSecond", lifetimeSecond )
- periodFirst = unpack('f', scmap.read(4) )[0]
- debugPrint( "periodFirst", periodFirst )
- periodSecond = unpack('f', scmap.read(4) )[0]
- debugPrint( "periodSecond", periodSecond )
- scaleFirst = unpack('f', scmap.read(4) )[0]
- debugPrint( "scaleFirst", scaleFirst )
- scaleSecond = unpack('f', scmap.read(4) )[0]
- debugPrint( "scaleSecond", scaleSecond )
- frameCount = unpack('f', scmap.read(4) )[0]
- debugPrint( "frameCount", frameCount )
- frameRateFirst = unpack('f', scmap.read(4) )[0]
- debugPrint( "frameRateFirst", frameRateFirst )
- frameRateSecond = unpack('f', scmap.read(4) )[0]
- debugPrint( "frameRateSecond", frameRateSecond )
- stripCount = unpack('f', scmap.read(4) )[0]
- debugPrint( "stripCount", stripCount )
- if fileVersionMinor >= 59:
- unkownData12 = scmap.read(28)
- debugPrint( "unkownData12", unkownData12.hex( ))
- elif fileVersionMinor > 53:
- unkownData12 = scmap.read(24)
- debugPrint( "unkownData12", unkownData12.hex( ))
- else:
- noTileset = readcstr(scmap)
- debugPrint( "noTileset", noTileset )
- if fileVersionMinor > 53:
- strata = ['LowerStratum','Stratum1','Stratum2','Stratum3','Stratum4','Stratum5','Stratum6','Stratum7','Stratum8','UpperStratum']
- debugPrint( "strata", strata )
- for stratum in strata:
- debugPrint( "stratum", stratum )
- albedoFile = readcstr(scmap)
- debugPrint( "albedoFile", albedoFile )
- albedoScale = unpack('f', scmap.read(4) )[0]
- debugPrint( "albedoScale", albedoScale )
- for stratum in strata:
- # fucking special cases
- if stratum == 'UpperStratum':
- # no Normal for UpperStratum
- continue
- debugPrint( "stratum", stratum )
- normalFile = readcstr(scmap)
- debugPrint( "normalFile", normalFile )
- normalScale = unpack('f', scmap.read(4) )[0]
- debugPrint( "normalScale", normalScale )
- else:
- strataCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "strataCount", strataCount )
- for stratum in range(strataCount):
- debugPrint( "stratum", stratum )
- albedoFile = readcstr(scmap)
- debugPrint( "albedoFile", albedoFile )
- normalFile = readcstr(scmap)
- debugPrint( "normalFile", normalFile )
- albedoScale = unpack('f', scmap.read(4) )[0]
- debugPrint( "albedoScale", albedoScale )
- normalScale = unpack('f', scmap.read(4) )[0]
- debugPrint( "normalScale", normalScale )
- unknown13 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown13", unknown13 )
- unknown14 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown14", unknown14 )
- #######################################################################
- ### Decals
- #######################################################################
- decalsBlockStartOffset = scmap.tell()
- infos["decalsBlockStartOffset"] = decalsBlockStartOffset
- decalsCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalsCount", decalsCount )
- decalsList = []
- for decalIndex in range(decalsCount):
- decalId = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalId", decalId )
- # albedo(1), normals(2)
- decalType = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalType", decalType )
- unknown15 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown15", unknown15 )
- decalsTexture1PathLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalsTexture1PathLength", decalsTexture1PathLength )
- if decalsTexture1PathLength > 1024:
- raise MapParsingException( "decalsTexture1PathLength", scmap )
- decalsTexture1Path = scmap.read(decalsTexture1PathLength)
- debugPrint( "decalsTexture1Path", decalsTexture1Path )
- decalsTexture2PathLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalsTexture2PathLength", decalsTexture2PathLength )
- if decalsTexture2PathLength > 1024:
- raise MapParsingException( "decalsTexture2PathLength", scmap )
- if decalsTexture2PathLength > 0:
- decalsTexture2Path = scmap.read(decalsTexture2PathLength)
- debugPrint( "decalsTexture2Path", decalsTexture2Path )
- else:
- decalsTexture2Path = b''
- scale = unpack('fff', scmap.read(12) )
- debugPrint( "scale", scale )
- position = unpack('fff', scmap.read(12) )
- debugPrint( "position", position )
- rotation = unpack('fff', scmap.read(12) )
- debugPrint( "rotation", rotation )
- cutOffLOD = unpack('f', scmap.read(4) )[0]
- debugPrint( "cutOffLOD", cutOffLOD )
- nearCutOffLOD = unpack('f', scmap.read(4) )[0]
- debugPrint( "nearCutOffLOD", nearCutOffLOD )
- removeTick = unpack('I', scmap.read(4) )[0]
- debugPrint( "removeTick", removeTick )
- decal = [
- decalId,decalType,unknown15,
- decalsTexture1Path,decalsTexture2Path,
- scale,position,rotation,
- cutOffLOD,nearCutOffLOD,removeTick
- ]
- decalsList.append(decal)
- infos["decalsList"] = decalsList
- decalsBlockEndOffset = scmap.tell()
- infos["decalsBlockEndOffset"] = decalsBlockEndOffset
- decalGroupsCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalGroupsCount", decalGroupsCount )
- for decalGroupIndex in range(decalGroupsCount):
- decalGroupId = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalGroupId", decalGroupId )
- decalGroupName = readcstr(scmap)
- debugPrint( "decalGroupName", decalGroupName )
- decalGroupEntriesCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalGroupEntriesCount", decalGroupEntriesCount )
- for i in range(decalGroupEntriesCount):
- decalGroupEntry = unpack('I', scmap.read(4) )[0]
- debugPrint( "decalGroupEntry", decalGroupEntry )
- #######################################################################
- ### Some DDS files
- #######################################################################
- (unknown19Width,unknown19Height) = unpack('II', scmap.read(8) )
- debugPrint( "unknown19Width", unknown19Width )
- debugPrint( "unknown19Height", unknown19Height )
- # most often 1, sometimes 4
- normalMapsCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "normalMapsCount", normalMapsCount )
- for normalMapIndex in range(normalMapsCount):
- normalMapDataLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "normalMapDataLength", normalMapDataLength )
- normalMapData = scmap.read(normalMapDataLength)
- debugPrint( "normalMapData", "{}...".format(normalMapData[:4]) )
- if fileVersionMinor < 56:
- unknown20 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown20", unknown20 )
- textureMapDataLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "textureMapDataLength", textureMapDataLength )
- textureMapData = scmap.read(textureMapDataLength)
- debugPrint( "textureMapData", "{}...".format(textureMapData[:4]) )
- if fileVersionMinor < 56:
- unknown21 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown21", unknown21 )
- waterMapDataLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "waterMapDataLength", waterMapDataLength )
- waterMapData = scmap.read(waterMapDataLength)
- debugPrint( "waterMapData", "{}...".format(waterMapData[:4]) )
- if fileVersionMinor > 53:
- unknown22 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown22", unknown22 )
- unknown23MapDataLength = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown23MapDataLength", unknown23MapDataLength )
- unknown23MapData = scmap.read(unknown23MapDataLength)
- debugPrint( "unknown23MapData", "{}...".format(unknown23MapData[:4]) )
- open('/tmp/unknown23MapData.dds','wb').write(unknown23MapData)
- someWaterMapLength = int( (mapWidth / 2) * (mapHeight / 2) )
- waterFoamMapData = scmap.read(someWaterMapLength)
- debugPrint( "waterFoamMapData", "{}...".format(waterFoamMapData[:4]) )
- waterFlatnessMapData = scmap.read(someWaterMapLength)
- debugPrint( "waterFlatnessMapData", "{}...".format(waterFlatnessMapData[:4]) )
- waterDepthBiasMapData = scmap.read(someWaterMapLength)
- debugPrint( "waterDepthBiasMapData", "{}...".format(waterDepthBiasMapData[:4]) )
- terrainTypeDataLength = mapWidth * mapHeight
- debugPrint( "terrainTypeDataLength", "{}...".format(terrainTypeDataLength) )
- terrainTypeData = scmap.read(terrainTypeDataLength)
- debugPrint( "terrainTypeData", "{}...".format(terrainTypeData[:4]) )
- if fileVersionMinor < 53:
- unknown24 = unpack('h', scmap.read(2) )[0]
- debugPrint( "unknown24", unknown24 )
- if fileVersionMinor >= 59:
- unknown25 = scmap.read(64)
- debugPrint( "unknown25", unknown25[:4] )
- unknown26String = readcstr(scmap)
- debugPrint( "unknown26String", unknown26String )
- unknown27String = readcstr(scmap)
- debugPrint( "unknown27String", unknown27String )
- unknown28 = unpack('I', scmap.read(4) )[0]
- debugPrint( "unknown28", unknown28 )
- unknown28MagicFactor = 40
- if unknown28 > 0:
- unknown29 = scmap.read( unknown28 * unknown28MagicFactor )
- debugPrint( "unknown29", unknown29[:4] )
- unknown30 = scmap.read(19)
- debugPrint( "unknown30", unknown30 )
- unknown31String = readcstr(scmap)
- debugPrint( "unknown31String", unknown31String )
- unknown31 = scmap.read(88)
- debugPrint( "unknown31", unknown31[:4] )
- propsBlockStartOffset = scmap.tell()
- infos["propsBlockStartOffset"] = propsBlockStartOffset
- propsCount = unpack('I', scmap.read(4) )[0]
- debugPrint( "propsCount", propsCount )
- propsList = []
- for propIndex in range( 0, propsCount ):
- blueprintPath = readcstr(scmap)
- debugPrint( "blueprintPath", blueprintPath )
- position = unpack('fff', scmap.read(12) )
- debugPrint( "position", position )
- rotationX = unpack('fff', scmap.read(12) )
- debugPrint( "rotationX", rotationX )
- rotationY = unpack('fff', scmap.read(12) )
- debugPrint( "rotationY", rotationY )
- rotationZ = unpack('fff', scmap.read(12) )
- debugPrint( "rotationZ", rotationZ )
- scale = unpack('fff', scmap.read(12) )
- debugPrint( "scale", scale )
- # add this prop to prop to props list
- propsList.append( [ blueprintPath,position,rotationX,rotationY,rotationZ,scale ] )
- infos["propsList"] = propsList + listOfDebugProps
- return infos
- def writeMirroredMap( pathToScmap, pathToNewScmap, infos ):
- with open(pathToScmap,'rb') as scmap:
- with open(pathToNewScmap,'wb') as newscmap:
- newscmap.write( scmap.read( infos['heightMapStartOffset'] ))
- newscmap.write( infos['heightMapData'] )
- scmap.seek(infos["heightMapEndOffset"])
- newscmap.write( scmap.read( infos["decalsBlockStartOffset"] - infos["heightMapEndOffset"] ))
- writeDecals( newscmap, infos["decalsList"] )
- scmap.seek(infos["decalsBlockEndOffset"])
- newscmap.write(scmap.read( infos["propsBlockStartOffset"] -infos["decalsBlockEndOffset"] ))
- writeProps( newscmap, infos["propsList"] )
- def writeDecals( newscmap, decalsList ):
- newscmap.write(pack('I',len(decalsList)))
- for decal in decalsList:
- (
- decalId,decalType,unknown15,
- decalsTexture1Path,decalsTexture2Path,
- scale,position,rotation,
- cutOffLOD,nearCutOffLOD,removeTick
- ) = decal
- decalRaw = pack('IIII',decalId,decalType,unknown15,len(decalsTexture1Path))
- decalRaw += decalsTexture1Path
- decalRaw += pack('I',len(decalsTexture2Path))
- if decalsTexture2Path:
- decalRaw += decalsTexture2Path
- decalRaw += pack('fffffffff',*scale,*position,*rotation)
- decalRaw += pack('ffI',cutOffLOD,nearCutOffLOD,removeTick)
- newscmap.write(decalRaw)
- def writeProps( newscmap, propsList ):
- newscmap.write(pack('I',len(propsList)))
- for prop in propsList:
- (blueprintPath,position,rotationX,rotationY,rotationZ,scale) = prop
- newscmap.write(blueprintPath)
- newscmap.write(b'\0')
- newscmap.write(pack('fffffffffffffff',*(*position,*rotationX,*rotationY,*rotationZ,*scale)))
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement