04-04-2023, 04:55 AM
(06-04-2021, 01:38 PM)DancingTwix Wrote: Howdy yall!
Here's what your after; how to rip models from Sky; Children of The Light.
Please bear in mind this is still a work in progress so not everything works 100% just yet.
Big shout out to users longbyte1 and cobrakyle for cracking the issue.
You'll need:
- Blender 3.2.2 or later.
- (Windows Only) LZ4
Also, nearly all the images in the game are stored as .ktx so you'll need the PVRTextTool linked below. TO export as an .png image go to file >>save Image
https://developer.imaginationtech.com/downloads/
(09-06-2022, 09:53 PM)longbyte1 Wrote:Code:import ctypes
import struct
import io
from ctypes import *
f = open('/tmp/UnitCube.mesh', 'rb')
lz4 = CDLL('liblz4.so.1')
# read uncompressed size
f.seek(0x52)
uncompressed_size = struct.unpack('i', f.read(4))[0]
# read compressed size
f.seek(0x4e)
compressed_size = struct.unpack('i', f.read(4))[0]
# read num lods
f.seek(0x44)
num_lods = struct.unpack('i', f.read(4))[0]
print('compressed_size', compressed_size)
print('uncompressed_size', uncompressed_size)
print('num_lods', num_lods)
# get compressed content
f.seek(0x56)
src = f.read(compressed_size)
# get decompressed content
dest = ctypes.create_string_buffer(uncompressed_size)
ret = lz4.LZ4_decompress_safe(src, dest, compressed_size, uncompressed_size)
if ret <= 0:
raise IOError('error decompressing mesh - file may not be valid')
buf = io.BytesIO(dest.raw)
buf.seek(0x74)
shared_vertex_count = struct.unpack('i', buf.read1(4))[0]
buf.seek(0x78)
total_vertex_count = struct.unpack('i', buf.read1(4))[0]
buf.seek(0x80)
point_count = struct.unpack('i', buf.read1(4))[0]
buf.seek(0x74)
uv_count = struct.unpack('i', buf.read1(4))[0]
print('shared_vertex_count', shared_vertex_count)
print('total_vertex_count', total_vertex_count)
print('point_count', point_count)
print('uv_count', uv_count)
# build vertex buffer
vertex_buffer = []
vertex_buffer_start = 0xb3
buf.seek(vertex_buffer_start)
for i in range(shared_vertex_count):
# 3 floats
x, y, z = struct.unpack('<fff4x', buf.read(16))
vertex_buffer.append((x, y, z))
# build uv buffer
uv_buffer = []
uv_header_size = uv_count * 4 - 4
buf.read1(uv_header_size) # move caret to uv start
for i in range(uv_count):
# 2 half-precision floats
u, v = struct.unpack('<4xee8x', buf.read(16))
uv_buffer.append((u, v))
# build index buffer
index_buffer = []
face_count = total_vertex_count // 3
buf.read1(4) # advance 4 bytes of padding
for i in range(face_count):
# 3 shorts (each indexing into the vertex buffer)
v1, v2, v3 = struct.unpack('<HHH', buf.read(6))
index_buffer.append((v1, v2, v3))
f.close()
buf.close()
print(vertex_buffer)
print(index_buffer)
# build geometry from buffers
vertices = []
edges = []
faces = []
for face in index_buffer:
v1i, v2i, v3i = face
edges += [(v1i, v2i), (v2i, v3i), (v3i, v1i)]
faces.append(face)
import bpy
mesh = bpy.data.meshes.new('created_mesh')
mesh.from_pydata(vertex_buffer, edges, faces)
mesh.update()
uvl = mesh.uv_layers.new()
uvl.data.foreach_set('uv', [uv for pair in [uv_buffer[l.vertex_index] for l in mesh.loops] for uv in pair])
mesh.uv_layers.active = uvl
obj = bpy.data.objects.new('created_object', mesh)
collection = bpy.data.collections.new('created_collection')
bpy.context.scene.collection.children.link(collection)
collection.objects.link(obj)
Just change the open() line to load the file you want. Note that this is only going to read the first LOD it finds, which should be the highest quality one.
For Windows users, you need to change the path in the CDLL() line to a path to a 64-bit LZ4 DLL (or 32-bit, depending on whether your Blender is 64- or 32-bit). You can find a working copy of LZ4 for Windows here, and you're looking for a DLL called either "msys-lz4-1.dll" or "liblz4.dll".
How to do start:
Open Blender and navigate to the Scripting tab along the top. On the right hand side a new window will appear, click the '+ New' button and paste in the code from the above quote.
You can save this file somewhere if you like.
On Windows
Open the LZ4 file you should have downloaded and unzipped/decompressed. In there navigate to the dll folder and make sure the file msys-lz4-1.dll is there.
The two most important lines in this code that we need to worry about are
line 6:
Code:f = open('/tmp/UnitCube.mesh', 'rb')
This line states what mesh we want to extract. You'll find all the meshes available in side the APK (link further down). In this example we'll use the AcestorShrine_06.mesh file. Below I've pointed the code to it's location on my computer, making sure to replace any backslash characters (\) with forward slashes instead (/)
Code:f = open('D:/Android/Test Meshses/AncestorShrine_06.mesh', 'rb')
line 8 (windows):
Code:lz4 = CDLL('liblz4.so.1')
The code uses this file to do it's magic, but sadly Windows wont have this which is why we have to download it from the link above. Oce you've downloaded it and located the msys-lz4-1.dll alter the code like so. Again, making sure any backslash characters (\) are changed to forward slash (/)
Code:lz4 = CDLL('C:/{path to my desktop}/Desktop/lz4_win64_v1_9_4/dll/msys-lz4-1.dll')
All thats left is to run the code! CLick that little play button at the top of the window and BAM! THe code will put the extracted mesh into a new collection in the outliner named 'created_object'.
Issues and drawbacks
As mentioned above, this is still a work in progress so there are still a few issues with files containing the following in their name which is still being investigated.
- StripGeo (strip geometry)
- StripAnim (strip animations)
- UncompAnim (uncompressed animation)
- CompOcc (compute occlusions)
- CompEdges (compute edges)
- CompAdj (compute adjacency)
- ZipPos (packed positions?)
- ZipUvs (packed UVs?)
- StripUv13 (?)
- StripNorm (strip normals)
- ForceIdx32 (use 32-bit indexes instead of 16-bit indexes)
We're also facing the following issues,
- Missing textures on all meshes -> Still being investigated but looks to be the textures are stored elsewhere that we just havent found yet.
- (will be added as found)
Original post and some links to various resources you might need or want.
I've been trying to rip the models from the mobile game Sky; Children of the light by That Game Company, for about a week or so now with moderate success having grabbed the textures, sprites, and sound, from the .apk
APK here
I'm now stuck trying to get hold of the actual models.
The current theory is that the models are stored in these .bin files as there is one for each level in the game which the code refers too along with .fbx files. There is a full version of the code snippet in the resources.lua files
Code:-- THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY THIS FILE!
-- Level objects
resource "LevelObjects" "DawnCave" { level = "DawnCave", source = "Levels/DawnCave/Objects.level", sourcesCount = 16, sources = { "Levels/DawnCave/Objects.level", "Levels/DawnCave/AP8_Intro.level", "Levels/DawnCave/AP8_Outro.level", "Levels/DawnCave/Art_Foreground.level", "Levels/DawnCave/Audio.level", "Levels/DawnCave/Candles.level", "Levels/DawnCave/MemoryCutscenesA.level", "Levels/DawnCave/MemoryCutscenesB.level", "Levels/DawnCave/NPC_Party.level", "Levels/DawnCave/Quest1.level", "Levels/DawnCave/Quest2.level", "Levels/DawnCave/Quest3.level", "Levels/DawnCave/Quest4.level", "Levels/DawnCave/Scripting.level", "Levels/DawnCave/TrailElementalChanges.level", "Levels/DawnCave/TrialLogic.level" }, stripDebug = true }
-- Prefabs
-- Meshes
resource "Mesh" "CharSkyNPC_Prop_Rock" { source = "CharSkyNPC_Prop_Rock.fbx", computeOcclusions = false, computeEdges = false, computeAdjacency = false, compressPositions = false, compressUvs = false, stripUv13 = false, stripNormals = false, forceIndex32 = false, registerCollision = true, stripAnimation = false }
resource "Mesh" "Cube" { source = "Cube.fbx", computeOcclusions = false, computeEdges = false, computeAdjacency = false, compressPositions = false, compressUvs = false, stripUv13 = false, stripNormals = false, forceIndex32 = false, registerCollision = true, stripAnimation = false }
resource "Mesh" "DawnCaveMask_01" { source = "DawnCaveMask_01.fbx", computeOcclusions = false, computeEdges = false, computeAdjacency = false, compressPositions = false, compressUvs = false, stripUv13 = false, stripNormals = false, forceIndex32 = false, registerCollision = true, stripAnimation = false }
I've tried using tools like winrar, poweriso, and a few others but all of them say the .bin files are in the wrong format. Same for when I rename the files to remove the '.level' bit.
Additionally, I'm able to access .mesh files but I have no idea how to open them. I think they're openGL but so far no luck in converting, even tried using Ogre3D.
The minimum requirements of Sky are (copied from the wiki)
- Android 8.1 Oreo or higher
- ARM 64bit with Neon (32 bit devices will never run Sky)
- At least 1.5GB RAM
- OpenGLES 3.1 Extension Pack
- Vulkan Version 1.0.3 or higher
- Vulkan Level 0 or higher
- 3GB storage to download and install all areas
From what I can tell the app itself is built using
- Maya - The environment artist posted some shots of the in game models
- Lua - a number of files are .lua, like the resources file included
- Android Studio - it's an android game
Any advice is greatly appreciated
I have included a selection of files but if you need or want any more just give us a shout!
Included are:
AncestorShrine_06.mesh
BstBaked.meshes
Objects.level.bin
resources.lua
thank you. I learned a lot from your tutorial. love you