void sAG1N_ang_set (void)
{
/ * Stage grid size * /
stage_grid_size = 0.250000f;
/ * Stage reference position * /
angulation_mod = -0.383000f;
/ * Stage edge coordinate setting * /
stage_edge_min_x = -29.500000f;
stage_edge_min_z = -34.500000f;
stage_edge_max_x = 35.500000f;
stage_edge_max_z = 35.250000f;
/ * Set the number of grids on the stage * /
intersect_ct_x = 261;
intersect_ct_z = 280;
/ * Pointer setting to undulation data * /
stage_dat_tbl = (STG_TBL *) (stage_dat_addr [CmnSceneBank]);
angu = (unsigned short *) ((UINT32) stage_dat_tbl-> ang_tbl + stage_dat_addr [CmnSceneBank]);
/ * Field boundary edge setting * /
boundary_edge_ct = 169;
/ * Pointer setting to field boundary data * /
bdr_vertex = (BDR_VERTEX *) ((UINT32) stage_dat_tbl-> bdr_tbl + stage_dat_addr [CmnSceneBank]);
bdr_edge = (BDR_EDGE *) ((UINT32) stage_dat_tbl-> edg_tbl + stage_dat_addr [CmnSceneBank]);
}
00 GL1 "THE WHITE STORM" <THE WHITE STORM>
01 AG1N "THE AERIAL GARDEN (NIGHT)" <THE NIGHT OF FIREFLY>
02 DS2 - <THE DANGER ZONE 2> {DANGERZONE survival bonusesDS2}
03 GL2 "THE WHITE STORM2" <THE BLUE CAVE>
04 X04 - / <THE GREAT OPERA>
05 WA - <THE DEATH VALLEY>
06 TST - / "TEST1" <MOTION EDIT> {test grid}
07 WAL "WALL_TEST" <UNDULATION CHECK> {test wals}
08 KL1 "The Kowloon" <THE CRIMSON>
09 DS1 "THE DANGERZONE" <THE DANGER ZONE>
0A OH "THE GREAT OPERA(FRAME)" <THE GREAT OPERA>
0B OH2 "THE GREAT OPERA" <THE GREAT OPERA>
0C SN "THE DEMON'S CHURCH" <THE DEMON'S CHURCH> {42+}
0D SN2 "THE DEMON'S CHURCH2" <THE DEMON'S CHURCH>
0E AM - <THE BIO LAB.>
0F AG1D "THE AERIAL GARDEN" <THE AERIAL GARDEN>
10 AG2 "THE AERIAL GARDEN2" <THE WATER FALL>
11 EL - <ELEVATOR> {city elevator}
12 MI "THE MIYAMA" <THE MIYAMA>
13 NK "THE KOKU AN" <THE KOKU AN>
14 MO - <THE SPIRAL>
15 PO "THE DRAGON HILL" <THE DRAGON HILLS>
16 X22 - / <THE MIYAMA>
17 BP "EGX_BP" <UNKNOWN> {bass tina's poster}
18 BL "THE DOATEC GERMANY" <THE BIO LAB.>
19 GL1Z "NIX_GL1Z" <UNKNOWN> {leon's desert}
1A AG2N "THE AERIAL GARDEN (NIGHT)2" <THE WATER FALL>
1B MISP <UNKNOWN> {miama spring deprecated}
1C GC "EGX_GC" <UNKNOWN> {young tina's PRAIRIE}
1D TF "EGX_TF" <UNKNOWN> {tina tv}
1E PO2 "THE DRAGON HILL2" <THE DRAGON HILLS>
1F PR "THE PRAIRIE" <THE PRAIRIE>
20 TR - "Trailer" <UNKNOWN> {bass' truck}
21 KL2 "The Kowloon2" <THE CRIMSON>
22 AG1S <THE AERIAL GARDEN>
23 AG2S <THE WATER FALL>
24 AL - <THE D OCTAGON>
25 COL - <THE PANCRATIUM>
26 OL - <THE IRON HELL>
27 SA - <THE BLANCA>
28 KK "The L's Castle" <THE L'S CASTLE>
29 TE "THE BURAIZENIN" <THE BURAI ZENIN>
2A TE2 <THE BURAI ZENIN>
2B HT - <UNKNOWN> {bamboo forest}
2C OL2 - <THE IRON HELL>
2D TF2 - "EGX_TF2" <UNKNOWN> {tina tv}
2E DJ - "EGX_DJ" {dojo}
2F KM - {montane flower}
30 NC "THE YOZAKURA"
31 BC "THE ISLAND"
32 FC "THE SHRINE"
33 LT "THE RAY HOUSE"
34 SV "THE SAFARI"
35 LC "THE GREAT WALL"
36 SEA "THE AQUARIUM"
37 DT "THE DOWNTOWN"
38 SP "THE CYCLOTRON"
39 SB "THE SUSPENSION BRIDGE"
3A EFC - "Effect Check1"
3B EF2 - "Effect Check2"
3C EAR - "EARTH"
3D WL2 "WALL_TEST2"
3E WL3 -
3F MISM "THE MIYAMA(SPRING)"
40 MIAU "THE MIYAMA(AUTUMN)"
41 MIWI "THE MIYAMA(WINTER)"
#-------------------------------------------------------------------------------
# import collision data from doau/doa3 stages to blender (script)
#-------------------------------------------------------------------------------
import struct,os
stg_path = r"C:\WORK\doaol\STG-bin\sDJ_dat.bin"
stg_dir = r"C:\WORK\doaol\STG-bin"
parse_dir = 0
imin_blender = 1
if not parse_dir:
stages_bin = [stg_path]
else:
stages_bin = [os.path.join(stg_dir, stg_name) for stg_name in os.listdir(stg_dir)]
if imin_blender:
import bpy
new_collection = None
testlist = []
for stg_path in stages_bin:
if os.path.isfile(stg_path) and '.bin' in stg_path:
with open(stg_path,'rb') as f:
#get a list of offsets to colision data objects in a stage (not the angulation data, only collision edges)
head_size = struct.unpack('<L', f.read(4))[0]
if head_size in [0x14242, 0xc0e29548, 0x4b4e4c, 0xd0027c0, 0xd0027c0, 0x168]: #doa2: HT/TR are unknown data; doa3: LNK data, TST/TS2 are 4b long; doa2 sSV_mot.bin
continue
print(hex(head_size),stg_path)
colobjects = []
is_single_object = False
if head_size in (0x10, 0x18):
f.seek(head_size)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe not in (0x10, 0x18, 0x6c, 0x3, 0x10310) or dprobe == 0xf000f000: #this bin is a single object
colobjects.append(0)
is_single_object = True
head_size = 0
if not is_single_object:
f.seek(0)
for i in range(head_size//4):
f.seek(i*4)
boffset = struct.unpack('<L', f.read(4))[0]
f.seek(boffset)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe in (0x10, 0x18):
colobjects.append(boffset)
#go throug all collision objects and import the data
for coloffset in colobjects:
f.seek(coloffset)
angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset = struct.unpack('<LLLL', f.read(0x10))
boundry_edge_count = (boundry_edge_end_offset - boundry_edge_offset) // 8
f.seek(boundry_edge_offset + coloffset)
edges_with_attribs = [struct.unpack('<HHHH', f.read(8)) for i in range(boundry_edge_count)]
edges = [e[:2] for e in edges_with_attribs]
print(stg_path, f.tell(), colobjects, angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset, boundry_edge_count)
## boundry_vertex_count = max([index for edge in edges for index in edge]) + 1
boundry_vertex_count = (boundry_edge_offset - boundry_vertex_offset) // 12
f.seek(boundry_vertex_offset + coloffset)
print(stg_path, f.tell(), colobjects, angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset, boundry_edge_count, boundry_vertex_count)
vertices = [struct.unpack('<3f', f.read(12)) for i in range(boundry_vertex_count)]
#print(vertices)
#print(edges)
stg_name_short = os.path.basename(stg_path)
## vertices = [(0, 0, 0),(1,1,1)]
## edges = [(0,1)]
faces = []
if imin_blender:
new_mesh = bpy.data.meshes.new(stg_name_short +'_mesh_')
new_mesh.from_pydata(vertices, edges, faces)
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(stg_name_short + '_object_', new_mesh)
# make collection
if not new_collection:
new_collection = bpy.data.collections.new('stags_col')
bpy.context.scene.collection.children.link(new_collection)
# add object to scene collection
new_collection.objects.link(new_object)
-Open blender.
-Change the timeline to text editor(Shift+F11).
-Press the [+ New] button.
-Paste the text of this script.
-Change the stg_path to the s??_dat.bin of the stage you want to preview the collision edges(walls only)
-Press the [Run script] button. The collision data will be loaded.
It didn't like the "\2" in the path. I fixed the script to support ms windows paths. Try it now, it should work.I tried this, sadly it didn't load anything (attempted sAG1N_dat.bin), and no console errors given. Used v2.93.1.
It didn't like the "\2" in the path. I fixed the script to support ms windows paths. Try it now, it should work.
Also added 'sSV_mot.bin' to ignore.
You can change the 'stg_dir =' to the folder with all your stages bins, and make 'parse_dir = 1', and it will load all collisions from all stages.
I extracted the data embedded in executable that complements the collision data inside the bin files. I'll try to parse it and extend the importer to load also the terrain data. Also not sure if the walls data is shown correctly, need to look more into it.
#-------------------------------------------------------------------------------
# import collision data from doau stages(only original names) to blender (script) [ver.2]
#-------------------------------------------------------------------------------
stg_path = r"E:\--\@stages\doauJPstages\sAG1N_dat.bin"
stg_dir = r"E:\--\@stages\doauJPstages"
parse_dir = 0
imin_blender = 1
terrain_area_flags = 0b1000 #(default:0b1111) {non_shoreline?,water?,solid_ground?,playable_area?} 0b0000 0b0001 0b0100 0b0101 0b0110 0b0111 0b1000 0b1001 0b1100 0b1111 //bc fc (gl1-gl2 has no ground?) kl2 lc wl2 (gl1 has flag1)
################################################################################
##use stg_path='path/to/stage_folder/sSTAGE_dat.bin' and parse_dir=0, to parse a single stage
##use stg_dir='path/to/stages_folder/' and parse_dir=1, to parse all stages in the folder
##imin_blender = 0 for testing the script outside blender / imin_blender = 1 for importing data in blender
##terrain_area_flags - set the flags for terrain area types; all null is non playable area, others are unknown, like the snow and water have their combination of flags
##note: I used in this script only the most common for all stages, embedded collision parameters and layers of data
##limitations: only two types of terrain can be visualized at the same time, no properties of the walls are displayed; maybe convert edges to grease_pencil and use color-coding?
##???
## TF and WL3 both have collision data but it's deprecated inside the game
## MISP is processed with all miyamas but has a bin of a test stage(edges count and angulation count is different)
## GL1_1,OH_3,WL2_5,OH2_3,GL1Z_0 are empty?
## TE2 is doa3 wall data type?
################################################################################
import struct,os
terrain_area_flags = terrain_area_flags << 12
if parse_dir:stages_bin = [os.path.join(stg_dir, stg_name) for stg_name in os.listdir(stg_dir)]
else: stages_bin = [stg_path]
if imin_blender: import bpy
testlist = []
embedded_angs = {
0x00:[(0.25,1024.0,-1.719,19.0,26.25,159,172,90),(0.25,512.0,-5.7249999,7.25,22.25,93,99,0)],
0x01:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)],
0x0F:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)],
0x03:[(0.25,1024.0,-0.102,25.25,28.5,177,200,103)],
0x07:[(0.25,1024.0,0.0,10.25,10.25,83,83,124)],
0x08:[(0.25,1024.0,-0.19400001,17.5,14.25,137,140,132),(0.25,1024.0,0.0,16.5,1.75,136,141,73),(0.25,1024.0,-0.75,16.5,1.75,136,141,69)],
0x09:[(0.25,1024.0,-0.090999998,16.25,16.25,131,131,24)],
0x0A:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)],
0x0B:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)],
0x0C:[(0.25,1024.0,-0.206,15.5,19.25,124,129,96)],
0x0D:[(0.25,1024.0,-0.414,25.75,40.75,207,269,141)],
0x10:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)],
0x1A:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)],
0x12:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x1B:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x3F:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x40:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x41:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x13:[(0.25,1024.0,0.015,23.5,17.5,221,192,137),(0.25,1024.0,-1.001,15.75,9.75,156,180,99)],
0x15:[(0.5,1024.0,0.0,17.5,16.5,70,67,40),(0.5,1024.0,-12.543,16.5,25.0,69,68,78),(0.375,1024.0,-69.033997,12.375,28.5,62,63,16),(0.25,256.0,-29.403999,28.5,14.75,85,51,8),(0.375,1024.0,-31.601999,47.625,38.25,145,133,145),(0.25,256.0,-29.403999,-7.75,14.75,85,51,8),(0.375,1024.0,-29.478001,-10.5,24.0,88,88,74)],
0x17:[(0.5,1024.0,0.0,50.5,50.5,203,203,16)],
0x18:[(0.25,1024.0,-0.52399999,18.5,34.5,191,205,169)],
0x19:[(0.25,1024.0,-0.52399999,33.5,46.0,280,368,0)],
0x1C:[(0.5,1024.0,-1.765,27.0,22.5,104,93,47)],
0x1E:[(0.25,1024.0,0.62400001,17.5,17.25,140,143,84)],
0x1F:[(0.5,1024.0,-1.765,27.0,22.5,104,93,50),(0.5,1024.0,-7.0430002,37.0,21.0,171,147,76),(0.5,1024.0,-7.152,38.5,49.5,167,145,68),(0.5,512.0,-12.416,3.5,36.0,112,137,135),(0.5,512.0,-5.6170001,32.5,10.0,128,92,27),(0.5,512.0,-11.283,16.0,33.5,106,96,26)],
0x21:[(0.25,1024.0,0.31999999,33.25,12.0,170,149,177),(0.25,1024.0,0.31999999,33.25,12.0,170,149,185)],
0x22:[(0.25,1024.0,-0.38299999,35.5,35.25,261,280,169)],
0x23:[(0.25,1024.0,-0.18799999,16.25,16.75,144,133,79)],
0x28:[(0.5,1024.0,0.048999999,15.5,15.5,63,63,28)],
0x29:[(0.175,1024.0,-0.001,25.375,24.15,290,254,162)],
0x2A:[(0.25,1024.0,-1.6,24.0,26.5,203,171,113)],
0x30:[(0.25,1024.0,-0.001,19.5,20.0,177,226,135)],
0x31:[(0.44999999,1024.0,-0.22,66.150002,54.0,302,212,101)],
0x32:[(0.25,1024.0,15.05,17.0,-12.75,133,151,120),(0.25,128.0,-0.491,7.75,2.5,63,130,18),(0.25,1024.0,-0.741,17.5,20.0,141,146,84)],
0x33:[(0.1,1024.0,0.0,19.4,19.4,389,389,44)],
0x34:[(0.25,819.20001,-0.15899999,25.25,24.0,209,204,67),(0.25,512.0,-3.678,25.25,4.5,103,66,66),(0.5,1024.0,-4.152,48.5,38.5,133,136,129),(0.5,1024.0,-4.1999998,18.0,38.5,148,136,124)],
0x35:[(0.25,1024.0,0.0,16.75,16.75,133,137,75),(0.25,256.0,-19.9,10.75,-5.25,78,114,114),(0.25,1024.0,-21.048,25.0,-14.5,157,149,97),(0.25,256.0,-33.764999,16.75,-28.0,122,136,112),(0.25,1024.0,-33.799999,16.5,-34.25,187,184,71),(0.25,256.0,-34.618,39.0,-25.5,121,93,114),(0.25,512.0,-37.717999,58.5,-7.0,182,300,144),(0.25,204.8,-24.667999,14.25,44.75,96,161,113),(0.25,1024.0,-24.664,37.5,78.25,208,223,119)],
0x36:[(0.25,1024.0,0.0,20.25,20.25,163,163,65)],
0x37:[(0.25,1024.0,-0.18799999,20.25,20.25,163,163,19)],
0x38:[(0.5,1024.0,-0.99900001,19.5,19.5,79,79,32)],
0x39:[(0.5,1024.0,-0.67799997,23.5,10.5,95,43,42),(0.5,512.0,-20.073,18.5,10.0,76,82,74),(0.5,512.0,-26.941999,17.5,17.0,71,55,58),(0.5,341.33334,-30.17,19.5,43.5,78,99,84),(0.75,256.0,-30.17,19.5,43.5,53,98,208)],
0x3D:[(0.25,1024.0,0.0,16.25,17.5,131,141,19),(0.25,256.0,-4.0,20.25,12.25,71,99,12),(0.25,1024.0,-4.0,44.25,37.75,179,303,8),(0.25,256.0,-8.0,12.25,-2.75,99,98,12),(0.25,1024.0,-8.0,20.25,-9.5,163,162,8),(0.25,256.0,-4.0,-3.75,12.25,53,99,0),(0.25,1024.0,-14.066,6.25,37.75,207,303,11)]}
stg_short_names = ["GL1","AG1N","DS2","GL2","X04","WA","TST","WAL","KL1","DS1","OH","OH2","SN","SN2","AM","AG1D","AG2","EL","MI","NK","MO","PO","X22","BP","BL","GL1Z","AG2N","MISP","GC","TF","PO2","PR","TR","KL2","AG1S","AG2S","AL","COL","OL","SA","KK","TE","TE2","HT","OL2","TF2","DJ","KM","NC","BC","FC","LT","SV","LC","SEA","DT","SP","SB","EFC","EF2","EAR","WL2","WL3","MISM","MIAU","MIWI","X66","X67","X68","X69","X70"]
for stg_path in stages_bin:
## print('hey', os.path.isfile(stg_path) , '.bin' in stg_path)
if os.path.isfile(stg_path) and '.bin' in stg_path:
with open(stg_path,'rb') as f:
#try to detect the offsets that contain angulation data
head_size = struct.unpack('<L', f.read(4))[0]
if head_size in [0x14242, 0xc0e29548, 0x4b4e4c, 0xd0027c0, 0x168]: #doa2: HT/TR are unknown data; doa3: LNK data, TST/TS2 are 4b long; doao sSV_mot.bin
continue
colobjects = []
is_single_object = False
if head_size in (0x10, 0x18):
f.seek(head_size)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe not in (0x10, 0x18, 0x6c, 0x3, 0x10310) or dprobe == 0xf000f000: #this bin is a single object
colobjects.append(0)
is_single_object = True
head_size = 0
if not is_single_object:
f.seek(0)
for i in range(head_size//4):
f.seek(i*4)
boffset = struct.unpack('<L', f.read(4))[0]
f.seek(boffset)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe in (0x10, 0x18):
colobjects.append(boffset)
srtname = os.path.basename(stg_path)[1:-8]
if srtname in stg_short_names:
stgidx = stg_short_names.index(srtname)
else:
print(srtname, 'IS NOT RECOGNIZED')
continue
if stgidx not in embedded_angs:
print(srtname, 'HAS NO COMPLEMENTARY ANGULATION DATA', colobjects)
continue
colobjects = colobjects[:len(embedded_angs[stgidx])] #removing the falsedetected collision blocks
## print(srtname, len(colobjects), len(embedded_angs[stgidx]))
#go throug all collision objects and import the data
for k in range(len(colobjects)):
coloffset = colobjects[k]
stage_grid_size,angulation_scale,angulation_mod, stage_edge_max_x,stage_edge_max_z, intersect_ct_x,intersect_ct_z, boundary_edge_ct = embedded_angs[stgidx][k]
stage_edge_min_x = stage_edge_max_x - stage_grid_size * (intersect_ct_x - 1)
stage_edge_min_z = stage_edge_max_z - stage_grid_size * (intersect_ct_z - 1)
f.seek(coloffset)
angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset = struct.unpack('<LLLL', f.read(0x10))
boundry_edge_count = (boundry_edge_end_offset - boundry_edge_offset) // 8
f.seek(boundry_edge_offset + coloffset)
edges_with_attribs = [struct.unpack('<HHHH', f.read(8)) for i in range(boundry_edge_count)]
edges = [e[:2] for e in edges_with_attribs]
boundry_vertex_count = (boundry_edge_offset - boundry_vertex_offset) // 12
f.seek(boundry_vertex_offset + coloffset)
vertices = [struct.unpack('<3f', f.read(12)) for i in range(boundry_vertex_count)]
#terrain
angulation_count = (boundry_vertex_offset - angulation_offset) // 2
if angulation_count > intersect_ct_x * intersect_ct_z: angulation_count = intersect_ct_x * intersect_ct_z
f.seek(angulation_offset + coloffset)
## angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_x, f.read(intersect_ct_x*2)) for i in range(intersect_ct_z)]
angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_z, f.read(intersect_ct_z*2)) for i in range(intersect_ct_x)]
trn_vecs = []
trn_edges = []
for z in range(intersect_ct_z):
for x in range(intersect_ct_x):
angval = angulation_tbl1[x][z]
trn_vecs.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod + 1, z*stage_grid_size+stage_edge_min_z])
if x != intersect_ct_x - 1:
if angval & terrain_area_flags:
if angulation_tbl1[x+1][z] & terrain_area_flags:
trn_edges.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1])
else:
if z != intersect_ct_z - 1:
if not angulation_tbl1[x][z+1] & terrain_area_flags:
trn_edges.append([z*intersect_ct_x+x, (z+1)*intersect_ct_x+x])
faces = []
if imin_blender:
new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_wall')
new_mesh.from_pydata(vertices, edges, faces)
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_wall', new_mesh)
# make collection
if srtname + '_col' not in bpy.data.collections:
new_collection = bpy.data.collections.new(srtname + '_col')
bpy.context.scene.collection.children.link(new_collection)
# add object to scene collection
new_collection.objects.link(new_object)
new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_terrain')
new_mesh.from_pydata(trn_vecs, trn_edges, [])
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_terrain', new_mesh)
# add object to scene collection
new_collection.objects.link(new_object)
Python script to preview angulation data in blender(2.93). [version 2]:
Python:#------------------------------------------------------------------------------- # import collision data from doau stages(only original names) to blender (script) [ver.2] #------------------------------------------------------------------------------- stg_path = r"E:\--\@stages\doauJPstages\sAG1N_dat.bin" stg_dir = r"E:\--\@stages\doauJPstages" parse_dir = 0 imin_blender = 1 terrain_area_flags = 0b1000 #(default:0b1111) {non_shoreline?,water?,solid_ground?,playable_area?} 0b0000 0b0001 0b0100 0b0101 0b0110 0b0111 0b1000 0b1001 0b1100 0b1111 //bc fc (gl1-gl2 has no ground?) kl2 lc wl2 (gl1 has flag1) ################################################################################ ##use stg_path='path/to/stage_folder/sSTAGE_dat.bin' and parse_dir=0, to parse a single stage ##use stg_dir='path/to/stages_folder/' and parse_dir=1, to parse all stages in the folder ##imin_blender = 0 for testing the script outside blender / imin_blender = 1 for importing data in blender ##terrain_area_flags - set the flags for terrain area types; all null is non playable area, others are unknown, like the snow and water have their combination of flags ##note: I used in this script only the most common for all stages, embedded collision parameters and layers of data ##limitations: only two types of terrain can be visualized at the same time, no properties of the walls are displayed; maybe convert edges to grease_pencil and use color-coding? ##??? ## TF and WL3 both have collision data but it's deprecated inside the game ## MISP is processed with all miyamas but has a bin of a test stage(edges count and angulation count is different) ## GL1_1,OH_3,WL2_5,OH2_3,GL1Z_0 are empty? ## TE2 is doa3 wall data type? ################################################################################ import struct,os terrain_area_flags = terrain_area_flags << 12 if parse_dir:stages_bin = [os.path.join(stg_dir, stg_name) for stg_name in os.listdir(stg_dir)] else: stages_bin = [stg_path] if imin_blender: import bpy testlist = [] embedded_angs = { 0x00:[(0.25,1024.0,-1.719,19.0,26.25,159,172,90),(0.25,512.0,-5.7249999,7.25,22.25,93,99,0)], 0x01:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)], 0x0F:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)], 0x03:[(0.25,1024.0,-0.102,25.25,28.5,177,200,103)], 0x07:[(0.25,1024.0,0.0,10.25,10.25,83,83,124)], 0x08:[(0.25,1024.0,-0.19400001,17.5,14.25,137,140,132),(0.25,1024.0,0.0,16.5,1.75,136,141,73),(0.25,1024.0,-0.75,16.5,1.75,136,141,69)], 0x09:[(0.25,1024.0,-0.090999998,16.25,16.25,131,131,24)], 0x0A:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)], 0x0B:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)], 0x0C:[(0.25,1024.0,-0.206,15.5,19.25,124,129,96)], 0x0D:[(0.25,1024.0,-0.414,25.75,40.75,207,269,141)], 0x10:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)], 0x1A:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)], 0x12:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x1B:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x3F:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x40:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x41:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x13:[(0.25,1024.0,0.015,23.5,17.5,221,192,137),(0.25,1024.0,-1.001,15.75,9.75,156,180,99)], 0x15:[(0.5,1024.0,0.0,17.5,16.5,70,67,40),(0.5,1024.0,-12.543,16.5,25.0,69,68,78),(0.375,1024.0,-69.033997,12.375,28.5,62,63,16),(0.25,256.0,-29.403999,28.5,14.75,85,51,8),(0.375,1024.0,-31.601999,47.625,38.25,145,133,145),(0.25,256.0,-29.403999,-7.75,14.75,85,51,8),(0.375,1024.0,-29.478001,-10.5,24.0,88,88,74)], 0x17:[(0.5,1024.0,0.0,50.5,50.5,203,203,16)], 0x18:[(0.25,1024.0,-0.52399999,18.5,34.5,191,205,169)], 0x19:[(0.25,1024.0,-0.52399999,33.5,46.0,280,368,0)], 0x1C:[(0.5,1024.0,-1.765,27.0,22.5,104,93,47)], 0x1E:[(0.25,1024.0,0.62400001,17.5,17.25,140,143,84)], 0x1F:[(0.5,1024.0,-1.765,27.0,22.5,104,93,50),(0.5,1024.0,-7.0430002,37.0,21.0,171,147,76),(0.5,1024.0,-7.152,38.5,49.5,167,145,68),(0.5,512.0,-12.416,3.5,36.0,112,137,135),(0.5,512.0,-5.6170001,32.5,10.0,128,92,27),(0.5,512.0,-11.283,16.0,33.5,106,96,26)], 0x21:[(0.25,1024.0,0.31999999,33.25,12.0,170,149,177),(0.25,1024.0,0.31999999,33.25,12.0,170,149,185)], 0x22:[(0.25,1024.0,-0.38299999,35.5,35.25,261,280,169)], 0x23:[(0.25,1024.0,-0.18799999,16.25,16.75,144,133,79)], 0x28:[(0.5,1024.0,0.048999999,15.5,15.5,63,63,28)], 0x29:[(0.175,1024.0,-0.001,25.375,24.15,290,254,162)], 0x2A:[(0.25,1024.0,-1.6,24.0,26.5,203,171,113)], 0x30:[(0.25,1024.0,-0.001,19.5,20.0,177,226,135)], 0x31:[(0.44999999,1024.0,-0.22,66.150002,54.0,302,212,101)], 0x32:[(0.25,1024.0,15.05,17.0,-12.75,133,151,120),(0.25,128.0,-0.491,7.75,2.5,63,130,18),(0.25,1024.0,-0.741,17.5,20.0,141,146,84)], 0x33:[(0.1,1024.0,0.0,19.4,19.4,389,389,44)], 0x34:[(0.25,819.20001,-0.15899999,25.25,24.0,209,204,67),(0.25,512.0,-3.678,25.25,4.5,103,66,66),(0.5,1024.0,-4.152,48.5,38.5,133,136,129),(0.5,1024.0,-4.1999998,18.0,38.5,148,136,124)], 0x35:[(0.25,1024.0,0.0,16.75,16.75,133,137,75),(0.25,256.0,-19.9,10.75,-5.25,78,114,114),(0.25,1024.0,-21.048,25.0,-14.5,157,149,97),(0.25,256.0,-33.764999,16.75,-28.0,122,136,112),(0.25,1024.0,-33.799999,16.5,-34.25,187,184,71),(0.25,256.0,-34.618,39.0,-25.5,121,93,114),(0.25,512.0,-37.717999,58.5,-7.0,182,300,144),(0.25,204.8,-24.667999,14.25,44.75,96,161,113),(0.25,1024.0,-24.664,37.5,78.25,208,223,119)], 0x36:[(0.25,1024.0,0.0,20.25,20.25,163,163,65)], 0x37:[(0.25,1024.0,-0.18799999,20.25,20.25,163,163,19)], 0x38:[(0.5,1024.0,-0.99900001,19.5,19.5,79,79,32)], 0x39:[(0.5,1024.0,-0.67799997,23.5,10.5,95,43,42),(0.5,512.0,-20.073,18.5,10.0,76,82,74),(0.5,512.0,-26.941999,17.5,17.0,71,55,58),(0.5,341.33334,-30.17,19.5,43.5,78,99,84),(0.75,256.0,-30.17,19.5,43.5,53,98,208)], 0x3D:[(0.25,1024.0,0.0,16.25,17.5,131,141,19),(0.25,256.0,-4.0,20.25,12.25,71,99,12),(0.25,1024.0,-4.0,44.25,37.75,179,303,8),(0.25,256.0,-8.0,12.25,-2.75,99,98,12),(0.25,1024.0,-8.0,20.25,-9.5,163,162,8),(0.25,256.0,-4.0,-3.75,12.25,53,99,0),(0.25,1024.0,-14.066,6.25,37.75,207,303,11)]} stg_short_names = ["GL1","AG1N","DS2","GL2","X04","WA","TST","WAL","KL1","DS1","OH","OH2","SN","SN2","AM","AG1D","AG2","EL","MI","NK","MO","PO","X22","BP","BL","GL1Z","AG2N","MISP","GC","TF","PO2","PR","TR","KL2","AG1S","AG2S","AL","COL","OL","SA","KK","TE","TE2","HT","OL2","TF2","DJ","KM","NC","BC","FC","LT","SV","LC","SEA","DT","SP","SB","EFC","EF2","EAR","WL2","WL3","MISM","MIAU","MIWI","X66","X67","X68","X69","X70"] for stg_path in stages_bin: ## print('hey', os.path.isfile(stg_path) , '.bin' in stg_path) if os.path.isfile(stg_path) and '.bin' in stg_path: with open(stg_path,'rb') as f: #try to detect the offsets that contain angulation data head_size = struct.unpack('<L', f.read(4))[0] if head_size in [0x14242, 0xc0e29548, 0x4b4e4c, 0xd0027c0, 0x168]: #doa2: HT/TR are unknown data; doa3: LNK data, TST/TS2 are 4b long; doao sSV_mot.bin continue colobjects = [] is_single_object = False if head_size in (0x10, 0x18): f.seek(head_size) dprobe = struct.unpack('<L', f.read(4))[0] if dprobe not in (0x10, 0x18, 0x6c, 0x3, 0x10310) or dprobe == 0xf000f000: #this bin is a single object colobjects.append(0) is_single_object = True head_size = 0 if not is_single_object: f.seek(0) for i in range(head_size//4): f.seek(i*4) boffset = struct.unpack('<L', f.read(4))[0] f.seek(boffset) dprobe = struct.unpack('<L', f.read(4))[0] if dprobe in (0x10, 0x18): colobjects.append(boffset) srtname = os.path.basename(stg_path)[1:-8] if srtname in stg_short_names: stgidx = stg_short_names.index(srtname) else: print(srtname, 'IS NOT RECOGNIZED') continue if stgidx not in embedded_angs: print(srtname, 'HAS NO COMPLEMENTARY ANGULATION DATA', colobjects) continue colobjects = colobjects[:len(embedded_angs[stgidx])] #removing the falsedetected collision blocks ## print(srtname, len(colobjects), len(embedded_angs[stgidx])) #go throug all collision objects and import the data for k in range(len(colobjects)): coloffset = colobjects[k] stage_grid_size,angulation_scale,angulation_mod, stage_edge_max_x,stage_edge_max_z, intersect_ct_x,intersect_ct_z, boundary_edge_ct = embedded_angs[stgidx][k] stage_edge_min_x = stage_edge_max_x - stage_grid_size * (intersect_ct_x - 1) stage_edge_min_z = stage_edge_max_z - stage_grid_size * (intersect_ct_z - 1) f.seek(coloffset) angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset = struct.unpack('<LLLL', f.read(0x10)) boundry_edge_count = (boundry_edge_end_offset - boundry_edge_offset) // 8 f.seek(boundry_edge_offset + coloffset) edges_with_attribs = [struct.unpack('<HHHH', f.read(8)) for i in range(boundry_edge_count)] edges = [e[:2] for e in edges_with_attribs] boundry_vertex_count = (boundry_edge_offset - boundry_vertex_offset) // 12 f.seek(boundry_vertex_offset + coloffset) vertices = [struct.unpack('<3f', f.read(12)) for i in range(boundry_vertex_count)] #terrain angulation_count = (boundry_vertex_offset - angulation_offset) // 2 if angulation_count > intersect_ct_x * intersect_ct_z: angulation_count = intersect_ct_x * intersect_ct_z f.seek(angulation_offset + coloffset) ## angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_x, f.read(intersect_ct_x*2)) for i in range(intersect_ct_z)] angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_z, f.read(intersect_ct_z*2)) for i in range(intersect_ct_x)] trn_vecs = [] trn_edges = [] for z in range(intersect_ct_z): for x in range(intersect_ct_x): angval = angulation_tbl1[x][z] trn_vecs.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod + 1, z*stage_grid_size+stage_edge_min_z]) if x != intersect_ct_x - 1: if angval & terrain_area_flags: if angulation_tbl1[x+1][z] & terrain_area_flags: trn_edges.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1]) else: if z != intersect_ct_z - 1: if not angulation_tbl1[x][z+1] & terrain_area_flags: trn_edges.append([z*intersect_ct_x+x, (z+1)*intersect_ct_x+x]) faces = [] if imin_blender: new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_wall') new_mesh.from_pydata(vertices, edges, faces) new_mesh.update() # make object from mesh new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_wall', new_mesh) # make collection if srtname + '_col' not in bpy.data.collections: new_collection = bpy.data.collections.new(srtname + '_col') bpy.context.scene.collection.children.link(new_collection) # add object to scene collection new_collection.objects.link(new_object) new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_terrain') new_mesh.from_pydata(trn_vecs, trn_edges, []) new_mesh.update() # make object from mesh new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_terrain', new_mesh) # add object to scene collection new_collection.objects.link(new_object)
If you import all the stages at once, you can right click on the 'Scene Collection' in the blender's 'Outliner', and select 'View'->'Hide one level'. And then click on a 'eye' button in a collection, with the 'Ctrl' key pressed, to show only that collection. This way you can go faster through all stages, there is some impressive imagery to see(like the collisions for the GreatWall).
Some ancient footage:
doesn't work in a spoiler
ThanksGreat job! So cool to look at.
The floor is created by a formula, but the geometry for the walls is loaded as it is. The game engine could have it's own perception of the things. Anyway for previz is no biggie to shift everything to look good. Well, I checked myself, the xpr importer is making an offset of 2.0. Will try to export angulation data as mesh to see if it's offset is wrong or not.when I import the stage modeling to Blender, the angulation+walls is offset from the stage model by (0,2,-1).
I shift the XPR by -2 units on the Y to align its origin with global orgin, then I lower the angulation+walls by -1 on the Z to line the ground terrain with the angulation plane.
Not really. I thought that maybe if I export the angulation from blender as xpr, and load it in the game, some alpha stages could become more playable. Well, that's not the case...Interesting
Yeah, is there a way to convert the geometry of this stage to xpr? Could happen that this is a playable stage...Yeah, strange, since the XPR looks either empty or just a shader. But angulation looks like the second tier of Burai Zenin from DOA2:HC.
#-------------------------------------------------------------------------------
# import collision data from doau stages(only original names) to blender (script) [ver.2]
#-------------------------------------------------------------------------------
stg_path = r"E:\--\@stages\doauJPstages\sAG1S_dat.bin"
stg_path = r"E:\--\@stages\1\doaOstages\sOH2_dat.bin"
stg_dir = r"E:\--\@stages\doauJPstages"
parse_dir = 0
imin_blender = 1
terrain_as_edges = 0
terrain_area_flags = 0b1000 #(default:0b1111) {non_shoreline?,water?,solid_ground?,playable_area?} 0b0000 0b0001 0b0100 0b0101 0b0110 0b0111 0b1000 0b1001 0b1100 0b1111 //bc fc (gl1-gl2 has no ground?) kl2 lc wl2 (gl1 has flag1)
## recreate the lost angulation data: 5_WA-350, E_AM-266, 14_MO-594, 24_AL-266, 25_COL-342, 26_OL-266, 27_SA-326, 2C_OL2-266, 2E_DJ-560, 2F_KM-560, 3A_EFC-532, 3B_EF2-532, 3E_WL3-606
################################################################################
##use stg_path='path/to/stage_folder/sSTAGE_dat.bin' and parse_dir=0, to parse a single stage
##use stg_dir='path/to/stages_folder/' and parse_dir=1, to parse all stages in the folder
##imin_blender = 0 for testing the script outside blender / imin_blender = 1 for importing data in blender
##terrain_area_flags - set the flags for terrain area types; all null is non playable area, others are unknown, like the snow and water have their combination of flags
##note: I used in this script only the most common for all stages, embedded collision paramameters and layers of data
##limitations: only two types of terrain can be visualized at the same time, no properties of the walls are displayed; maybe convert edges to grease_pencil and use colorcoding?
##???
## created comlementary data to load WA,AM,MO,AL,COL,OL,SA,OL2,DJ,KM,EFC,EF2,WL3
## TF and WL3 both have collision data but it's deprecated inside the game
## MISP is processed with all miyamas but has a bin of a test stage(edges count and angulation count is different)
## GL1_1,OH_3,WL2_5,OH2_3,GL1Z_0 are empty?
## TE2 is doa3 wall data type?
################################################################################
import struct,os
terrain_area_flags = terrain_area_flags << 12
if parse_dir:stages_bin = [os.path.join(stg_dir, stg_name) for stg_name in os.listdir(stg_dir)]
else: stages_bin = [stg_path]
if imin_blender: import bpy
testlist = []
embedded_angs = {
0x00:[(0.25,1024.0,-1.719,19.0,26.25,159,172,90),(0.25,512.0,-5.7249999,7.25,22.25,93,99,0)],
0x01:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)],
0x0F:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)],
0x03:[(0.25,1024.0,-0.102,25.25,28.5,177,200,103)],
0x07:[(0.25,1024.0,0.0,10.25,10.25,83,83,124)],
0x08:[(0.25,1024.0,-0.19400001,17.5,14.25,137,140,132),(0.25,1024.0,0.0,16.5,1.75,136,141,73),(0.25,1024.0,-0.75,16.5,1.75,136,141,69)],
0x09:[(0.25,1024.0,-0.090999998,16.25,16.25,131,131,24)],
0x0A:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)],
0x0B:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)],
0x0C:[(0.25,1024.0,-0.206,15.5,19.25,124,129,96)],
0x0D:[(0.25,1024.0,-0.414,25.75,40.75,207,269,141)],
0x10:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)],
0x1A:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)],
0x12:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
##0x1B:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x3F:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x40:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x41:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)],
0x13:[(0.25,1024.0,0.015,23.5,17.5,221,192,137),(0.25,1024.0,-1.001,15.75,9.75,156,180,99)],
0x15:[(0.5,1024.0,0.0,17.5,16.5,70,67,40),(0.5,1024.0,-12.543,16.5,25.0,69,68,78),(0.375,1024.0,-69.033997,12.375,28.5,62,63,16),(0.25,256.0,-29.403999,28.5,14.75,85,51,8),(0.375,1024.0,-31.601999,47.625,38.25,145,133,145),(0.25,256.0,-29.403999,-7.75,14.75,85,51,8),(0.375,1024.0,-29.478001,-10.5,24.0,88,88,74)],
0x17:[(0.5,1024.0,0.0,50.5,50.5,203,203,16)],
0x18:[(0.25,1024.0,-0.52399999,18.5,34.5,191,205,169)],
0x19:[(0.25,1024.0,-0.52399999,33.5,46.0,280,368,0)],
0x1C:[(0.5,1024.0,-1.765,27.0,22.5,104,93,47)],
0x1E:[(0.25,1024.0,0.62400001,17.5,17.25,140,143,84)],
0x1F:[(0.5,1024.0,-1.765,27.0,22.5,104,93,50),(0.5,1024.0,-7.0430002,37.0,21.0,171,147,76),(0.5,1024.0,-7.152,38.5,49.5,167,145,68),(0.5,512.0,-12.416,3.5,36.0,112,137,135),(0.5,512.0,-5.6170001,32.5,10.0,128,92,27),(0.5,512.0,-11.283,16.0,33.5,106,96,26)],
0x21:[(0.25,1024.0,0.31999999,33.25,12.0,170,149,177),(0.25,1024.0,0.31999999,33.25,12.0,170,149,185)],
0x22:[(0.25,1024.0,-0.38299999,35.5,35.25,261,280,169)],
0x23:[(0.25,1024.0,-0.18799999,16.25,16.75,144,133,79)],
0x28:[(0.5,1024.0,0.048999999,15.5,15.5,63,63,28)],
0x29:[(0.175,1024.0,-0.001,25.375,24.15,290,254,162)],
0x2A:[(0.25,1024.0,-1.6,24.0,26.5,203,171,113)],
0x30:[(0.25,1024.0,-0.001,19.5,20.0,177,226,135)],
0x31:[(0.44999999,1024.0,-0.22,66.150002,54.0,302,212,101)],
0x32:[(0.25,1024.0,15.05,17.0,-12.75,133,151,120),(0.25,128.0,-0.491,7.75,2.5,63,130,18),(0.25,1024.0,-0.741,17.5,20.0,141,146,84)],
0x33:[(0.1,1024.0,0.0,19.4,19.4,389,389,44)],
0x34:[(0.25,819.20001,-0.15899999,25.25,24.0,209,204,67),(0.25,512.0,-3.678,25.25,4.5,103,66,66),(0.5,1024.0,-4.152,48.5,38.5,133,136,129),(0.5,1024.0,-4.1999998,18.0,38.5,148,136,124)],
0x35:[(0.25,1024.0,0.0,16.75,16.75,133,137,75),(0.25,256.0,-19.9,10.75,-5.25,78,114,114),(0.25,1024.0,-21.048,25.0,-14.5,157,149,97),(0.25,256.0,-33.764999,16.75,-28.0,122,136,112),(0.25,1024.0,-33.799999,16.5,-34.25,187,184,71),(0.25,256.0,-34.618,39.0,-25.5,121,93,114),(0.25,512.0,-37.717999,58.5,-7.0,182,300,144),(0.25,204.8,-24.667999,14.25,44.75,96,161,113),(0.25,1024.0,-24.664,37.5,78.25,208,223,119)],
0x36:[(0.25,1024.0,0.0,20.25,20.25,163,163,65)],
0x37:[(0.25,1024.0,-0.18799999,20.25,20.25,163,163,19)],
0x38:[(0.5,1024.0,-0.99900001,19.5,19.5,79,79,32)],
0x39:[(0.5,1024.0,-0.67799997,23.5,10.5,95,43,42),(0.5,512.0,-20.073,18.5,10.0,76,82,74),(0.5,512.0,-26.941999,17.5,17.0,71,55,58),(0.5,341.33334,-30.17,19.5,43.5,78,99,84),(0.75,256.0,-30.17,19.5,43.5,53,98,208)],
0x3D:[(0.25,1024.0,0.0,16.25,17.5,131,141,19),(0.25,256.0,-4.0,20.25,12.25,71,99,12),(0.25,1024.0,-4.0,44.25,37.75,179,303,8),(0.25,256.0,-8.0,12.25,-2.75,99,98,12),(0.25,1024.0,-8.0,20.25,-9.5,163,162,8),(0.25,256.0,-4.0,-3.75,12.25,53,99,0),(0.25,1024.0,-14.066,6.25,37.75,207,303,11)],
0x05:[[0.25, 1024, 0, 0, 0, 175, 175, 52]], #these are made up, they have angulation but the game doesn't use it and do not have complementary data for them.
0x14:[[0.25, 1024, 0, 0, 0, 142, 297, 98]],
0x25:[[0.25, 1024, 0, 0, 0, 171, 171, 128]],
0x27:[[0.25, 1024, 0, 0, 0, 163, 163, 82]],
0x26:[[0.25, 1024, 0, 0, 0, 153, 133, 54]],
0x2C:[[0.25, 1024, 0, 0, 0, 153, 133, 54]],
0x2E:[[0.25, 1024, 0, 0, 0, 261, 280, 169]],
0x2F:[[0.25, 1024, 0, 0, 0, 261, 280, 169]],
0x3E:[[0.25, 1024, 0, 0, 0, 163, 303, 11]],
0x1B:[[0.25, 1024, 0, 0, 0, 163, 303, 11]],
0x3A:[[0.25, 1024, 0, 0, 0, 144, 133, 79]],
0x3B:[[0.25, 1024, 0, 0, 0, 144, 133, 79]],
0x24:[[0.25, 1024, 0, 0, 0, 144, 133, 79]],
0x0E:[[0.25, 1024, 0, 0, 0, 144, 133, 79]]}
stg_short_names = ["GL1","AG1N","DS2","GL2","X04","WA","TST","WAL","KL1","DS1","OH","OH2","SN","SN2","AM","AG1D","AG2","EL","MI","NK","MO","PO","X22","BP","BL","GL1Z","AG2N","MISP","GC","TF","PO2","PR","TR","KL2","AG1S","AG2S","AL","COL","OL","SA","KK","TE","TE2","HT","OL2","TF2","DJ","KM","NC","BC","FC","LT","SV","LC","SEA","DT","SP","SB","EFC","EF2","EAR","WL2","WL3","MISM","MIAU","MIWI","X66","X67","X68","X69","X70"]
##add materials to the scene, for the floor will be the four bits 1111, first theree i'll make rgb and the fourth will be for darkness, and for the walls i'll need only color tones?
for stg_path in stages_bin:
## print('hey', os.path.isfile(stg_path) , '.bin' in stg_path)
if os.path.isfile(stg_path) and '.bin' in stg_path:
with open(stg_path,'rb') as f:
#try to detect the offsets that contain angulation data
head_size = struct.unpack('<L', f.read(4))[0]
if head_size in [0x14242, 0xc0e29548, 0x4b4e4c, 0xd0027c0, 0x168]: #doa2: HT/TR are unknown data; doa3: LNK data, TST/TS2 are 4b long; doao sSV_mot.bin
continue
colobjects = []
is_single_object = False
if head_size in (0x10, 0x18):
f.seek(head_size)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe not in (0x10, 0x18, 0x6c, 0x3, 0x10310) or dprobe == 0xf000f000: #this bin is a single object
colobjects.append(0)
is_single_object = True
head_size = 0
if not is_single_object:
f.seek(0)
for i in range(head_size//4):
f.seek(i*4)
boffset = struct.unpack('<L', f.read(4))[0]
f.seek(boffset)
dprobe = struct.unpack('<L', f.read(4))[0]
if dprobe in (0x10, 0x18):
colobjects.append(boffset)
srtname = os.path.basename(stg_path)[1:-8]
print(srtname)
if srtname == 'WA': colobjects.append(8)
## if not srtname in ["WA","AM","MO","AL","COL","OL","SA","OL2","DJ","KM","EFC","EF2","WL3","MISP"]: # DELETE THIS NOW!!!!!!!!!!!!!!!!!!!!!!!!!!
## continue
if srtname in stg_short_names:
stgidx = stg_short_names.index(srtname)
else:
print(srtname, 'IS NOT RECOGNIZED')
continue
if stgidx not in embedded_angs:
print(srtname, 'HAS NO COMPLEMENTARY ANGULATION DATA', colobjects)
continue
colobjects = colobjects[:len(embedded_angs[stgidx])] #removing the falsedetected collision blocks
## print(srtname, len(colobjects), len(embedded_angs[stgidx]))
#go throug all collision objects and import the data
for k in range(len(colobjects)):
coloffset = colobjects[k]
stage_grid_size,angulation_scale,angulation_mod, stage_edge_max_x,stage_edge_max_z, intersect_ct_x,intersect_ct_z, boundary_edge_ct = embedded_angs[stgidx][k]
stage_edge_min_x = stage_edge_max_x - stage_grid_size * (intersect_ct_x - 1)
stage_edge_min_z = stage_edge_max_z - stage_grid_size * (intersect_ct_z - 1)
f.seek(coloffset)
angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset = struct.unpack('<LLLL', f.read(0x10))
boundry_edge_count = (boundry_edge_end_offset - boundry_edge_offset) // 8
f.seek(boundry_edge_offset + coloffset)
edges_with_attribs = [struct.unpack('<HHHH', f.read(8)) for i in range(boundry_edge_count)]
edges = [e[:2] for e in edges_with_attribs]
boundry_vertex_count = (boundry_edge_offset - boundry_vertex_offset) // 12
f.seek(boundry_vertex_offset + coloffset)
vertices = [struct.unpack('<3f', f.read(12)) for i in range(boundry_vertex_count)]
#terrain
angulation_count = (boundry_vertex_offset - angulation_offset) // 2
if angulation_count > intersect_ct_x * intersect_ct_z: angulation_count = intersect_ct_x * intersect_ct_z
f.seek(angulation_offset + coloffset)
## angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_x, f.read(intersect_ct_x*2)) for i in range(intersect_ct_z)]
angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_z, f.read(intersect_ct_z*2)) for i in range(intersect_ct_x)]
trn_verts = []
trn_edges = []
trn_faces = []
for z in range(intersect_ct_z):
for x in range(intersect_ct_x):
angval = angulation_tbl1[x][z]
## trn_verts.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod + 1, z*stage_grid_size+stage_edge_min_z]) #is +1 for intersection?
trn_verts.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod, z*stage_grid_size+stage_edge_min_z])
if not terrain_as_edges:
if x != intersect_ct_x - 1 and z != intersect_ct_z - 1:
trn_faces.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1, (z+1)*intersect_ct_x+x+1, (z+1)*intersect_ct_x+x])
else:
if x != intersect_ct_x - 1:
if angval & terrain_area_flags:
if angulation_tbl1[x+1][z] & terrain_area_flags:
trn_edges.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1])
else:
if z != intersect_ct_z - 1:
if not angulation_tbl1[x][z+1] & terrain_area_flags:
trn_edges.append([z*intersect_ct_x+x, (z+1)*intersect_ct_x+x])
flags = (angval & 0xF000) >> 12
faces = []
if imin_blender:
new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_wall')
new_mesh.from_pydata(vertices, edges, faces)
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_wall', new_mesh)
# make collection
if srtname + '_col' not in bpy.data.collections:
new_collection = bpy.data.collections.new(srtname + '_col')
bpy.context.scene.collection.children.link(new_collection)
# add object to scene collection
new_collection.objects.link(new_object)
new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_terrain')
new_mesh.from_pydata(trn_verts, trn_edges, trn_faces)
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_terrain', new_mesh)
# add object to scene collection
new_collection.objects.link(new_object)
##DODO: add materials to assign colors to show the collision's type.
00 THE WHITE STORM (UPPER)
01 THE AERIAL GARDEN (NIGHT UPPER)
02 THE DANGER ZONE 2
03 THE WHITE STORM (LOWER)
04 -THE GREAT OPERA (BURNING UPPER)
05 -THE DEATH VALLEY (UPPER)
06 -EDITOR STAGE
07 -UNDULATION CHECK
08 THE CRIMSON (UPPER)
09 THE DANGER ZONE
0A THE GREAT OPERA (UPPER)
0B THE GREAT OPERA (LOWER)
0C THE DEMON'S CHURCH (UPPER)
0D THE DEMON'S CHURCH (LOWER)
0E +AMOEBA
0F THE AERIAL GARDEN (DAY UPPER)
10 THE AERIAL GARDEN (DAY LOWER)
11 +ELEVATOR
12 THE MIYAMA
13 THE KOKU AN
14 -THE SPIRAL
15 THE DRAGON HILLS (UPPER)
16 -THE MIYAMA (DUMMY)
17 +BASS POSTER
18 THE BIO LAB
19 +DESSERT OF DEATH
1A THE AERIAL GARDEN (NIGHT LOWER)
1B -THE MIYAMA (WINTER)
1C +GRAND CANYON
1D +TINA'S FASHION SHOW
1E THE DRAGON HILLS (LOWER)
1F THE PRAIRIE
20 +TRAILER TRUCK
21 THE CRIMSON (LOWER)
22 -THE AERIAL GARDEN (EVENING UPPER)
23 -THE AERIAL GARDEN (EVENING LOWER)
24 -THE D OCTAGON
25 -THE PANCRATIUM
26 -THE IRON HELL (EVENING)
27 -THE BLANCA
28 -THE L'S CASTLE
29 THE BURAI ZEN-IN (UPPER)
2A -THE BURAI ZEN-IN (LOWER)
2B +BAMBOO THICHET
2C -THE IRON HELL (NIGHT)
+1 {- tina show} (DJ stage is the last one for DoA2:HC)
2E- +CHOPPING BEER BOTTLE
2F- -WALL TEST {+montane flower}
30- -LORELEI {THE YOZAKURA}
31- -DOATEC HK (UPPER) {THE ISLAND}
32- -LOST WORLD (UPPER) {THE SHRINE}
33- -ICE CAVE {THE RAY HOUSE}
34- -BEACH (EVENING) {THE SAFARI}
35- -AZUCHI (COURTYARD) {THE GREAT WALL}
36- -FOREST {THE AQUARIUM}
37- -TAG : IRON HELL {THE DOWNTOWN}
38- -DOATEC HK (LOWER) {THE CYCLOTRON}
39- -TAG : DANGER ZONE {THE SUSPENSION BRIDGE}
3A- -TAG : PANCRATIUM {Effect Check1}
3B- -AZUCHI (UPPER) {Effect Check2}
3C- -LOST WORLD (LOWER) {EARTH}
3D- -TAG : X OCTAGON {WALL_TEST2}
3E- -LORELEI (INDOOR) {WALL_TEST3}
3F- -AZUCHI (LOWER) {THE MIYAMA(SPRING)}
40- -TEST2 {THE MIYAMA(AUTUMN)}
41- -BEACH (DAY) {THE MIYAMA(WINTER)}
42- -TAG : AQUA PALACE {/}
43- -GENRA'S RAID {/}
#-------------------------------------------------------------------------------
# unpack dead or alive 2 hardcore - ROM_00.BIN
#-------------------------------------------------------------------------------
SLPS_250_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\SLPS_250.26"
ROM_00_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\ROM_00.BIN"
out_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\ROM_00"
import os, struct
filetable_offset = 0x0030C6D0
ram_offset = 0x0050B6D0 - filetable_offset
file_count = 1722
all_out_paths = []
def main():
with open(SLPS_250_path, 'rb') as f:
f.seek(filetable_offset)
fnames_offsets = struct.unpack('<%dL'%(file_count*2), f.read(file_count*8))
foffsets_sizes = struct.unpack('<%dL'%(file_count*2), f.read(file_count*8))
fnames = []
with open(ROM_00_path, 'rb') as fr:
for i in range(file_count):
f.seek(fnames_offsets[i*2+1] - ram_offset)
bname = list(f.read(9))
while bname[-1]:
bname.append(f.read(1)[0])
name = bytearray(bname[6:-1]).decode('ASCII')
save_path = os.path.join(out_path,name)
save_dirs = os.path.dirname(save_path)
if save_dirs not in all_out_paths:
all_out_paths.append(save_dirs)
os.makedirs(save_dirs)
foffset = foffsets_sizes[i*2]
fsize = foffsets_sizes[i*2+1]
with open(save_path, 'wb') as fs:
fr.seek(foffset)
fs.write(fr.read(fsize))
print('finished')
if __name__ == '__main__':
main()
What is a '+AMOEBA'?Yeah, is there a way to convert the geometry of this stage to xpr? Could happen that this is a playable stage...
Added to the script attributes to load stages that have angulation data but it's not loaded in the game. These would be:
WA AM MO AL COL OL SA OL2 DJ KM EFC EF2 WL3 MISP
Just some placeholder angulation data, and the stage "14 MO - <THE SPIRAL> "Python:#------------------------------------------------------------------------------- # import collision data from doau stages(only original names) to blender (script) [ver.2] #------------------------------------------------------------------------------- stg_path = r"E:\--\@stages\doauJPstages\sAG1S_dat.bin" stg_path = r"E:\--\@stages\1\doaOstages\sOH2_dat.bin" stg_dir = r"E:\--\@stages\doauJPstages" parse_dir = 0 imin_blender = 1 terrain_as_edges = 0 terrain_area_flags = 0b1000 #(default:0b1111) {non_shoreline?,water?,solid_ground?,playable_area?} 0b0000 0b0001 0b0100 0b0101 0b0110 0b0111 0b1000 0b1001 0b1100 0b1111 //bc fc (gl1-gl2 has no ground?) kl2 lc wl2 (gl1 has flag1) ## recreate the lost angulation data: 5_WA-350, E_AM-266, 14_MO-594, 24_AL-266, 25_COL-342, 26_OL-266, 27_SA-326, 2C_OL2-266, 2E_DJ-560, 2F_KM-560, 3A_EFC-532, 3B_EF2-532, 3E_WL3-606 ################################################################################ ##use stg_path='path/to/stage_folder/sSTAGE_dat.bin' and parse_dir=0, to parse a single stage ##use stg_dir='path/to/stages_folder/' and parse_dir=1, to parse all stages in the folder ##imin_blender = 0 for testing the script outside blender / imin_blender = 1 for importing data in blender ##terrain_area_flags - set the flags for terrain area types; all null is non playable area, others are unknown, like the snow and water have their combination of flags ##note: I used in this script only the most common for all stages, embedded collision paramameters and layers of data ##limitations: only two types of terrain can be visualized at the same time, no properties of the walls are displayed; maybe convert edges to grease_pencil and use colorcoding? ##??? ## created comlementary data to load WA,AM,MO,AL,COL,OL,SA,OL2,DJ,KM,EFC,EF2,WL3 ## TF and WL3 both have collision data but it's deprecated inside the game ## MISP is processed with all miyamas but has a bin of a test stage(edges count and angulation count is different) ## GL1_1,OH_3,WL2_5,OH2_3,GL1Z_0 are empty? ## TE2 is doa3 wall data type? ################################################################################ import struct,os terrain_area_flags = terrain_area_flags << 12 if parse_dir:stages_bin = [os.path.join(stg_dir, stg_name) for stg_name in os.listdir(stg_dir)] else: stages_bin = [stg_path] if imin_blender: import bpy testlist = [] embedded_angs = { 0x00:[(0.25,1024.0,-1.719,19.0,26.25,159,172,90),(0.25,512.0,-5.7249999,7.25,22.25,93,99,0)], 0x01:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)], 0x0F:[(0.25,1024.0,0.0,-0.25,24.25,187,175,122),(0.25,1024.0,-0.41100001,22.0,15.25,222,221,251)], 0x03:[(0.25,1024.0,-0.102,25.25,28.5,177,200,103)], 0x07:[(0.25,1024.0,0.0,10.25,10.25,83,83,124)], 0x08:[(0.25,1024.0,-0.19400001,17.5,14.25,137,140,132),(0.25,1024.0,0.0,16.5,1.75,136,141,73),(0.25,1024.0,-0.75,16.5,1.75,136,141,69)], 0x09:[(0.25,1024.0,-0.090999998,16.25,16.25,131,131,24)], 0x0A:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)], 0x0B:[(0.30000001,1024.0,0.0,21.0,25.500002,140,149,51),(0.5,1024.0,-2.825,17.0,3.5,68,50,23),(0.5,1024.0,-3.7,25.0,7.5,101,84,25),(0.5,1024.0,-3.7,25.0,25.5,101,120,0)], 0x0C:[(0.25,1024.0,-0.206,15.5,19.25,124,129,96)], 0x0D:[(0.25,1024.0,-0.414,25.75,40.75,207,269,141)], 0x10:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)], 0x1A:[(0.1,1024.0,6.6929998,12.8,21.1,311,310,110),(0.25,1024.0,-0.338,21.25,18.75,178,156,204),(0.25,256.0,4.0900002,7.75,17.25,65,70,110)], 0x12:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], ##0x1B:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x3F:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x40:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x41:[(0.25,1024.0,-2.77,20.75,21.0,173,193,158)], 0x13:[(0.25,1024.0,0.015,23.5,17.5,221,192,137),(0.25,1024.0,-1.001,15.75,9.75,156,180,99)], 0x15:[(0.5,1024.0,0.0,17.5,16.5,70,67,40),(0.5,1024.0,-12.543,16.5,25.0,69,68,78),(0.375,1024.0,-69.033997,12.375,28.5,62,63,16),(0.25,256.0,-29.403999,28.5,14.75,85,51,8),(0.375,1024.0,-31.601999,47.625,38.25,145,133,145),(0.25,256.0,-29.403999,-7.75,14.75,85,51,8),(0.375,1024.0,-29.478001,-10.5,24.0,88,88,74)], 0x17:[(0.5,1024.0,0.0,50.5,50.5,203,203,16)], 0x18:[(0.25,1024.0,-0.52399999,18.5,34.5,191,205,169)], 0x19:[(0.25,1024.0,-0.52399999,33.5,46.0,280,368,0)], 0x1C:[(0.5,1024.0,-1.765,27.0,22.5,104,93,47)], 0x1E:[(0.25,1024.0,0.62400001,17.5,17.25,140,143,84)], 0x1F:[(0.5,1024.0,-1.765,27.0,22.5,104,93,50),(0.5,1024.0,-7.0430002,37.0,21.0,171,147,76),(0.5,1024.0,-7.152,38.5,49.5,167,145,68),(0.5,512.0,-12.416,3.5,36.0,112,137,135),(0.5,512.0,-5.6170001,32.5,10.0,128,92,27),(0.5,512.0,-11.283,16.0,33.5,106,96,26)], 0x21:[(0.25,1024.0,0.31999999,33.25,12.0,170,149,177),(0.25,1024.0,0.31999999,33.25,12.0,170,149,185)], 0x22:[(0.25,1024.0,-0.38299999,35.5,35.25,261,280,169)], 0x23:[(0.25,1024.0,-0.18799999,16.25,16.75,144,133,79)], 0x28:[(0.5,1024.0,0.048999999,15.5,15.5,63,63,28)], 0x29:[(0.175,1024.0,-0.001,25.375,24.15,290,254,162)], 0x2A:[(0.25,1024.0,-1.6,24.0,26.5,203,171,113)], 0x30:[(0.25,1024.0,-0.001,19.5,20.0,177,226,135)], 0x31:[(0.44999999,1024.0,-0.22,66.150002,54.0,302,212,101)], 0x32:[(0.25,1024.0,15.05,17.0,-12.75,133,151,120),(0.25,128.0,-0.491,7.75,2.5,63,130,18),(0.25,1024.0,-0.741,17.5,20.0,141,146,84)], 0x33:[(0.1,1024.0,0.0,19.4,19.4,389,389,44)], 0x34:[(0.25,819.20001,-0.15899999,25.25,24.0,209,204,67),(0.25,512.0,-3.678,25.25,4.5,103,66,66),(0.5,1024.0,-4.152,48.5,38.5,133,136,129),(0.5,1024.0,-4.1999998,18.0,38.5,148,136,124)], 0x35:[(0.25,1024.0,0.0,16.75,16.75,133,137,75),(0.25,256.0,-19.9,10.75,-5.25,78,114,114),(0.25,1024.0,-21.048,25.0,-14.5,157,149,97),(0.25,256.0,-33.764999,16.75,-28.0,122,136,112),(0.25,1024.0,-33.799999,16.5,-34.25,187,184,71),(0.25,256.0,-34.618,39.0,-25.5,121,93,114),(0.25,512.0,-37.717999,58.5,-7.0,182,300,144),(0.25,204.8,-24.667999,14.25,44.75,96,161,113),(0.25,1024.0,-24.664,37.5,78.25,208,223,119)], 0x36:[(0.25,1024.0,0.0,20.25,20.25,163,163,65)], 0x37:[(0.25,1024.0,-0.18799999,20.25,20.25,163,163,19)], 0x38:[(0.5,1024.0,-0.99900001,19.5,19.5,79,79,32)], 0x39:[(0.5,1024.0,-0.67799997,23.5,10.5,95,43,42),(0.5,512.0,-20.073,18.5,10.0,76,82,74),(0.5,512.0,-26.941999,17.5,17.0,71,55,58),(0.5,341.33334,-30.17,19.5,43.5,78,99,84),(0.75,256.0,-30.17,19.5,43.5,53,98,208)], 0x3D:[(0.25,1024.0,0.0,16.25,17.5,131,141,19),(0.25,256.0,-4.0,20.25,12.25,71,99,12),(0.25,1024.0,-4.0,44.25,37.75,179,303,8),(0.25,256.0,-8.0,12.25,-2.75,99,98,12),(0.25,1024.0,-8.0,20.25,-9.5,163,162,8),(0.25,256.0,-4.0,-3.75,12.25,53,99,0),(0.25,1024.0,-14.066,6.25,37.75,207,303,11)], 0x05:[[0.25, 1024, 0, 0, 0, 175, 175, 52]], #these are made up, they have angulation but the game doesn't use it and do not have complementary data for them. 0x14:[[0.25, 1024, 0, 0, 0, 142, 297, 98]], 0x25:[[0.25, 1024, 0, 0, 0, 171, 171, 128]], 0x27:[[0.25, 1024, 0, 0, 0, 163, 163, 82]], 0x26:[[0.25, 1024, 0, 0, 0, 153, 133, 54]], 0x2C:[[0.25, 1024, 0, 0, 0, 153, 133, 54]], 0x2E:[[0.25, 1024, 0, 0, 0, 261, 280, 169]], 0x2F:[[0.25, 1024, 0, 0, 0, 261, 280, 169]], 0x3E:[[0.25, 1024, 0, 0, 0, 163, 303, 11]], 0x1B:[[0.25, 1024, 0, 0, 0, 163, 303, 11]], 0x3A:[[0.25, 1024, 0, 0, 0, 144, 133, 79]], 0x3B:[[0.25, 1024, 0, 0, 0, 144, 133, 79]], 0x24:[[0.25, 1024, 0, 0, 0, 144, 133, 79]], 0x0E:[[0.25, 1024, 0, 0, 0, 144, 133, 79]]} stg_short_names = ["GL1","AG1N","DS2","GL2","X04","WA","TST","WAL","KL1","DS1","OH","OH2","SN","SN2","AM","AG1D","AG2","EL","MI","NK","MO","PO","X22","BP","BL","GL1Z","AG2N","MISP","GC","TF","PO2","PR","TR","KL2","AG1S","AG2S","AL","COL","OL","SA","KK","TE","TE2","HT","OL2","TF2","DJ","KM","NC","BC","FC","LT","SV","LC","SEA","DT","SP","SB","EFC","EF2","EAR","WL2","WL3","MISM","MIAU","MIWI","X66","X67","X68","X69","X70"] ##add materials to the scene, for the floor will be the four bits 1111, first theree i'll make rgb and the fourth will be for darkness, and for the walls i'll need only color tones? for stg_path in stages_bin: ## print('hey', os.path.isfile(stg_path) , '.bin' in stg_path) if os.path.isfile(stg_path) and '.bin' in stg_path: with open(stg_path,'rb') as f: #try to detect the offsets that contain angulation data head_size = struct.unpack('<L', f.read(4))[0] if head_size in [0x14242, 0xc0e29548, 0x4b4e4c, 0xd0027c0, 0x168]: #doa2: HT/TR are unknown data; doa3: LNK data, TST/TS2 are 4b long; doao sSV_mot.bin continue colobjects = [] is_single_object = False if head_size in (0x10, 0x18): f.seek(head_size) dprobe = struct.unpack('<L', f.read(4))[0] if dprobe not in (0x10, 0x18, 0x6c, 0x3, 0x10310) or dprobe == 0xf000f000: #this bin is a single object colobjects.append(0) is_single_object = True head_size = 0 if not is_single_object: f.seek(0) for i in range(head_size//4): f.seek(i*4) boffset = struct.unpack('<L', f.read(4))[0] f.seek(boffset) dprobe = struct.unpack('<L', f.read(4))[0] if dprobe in (0x10, 0x18): colobjects.append(boffset) srtname = os.path.basename(stg_path)[1:-8] print(srtname) if srtname == 'WA': colobjects.append(8) ## if not srtname in ["WA","AM","MO","AL","COL","OL","SA","OL2","DJ","KM","EFC","EF2","WL3","MISP"]: # DELETE THIS NOW!!!!!!!!!!!!!!!!!!!!!!!!!! ## continue if srtname in stg_short_names: stgidx = stg_short_names.index(srtname) else: print(srtname, 'IS NOT RECOGNIZED') continue if stgidx not in embedded_angs: print(srtname, 'HAS NO COMPLEMENTARY ANGULATION DATA', colobjects) continue colobjects = colobjects[:len(embedded_angs[stgidx])] #removing the falsedetected collision blocks ## print(srtname, len(colobjects), len(embedded_angs[stgidx])) #go throug all collision objects and import the data for k in range(len(colobjects)): coloffset = colobjects[k] stage_grid_size,angulation_scale,angulation_mod, stage_edge_max_x,stage_edge_max_z, intersect_ct_x,intersect_ct_z, boundary_edge_ct = embedded_angs[stgidx][k] stage_edge_min_x = stage_edge_max_x - stage_grid_size * (intersect_ct_x - 1) stage_edge_min_z = stage_edge_max_z - stage_grid_size * (intersect_ct_z - 1) f.seek(coloffset) angulation_offset, boundry_vertex_offset, boundry_edge_offset, boundry_edge_end_offset = struct.unpack('<LLLL', f.read(0x10)) boundry_edge_count = (boundry_edge_end_offset - boundry_edge_offset) // 8 f.seek(boundry_edge_offset + coloffset) edges_with_attribs = [struct.unpack('<HHHH', f.read(8)) for i in range(boundry_edge_count)] edges = [e[:2] for e in edges_with_attribs] boundry_vertex_count = (boundry_edge_offset - boundry_vertex_offset) // 12 f.seek(boundry_vertex_offset + coloffset) vertices = [struct.unpack('<3f', f.read(12)) for i in range(boundry_vertex_count)] #terrain angulation_count = (boundry_vertex_offset - angulation_offset) // 2 if angulation_count > intersect_ct_x * intersect_ct_z: angulation_count = intersect_ct_x * intersect_ct_z f.seek(angulation_offset + coloffset) ## angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_x, f.read(intersect_ct_x*2)) for i in range(intersect_ct_z)] angulation_tbl1 = [struct.unpack('<%dH'%intersect_ct_z, f.read(intersect_ct_z*2)) for i in range(intersect_ct_x)] trn_verts = [] trn_edges = [] trn_faces = [] for z in range(intersect_ct_z): for x in range(intersect_ct_x): angval = angulation_tbl1[x][z] ## trn_verts.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod + 1, z*stage_grid_size+stage_edge_min_z]) #is +1 for intersection? trn_verts.append([x*stage_grid_size+stage_edge_min_x, float(angval & 0xFFF) / angulation_scale + angulation_mod, z*stage_grid_size+stage_edge_min_z]) if not terrain_as_edges: if x != intersect_ct_x - 1 and z != intersect_ct_z - 1: trn_faces.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1, (z+1)*intersect_ct_x+x+1, (z+1)*intersect_ct_x+x]) else: if x != intersect_ct_x - 1: if angval & terrain_area_flags: if angulation_tbl1[x+1][z] & terrain_area_flags: trn_edges.append([z*intersect_ct_x+x, z*intersect_ct_x+x+1]) else: if z != intersect_ct_z - 1: if not angulation_tbl1[x][z+1] & terrain_area_flags: trn_edges.append([z*intersect_ct_x+x, (z+1)*intersect_ct_x+x]) flags = (angval & 0xF000) >> 12 faces = [] if imin_blender: new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_wall') new_mesh.from_pydata(vertices, edges, faces) new_mesh.update() # make object from mesh new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_wall', new_mesh) # make collection if srtname + '_col' not in bpy.data.collections: new_collection = bpy.data.collections.new(srtname + '_col') bpy.context.scene.collection.children.link(new_collection) # add object to scene collection new_collection.objects.link(new_object) new_mesh = bpy.data.meshes.new(srtname + "_%X_"%stgidx + str(k) +'_terrain') new_mesh.from_pydata(trn_verts, trn_edges, trn_faces) new_mesh.update() # make object from mesh new_object = bpy.data.objects.new(srtname + "_%X_"%stgidx + str(k) + '_terrain', new_mesh) # add object to scene collection new_collection.objects.link(new_object) ##DODO: add materials to assign colors to show the collision's type.
has angulation data for THE SPIRAL.
I made a script for unpacking files from doa2:hc japan to see how many stages it has, it's 45 stages, in hex that would be 0x2D, both doa3 and doa2u are keeping this list intact and add their stages that do not fit in that list after 0x2D.Code:00 THE WHITE STORM (UPPER) 01 THE AERIAL GARDEN (NIGHT UPPER) 02 THE DANGER ZONE 2 03 THE WHITE STORM (LOWER) 04 -THE GREAT OPERA (BURNING UPPER) 05 -THE DEATH VALLEY (UPPER) 06 -EDITOR STAGE 07 -UNDULATION CHECK 08 THE CRIMSON (UPPER) 09 THE DANGER ZONE 0A THE GREAT OPERA (UPPER) 0B THE GREAT OPERA (LOWER) 0C THE DEMON'S CHURCH (UPPER) 0D THE DEMON'S CHURCH (LOWER) 0E +AMOEBA 0F THE AERIAL GARDEN (DAY UPPER) 10 THE AERIAL GARDEN (DAY LOWER) 11 +ELEVATOR 12 THE MIYAMA 13 THE KOKU AN 14 -THE SPIRAL 15 THE DRAGON HILLS (UPPER) 16 -THE MIYAMA (DUMMY) 17 +BASS POSTER 18 THE BIO LAB 19 +DESSERT OF DEATH 1A THE AERIAL GARDEN (NIGHT LOWER) 1B -THE MIYAMA (WINTER) 1C +GRAND CANYON 1D +TINA'S FASHION SHOW 1E THE DRAGON HILLS (LOWER) 1F THE PRAIRIE 20 +TRAILER TRUCK 21 THE CRIMSON (LOWER) 22 -THE AERIAL GARDEN (EVENING UPPER) 23 -THE AERIAL GARDEN (EVENING LOWER) 24 -THE D OCTAGON 25 -THE PANCRATIUM 26 -THE IRON HELL (EVENING) 27 -THE BLANCA 28 -THE L'S CASTLE 29 THE BURAI ZEN-IN (UPPER) 2A -THE BURAI ZEN-IN (LOWER) 2B +BAMBOO THICHET 2C -THE IRON HELL (NIGHT) +1 {- tina show} (DJ stage is the last one for DoA2:HC) 2E- +CHOPPING BEER BOTTLE 2F- -WALL TEST {+montane flower} 30- -LORELEI {THE YOZAKURA} 31- -DOATEC HK (UPPER) {THE ISLAND} 32- -LOST WORLD (UPPER) {THE SHRINE} 33- -ICE CAVE {THE RAY HOUSE} 34- -BEACH (EVENING) {THE SAFARI} 35- -AZUCHI (COURTYARD) {THE GREAT WALL} 36- -FOREST {THE AQUARIUM} 37- -TAG : IRON HELL {THE DOWNTOWN} 38- -DOATEC HK (LOWER) {THE CYCLOTRON} 39- -TAG : DANGER ZONE {THE SUSPENSION BRIDGE} 3A- -TAG : PANCRATIUM {Effect Check1} 3B- -AZUCHI (UPPER) {Effect Check2} 3C- -LOST WORLD (LOWER) {EARTH} 3D- -TAG : X OCTAGON {WALL_TEST2} 3E- -LORELEI (INDOOR) {WALL_TEST3} 3F- -AZUCHI (LOWER) {THE MIYAMA(SPRING)} 40- -TEST2 {THE MIYAMA(AUTUMN)} 41- -BEACH (DAY) {THE MIYAMA(WINTER)} 42- -TAG : AQUA PALACE {/} 43- -GENRA'S RAID {/}
this script will extract the file table from inside the executable file.
Python:#------------------------------------------------------------------------------- # unpack dead or alive 2 hardcore - ROM_00.BIN #------------------------------------------------------------------------------- SLPS_250_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\SLPS_250.26" ROM_00_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\ROM_00.BIN" out_path = r"E:\--\doa2hcJP\DOA2 - Hardcore (Japan) (En,Ja,Fr,De,Es,It)\ROM_00" import os, struct filetable_offset = 0x0030C6D0 ram_offset = 0x0050B6D0 - filetable_offset file_count = 1722 all_out_paths = [] def main(): with open(SLPS_250_path, 'rb') as f: f.seek(filetable_offset) fnames_offsets = struct.unpack('<%dL'%(file_count*2), f.read(file_count*8)) foffsets_sizes = struct.unpack('<%dL'%(file_count*2), f.read(file_count*8)) fnames = [] with open(ROM_00_path, 'rb') as fr: for i in range(file_count): f.seek(fnames_offsets[i*2+1] - ram_offset) bname = list(f.read(9)) while bname[-1]: bname.append(f.read(1)[0]) name = bytearray(bname[6:-1]).decode('ASCII') save_path = os.path.join(out_path,name) save_dirs = os.path.dirname(save_path) if save_dirs not in all_out_paths: all_out_paths.append(save_dirs) os.makedirs(save_dirs) foffset = foffsets_sizes[i*2] fsize = foffsets_sizes[i*2+1] with open(save_path, 'wb') as fs: fr.seek(foffset) fs.write(fr.read(fsize)) print('finished') if __name__ == '__main__': main()