Compare commits

..

40 Commits

Author SHA1 Message Date
CakeLancelot
a3856cdc9d Merge pull request #2 from CakeLancelot/main
dx2cg: Allow usage as module and handle a few more features used by built-in Unity shaders
2024-02-14 05:11:14 -06:00
CakeLancelot
672d7e2b42 Address PR review comments 2024-02-14 03:15:09 -06:00
CakeLancelot
d5b58d96da dx2cg: Implement RECT, CUBE texture types
Also tried to implement 3D texture type, but that will likely require a few other instructions to get working - namely texldp and dcl_volume.
2024-02-13 03:18:19 -06:00
CakeLancelot
06869164a1 dx2cg: Implement mov_sat instruction
Basically just mov but clamp the result.
https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx9-graphics-reference-asm-ps-instructions-modifiers-ps-2-0#saturate
2024-02-13 03:18:19 -06:00
CakeLancelot
99c80c9c7b dx2cg: Allow usage as Python module
e.g.
from dx2cg.swapper import process_shader
cg_shader = process_shader(text)
2024-02-13 01:40:07 -06:00
4196766a2b Client credentials and change root password 2024-01-26 15:28:53 -05:00
7922fc060a Set XDB server charset to utf8 2024-01-26 13:46:43 -05:00
5bf1ce01e3 Change db name to XDB 2024-01-24 20:09:14 -05:00
27143ccb01 Schemas, split fields, and padding 2024-01-24 17:47:06 -05:00
349e7b6e07 Table schemas 2024-01-24 16:10:26 -05:00
9fd6441606 Fix for large packet size 2024-01-24 13:57:43 -05:00
8b1bd5b1ee Update gitignore 2024-01-24 13:52:59 -05:00
4f7b6b28e4 Fix DB table mappings 2024-01-24 13:52:41 -05:00
99193a198b Add json2xdb 2024-01-24 05:15:00 -05:00
CakeLancelot
249af1d2d3 Ensure all objects are deleted after export + use env var for user directory 2024-01-19 14:30:17 -06:00
gsemaj
c7b4182a4b Export terrain meshes as FBX instead of OBJ 2024-01-19 10:42:49 -08:00
FinnHornhoover
b70816fa64 Non-updating rank table fix and tiebreaker logic (#1) 2023-12-25 21:37:22 +01:00
9deba1956f [rankendpoint] Add invocation instructions 2023-12-24 21:35:35 +01:00
1a3a530c7f [rankendpoint] Get DB path and endpoint route from env vars
Also reverted the use of main() that made it incompatible with uwsgi and
flask run.
2023-12-24 20:57:05 +01:00
f62f3af483 Revert "Autorun Flask app"
This reverts commit e0e0b3c5e3.

Flask applications are not supposed to be run this way. We run the
endpoints with uwsgi in prod or with the flask program for debugging.

https://flask.palletsprojects.com/en/3.0.x/api/#flask.Flask.run
2023-12-24 03:36:33 +01:00
gsemaj
f02c960497 Use same rank for tied scores 2023-12-22 22:14:05 -08:00
gsemaj
e0e0b3c5e3 Autorun Flask app 2023-12-22 22:04:05 -08:00
gsemaj
615bd3d9a3 Add ranked endpoint server 2023-12-22 21:48:18 -08:00
gsemaj
4d6490933d Add option for score capping 2023-12-19 13:21:37 -05:00
gsemaj
073666e6f4 Add OG racing score conversion script 2023-10-11 18:54:35 -04:00
gsemaj
7af28d8e70 fix submesh overlap 2023-09-09 17:45:47 -07:00
gsemaj
3bb3df7a2d Split terrain into chunks of chunk_size 2023-09-09 16:40:22 -07:00
gsemaj
f6dc2d690f code to show all selected faces 2023-09-09 11:24:47 -07:00
gsemaj
af2988e062 fix error 2023-09-09 10:43:43 -07:00
gsemaj
03a3e21532 Fix UVs 2023-09-09 10:28:48 -07:00
gsemaj
20d5f6231c Export as OBJ 2023-07-20 14:27:39 -04:00
gsemaj
131297552e Fix normals 2023-07-20 14:27:24 -04:00
gsemaj
ef8ae1fb6e Fix vertex positioning and outfile name 2023-07-20 14:04:00 -04:00
gsemaj
3aabb35f33 Add terrain mesh extractor 2023-07-13 16:18:09 -04:00
gsemaj
7b750db9f9 Update README 2022-08-11 15:56:07 -04:00
gsemaj
aa564926a0 Fix CameraPos translation 2022-08-11 15:46:58 -04:00
gsemaj
2496f04987 Add semantic to f2a struct 2022-08-11 13:21:33 -04:00
gsemaj
916857edc3 Unity 3 fixes 2022-08-11 12:20:51 -04:00
gsemaj
3791e889c8 Add support for multiple subprograms 2022-08-11 11:53:38 -04:00
gsemaj
d0e67d55c9 Standalone support for basic fragment shaders 2022-08-11 04:06:03 -04:00
164 changed files with 3085 additions and 55 deletions

136
db_migration/ogracing.py Normal file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env python3
# OG racing score conversion script
#
# This script is meant to update racing scoreboards from Retro-style scores
# to OG-accurate scores. Be careful to only run this once!
#
# The script will create a backup of your DB, as well as a log file
# containing a record of all the changes that were made. Make sure to
# preserve this log file in case you need to reference it in the future.
#
# If something goes wrong with the first invocation, you'll need to move the
# DB backup and log files out of the way before the script can be re-run.
#
# If all goes well, you should see different, OG-true scores in IZ scoreboards.
#
# Do not hesitate to ask the OF developers for assistance if necessary.
import sys
import os.path
import shutil
import logging
import json
from math import exp
import sqlite3
LOGFILE = 'ogracing.log'
DRY_RUN = False # set to True if testing the script
CAP_SCORES = True # set to False to disable capping scores to the IZ maximum
class EpData:
max_score = 0
pod_factor = 0
max_pods = 0
max_time = 0
time_factor = 0
scale_factor = 0
class RaceResult:
epid = 0
playerid = 0
score = 0
timestamp = 0
ring_count = 0
time = 0
def check_version(cur):
cur.execute("SELECT Value FROM Meta WHERE Key = 'DatabaseVersion';")
ver = cur.fetchone()[0]
if ver < 2:
sys.exit('fatal: you must first upgrade your DB version to 2 by running the server at least once')
def load_epinfo():
epinfo = {}
with open("drops.json", "r") as f:
dat = json.load(f)["Racing"]
for key in dat:
val = dat[key]
epdata = EpData()
epid = int(val["EPID"])
epdata.max_score = int(val["ScoreCap"])
epdata.pod_factor = float(val["PodFactor"])
epdata.max_pods = int(val["TotalPods"])
epdata.max_time = int(val["TimeLimit"])
epdata.time_factor = float(val["TimeFactor"])
epdata.scale_factor = float(val["ScaleFactor"])
epinfo[epid] = epdata
return epinfo
def get_results(cur):
results = []
cur.execute('SELECT EPID, PlayerID, Timestamp, RingCount, Time, Score FROM RaceResults;')
for x in cur.fetchall():
result = RaceResult()
result.epid = int(x[0])
result.playerid = int(x[1])
result.timestamp = int(x[2])
result.ring_count = int(x[3])
result.time = int(x[4])
result.score = int(x[5])
results.append(result)
return results
def process_result(cur, result, epinfo):
epdata = epinfo[result.epid]
pod_score = (epdata.pod_factor * result.ring_count) / epdata.max_pods
time_score = (epdata.time_factor * result.time) / epdata.max_time
newscore = int(exp(pod_score - time_score + epdata.scale_factor))
if CAP_SCORES and newscore > epdata.max_score:
logging.warning('score {} greater than max ({}) for epid {}, capping'.format(newscore, epdata.max_score, result.epid))
print('warning: score {} greater than max ({}) for epid {}, capping'.format(newscore, epdata.max_score, result.epid))
newscore = epdata.max_score
logging.info('* {} -> {} (EPID: {}, pods: {}, time: {})'.format(result.score, newscore, result.epid, result.ring_count, result.time))
if not DRY_RUN:
cur.execute('UPDATE RaceResults SET Score = ? WHERE (PlayerID, Timestamp) = (?, ?);', (newscore, result.playerid, result.timestamp))
def main(path):
if os.path.isfile(LOGFILE):
sys.exit('fatal: a log file named {} already exists. refusing to modify.'.format(LOGFILE))
logging.basicConfig(filename=LOGFILE, level=20, format='%(levelname)s: %(message)s')
if not os.path.isfile(path):
sys.exit('fatal: {} is not a file'.format(path))
bakpath = path + '.ogracing.bak'
if os.path.isfile(bakpath):
sys.exit('fatal: a DB backup named {} already exists. refusing to overwrite.'.format(bakpath))
shutil.copy(path, bakpath)
logging.info('saved database backup to {}'.format(bakpath))
print('saved database backup to {}'.format(bakpath))
epinfo = load_epinfo()
with sqlite3.connect(path) as db:
cur = db.cursor()
check_version(cur)
results = get_results(cur)
for result in results:
process_result(cur, result, epinfo)
logging.info('done.')
print('done.')
if __name__ == '__main__':
if len(sys.argv) != 2:
sys.exit('usage: {} database.db'.format(sys.argv[0]))
main(sys.argv[1])

View File

@@ -6,6 +6,6 @@ Tools for converting d3d9 shader assembly to HLSL/Cg.
## Known issues
- Only vertex shaders with profile `vs_1_1` are supported
- No fragment shaders are supported yet
- Only one subprogram in a subshader will be converted (for now)
- Only fragment shaders with profile `ps_2_0` are supported
- Only a limited set of instructions (those used by FF and Unity 2.6) are supported
- Properties that don't begin with an underscore do not get captured as locals

0
dx2cg/__init__.py Normal file
View File

View File

@@ -29,16 +29,19 @@ reserved = {
}
decls = {
"dcl_position": "float4 {0} = vardat.vertex;",
"dcl_normal": "float4 {0} = float4(vardat.normal.x, vardat.normal.y, vardat.normal.z, 0);",
"dcl_texcoord0": "float4 {0} = vardat.texcoord;",
"dcl_texcoord1": "float4 {0} = vardat.texcoord1;",
"dcl_color": "float4 {0} = vardat.color;",
"dcl_position": "float4 {0} = vdat.vertex;",
"dcl_normal": "float4 {0} = float4(vdat.normal, 0);",
"dcl_texcoord0": "float4 {0} = vdat.texcoord;",
"dcl_texcoord1": "float4 {0} = vdat.texcoord1;",
"dcl_color": "float4 {0} = vdat.color;",
"dcl_2d": "; // no operation",
"dcl": "float4 {0[0]}{0[1]} = pdat.{0[0]}{0[1]};",
"def": "const float4 {0} = float4({1}, {2}, {3}, {4});",
}
ops = {
"mov": "{0} = {1};",
"mov_sat": "{0} = clamp({1}, 0.0, 1.0)",
"add": "{0} = {1} + {2};",
"mul": "{0} = {1} * {2};",
"mad": "{0} = {1} * {2} + {3};",
@@ -51,9 +54,10 @@ ops = {
"slt": "{0} = float4(({1}.x < {2}.x) ? 1.0f : 0.0f, ({1}.y < {2}.y) ? 1.0f : 0.0f, ({1}.z < {2}.z) ? 1.0f : 0.0f, ({1}.w < {2}.w) ? 1.0f : 0.0f);",
"sge": "{0} = float4(({1}.x >= {2}.x) ? 1.0f : 0.0f, ({1}.y >= {2}.y) ? 1.0f : 0.0f, ({1}.z >= {2}.z) ? 1.0f : 0.0f, ({1}.w >= {2}.w) ? 1.0f : 0.0f);",
"rcp": "{0} = ({1} == 0.0f) ? FLT_MAX : (({1} == 1.0f) ? {1} : (1 / {1}));",
"texld": "{0} = {3}({2}, (float2){1});",
}
struct_appdata = """struct appdata {
struct_a2v = """struct a2v {
\tfloat4 vertex : POSITION;
\tfloat3 normal : NORMAL;
\tfloat4 texcoord : TEXCOORD0;
@@ -76,6 +80,11 @@ struct_v2f = f"""struct v2f {{
}};
"""
struct_f2a = """struct f2a {
\tfloat4 c0 : COLOR0;
};
"""
cg_header = """CGPROGRAM
#include "UnityCG.cginc"
#pragma exclude_renderers xbox360 ps3 gles
@@ -83,18 +92,30 @@ cg_header = """CGPROGRAM
cg_footer = """ENDCG"""
vertex_header = """v2f vert(appdata vardat) {
vertex_func = """v2f vert(a2v vdat) {{
\tfloat4 r0, r1, r2, r3, r4;
\tfloat4 tmp;
\tv2f o;
{0}
\treturn o;
}}
"""
vertex_footer = """\treturn o;
}"""
fragment_func = """f2a frag(v2f pdat) {{
\tfloat4 r0, r1, r2, r3, r4;
\tfloat4 tmp;
\tf2a o;
{0}
\treturn o;
}}
"""
def process_header(prog):
keywords = []
header = []
loctab = {}
locdecl = []
binds = []
@@ -134,6 +155,11 @@ def process_header(prog):
if lightval:
val = f"glstate.light[{lightval[1]}].{lightval[2]}"
lighting = True
elif val == "_ObjectSpaceCameraPos" and not legacy:
val = "mul(_World2Object, float4(_WorldSpaceCameraPos, 1.0f))"
elif val == "_ObjectSpaceLightPos0" and not legacy:
val = "mul(_World2Object, _WorldSpaceLightPos0)"
lighting = True
elif val == "glstate_lightmodel_ambient":
val = "glstate.lightmodel.ambient"
lighting = True
@@ -160,29 +186,29 @@ def process_header(prog):
i = i - 1
elif line.startswith("SetTexture"):
dec = line.split(' ')
if dec[2] != "{2D}":
if dec[2] == "{2D}":
texture_type = "sampler2D"
elif dec[2] == "{3D}":
texture_type = "sampler3D"
elif dec[2] == "{RECT}":
texture_type = "samplerRECT"
elif dec[2] == "{CUBE}":
texture_type = "samplerCUBE"
else:
raise ValueError(f"Unknown texture type {dec[2]}")
key = f"s{textures}"
val = dec[1][1:-1]
loctab[key] = val
locdecl.append(f"{texture_type} {val};")
textures = textures + 1
del prog[i]
i = i - 1
i = i + 1
if len(binds) > 0:
header.append("BindChannels {")
for b in binds:
header.append(f"\t{b}")
header.append("}")
if lighting:
header.append("Lighting On")
# print(loctab)
return (keywords, header, loctab, locdecl)
return (keywords, loctab, locdecl, binds, lighting)
def resolve_args(args, loctab, consts):
for a in range(0, len(args)):
@@ -200,11 +226,13 @@ def resolve_args(args, loctab, consts):
arg = arg[:dot]
else:
swiz = ""
if arg[0] == 'r':
pass
elif arg[0] == 'v':
pass
elif arg[0] == 't':
pass
elif arg[0] == 'c':
if arg not in consts:
arg = loctab[arg]
@@ -219,7 +247,23 @@ def resolve_args(args, loctab, consts):
args[a] = neg + arg + swiz
def decode(code, args):
def get_cgtex_type(name, locdecl):
for loc in locdecl:
loc = loc.split(' ')
if name == loc[1][:-1]:
if loc[0] == 'sampler2D':
return "tex2D"
elif loc[0] == 'sampler3D':
return "tex3D"
elif loc[0] == 'samplerCUBE':
return "texCUBE"
elif loc[0] == 'samplerRECT':
return "texRECT"
else:
raise ValueError(f"Unknown CG texture type {loc[0]}")
raise ValueError(f"Could not find texture {name} in locals")
def decode(code, args, locdecl):
if code in decls:
return [decls[code].format(*args)]
elif code in ops:
@@ -234,17 +278,23 @@ def decode(code, args):
else:
swiz = "xyzw"
lines = [ops[code].format("tmp", *args[1:])]
if code == "texld":
lines = [ops[code].format("tmp", *args[1:], get_cgtex_type(args[2], locdecl))]
else:
lines = [ops[code].format("tmp", *args[1:])]
for c in swiz:
lines.append(f"{target}.{c} = tmp.{c};")
return lines
else:
raise ValueError(f"Unknown code {code}")
raise ValueError(f"Unknown opcode {code}")
def process_asm(asm, loctab):
def process_asm(asm, loctab, locdecl):
shadertype = ""
if asm[0] == "\"vs_1_1":
shadertype = "vertex"
elif asm[0] == "\"ps_2_0":
shadertype = "fragment"
else:
raise ValueError(f"Unsupported shader type: {asm[0][1:]}")
@@ -266,9 +316,13 @@ def process_asm(asm, loctab):
if code == "def":
consts.add(args[0])
pp = code.find("_pp")
if pp > -1:
code = code[:pp]
resolve_args(args, loctab, consts)
disasm = decode(code, args)
disasm = decode(code, args, locdecl)
# print(f"{instruction} \t==>\t{disasm}")
disasm.insert(0, f"// {instruction}")
translated.extend(disasm)
@@ -276,25 +330,52 @@ def process_asm(asm, loctab):
return (shadertype, translated)
def disassemble(text):
asm = text.split('\n')[1:-1]
(keywords, header, loctab, locdecl) = process_header(asm)
(shadertype, disasm) = process_asm(asm, loctab)
def disassemble(blocks):
shaders = {}
keywords = set()
locdecl = set()
binds = set()
lighting = False
for block in blocks:
asm = block.split('\n')[1:-1]
(kw, ltab, ldecl, bds, light) = process_header(asm)
keywords.update(kw)
locdecl.update(ldecl)
binds.update(bds)
lighting |= light
(shadertype, disasm) = process_asm(asm, ltab, locdecl)
shaders[shadertype] = disasm
text = ""
if len(binds) > 0:
text += "BindChannels {\n"
for b in binds:
text += f"\t{b}\n"
text += "}\n"
text = "\n".join(header) + "\n"
if lighting:
text += "Lighting On\n"
text += cg_header
if keywords:
if len(keywords) > 0:
text += "#pragma multi_compile " + " ".join(keywords)
if shadertype == "vertex":
text += "#pragma vertex vert\n\n"
text += struct_appdata + "\n"
text += struct_v2f + "\n"
if "vertex" in shaders:
text += "#pragma vertex vert\n"
if "fragment" in shaders:
text += "#pragma fragment frag\n"
text += "\n"
if "vertex" in shaders:
text += struct_a2v + "\n"
text += struct_v2f + "\n"
if "fragment" in shaders:
text += struct_f2a + "\n"
text += "\n".join(locdecl) + "\n"
if shadertype == "vertex":
text += vertex_header + "\n"
text += "\t" + "\n\t".join(disasm) + "\n\n"
if shadertype == "vertex":
text += vertex_footer + "\n"
if "vertex" in shaders:
text += "\n" + vertex_func.format("\t" + "\n\t".join(shaders["vertex"]))
if "fragment" in shaders:
text += "\n" + fragment_func.format("\t" + "\n\t".join(shaders["fragment"]))
text += cg_footer
return text
@@ -304,5 +385,5 @@ if __name__ == "__main__":
else:
with open(sys.argv[1], "r") as fi:
buf = fi.read()
disasm = disassemble(buf)
disasm = disassemble(buf.split('~'))
print(disasm)

View File

@@ -3,7 +3,7 @@
import os
import sys
from swapper import process
from .swapper import process
def process_file(filename, suffix):
dot = filename.rfind(".")

View File

@@ -5,7 +5,7 @@
import re
import sys
from disassembler import disassemble
from .disassembler import disassemble
tabs = 3
def indent(block):
@@ -27,18 +27,24 @@ def find_closing_bracket(block, i):
raise ValueError(f"Block at {i} has no closing bracket")
def process_program(prog):
# print("processing:\n" + prog)
subprogs = []
subprog_index = prog.find("SubProgram \"d3d9")
if subprog_index == -1:
raise ValueError(f"Program has no d3d9 subprogram")
subprog_end_index = find_closing_bracket(prog, subprog_index)
subprog = prog[subprog_index:subprog_end_index+1]
processed = disassemble(subprog) + "\n"
while subprog_index > -1:
subprog_end_index = find_closing_bracket(prog, subprog_index)
subprog = prog[subprog_index:subprog_end_index+1]
subprogs.append(subprog)
prog = prog[subprog_end_index+1:]
subprog_index = prog.find("SubProgram \"d3d9")
if len(subprogs) < 1:
raise ValueError(f"Program has no d3d9 subprograms")
processed = disassemble(subprogs) + "\n"
return indent(processed)
def process_shader(shader):
buf = shader
processed = ''
program_index = buf.find('Program')
program_index = buf.find("Program \"\"")
while program_index > -1:
processed = processed + buf[:program_index]
buf = buf[program_index:]
@@ -46,11 +52,11 @@ def process_shader(shader):
if not line:
raise ValueError(f"Program at {program_index} has no #LINE marker")
end_index = line.end() + 1
program_section = buf[program_index:end_index+1]
program_section = buf[:end_index+1]
processed = processed + process_program(program_section)
buf = buf[end_index+1:]
program_index = buf.find('Program')
program_index = buf.find("Program \"\"")
processed = processed + buf
return processed

2
json2xdb/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.vscode
xdt*.json

9
json2xdb/README.md Normal file
View File

@@ -0,0 +1,9 @@
# json2xdb
This script populates a functional, near-complete FusionFall XDB tabledata server.
You need an existing MySQL server (an old version; 5.5.42 seems to work with the FusionFall client). This can be set up pretty easily using Docker.
You also need a copy of xdt.json from the [OpenFusion tabledata repository](https://github.com/OpenFusionProject/tabledata).
It is interesting to note that the JSON tabledata file is really just a Unity ScriptableObject containing all the XDT/XDB state packaged into a FusionFall client build. The devs likely kept a central tabledata server around (XDB) and, whenever it was time for a client build, they fetched it into local binary files (XDT) before finally packing them into the XdtTableScript asset.
I would like to thank my girlfriend for showing me the wonders of `tqdm`. It really helped being able to see that things were happening.

View File

@@ -0,0 +1,13 @@
# Uses default credentials
version: '3.1'
services:
db:
image: mysql:5.5.42
command: --character-set-server utf8
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: mypassword
MYSQL_DATABASE: XDB

View File

@@ -0,0 +1,76 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"import json"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def to_db_table_name_mappings(table):\n",
" mappings = {}\n",
" for subtable_name in table:\n",
" mappings[subtable_name] = \"\"\n",
" return mappings"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"def gen_mappings(xdt):\n",
" mappings = {}\n",
" for table_name in xdt:\n",
" if \"table\" not in table_name.lower():\n",
" continue\n",
" table = xdt[table_name]\n",
" mappings[table_name] = to_db_table_name_mappings(table)\n",
" return mappings\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"with open('xdt.json', 'r') as f:\n",
" xdt = json.load(f)\n",
"mappings = gen_mappings(xdt)\n",
"with open('mappings.json', 'w') as f:\n",
" json.dump(mappings, f, indent=4)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

222
json2xdb/json2xdb.py Normal file
View File

@@ -0,0 +1,222 @@
# %%
import json
import sys
from tqdm import tqdm
import mysql.connector
SPLIT_FIELDS = {
"m_iMissionRewardItem": ("m_iMissionRewardItemID", "m_iMissionRewarItemType"),
"m_iMissionRewardItem2": ("m_iMissionRewardItemID2", "m_iMissionRewardItemType2"),
"m_iCSTItem": ("m_iCSTItemID", "m_iCSTItemNumNeeded"),
"m_iCSUEnemy": ("m_iCSUEnemyID", "m_iCSUNumToKill"),
"m_iCSUItem": ("m_iCSUItemID", "m_iCSUItemNumNeeded"),
"m_iSTItem": ("m_iSTItemID", "m_iSTItemNumNeeded", "m_iSTItemDropRate"),
"m_iSUItem_": ("m_iSUItem", "m_iSUInstancename"),
"m_iFItem": ("m_iFItemID", "m_iFItemNumNeeded"),
}
# %%
def get_db_column_name(xdt_field_name):
# special case 1
if xdt_field_name == "m_iitemID":
return "ItemID"
try:
# find the first uppercase character and split the string there
idx_of_first_uppercase = next(i for i, c in enumerate(xdt_field_name) if c.isupper())
except StopIteration:
# special case 2
if xdt_field_name == "m_ibattery":
idx_of_first_uppercase = 3
else:
print(f"Could not find uppercase character in {xdt_field_name}")
sys.exit(1)
prefix = xdt_field_name[:idx_of_first_uppercase]
db_field_name = xdt_field_name[idx_of_first_uppercase:]
return db_field_name
# %%
def table_entry_to_tuple(table_entry):
vals = []
for field_name in table_entry:
field = table_entry[field_name]
vals.append(field)
return tuple(vals)
def flatten_table_entry(table_entry):
flattened_entry = {}
for field_name in table_entry:
field = table_entry[field_name]
if type(field) == list:
for i, item in enumerate(field):
flattened_entry[f"{field_name}{i}"] = item
else:
flattened_entry[field_name] = field
return flattened_entry
def handle_dict_table(table_entries, identifier_key, items_key):
new_table_entries = []
for table_entry in table_entries:
identifier = table_entry[identifier_key]
items = table_entry[items_key]
for item in items:
new_item = {}
new_item[identifier_key] = identifier # needs to be first
for field_name in item:
new_item[field_name] = item[field_name]
new_table_entries.append(new_item)
return new_table_entries
def apply_schema(schema, entry):
fixed_entry = {}
padding = 0
for field_name in schema:
if field_name is None:
fixed_entry[f"m_iPadding{padding}"] = 0
padding += 1
continue
if field_name in entry:
fixed_entry[field_name] = entry[field_name]
elif field_name in SPLIT_FIELDS:
split_field_names = SPLIT_FIELDS[field_name]
interleaved_arr_len = len(entry[split_field_names[0]])
val = []
for i in range(interleaved_arr_len):
for split_field_name in split_field_names:
val.append(entry[split_field_name][i])
for l in range(len(val)):
n = l % len(split_field_names)
i = l // len(split_field_names)
new_field_name = f"{split_field_names[n]}{i}"
fixed_entry[new_field_name] = val[l]
else:
print("Missing field: {}".format(field_name))
return fixed_entry
# %%
def gen_column_sql(field_name, field_value):
field_type = type(field_value)
if field_type == int:
return f"`{field_name}` INT,"
elif field_type == float:
return f"`{field_name}` FLOAT,"
elif field_type == str:
# TODO maybe ascii vs unicode?
return f"`{field_name}` TEXT,"
else:
print(f"Unknown type {field_type} for field {field_name}, skipping")
return ""
# %%
def table_create(cursor, table_name, xdt_template_entry):
sql = f"CREATE TABLE {table_name} ("
sql += "id INT AUTO_INCREMENT PRIMARY KEY,"
for field_name in xdt_template_entry:
db_field_name = get_db_column_name(field_name)
val = xdt_template_entry[field_name]
sql += gen_column_sql(db_field_name, val)
sql = sql[:-1] # remove trailing comma
sql += ")"
cursor.execute(sql)
# %%
def table_populate(cursor, table_name, table_entries):
# generate the SQL first
sql = f"INSERT INTO {table_name} ("
template_entry = table_entries[0]
for field_name in template_entry:
db_field_name = get_db_column_name(field_name)
sql += f"`{db_field_name}`,"
sql = sql[:-1] # remove trailing comma
sql += ") VALUES ("
for field_name in template_entry:
sql += f"%s,"
sql = sql[:-1] # remove trailing comma
sql += ")"
vals = [table_entry_to_tuple(entry) for entry in table_entries]
try:
cursor.executemany(sql, vals)
except Exception as e:
print(sql)
print(vals)
raise e
# %%
def process_xdt_table(cursor, root, table_name, mappings):
table = root[table_name]
for subtable_name in tqdm(table, desc=table_name, total=len(table)):
if subtable_name not in mappings[table_name]:
print(f"No mapping found for {table_name}.{subtable_name}")
raise Exception()
db_table_name = mappings[table_name][subtable_name]
with open(f"schema/{db_table_name}.json", 'r') as f:
schema = json.load(f)
#print(f"{subtable_name} => {db_table_name}")
table_entries = table[subtable_name]
if db_table_name == "CutSceneText":
table_entries = handle_dict_table(table_entries, "m_iEvent", "m_TextElement")
table_entries = [apply_schema(schema, entry) for entry in table_entries]
table_entries = [flatten_table_entry(entry) for entry in table_entries]
# clear the table
drop_sql = f"DROP TABLE IF EXISTS {db_table_name}"
cursor.execute(drop_sql)
# create the table
table_create(cursor, db_table_name, table_entries[0])
table_populate(cursor, db_table_name, table_entries)
# %%
def main(conn, xdt_path):
with open("mappings.json", 'r') as f:
mappings = json.load(f)
with open(xdt_path, 'r') as f:
root = json.load(f)
cursor = conn.cursor()
for table_name in root:
if "Table" in table_name:
process_xdt_table(cursor, root, table_name, mappings)
finalize(cursor)
conn.commit()
def connect_to_db():
return mysql.connector.connect(
host="localhost",
user="root",
password="mypassword",
database="XDB"
)
def prep_db():
conn = connect_to_db()
cursor = conn.cursor()
# we have to upload a lot of data, so we need to raise the limit
cursor.execute("SET GLOBAL max_allowed_packet=1073741824")
conn.commit()
conn.close()
def finalize(cursor):
# credentials used by the game
cursor.execute("GRANT SELECT ON XDB.* TO 'cmog' IDENTIFIED BY 'scooby'")
# change the root password to something more secure
new_root_pw = input("Enter new root password: ")
cursor.execute(f"SET PASSWORD = PASSWORD('{new_root_pw}')")
# %%
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 json2xdb.py <path to xdt file>")
sys.exit(1)
xdt_path = sys.argv[1]
prep_db()
conn = connect_to_db()
main(conn, xdt_path)
conn.close()
# %%

232
json2xdb/mappings.json Normal file
View File

@@ -0,0 +1,232 @@
{
"m_pAnimationTable": {
"m_pAvatarData": "M",
"m_pNpcData": "Mob",
"m_pNanoData": "Nano"
},
"m_pAvatarTable": {
"m_pAvatarData": "AvatarTable",
"m_pAvatarGrowData": "AvatarGrowTable"
},
"m_pChatTable": {
"m_pFirstChatData": "1stChatTable",
"m_pSecondChatData": "2ndChatTable",
"m_pThirdChatData": "3rdChatTable",
"m_pChatStringData": "ChatString",
"m_pMenuChatData": "ChatTable",
"m_pMenuChatClassData": "ClassTable",
"m_pMenuChatEmoteData": "EmoteLink"
},
"m_pEmoteTable": {
"m_pEmoteAnimationData": "EmoteTable",
"m_pEmoteTextureData": "EmoteTexture"
},
"m_pGuideTable": {
"m_pGuideData": "GuideTable",
"m_pGuideStringData": "GuideStringTable"
},
"m_pInstanceTable": {
"m_pInstanceData": "InstanceTable",
"m_pWarpData": "WarpTable",
"m_pWarpNameData": "NameString"
},
"m_pMessageTable": {
"m_pMessageData": "SystemMessage"
},
"m_pMissionTable": {
"m_pJournalData": "Journal_ID",
"m_pMissionData": "MissionField",
"m_pMissionStringData": "MissionStringTable",
"m_pRewardData": "Reward"
},
"m_pNameTable": {
"m_pFirstName": "FirstNameTable",
"m_pMiddleName": "MiddleNameTable",
"m_pLastName": "LastNameTable"
},
"m_pNanoTable": {
"m_pNanoData": "NanoTable",
"m_pNanoStringData": "NanoString",
"m_pNanoMeshData": "NanoMesh",
"m_pNanoIconData": "NanoIcon",
"m_pNanoTuneData": "NanoTuneTable",
"m_pNanoTuneStringData": "NanoTuneString",
"m_pNanoTuneIconData": "NanoTuneIcon"
},
"m_pNpcTable": {
"m_pNpcData": "NpcTable",
"m_pNpcStringData": "NpcString",
"m_pNpcBarkerData": "BarkerTable",
"m_pNpcMeshData": "NpcMesh",
"m_pNpcIconData": "NpcIcon",
"m_pNpcGroupData": "NpcGroup",
"m_pNpcServiceData": "ServiceString"
},
"m_pShinyTable": {
"m_pShinyData": "ShinyTable",
"m_pShinyStringData": "ShinyString",
"m_pShinyMeshData": "ShinyMesh"
},
"m_pSkillTable": {
"m_pSkillData": "SkillTable",
"m_pSkillBuffData": "SkillBuffEffect",
"m_pSkillIconData": "SkillIcon",
"m_pSkillStringData": "SkillString"
},
"m_pConditionTable": {
"m_pConditionData": "StatusTable"
},
"m_pTransportationTable": {
"m_pTransportationMeshData": "TransportationMesh",
"m_pTransportationData": "TransportationTable",
"m_pTransportationWarpLocation": "WarpLocationTable",
"m_pTransportationWarpString": "TransportationWarpString",
"m_pBroomstickLocation": "WyvernLocationTable",
"m_pBroomstickString": "TransportationWyvernString",
"m_pTransIcon": "TransIcon"
},
"m_pVendorTable": {
"m_pItemData": "VendorTable"
},
"m_pXComTable": {
"m_pXComData": "XComTable"
},
"m_pBackItemTable": {
"m_pItemData": "ItemBackTable",
"m_pItemStringData": "ItemBackString",
"m_pItemIconData": "ItemBackIcon",
"m_pItemMeshData": "ItemBackMesh",
"m_pItemSoundData": "ItemBackSound"
},
"m_pFaceItemTable": {
"m_pItemData": "ItemFaceTable",
"m_pItemStringData": "ItemFaceString",
"m_pItemIconData": "ItemFaceIcon",
"m_pItemMeshData": "ItemFaceMesh",
"m_pItemSoundData": "ItemFaceSound"
},
"m_pGlassItemTable": {
"m_pItemData": "ItemGlassTable",
"m_pItemStringData": "ItemGlassString",
"m_pItemIconData": "ItemGlassIcon",
"m_pItemMeshData": "ItemGlassMesh",
"m_pItemSoundData": "ItemGlassSound"
},
"m_pHatItemTable": {
"m_pItemData": "ItemHatTable",
"m_pItemStringData": "ItemHatString",
"m_pItemIconData": "ItemHatIcon",
"m_pItemMeshData": "ItemHatMesh",
"m_pItemSoundData": "ItemHatSound"
},
"m_pHeadItemTable": {
"m_pItemData": "ItemHeadTable",
"m_pItemStringData": "ItemHeadString",
"m_pItemIconData": "ItemHeadIcon",
"m_pItemMeshData": "ItemHeadMesh",
"m_pItemSoundData": "ItemHeadSound"
},
"m_pPantsItemTable": {
"m_pItemData": "ItemPantsTable",
"m_pItemStringData": "ItemPantsString",
"m_pItemIconData": "ItemPantsIcon",
"m_pItemMeshData": "ItemPantsMesh",
"m_pItemSoundData": "ItemPantsSound"
},
"m_pShirtsItemTable": {
"m_pItemData": "ItemShirtTable",
"m_pItemStringData": "ItemShirtString",
"m_pItemIconData": "ItemShirtIcon",
"m_pItemMeshData": "ItemShirtMesh",
"m_pItemSoundData": "ItemShirtSound"
},
"m_pShoesItemTable": {
"m_pItemData": "ItemShoesTable",
"m_pItemStringData": "ItemShoesString",
"m_pItemIconData": "ItemShoesIcon",
"m_pItemMeshData": "ItemShoesMesh",
"m_pItemSoundData": "ItemShoesSound"
},
"m_pWeaponItemTable": {
"m_pItemData": "ItemWpnTable",
"m_pItemStringData": "ItemWpnString",
"m_pItemIconData": "ItemWpnIcon",
"m_pItemMeshData": "ItemWpnMesh",
"m_pItemSoundData": "ItemWpnSound"
},
"m_pVehicleItemTable": {
"m_pItemData": "ItemVehicleTable",
"m_pItemStringData": "ItemVehicleString",
"m_pItemIconData": "ItemVehicleIcon",
"m_pItemMeshData": "ItemVehicleMesh",
"m_pItemSoundData": "ItemVehicleSound"
},
"m_pGeneralItemTable": {
"m_pItemData": "ItemGeneralTable",
"m_pItemStringData": "ItemGeneralString",
"m_pItemIconData": "ItemGeneralIcon"
},
"m_pChestItemTable": {
"m_pItemData": "ItemChestTable",
"m_pItemStringData": "ItemChestString",
"m_pItemIconData": "ChestIconTable"
},
"m_pQuestItemTable": {
"m_pItemData": "ItemQuestTable",
"m_pItemStringData": "ItemQuestString",
"m_pItemIconData": "ItemQuestIcon"
},
"m_pCreationItemTable": {
"m_pCreationItemData": "ItemCreationTable"
},
"m_pFirstUseTable": {
"m_pFirstUseData": "FirstUseTable",
"m_pFirstUseString": "FirstUseString"
},
"m_pRulesTable": {
"m_pRulesData": "RulesTable",
"m_pRulesString": "RulesString"
},
"m_pHelpTable": {
"m_pHelpData": "HelpTable",
"m_pHelpPageData": "DescriptionTable",
"m_pHelpPageDescData": "Description",
"m_pHelpPageString": "DescriptionString",
"m_pHelpString": "HelpString"
},
"m_pCutSceneTable": {
"m_SceneData": "CutSceneText"
},
"m_pCombiningTable": {
"m_pCombiningData": "CombiningTable"
},
"m_pFilterTable": {
"m_pWhiteFilterData": "UnfilterTable",
"m_pBlackFilterData": "FilterTable",
"m_pNameFilterData": "NamefilterTable"
},
"m_pClassTable": {
"m_pClassTypeData": "ClassType",
"m_pClassWpnTypeData": "ClassWpnType",
"m_pClassString": "ClassString",
"m_pClassIconData": "ClassIcon"
},
"m_pEnchantTable": {
"m_pEnchantData": "EnchantTable"
},
"m_pClassSkillTable": {
"m_pClassSkillChargingElement": "ClassSkill_Charging",
"m_pSkillManagerData": "ClassSkill_Manager",
"m_pSkillData": "ClassSkill_Skill",
"m_pSkillBuffElement": "ClassSkill_BuffEffect",
"m_pSkillString": "ClassSkill_String",
"m_pClassSkillCondition": "Condition_Character",
"m_pSkillIconData": "ClassSkill_Icon",
"m_pSkillSound": "ClassSkill_Sound"
},
"m_pSkillBookTable": {
"m_pSkillBookElement": "ItemSkillBookTable",
"m_pSkillBookString": "ItemSkillBookString",
"m_pSkillBookIconData": "ItemSkillBookIcon"
}
}

283
json2xdb/prototyping.ipynb Normal file
View File

@@ -0,0 +1,283 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import sys\n",
"from tqdm import tqdm\n",
"import mysql.connector"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
"def get_db_column_name(xdt_field_name):\n",
" # special case 1\n",
" if xdt_field_name == \"m_iitemID\":\n",
" return \"ItemID\"\n",
" \n",
" try:\n",
" # find the first uppercase character and split the string there\n",
" idx_of_first_uppercase = next(i for i, c in enumerate(xdt_field_name) if c.isupper())\n",
" except StopIteration:\n",
" # special case 2\n",
" if xdt_field_name == \"m_ibattery\":\n",
" idx_of_first_uppercase = 3\n",
" else:\n",
" print(f\"Could not find uppercase character in {xdt_field_name}\")\n",
" sys.exit(1)\n",
" prefix = xdt_field_name[:idx_of_first_uppercase]\n",
" db_field_name = xdt_field_name[idx_of_first_uppercase:]\n",
" return db_field_name"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
"def table_entry_to_tuple(table_entry):\n",
" vals = []\n",
" for field_name in table_entry:\n",
" field = table_entry[field_name]\n",
" vals.append(field)\n",
" return tuple(vals)\n",
"\n",
"def flatten_table_entry(table_entry):\n",
" flattened_entry = {}\n",
" for field_name in table_entry:\n",
" field = table_entry[field_name]\n",
" if type(field) == list:\n",
" for i, item in enumerate(field):\n",
" flattened_entry[f\"{field_name}{i}\"] = item\n",
" else:\n",
" flattened_entry[field_name] = field\n",
" return flattened_entry\n",
"\n",
"def handle_dict_table(table_entries, identifier_key, items_key):\n",
" new_table_entries = []\n",
" for table_entry in table_entries:\n",
" identifier = table_entry[identifier_key]\n",
" items = table_entry[items_key]\n",
" for item in items:\n",
" new_item = {}\n",
" new_item[identifier_key] = identifier # needs to be first\n",
" for field_name in item:\n",
" new_item[field_name] = item[field_name]\n",
" new_table_entries.append(new_item)\n",
" return new_table_entries\n"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"def gen_column_sql(field_name, field_value):\n",
" field_type = type(field_value)\n",
" if field_type == int:\n",
" return f\"`{field_name}` INT,\"\n",
" elif field_type == float:\n",
" return f\"`{field_name}` FLOAT,\"\n",
" elif field_type == str:\n",
" # TODO maybe ascii vs unicode?\n",
" return f\"`{field_name}` TEXT,\"\n",
" else:\n",
" print(f\"Unknown type {field_type} for field {field_name}, skipping\")\n",
" return \"\""
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [],
"source": [
"def table_create(cursor, table_name, xdt_template_entry):\n",
" sql = f\"CREATE TABLE {table_name} (\"\n",
" sql += \"id INT AUTO_INCREMENT PRIMARY KEY,\"\n",
" for field_name in xdt_template_entry:\n",
" db_field_name = get_db_column_name(field_name)\n",
" val = xdt_template_entry[field_name]\n",
" sql += gen_column_sql(db_field_name, val)\n",
" sql = sql[:-1] # remove trailing comma\n",
" sql += \")\"\n",
" cursor.execute(sql)"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [],
"source": [
"def table_populate(cursor, table_name, table_entries):\n",
" # generate the SQL first\n",
" sql = f\"INSERT INTO {table_name} (\"\n",
" template_entry = table_entries[0]\n",
" for field_name in template_entry:\n",
" db_field_name = get_db_column_name(field_name)\n",
" sql += f\"`{db_field_name}`,\"\n",
" sql = sql[:-1] # remove trailing comma\n",
" sql += \") VALUES (\"\n",
" for field_name in template_entry:\n",
" sql += f\"%s,\"\n",
" sql = sql[:-1] # remove trailing comma\n",
" sql += \")\"\n",
" \n",
" vals = [table_entry_to_tuple(entry) for entry in table_entries]\n",
" try:\n",
" cursor.executemany(sql, vals)\n",
" except Exception as e:\n",
" print(sql)\n",
" print(vals)\n",
" raise e"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [],
"source": [
"def process_xdt_table(cursor, root, table_name, mappings):\n",
" table = root[table_name]\n",
" for (i, subtable_name) in tqdm(enumerate(table), desc=table_name, total=len(table)):\n",
" db_table_name = mappings[table_name][i]\n",
" #print(f\"{subtable_name} => {db_table_name}\")\n",
" \n",
" table_entries = table[subtable_name]\n",
" if db_table_name == \"CutSceneText\":\n",
" table_entries = handle_dict_table(table_entries, \"m_iEvent\", \"m_TextElement\")\n",
" table_entries = [flatten_table_entry(entry) for entry in table_entries]\n",
"\n",
" # clear the table\n",
" drop_sql = f\"DROP TABLE IF EXISTS {db_table_name}\"\n",
" cursor.execute(drop_sql)\n",
"\n",
" # create the table\n",
" table_create(cursor, db_table_name, table_entries[0])\n",
" table_populate(cursor, db_table_name, table_entries)"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [],
"source": [
"def main(conn, xdt_path):\n",
" with open(\"mappings.json\", 'r') as f:\n",
" mappings = json.load(f)\n",
" with open(xdt_path, 'r') as f:\n",
" root = json.load(f)\n",
" cursor = conn.cursor()\n",
" for table_name in root:\n",
" if \"Table\" in table_name:\n",
" process_xdt_table(cursor, root, table_name, mappings)\n",
" conn.commit()"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"m_pAnimationTable: 100%|██████████| 3/3 [00:00<00:00, 9.30it/s]\n",
"m_pAvatarTable: 100%|██████████| 2/2 [00:00<00:00, 5.76it/s]\n",
"m_pChatTable: 100%|██████████| 7/7 [00:00<00:00, 9.55it/s]\n",
"m_pEmoteTable: 100%|██████████| 2/2 [00:00<00:00, 12.84it/s]\n",
"m_pGuideTable: 100%|██████████| 2/2 [00:00<00:00, 11.23it/s]\n",
"m_pInstanceTable: 100%|██████████| 3/3 [00:00<00:00, 9.99it/s]\n",
"m_pMessageTable: 100%|██████████| 1/1 [00:00<00:00, 8.64it/s]\n",
"m_pMissionTable: 100%|██████████| 4/4 [00:01<00:00, 2.57it/s]\n",
"m_pNameTable: 100%|██████████| 3/3 [00:00<00:00, 9.74it/s]\n",
"m_pNanoTable: 100%|██████████| 7/7 [00:00<00:00, 9.92it/s]\n",
"m_pNpcTable: 100%|██████████| 7/7 [00:01<00:00, 4.86it/s]\n",
"m_pShinyTable: 100%|██████████| 3/3 [00:00<00:00, 9.07it/s]\n",
"m_pSkillTable: 100%|██████████| 4/4 [00:00<00:00, 11.26it/s]\n",
"m_pConditionTable: 100%|██████████| 1/1 [00:00<00:00, 11.58it/s]\n",
"m_pTransportationTable: 100%|██████████| 7/7 [00:00<00:00, 10.35it/s]\n",
"m_pVendorTable: 100%|██████████| 1/1 [00:00<00:00, 4.68it/s]\n",
"m_pXComTable: 100%|██████████| 1/1 [00:00<00:00, 9.32it/s]\n",
"m_pBackItemTable: 100%|██████████| 5/5 [00:00<00:00, 9.05it/s]\n",
"m_pFaceItemTable: 100%|██████████| 5/5 [00:00<00:00, 9.85it/s]\n",
"m_pGlassItemTable: 100%|██████████| 5/5 [00:00<00:00, 8.95it/s]\n",
"m_pHatItemTable: 100%|██████████| 5/5 [00:00<00:00, 10.58it/s]\n",
"m_pHeadItemTable: 100%|██████████| 5/5 [00:00<00:00, 9.31it/s]\n",
"m_pPantsItemTable: 100%|██████████| 5/5 [00:00<00:00, 8.16it/s]\n",
"m_pShirtsItemTable: 100%|██████████| 5/5 [00:00<00:00, 7.10it/s]\n",
"m_pShoesItemTable: 100%|██████████| 5/5 [00:00<00:00, 6.49it/s]\n",
"m_pWeaponItemTable: 100%|██████████| 5/5 [00:00<00:00, 6.26it/s]\n",
"m_pVehicleItemTable: 100%|██████████| 5/5 [00:00<00:00, 9.41it/s]\n",
"m_pGeneralItemTable: 100%|██████████| 3/3 [00:00<00:00, 11.56it/s]\n",
"m_pChestItemTable: 100%|██████████| 3/3 [00:00<00:00, 6.83it/s]\n",
"m_pQuestItemTable: 100%|██████████| 3/3 [00:00<00:00, 11.19it/s]\n",
"m_pCreationItemTable: 100%|██████████| 1/1 [00:00<00:00, 12.50it/s]\n",
"m_pFirstUseTable: 100%|██████████| 2/2 [00:00<00:00, 10.72it/s]\n",
"m_pRulesTable: 100%|██████████| 2/2 [00:00<00:00, 8.38it/s]\n",
"m_pHelpTable: 100%|██████████| 5/5 [00:00<00:00, 9.11it/s]\n",
"m_pCutSceneTable: 100%|██████████| 1/1 [00:00<00:00, 11.51it/s]\n",
"m_pCombiningTable: 100%|██████████| 1/1 [00:00<00:00, 13.88it/s]\n",
"m_pFilterTable: 100%|██████████| 3/3 [00:00<00:00, 9.08it/s]\n",
"m_pClassTable: 100%|██████████| 4/4 [00:00<00:00, 10.94it/s]\n",
"m_pEnchantTable: 100%|██████████| 1/1 [00:00<00:00, 11.75it/s]\n",
"m_pClassSkillTable: 100%|██████████| 8/8 [00:00<00:00, 9.37it/s]\n",
"m_pSkillBookTable: 100%|██████████| 3/3 [00:00<00:00, 10.67it/s]\n"
]
}
],
"source": [
"xdt_path = \"tdata/xdt.json\"\n",
"conn = mysql.connector.connect(\n",
" host=\"localhost\",\n",
" user=\"root\",\n",
" password=\"mypassword\",\n",
" database=\"tabledata\"\n",
")\n",
"main(conn, xdt_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -0,0 +1,6 @@
[
"m_iItemStringIndex",
"m_iItemConditionNum",
"m_iItemConditionNum2",
"m_iNextItem"
]

View File

@@ -0,0 +1,6 @@
[
"m_iItemStringIndex",
"m_iItemConditionNum",
"m_iItemConditionNum2",
"m_iNextItem"
]

View File

@@ -0,0 +1,6 @@
[
"m_iItemStringIndex",
"m_iItemConditionNum",
"m_iItemConditionNum2",
"m_iNextItem"
]

View File

@@ -0,0 +1,17 @@
[
"m_iLevel",
"m_iHpUp",
"m_iMaxHP",
"m_iAccuracy",
"m_iDodge",
"m_iPower",
"m_iProtection",
"m_iReqBlob_NanoCreate",
"m_iReqBlob_NanoTune",
"m_iFMLimit",
"m_iMobFM",
"m_iNanoQuestTaskID",
"m_iNanoID",
"m_iBonusFM",
"m_iDeathFM"
]

View File

@@ -0,0 +1,22 @@
[
"m_iPower",
"m_iAccuracy",
"m_iProtection",
"m_iDodge",
"m_iRunSpeed",
"m_iSwimSpeed",
"m_iJumpHeight",
"m_iJumpDistance",
"m_iViewAngle",
"m_iViewDistance",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitialTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iBonusUp"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,12 @@
[
"m_iChatNum1",
"m_iChatNum2",
"m_iChatNum3",
"m_iChatNum4",
"m_iChatNum5",
"m_iChatNum6",
"m_iChatNum7",
"m_iChatNum8",
"m_iChatNum9",
"m_iChatNum10"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,5 @@
[
"m_iBuffNumber",
"m_iBuffEffect",
"m_iBuffEffectInstant"
]

View File

@@ -0,0 +1,10 @@
[
"m_iMax",
"m_iStandard",
"m_fTickplus",
"m_fTickminus",
"m_iAttackplus",
"m_iDefensplus",
"m_iDeadline",
"m_iDeaddown"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,10 @@
[
"m_iPriority",
"m_iSkillbutton",
"m_iSkillNumber",
"m_iConditionChar",
"m_iConditionSkill",
"m_iKeyMintime",
"m_iKeyMaxtime",
"m_iMotionSkipTime"
]

View File

@@ -0,0 +1,41 @@
[
"m_iSkillNumber",
"m_iSkillType",
"m_iEffectTarget",
"m_iEffectType",
"m_iTargetType",
"m_iValueA_Type",
"m_iValueA",
"m_iValueB_Type",
"m_iValueB",
"m_iValueC_Type",
"m_iValueC",
"m_iEffectRange",
"m_iEffectAngle",
"m_iEffectArea",
"m_iCoolTime",
"m_iTargetNumber",
"m_iBatteryDrainType",
"m_iBatteryDrainUse",
"m_iInitialTime",
"m_iDeleverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iDBType",
"m_iIcon",
"m_iEffect",
"m_iTargetEffect",
"m_iBuffEffect",
"m_iSound",
"m_iCoolType",
"m_iClassType",
"m_iClassNum",
"m_iWpnType",
"m_iDisCharging",
"m_iPlusCharging",
"m_iMoveType",
"m_iMoveSpeed",
"m_iJumpPower",
"m_iSkillLevel",
"m_iAnimationTime"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,8 @@
[
"m_SkillName",
"m_ClassName",
"m_WpnName",
"m_skillAccount1",
"m_skillAccount2",
"m_animationName"
]

View File

@@ -0,0 +1,4 @@
[
"m_strName",
"m_strComment"
]

View File

@@ -0,0 +1,4 @@
[
"m_iChatString",
"m_iLinkChatNum"
]

View File

@@ -0,0 +1,13 @@
[
"m_iClassNum",
"m_iClassName",
"m_iWpnType1",
"m_iWpnType2",
"m_iBasicWpn",
"m_iBonusHP",
"m_iBonusPow",
"m_iBonusAcc",
"m_iBonusPro",
"m_iBonusDod",
"m_iIcon"
]

View File

@@ -0,0 +1,10 @@
[
"m_iWpnType",
"m_iRateOfFire",
"m_iOverheatMax",
"m_iOverheatUse",
"m_iUnuseCool",
"m_iUseCool",
"m_iCoolTime",
"m_iOverheatEffect"
]

View File

@@ -0,0 +1,10 @@
[
"m_iLevelGap",
"m_fSameGrade",
"m_fOneGrade",
"m_fTwoGrade",
"m_fThreeGrade",
"m_fLevelGapStandard",
"m_iLookConstant",
"m_iStatConstant"
]

View File

@@ -0,0 +1,6 @@
[
"m_iIndex",
"m_iBattleCondition",
"m_iMoveCondition",
"m_iJumpCondition"
]

View File

@@ -0,0 +1,5 @@
[
"m_iEvent",
"m_iLine",
"m_strText"
]

View File

@@ -0,0 +1,6 @@
[
"m_iType",
"m_iSize",
"m_iColor",
"m_iString"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,4 @@
[
"m_iStartContent",
"m_iEndContent"
]

View File

@@ -0,0 +1,3 @@
[
"m_iLinkEmoteNum"
]

View File

@@ -0,0 +1,8 @@
[
"m_iEmoteNumber",
"m_iUser",
"m_iAvatarAnimation",
"m_iNanoAnimation",
"m_iTexture",
"m_iDuration"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,16 @@
[
"m_iEnchantGrade",
"m_iCost",
"m_iClass",
"m_iWpnMatter",
"m_iCostumeMatter",
"m_iProbability",
"m_iOffenceUp",
"m_iDefenceUp",
"m_iFailType",
"m_iNoDropProb",
"m_iOneDropProb",
"m_iTwoDropProb",
"m_iThreeDropProb",
"m_iFourDropProb"
]

View File

@@ -0,0 +1,3 @@
[
"m_strText"
]

View File

@@ -0,0 +1,3 @@
[
"m_pstrNameString"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,7 @@
[
"m_iString",
"m_iCompDuration",
"m_iIconDuration",
"m_iHelpSub",
"m_iHelpMain"
]

View File

@@ -0,0 +1,4 @@
[
"m_iNum",
"m_pszString"
]

View File

@@ -0,0 +1,8 @@
[
"m_iNameIndex",
"m_iQuest",
"m_iSelect",
"m_iLoginNomail",
"m_iLoginMail",
"m_iLevelUp"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,4 @@
[
"m_iTitleStartString",
"m_iSubEndString"
]

View File

@@ -0,0 +1,26 @@
[
null,
"m_iZoneX",
"m_iZoneY",
null,
"m_iInstanceNameID",
null,
null,
null,
"m_iIsEP",
null,
null,
null,
null,
null,
"m_ScoreMax",
null,
null,
null,
null,
null,
null,
null,
null,
"m_SortIndex"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,12 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iChestDesc",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iIcon",
"m_iChestCheck"
]

View File

@@ -0,0 +1,13 @@
[
"m_iShirtM",
"m_iPantsM",
"m_iShoesM",
"m_iHairM",
"m_iFaceM",
"m_iShirtF",
"m_iPantsF",
"m_iShoesF",
"m_iHairF",
"m_iFaceF",
"m_iWeapon"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,16 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iBatteryRecharge",
"m_iItemType",
"m_iStimPackAttri",
"m_iLinkSkill",
"m_iIcon",
"m_iCash"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iQuestStart",
"m_iDelete",
"m_iIcon"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

View File

@@ -0,0 +1,7 @@
[
"m_strName",
"m_strComment",
"m_strComment1",
"m_strComment2",
"m_iExtraNumber"
]

View File

@@ -0,0 +1,50 @@
[
"m_iItemNumber",
"m_iItemName",
"m_iComment",
"m_iTradeAble",
"m_iItemPrice",
"m_iItemSellPrice",
"m_iSellAble",
"m_iStackNumber",
"m_iEquipLoc",
"m_iEquipType",
"m_ibattery",
"m_iBatteryDrain",
"m_iMinReqLev",
"m_iReqSex",
"m_iMentor",
"m_iAtkRange",
"m_iAtkAngle",
"m_iEffectArea",
"m_iTargetMode",
"m_iTargetNumber",
"m_iInitalTime",
"m_iDeliverTime",
"m_iDelayTime",
"m_iDurationTime",
"m_iUp_power",
"m_iUp_accuracy",
"m_iUp_protection",
"m_iUp_dodge",
"m_iUp_runSpeed",
"m_iUp_swimSpeed",
"m_iUp_jumpHeight",
"m_iUp_jumpDistance",
"m_iUp_atkRate",
"m_iUp_effectArea",
"m_iUp_addFusionMatter",
"m_iUp_addCandy",
"m_iUp_addItemfind",
"m_iMesh",
"m_iIcon",
"m_iEffect1",
"m_iSound1",
"m_iRarity",
"m_iPointRat",
"m_iGroupRat",
"m_iDefenseRat",
"m_iEffect2",
"m_iSound2",
"m_iCashAble"
]

View File

@@ -0,0 +1,4 @@
[
"m_iIconType",
"m_iIconNumber"
]

View File

@@ -0,0 +1,8 @@
[
"m_pstrMMeshModelString",
"m_pstrMTextureString",
"m_pstrMTextureString2",
"m_pstrFMeshModelString",
"m_pstrFTextureString",
"m_pstrFTextureString2"
]

View File

@@ -0,0 +1,5 @@
[
"m_pstrSoundString1",
"m_pstrSoundString2",
"m_pstrSoundString3"
]

Some files were not shown because too many files have changed in this diff Show More