Markemp / Cryengine-Converter

A c# program to convert Crytek files to Collada (XML) format
https://www.heffaypresents.com/GitHub/
GNU General Public License v2.0
208 stars 53 forks source link

Warface support. #140

Open nathan130200 opened 1 year ago

nathan130200 commented 1 year ago

Is your feature request related to a problem? Please describe. Its a feture request.

Describe the solution you'd like Add support for Warface that uses DBA and CAF files.

Additional context Tried to drag and drop as cga (renamed to cga to test if works) but didn't worked.

Version 829 of ChunkController is not supported

   at CgfConverter.CryEngineCore.Chunk.New[T](UInt32 version)
   at CgfConverter.CryEngineCore.Chunk.New(ChunkType chunkType, UInt32 version)
   at CgfConverter.CryEngineCore.Model.ReadChunks(BinaryReader reader)
   at CgfConverter.CryEngineCore.Model.Load(String fileName)
   at CgfConverter.CryEngine.ProcessCryengineFiles()
   at CgfConverterConsole.Program.Main(String[] args)

Sample files for CAF and DBA

sample_files.zip

Additional notes I can actually conver just models (cgf/skin) and textures, but not streamed animations with CAF/DBA.

EDIT: I've identified that chunk controller 829 is compressed animations (my guess, based on CE5 source code)

nathan130200 commented 1 year ago

Searching for information about chunk controller 0829 i've found this code part inside game executable (translated to pseudo code using IDA), also this is the only thing i was able to found about CONTROLLER_CHUNK_DESC_0829.

CONTROLLER_CHUNK_DESC_0829 struct type info (maybe used for pseudo reflection) - `defVal` is pointing to a null value (in exactly, is pointing to a global variable `const char[3]` then i guess is a "nulled" value, because IDA don't give me a exactly the type) - value `536870913` seems to be a "invalid" tag - Only if `dword_141C19F98` is not defined then 0829 chunk controller set to disabled (but in some point an external function set it to non zero) - The `TypeInfo` looks like a pseudo reflection implemented in C++ like in Unreal Engine reflection system `Cast` etc. I guess this struct type: ```cpp // all fields aligned? struct CONTROLLER_CHUNK_DESC_0829 { struct CHUNK_HEADER_0744 chdr; uint32 nControllerId; uint16 numRotationKeys; uint16 numPositionKeys; uint8 rotationFormat; uint8 rotationTimeFormat; uint8 positionFormat; uint8 positionKeysInfo; uint8 positionTimeFormat; }; struct CHUNK_HEADER_0744 { int32 chunkVersion; int32 fileOffset; int32 chunkId; }; ```
CONTROLLER_CHUNK_DESC_0829 ```cpp CStructInfo *__fastcall CONTROLLER_CHUNK_DESC_0829::TypeInfo(CONTROLLER_CHUNK_DESC_0829 *this) { CONTROLLER_CHUNK_DESC_0829 *v1; // rdi@1 _DWORD *v2; // rbx@1 CStructInfo *result; // rax@3 v1 = this; v2 = (*(*MK_FP(__GS__, 88i64) + 8i64 * tls_index) + 16i64); if ( dword_141C1A130 > *v2 ) { Init_thread_header(&dword_141C1A130); if ( dword_141C1A130 == -1 ) { if ( dword_141C19F98 > *v2 ) { Init_thread_header(&dword_141C19F98); if ( dword_141C19F98 == -1 ) { CStructInfo::CStructInfo(&Info_77, defVal, 1ui64, 0i64, 0i64); atexit(TypeInfo_BASE_CONTROLLER_CHUNK__::_2_::_dynamic_atexit_destructor_for__Info__); Init_thread_footer(&dword_141C19F98); } } Vars_51[0].Type = &Info_77; *(Vars_51 + 4) = 536870913; Vars_51[0].Name = defVal; Vars_51[0].Attrs = defVal; Vars_51[1].Offset = 0; Vars_51[1].Type = TypeInfo(&v1->chdr); *(&Vars_51[1] + 4) = 1; Vars_51[1].Name = defVal; Vars_51[1].Attrs = defVal; Vars_51[2].Offset = 16; Vars_51[2].Type = TypeInfo(&v1->nControllerId); *(&Vars_51[2] + 4) = 1; Vars_51[2].Name = defVal; Vars_51[2].Attrs = defVal; Vars_51[3].Offset = 20; Vars_51[3].Type = TypeInfo(&v1->numRotationKeys); *(&Vars_51[3] + 4) = 1; Vars_51[3].Name = defVal; Vars_51[3].Attrs = defVal; Vars_51[4].Offset = 22; Vars_51[4].Type = TypeInfo(&v1->numPositionKeys); *(&Vars_51[4] + 4) = 1; Vars_51[4].Name = defVal; Vars_51[4].Attrs = defVal; Vars_51[5].Offset = 24; Vars_51[5].Type = TypeInfo(&v1->RotationFormat); *(&Vars_51[5] + 4) = 1; Vars_51[5].Name = defVal; Vars_51[5].Attrs = defVal; Vars_51[6].Offset = 25; Vars_51[6].Type = TypeInfo(&v1->RotationTimeFormat); *(&Vars_51[6] + 4) = 1; Vars_51[6].Name = defVal; Vars_51[6].Attrs = defVal; Vars_51[7].Offset = 26; Vars_51[7].Type = TypeInfo(&v1->PositionFormat); *(&Vars_51[7] + 4) = 1; Vars_51[7].Name = defVal; Vars_51[7].Attrs = defVal; Vars_51[8].Offset = 27; Vars_51[8].Type = TypeInfo(&v1->PositionKeysInfo); *(&Vars_51[8] + 4) = 1; Vars_51[8].Name = defVal; Vars_51[8].Attrs = defVal; Vars_51[9].Offset = 28; Vars_51[9].Type = TypeInfo(&v1->PositionTimeFormat); *(&Vars_51[9] + 4) = 1; Vars_51[9].Name = defVal; Vars_51[9].Attrs = defVal; Init_thread_footer(&dword_141C1A130); } } if ( dword_141C1A1C8 <= *v2 || (Init_thread_header(&dword_141C1A1C8), dword_141C1A1C8 != -1) ) { result = &Info_78; } else { CStructInfo::CStructInfo(&Info_78, defVal, 0x20ui64, 0xAui64, Vars_51); atexit(CONTROLLER_CHUNK_DESC_0829::TypeInfo_::_2_::_dynamic_atexit_destructor_for__Info__); Init_thread_footer(&dword_141C1A1C8); result = &Info_78; } return result; } ```
CHUNK_HEADER_0744 ```cpp CStructInfo *__fastcall CHUNK_HEADER_0744::TypeInfo(CHUNK_HEADER_0744 *this) { CHUNK_HEADER_0744 *v1; // rdi@1 _DWORD *v2; // rbx@1 CStructInfo *result; // rax@3 v1 = this; v2 = (*(*MK_FP(__GS__, 88i64) + 8i64 * tls_index) + 16i64); if ( dword_141C16C50 > *v2 ) { Init_thread_header(&dword_141C16C50); if ( dword_141C16C50 == -1 ) { if ( dword_141C16BAC > *v2 ) { Init_thread_header(&dword_141C16BAC); if ( dword_141C16BAC == -1 ) { Info_17.Name = defVal; Info_17.vfptr = &CTypeInfo::`vftable'; Info_17.Size = 4i64; atexit(TypeInfo_enum_ChunkTypes__::_2_::_dynamic_atexit_destructor_for__Info__); Init_thread_footer(&dword_141C16BAC); } } dword_141C16BC0 = 1; qword_141C16BB8 = &Info_17; qword_141C16BC8 = defVal; qword_141C16BD0 = defVal; dword_141C16BD8 = 4; qword_141C16BE0 = TypeInfo(&v1->ChunkVersion); dword_141C16BE8 = 1; qword_141C16BF0 = defVal; qword_141C16BF8 = defVal; dword_141C16C00 = 8; qword_141C16C08 = TypeInfo(&v1->FileOffset); dword_141C16C10 = 1; qword_141C16C18 = defVal; qword_141C16C20 = defVal; dword_141C16C28 = 12; qword_141C16C30 = TypeInfo(&v1->ChunkID); dword_141C16C38 = 1; qword_141C16C40 = defVal; qword_141C16C48 = defVal; Init_thread_footer(&dword_141C16C50); } } if ( dword_141C16CE8 <= *v2 || (Init_thread_header(&dword_141C16CE8), dword_141C16CE8 != -1) ) { result = &Info_40; } else { CStructInfo::CStructInfo(&Info_40, defVal, 0x10ui64, 4ui64, Vars_14); atexit(CHUNK_HEADER_0744::TypeInfo_::_2_::_dynamic_atexit_destructor_for__Info__); Init_thread_footer(&dword_141C16CE8); result = &Info_40; } return result; } ```

I will keep search for CAF and DBA files parsing.

Markemp commented 3 months ago

The 0x0829 controller chunk is supported now, but I can't test against these files. The game files are tough to extract. :(

nathan130200 commented 3 months ago

Thanks alot! Yeah, warface files uses some different methods to decrypt files, i will test and give feeback :D

nathan130200 commented 1 month ago

I was able to test, i'm having an different exception now.

cgf-converter.exe "%cd%\combat_idleAimPoses_mech_01.caf" -objectdir "D:\WarfaceBackup\BR-1.18000.944.21500\Game_Ext"
Using up to 4 threads
Source [Filesystem]: D:\WarfaceBackup\BR-1.18000.944.21500\Game_Ext
Found input: D:\WarfaceBackup\br-1.18000.944.21500\game_ext\animations\boss\mech\combat\combat_idleaimposes_mech_01.caf
[INF:Program] [1/1 0.00%] D:\WarfaceBackup\br-1.18000.944.21500\game_ext\animations\boss\mech\combat\combat_idleaimposes_mech_01.caf
[INF:combat_idleaimposes_mech_01.caf] Found following potential material files.  If you are not specifying a material file and the materials don't look right, trying one of the following files:
[ERR:Program] Failed to render: D:\WarfaceBackup\br-1.18000.944.21500\game_ext\animations\boss\mech\combat\combat_idleaimposes_mech_01.caf
  AggregateException: One or more errors occurred. (Sequence contains no elements)
    InvalidOperationException: Sequence contains no elements
       at System.Linq.ThrowHelper.ThrowNoElementsException()
       at System.Linq.Enumerable.MinMaxInteger[T,TMinMax](IEnumerable`1 source)
       at System.Linq.Enumerable.Max[TSource](IEnumerable`1 source, IComparer`1 comparer)
       at CgfConverter.CryEngine.CreateMaterials()
       at CgfConverter.CryEngine.ProcessCryengineFiles()
       at CgfConverter.Program.ExportSingleModel(String inputFile)
       at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
    --- End of stack trace from previous location ---
       at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
[INF:Program] Finished. Rendered 0 file(s)
[ERR:Program] Failed to convert 1 file(s).

I really don't know how to convert these CAF/DBA files :(

If you want i can provide the extracted paks with anim/obj files.