diff --git a/HadesMapper/HadesMapper.py b/HadesMapper/HadesMapper.py index 6d0d50a..701a576 100644 --- a/HadesMapper/HadesMapper.py +++ b/HadesMapper/HadesMapper.py @@ -116,46 +116,6 @@ def WriteTriBoolean(value): #write a float using struct def WriteSingle(inp): if inp != 0: - # #see https://en.wikipedia.org/wiki/Single-precision_floating-point_format - # #get rid of sign so that math isnt affected, its added back alter - # value = abs(inp) - # decimalBinary = "" - # decimal = value - math.floor(value) - # currentDecimal = decimal - # #convert fraction into binary fraction, limiting at precision limit (23 repetitions) - # for i in range(0, 23): - # currentDecimal = currentDecimal * 2 - # decimalBinary += str(int(currentDecimal >= 1)) - # if currentDecimal >= 1: - # currentDecimal -= 1 - # if currentDecimal == 0: - # break - - # #get whole value - # wholeBinary = str(bin(math.floor(value - decimal)))[2:] - # #get normalized exponent (offset from 127) - # normalExponent = 127 + (len(wholeBinary) - 1) - # #convert exponent into binary - # exponentBinary = str(bin(normalExponent))[2:] - # #shift so that only 1 digit remains before decimal point - # fractionBinary = wholeBinary[1:] + decimalBinary - - # #concat all parts together into an unrounded (and thus unlimited in size) set of bits - # unroundedBinary = str(int(inp < 0)) + exponentBinary + fractionBinary - - # binaryRep = "" - # #if less then len 32 append 0s to end to get to 32 bits - # if len(unroundedBinary) < 32: - # binaryRep = unroundedBinary + "0" * (32 - len(binaryRep)) - # #if greater then 32 round to 32 bits 1s round up the next digit, very simply so if we get any 1 then the rest chain causing the final bit to be 1 - # #it should not round like this, this is not how it works but its a quick implementation - # elif len(unroundedBinary) > 32: - # for i in range(len(unroundedBinary) - 1, 32, -1): - # if unroundedBinary[i] == "1": - # binaryRep = unroundedBinary[0:31] + "1" - # break - # else: - # binaryRep = unroundedBinary binaryRep =''.join('{:0>8b}'.format(c) for c in struct.pack('!f', inp)) #store binary as 2 chunks of 16 sections = ["",""] @@ -163,7 +123,6 @@ def WriteSingle(inp): byte = binaryRep[i-8:i] sections[(i - 1) // 16] += byte - #f.write(chr(int(bytes[1][0:8], 2))) #1,2 #write in order of D C B A for section in reversed(sections): sectionBytes = [int(section[8:16], 2), int(section[0:8], 2)] @@ -215,7 +174,7 @@ def WriteDataType(type): #read a binary file and write it to JSON def DecodeBinaries(inputFilePath, outputFilePath, issequel): global f - f = open(inputFilePath, "rb") + f = open(inputFilePath + ".thing_bin", "rb") f.read(4) #read SGB1, whatever it is f.read(4) #always going be 12, put need to read it to get it out of the way obstacleCount = ReadUInt32() @@ -296,17 +255,17 @@ def DecodeBinaries(inputFilePath, outputFilePath, issequel): f.close() jsonString = json.dumps(obstacleTable, sort_keys=True, indent=4) - with open(outputFilePath, "w+") as oid: + with open(outputFilePath + ".thing_text", "w+") as oid: oid.write(jsonString) #read a json file and write it to binaries def EncodeBinaries(inputFilePath, outputFilePath, issequel): global f - f = open(outputFilePath, "wb+") + f = open(outputFilePath + ".thing_bin", "wb+") inputFileContent = "" - with open(inputFilePath) as fid: + with open(inputFilePath + ".thing_text") as fid: lines = fid.readlines() inputFileContent = "".join(lines) @@ -326,8 +285,14 @@ def EncodeBinaries(inputFilePath, outputFilePath, issequel): WriteBoolean(item["Active"]) - WriteBoolean(item["AllowMovementReaction"]) - WriteSingle(item["Ambient"]) + if "AllowMovementReaction" in item: + WriteBoolean(item["AllowMovementReaction"]) + else: + WriteBoolean(True) + if "Material" in item and item["Material"] != None: + WriteSingle(item["Material"]["Ambient"]) + else: + WriteSingle(0) WriteSingle(item["Angle"]) WriteInt32(len(item["AttachedIDs"])) @@ -335,17 +300,40 @@ def EncodeBinaries(inputFilePath, outputFilePath, issequel): WriteInt32(attachedID) WriteInt32(item["AttachToID"]) - WriteBoolean(item["CausesOcculsion"]) - WriteBoolean(item["Clutter"]) + + if "CausesOcculsion" in item: + WriteBoolean(item["CausesOcculsion"]) + else: + WriteBoolean(True) + + if "Clutter" in item: + WriteBoolean(item["Clutter"]) + else: + WriteBoolean(False) WriteBoolean(item["Collision"]) WriteColor(item["Color"]) - WriteStringAllowNull(item["Comments"]) + if "Comments" in item: + WriteStringAllowNull(item["Comments"]) + else: + WriteStringAllowNull("") + + if "CreatesShadows" in item: + WriteTriBoolean(item["CreatesShadows"]) + else: + WriteTriBoolean(True) + + if item["DataType"] != "MapArea": + WriteDataType(item["DataType"]) + else: + WriteDataType("Obstacle") + + if "DrawVfxOnTop" in item: + WriteTriBoolean(item["DrawVfxOnTop"]) + else: + WriteTriBoolean(True) - WriteTriBoolean(item["CreatesShadows"]) - WriteDataType(item["DataType"]) - WriteTriBoolean(item["DrawVfxOnTop"]) WriteBoolean(item["FlipHorizontal"]) WriteBoolean(item["FlipVertical"]) @@ -355,11 +343,19 @@ def EncodeBinaries(inputFilePath, outputFilePath, issequel): if issequel == False: f.write(bytes([0,0,0,0])) WriteStringAllowNull(group) - WriteStringAllowNull(item["HelpTextID"]) + if "HelpTextID" in item: + WriteStringAllowNull(item["HelpTextID"]) + else: + WriteStringAllowNull("") - WriteSingle(item["Hue"]) - WriteSingle(item["Saturation"]) - WriteSingle(item["Value"]) + if "Hue" in item: + WriteSingle(item["Hue"]) + WriteSingle(item["Saturation"]) + WriteSingle(item["Value"]) + else: + WriteSingle(0) + WriteSingle(0) + WriteSingle(0) WriteInt32(item["Id"]) @@ -369,25 +365,47 @@ def EncodeBinaries(inputFilePath, outputFilePath, issequel): WriteSingle(item["Location"]["X"]) WriteSingle(item["Location"]["Y"]) - WriteStringAllowNull(item["Name"]) + if "Name" in item: + WriteStringAllowNull(item["Name"]) + else: + WriteStringAllowNull("") WriteSingle(item["OffsetZ"]) WriteSingle(item["ParallaxAmount"]) - WriteInt32(len(item["Points"])) - for point in item["Points"]: - WriteSingle(point["X"]) - WriteSingle(point["Y"]) + if "Points" in item: + WriteInt32(len(item["Points"])) + for point in item["Points"]: + WriteSingle(point["X"]) + WriteSingle(point["Y"]) + else: + WriteInt32(0) WriteSingle(item["Scale"]) - WriteSingle(item["SkewAngle"]) - WriteSingle(item["SkewScale"]) - - WriteInt32(item["SortIndex"]) + if "SkewAngle" in item: + WriteSingle(item["SkewAngle"]) + else: + WriteSingle(0) - WriteTriBoolean(item["StopsLight"]) + if "SkewScale" in item: + WriteSingle(item["SkewScale"]) + else: + WriteSingle(0) - WriteSingle(item["Tallness"]) + WriteInt32(item["SortIndex"]) - WriteTriBoolean(item["UseBoundsForSortArea"]) + if "StopsLight" in item: + WriteTriBoolean(item["StopsLight"]) + else: + WriteTriBoolean(True) + + if "Tallness" in item: + WriteSingle(item["Tallness"]) + else: + WriteSingle(0) + + if "UseBoundsForSortArea" in item: + WriteTriBoolean(item["UseBoundsForSortArea"]) + else: + WriteTriBoolean(False) f.close() \ No newline at end of file diff --git a/HadesMapper/cli.py b/HadesMapper/cli.py index d52855f..260a056 100644 --- a/HadesMapper/cli.py +++ b/HadesMapper/cli.py @@ -10,15 +10,15 @@ def main(): # encode parser encode_parser = subparsers.add_parser('encode', help='Encode JSON into a binary file', aliases=['ec']) - encode_parser.add_argument('-i', '-input', metavar='input', default='input.thing_text', type=str, help='The JSON file to encode, default is input.thing_text') - encode_parser.add_argument('-o', '-output', metavar='output', default='output.thing_bin', type=str, help='The binary file to output, default is output.thing_bin') + encode_parser.add_argument('-i', '-input', metavar='input', default='input', type=str, help='The .thing_text (JSON) file to encode, default is input') + encode_parser.add_argument('-o', '-output', metavar='output', default='output', type=str, help='The .thing_bin (binary) file to output, default is output') encode_parser.add_argument('-s', '-sequel', action='store_true', help='Flag for whether the game is the sequel (Hades II), default is Hades 1') encode_parser.set_defaults(func=cli_encode) #decode parser decode_parser = subparsers.add_parser('decode', help='Encode JSON into a binary file', aliases=['dc']) - decode_parser.add_argument('-i', '-input', metavar='input', default='input.thing_bin', type=str, help='The binary file to decode, default is input.thing_bin') - decode_parser.add_argument('-o', '-output', metavar='output', default='output.thing_text', type=str, help='The JSON file to output to, default is output.thing_text') + decode_parser.add_argument('-i', '-input', metavar='input', default='input.thing_bin', type=str, help='The .thing_bin (binary) file to decode, default is input') + decode_parser.add_argument('-o', '-output', metavar='output', default='output.thing_text', type=str, help='The ,thing_text (JSON) file to output to, default is output') decode_parser.add_argument('-s', '-sequel', action='store_true', help='Flag for whether the game is the sequel (Hades II), default is Hades 1') decode_parser.set_defaults(func=cli_decode) diff --git a/README.md b/README.md index 81391ac..a5ef666 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is a tool to read the game's binary map files and decompile them to a JSON Download the latest [Release](https://github.com/SGG-Modding/HadesMapper/releases). Open command prompt and cd into the directory of the wheel and pip install it, remember to have .whl at the end of the name. # How to use this? -The script has 2 modes, encode and decode, as well as 2 submodes for Hades I and Hades II. +The script has 2 modes, encode and decode, as well as 2 submodes for Hades I and Hades II. **When inputting file names do not put their extension, the script automatically handles that (files must have the extension `.thing_text` or `.thing_bin`)**. ## Hades 2 To encode or decode any map binaries to or from Hades II, add `-s` or `-sequel` as a flag. Defaults to using Hades I formating. @@ -17,18 +17,18 @@ To encode or decode any map binaries to or from Hades II, add `-s` or `-sequel` ``` HadesMapper ec ``` -Defaults to an input of `input.thing_text` and output of `output.thing_bin`. +Defaults to an input of `input` and output of `output`. ## Decode Mode (Binaries to JSON) ``` HadesMapper dc ``` -Defaults to an input of `input.thing_bin` and output of `output.thing_text`. +Defaults to an input of `input` and output of `output`. ## Arguments Both commands share the same arguments which are -* `-i` or `-input`: changes the input file the script reads from -* `-o` or `-output`: changes the output file the script writes to +* `-i` or `-input`: changes the input file the script reads from, automatically adds `.thing_text` or `.thing_bin` to the name given. +* `-o` or `-output`: changes the output file the script writes to, automatically adds `.thing_text` or `.thing_bin` to the name given. # Putting it in Game To put a new binary in the game, name your new binary to whatever your map name is, for example `MyNewMap.thing_bin`. Put it in your mod folder and put 2 lines in your modfile.txt which read: diff --git a/setup.py b/setup.py index a414c97..cb0e6c5 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ entry_points = { "console_scripts": ['HadesMapper = HadesMapper.cli:main'] }, - version = "2.0", + version = "2.1", description = "Unpack Hades and Hades II's map binaries into JSON and pack JSON into game usable map binaries", #long_description = long_descr, author = "erumi321",