Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add DDS2 library #95

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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