Skip to content

Commit

Permalink
Merge pull request #95 from tge-was-taken/dds2
Browse files Browse the repository at this point in the history
Add DDS2 library
  • Loading branch information
tge-was-taken authored Dec 2, 2024
2 parents 11db99a + c1cbca2 commit 1aea80c
Show file tree
Hide file tree
Showing 7 changed files with 7,752 additions and 187 deletions.
111 changes: 56 additions & 55 deletions Scripts/SMT3StringTableToEnum.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,60 @@
import idc
import re

def ParseStringTableToEnum( enumName, address, count ):

print '{'
print '"Name": "%s",' % enumName
print '"Description": "This enum represents the available skills in battle.",'
print '"Members": ['

stringHashSet = set()

for i in range( 0, count ):
pString = get_32bit( address + (i * 4) )
string = get_ascii_contents( pString, get_max_ascii_length( pString, ASCSTR_C ), ASCSTR_C )

if ( string ):
enumValueName = string
enumValueName = enumValueName.replace( ' ', '' )
enumValueName = enumValueName.replace( ',', '' )
enumValueName = enumValueName.replace( '_', '' )
enumValueName = enumValueName.replace( "'", '' )
enumValueName = enumValueName.replace( '(', '' )
enumValueName = enumValueName.replace( ')', '' )
enumValueName = enumValueName.replace( ':', '' )
enumValueName = enumValueName.replace( '-', '' )
enumValueName = enumValueName.replace( '&', 'And' )
enumValueName = enumValueName.replace( '!', '' )

if ( enumValueName[0].isdigit() ):
enumValueName = '_' + enumValueName
else:
enumValueName = "Null"

duplicateEnumValueName = enumValueName
duplicateCounter = 1
while ( enumValueName in stringHashSet ):
enumValueName = '%s%d' % ( duplicateEnumValueName, duplicateCounter )
duplicateCounter += 1

stringHashSet.add( enumValueName )

print '{'
print '"Name": "%s",' % enumValueName
print '"Value": %d,' % i
print '"Description": "Generated from skill name table entry: %s"' % string

if ( i != count - 1):
print '},'
else:
print '}'

print ']'
print '}'


def ParseStringTableToEnum(enumName, address, count):
print('{')
print('"Name": "{}",'.format(enumName))
print('"Description": "This enum represents the available skills in battle.",')
print('"Members": [')

stringHashSet = set()

for i in range(count):
pString = idc.get_wide_dword(address + (i * 4))
string = idc.get_strlit_contents(pString, -1, STRTYPE_C)

if string:
string = string.decode('utf-8') # Decode bytes to string if needed
enumValueName = string
# Remove or replace invalid characters for enum names
enumValueName = enumValueName.replace(' ', '')
enumValueName = enumValueName.replace(',', '')
enumValueName = enumValueName.replace('_', '')
enumValueName = enumValueName.replace("'", '')
enumValueName = enumValueName.replace('(', '')
enumValueName = enumValueName.replace(')', '')
enumValueName = enumValueName.replace(':', '')
enumValueName = enumValueName.replace('-', '')
enumValueName = enumValueName.replace('&', 'And')
enumValueName = enumValueName.replace('!', '')

if enumValueName[0].isdigit():
enumValueName = '_' + enumValueName
else:
enumValueName = "Null"

duplicateEnumValueName = enumValueName
duplicateCounter = 1
while enumValueName in stringHashSet:
enumValueName = '{}{}'.format(duplicateEnumValueName, duplicateCounter)
duplicateCounter += 1

stringHashSet.add(enumValueName)

print(' {')
print(' "Name": "{}",'.format(enumValueName))
print(' "Value": {},'.format(i))
print(' "Description": "Generated from skill name table entry: {}"'.format(string))

if i != count - 1:
print(' },')
else:
print(' }')

print(']')
print('}')

# Example usage
# Nocturne
#ParseStringTableToEnum( "BattleSkill", 0x003E83F0, 512 )
ParseStringTableToEnum( "BattleUnit", 0x003E7328, 386 )
# ParseStringTableToEnum("BattleSkill", 0x003E83F0, 512)
ParseStringTableToEnum("BattleUnit", 0x003E7328, 386)
249 changes: 120 additions & 129 deletions Scripts/ScriptInterpreterCOMMTableToJson_SMT.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,141 +2,132 @@
import idautils
import idc

MIPS_JR = 0x03E00008;
MIPS_JR = 0x03E00008

def IsBranchToFunctionAddress( address, funcAddress ):
operandValue = GetOperandValue( address, 0 )
#print "%04X" % operandValue

return operandValue == funcAddress
def IsBranchToFunctionAddress(address, funcAddress):
operandValue = idc.get_operand_value(address, 0)
# print("{:04X}".format(operandValue))
return operandValue == funcAddress

def GetLastImmediateRegisterValue(address, regIndex):
while True:

if (GetOperandValue(address, 0) == regIndex):
mnem = GetMnem(address)

if (mnem == "li"):
return GetOperandValue( address, 1 )
elif (mnem == "move"):
regIndex = GetOperandValue( address, 1 )
if ( regIndex == 0 ):
return 0

return GetLastImmediateRegisterValue( address - 4, regIndex )
else:
return GetLastImmediateRegisterValue( address - 4, regIndex )
while True:
if idc.get_operand_value(address, 0) == regIndex:
mnem = idc.print_insn_mnem(address)

if mnem == "li":
return idc.get_operand_value(address, 1)
elif mnem == "move":
regIndex = idc.get_operand_value(address, 1)
if regIndex == 0:
return 0
return GetLastImmediateRegisterValue(address - 4, regIndex)
else:
return GetLastImmediateRegisterValue(address - 4, regIndex)

address -= 4

address -= 4

def GetFunctionArgument(funcAddress, index):
return GetLastImmediateRegisterValue(funcAddress + 4, index + 4)

def ParseCOMMTable( address, getIntArgFuncAddress, getFloatArgFuncAddress, getStringArgFuncAddress, setIntRetValueFuncAddress, setFloatRetValueFuncAddress, entryCount ):

print "["

for i in range( 0, entryCount ):

entryAddress = address + ( i * 8 )

functionAddress = get_32bit( entryAddress )
parameterCount = get_32bit( entryAddress + 4 )

functionName = "FUNCTION_%04X" % i
functionDescription = ""
functionReturnType = "void"

# Fill parameter types
parameterTypes = []
for j in range( 0, parameterCount ):
parameterTypes.append( "unk" )

if ( functionAddress == 0 ):
functionDescription = "Null pointer"
else:

# Traverse function body to infer argument types
currentInstructionAddress = functionAddress
a0Register = 0

while True:
instruction = get_32bit( currentInstructionAddress )

# Stop looping when we hit a return instruction
if ( instruction == MIPS_JR ):
break;

# Check if it's a branch to the get int argument function address
if ( IsBranchToFunctionAddress( currentInstructionAddress, getIntArgFuncAddress ) ):
parameterIndex = GetFunctionArgument( currentInstructionAddress, 0 )
if ( parameterIndex >= 0 and parameterIndex < parameterCount ):
parameterTypes[ parameterIndex ] = "int"

# Check if it's a branch to the get float argument function address
if ( IsBranchToFunctionAddress( currentInstructionAddress, getFloatArgFuncAddress ) ):
parameterIndex = GetFunctionArgument( currentInstructionAddress, 0 )
if ( parameterIndex >= 0 and parameterIndex < parameterCount ):
parameterTypes[ parameterIndex ] = "float"

# Check if it's a branch to the get float argument function address
if ( IsBranchToFunctionAddress( currentInstructionAddress, getStringArgFuncAddress ) ):
parameterIndex = GetFunctionArgument( currentInstructionAddress, 0 )
if ( parameterIndex >= 0 and parameterIndex < parameterCount ):
parameterTypes[ parameterIndex ] = "string"

# Check if it's a branch to the set int return value function address
if ( IsBranchToFunctionAddress( currentInstructionAddress, setIntRetValueFuncAddress ) ):
functionReturnType = "int"

# Check if it's a branch to the set float return value function address
if ( IsBranchToFunctionAddress( currentInstructionAddress, setFloatRetValueFuncAddress ) ):
functionReturnType = "float"

currentInstructionAddress += 4


print ' {'
print ' "Index": "0x%04x",' % i
print ' "ReturnType": "%s",' % functionReturnType
print ' "Name": "%s",' % functionName
print ' "Description": "%s",' % functionDescription
print ' "Parameters":'
print ' ['

for j in range( 0, parameterCount ):

parameterDescription = ""
parameterType = parameterTypes[ j ]
if ( parameterType == "unk" ):
parameterDescription = "Unknown type; assumed int"
parameterType = "int"

parameterName = "param%s" % ( j + 1 )

print ' {'
print ' "Type": "%s",' % parameterType
print ' "Name": "%s",' % parameterName
print ' "Description": "%s"' % parameterDescription

if ( j != parameterCount - 1 ):
print ' },'
else:
print ' }'

print ' ]'

if ( i != entryCount - 1 ):
print ' },'
else:
print ' }'

print "]"

return
return GetLastImmediateRegisterValue(funcAddress + 4, index + 4)

def ParseCOMMTable(address, getIntArgFuncAddress, getFloatArgFuncAddress, getStringArgFuncAddress, setIntRetValueFuncAddress, setFloatRetValueFuncAddress, entryCount):
print("[")
for i in range(entryCount):
entryAddress = address + (i * 8)

functionAddress = idc.get_wide_dword(entryAddress)
parameterCount = idc.get_wide_dword(entryAddress + 4)

functionName = "FUNCTION_{:04X}".format(i)
functionDescription = ""
functionReturnType = "void"

# Fill parameter types
parameterTypes = ["unk"] * parameterCount

if functionAddress == 0:
functionDescription = "Null pointer"
else:
# Traverse function body to infer argument types
currentInstructionAddress = functionAddress

while True:
instruction = idc.get_wide_dword(currentInstructionAddress)

# Stop looping when we hit a return instruction
if instruction == MIPS_JR:
break

# Check if it's a branch to the get int argument function address
if IsBranchToFunctionAddress(currentInstructionAddress, getIntArgFuncAddress):
parameterIndex = GetFunctionArgument(currentInstructionAddress, 0)
if 0 <= parameterIndex < parameterCount:
parameterTypes[parameterIndex] = "int"

# Check if it's a branch to the get float argument function address
if IsBranchToFunctionAddress(currentInstructionAddress, getFloatArgFuncAddress):
parameterIndex = GetFunctionArgument(currentInstructionAddress, 0)
if 0 <= parameterIndex < parameterCount:
parameterTypes[parameterIndex] = "float"

# Check if it's a branch to the get string argument function address
if IsBranchToFunctionAddress(currentInstructionAddress, getStringArgFuncAddress):
parameterIndex = GetFunctionArgument(currentInstructionAddress, 0)
if 0 <= parameterIndex < parameterCount:
parameterTypes[parameterIndex] = "string"

# Check if it's a branch to the set int return value function address
if IsBranchToFunctionAddress(currentInstructionAddress, setIntRetValueFuncAddress):
functionReturnType = "int"

# Check if it's a branch to the set float return value function address
if IsBranchToFunctionAddress(currentInstructionAddress, setFloatRetValueFuncAddress):
functionReturnType = "float"

currentInstructionAddress += 4

print(" {")
print(' "Index": "0x{:04x}",'.format(i))
print(' "ReturnType": "{}",'.format(functionReturnType))
print(' "Name": "{}",'.format(functionName))
print(' "Description": "{}",'.format(functionDescription))
print(' "Parameters":')
print(" [")

for j in range(parameterCount):
parameterDescription = ""
parameterType = parameterTypes[j]
if parameterType == "unk":
parameterDescription = "Unknown type; assumed int"
parameterType = "int"

parameterName = "param{}".format(j + 1)

print(" {")
print(' "Type": "{}",'.format(parameterType))
print(' "Name": "{}",'.format(parameterName))
print(' "Description": "{}"'.format(parameterDescription))

if j != parameterCount - 1:
print(" },")
else:
print(" }")

print(" ]")

if i != entryCount - 1:
print(" },")
else:
print(" }")

print("]")

return

# DDS 1
#ParseCOMMTable( 0x0039E388, 0, 0, 0, 544 )
# ParseCOMMTable(0x0039E388, 0, 0, 0, 544)

# DDS 2
ParseCOMMTable(0x00411408, 0x0010D650, 0x0010D718, 0x0010D7D0, 0x0010D818, 0x0010D830, 544)

# Nocturne
ParseCOMMTable( 0x0052E350, 0x0010B5C8, 0x0010B690, 0x0010B748, 0x0010B790, 0x0010B7A8, 544 )
# ParseCOMMTable(0x0052E350, 0x0010B5C8, 0x0010B690, 0x0010B748, 0x0010B790, 0x0010B7A8, 544)
3 changes: 0 additions & 3 deletions Source/AtlusScriptLibrary/AtlusScriptLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,5 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="MessageScriptLanguage\BinaryModel\NewFolder\" />
</ItemGroup>
<PropertyGroup />
</Project>
6 changes: 6 additions & 0 deletions Source/AtlusScriptLibrary/Libraries/DigitalDevilSaga2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Name": "Digital Devil Saga 2",
"ShortName": "dds2",
"FlowScriptModulesPath": "DigitalDevilSaga2/FlowScriptModules.json",
"MessageScriptLibraryPath": "DigitalDevilSaga2/MessageScriptLibrary.json"
}
Loading

0 comments on commit 1aea80c

Please sign in to comment.