├── .gitattributes ├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Converters ├── CustomFormat │ ├── CustomFormatConverter.cpp │ ├── CustomFormatConverter.h │ ├── TerrainDatConvert.cpp │ ├── TerrainDatConvert.h │ ├── VegetationDumpConvert.cpp │ └── VegetationDumpConvert.h └── XML │ ├── Converter.cpp │ ├── Converter.h │ ├── EntArchetypeObtainer.cpp │ ├── EntArchetypeObtainer.h │ ├── MissionConvert.cpp │ ├── MissionConvert.h │ ├── MovieDataConvert.cpp │ ├── MovieDataConvert.h │ ├── TerrainLayerInfoConvert.cpp │ └── TerrainLayerInfoConvert.h ├── LICENSE ├── README.md ├── Utils ├── CLCHeaders.h ├── CryVersionDetect.cpp ├── CryVersionDetect.h ├── Util.cpp └── Util.h ├── VersionDBFinal.clcdb ├── _config.yml ├── git └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: push 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | build_path: "" 9 | 10 | jobs: 11 | build: 12 | # The CMake configure and build commands are platform agnostic and should work equally 13 | # well on Windows or Mac. You can convert this to a matrix build if you need 14 | # cross-platform coverage. 15 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [windows-latest, ubuntu-latest, macos-latest] 21 | include: 22 | - os: ubuntu-latest 23 | - os: macos-latest 24 | - os: windows-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | with: 29 | submodules: 'recursive' 30 | 31 | 32 | - name: Recursive submodules update 33 | run: git submodule update --init --recursive 34 | 35 | - name: Create Build Environment 36 | # Some projects don't allow in-source building, so create a separate build directory 37 | # We'll use this as our working directory for all subsequent commands 38 | run: cmake -E make_directory ${{github.workspace}}/build 39 | 40 | - name: Configure CMake 41 | # Use a bash shell so we can use the same syntax for environment variable 42 | # access regardless of the host operating system 43 | shell: bash 44 | working-directory: ${{github.workspace}}/build 45 | # Note the current convention is to use the -S and -B options here to specify source 46 | # and build directories, but this is only available with CMake 3.13 and higher. 47 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 48 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 49 | 50 | - name: Configure CMake again.. 51 | shell: bash 52 | working-directory: ${{github.workspace}}/build 53 | # Required for correct static zlib setup... 54 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 55 | 56 | - name: Build 57 | working-directory: ${{github.workspace}}/build 58 | shell: bash 59 | # Execute the build. You can specify a specific target with "--target " 60 | run: cmake --build . --config $BUILD_TYPE 61 | 62 | - uses: actions/upload-artifact@v2 63 | if: runner.os == 'Windows' 64 | with: 65 | name: CLC-${{ matrix.os }} 66 | path: ${{github.workspace}}/build/Release/CryLevelConvert.exe 67 | 68 | - uses: actions/upload-artifact@v2 69 | if: runner.os != 'Windows' 70 | with: 71 | name: CLC-${{ matrix.os }} 72 | path: ${{github.workspace}}/build/CryLevelConvert 73 | 74 | release: 75 | if: github.ref == 'refs/heads/master' 76 | name: "Release" 77 | needs: build 78 | runs-on: ubuntu-latest 79 | steps: 80 | - uses: actions/download-artifact@v2 81 | - name: Display fetched artifacts 82 | run: ls -R 83 | - name: Automatic GitHub Release 84 | uses: "marvinpinto/action-automatic-releases@latest" 85 | with: 86 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 87 | automatic_release_tag: "latest" 88 | title: "CLC Build" 89 | files: | 90 | CLC-windows-latest 91 | CLC-ubuntu-latest 92 | CLC-macos-latest 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | 342 | #Cryengine layer files 343 | *.lyr 344 | .lyr 345 | 346 | 347 | EntityArchetypes 348 | EntityArchetypes/ 349 | cmake-build-release 350 | cmake-build-debug 351 | build -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdParty/pugixml"] 2 | path = 3rdParty/pugixml 3 | url = https://github.com/zeux/pugixml 4 | [submodule "3rdParty/zipper"] 5 | path = 3rdParty/zipper 6 | url = https://github.com/sebastiandev/zipper 7 | branch = develop 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19.2) 2 | project(CryLevelConvert) 3 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | SET(CMAKE_CXX_FLAGS -pthread) 7 | 8 | include_directories(3rdParty/pugixml/src) 9 | include_directories(Converters) 10 | include_directories(Converters/CustomFormat) 11 | include_directories(Converters/XML) 12 | include_directories(Utils) 13 | include_directories(3rdParty/zipper) 14 | 15 | add_executable(CryLevelConvert 16 | 3rdParty/pugixml/src/pugiconfig.hpp 17 | 3rdParty/pugixml/src/pugixml.cpp 18 | 3rdParty/pugixml/src/pugixml.hpp 19 | Converters/CustomFormat/CustomFormatConverter.cpp 20 | Converters/CustomFormat/CustomFormatConverter.h 21 | Converters/CustomFormat/VegetationDumpConvert.cpp 22 | Converters/CustomFormat/VegetationDumpConvert.h 23 | Converters/XML/MissionConvert.cpp 24 | Converters/XML/MissionConvert.h 25 | Converters/XML/MovieDataConvert.cpp 26 | Converters/XML/MovieDataConvert.h 27 | Converters/XML/TerrainLayerInfoConvert.cpp 28 | Converters/XML/TerrainLayerInfoConvert.h 29 | Converters/XML/Converter.cpp 30 | Converters/XML/Converter.h 31 | Converters/XML/EntArchetypeObtainer.cpp 32 | Converters/XML/EntArchetypeObtainer.h 33 | Utils/CLCHeaders.h 34 | Utils/CryVersionDetect.cpp 35 | Utils/CryVersionDetect.h 36 | Utils/Util.cpp 37 | Utils/Util.h 38 | main.cpp Converters/CustomFormat/TerrainDatConvert.cpp Converters/CustomFormat/TerrainDatConvert.h) 39 | 40 | option(USE_PAK_INTERACTION "Activate support of .pak files." ON) 41 | 42 | if (USE_PAK_INTERACTION) 43 | add_compile_definitions(USE_PAK_INTERACTION) 44 | option(BUILD_TEST "Build the test program." OFF) 45 | option(BUILD_SHARED_VERSION "Build the shared version of the library." OFF) 46 | add_subdirectory(3rdParty/zipper) 47 | target_link_libraries(CryLevelConvert staticZipper) 48 | set_target_properties(dist check staticZipper Zipper-static PROPERTIES FOLDER ZipperTargets) 49 | endif() 50 | 51 | file(GLOB_RECURSE _source_list *.cpp* *.h* *.hpp*) 52 | foreach(_source IN ITEMS ${_source_list}) 53 | get_filename_component(_source_path "${_source}" PATH) 54 | string(REPLACE "${CMAKE_SOURCE_DIR}" "" _group_path "${_source_path}") 55 | string(REPLACE "/" "\\" _group_path "${_group_path}") 56 | source_group("${_group_path}" FILES "${_source}") 57 | endforeach() 58 | 59 | set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT CryLevelConvert) -------------------------------------------------------------------------------- /Converters/CustomFormat/CustomFormatConverter.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomFormatConverter.h" 2 | 3 | CustomFormatConverter::CustomFormatConverter(std::string_view path_in, std::string_view path_out) 4 | { 5 | fstream_out.open(path_out.data()); 6 | fstream_in.open(path_in.data()); 7 | } 8 | -------------------------------------------------------------------------------- /Converters/CustomFormat/CustomFormatConverter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../Utils/CLCHeaders.h" 3 | class CustomFormatConverter 4 | { 5 | private: 6 | std::ofstream fstream_out; 7 | std::ifstream fstream_in; 8 | std::string buf; 9 | public: 10 | CustomFormatConverter(std::string_view path_in, std::string_view path_out); 11 | virtual bool Convert() = 0; 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /Converters/CustomFormat/TerrainDatConvert.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Admin on 18.05.2021. 3 | // 4 | 5 | #include "TerrainDatConvert.h" 6 | -------------------------------------------------------------------------------- /Converters/CustomFormat/TerrainDatConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CustomFormatConverter.h" 3 | 4 | class TerrainDatConvert : public CustomFormatConverter { 5 | 6 | }; 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Converters/CustomFormat/VegetationDumpConvert.cpp: -------------------------------------------------------------------------------- 1 | #include "VegetationDumpConvert.h" 2 | 3 | std::fstream file_r_dump_veg_read; 4 | std::fstream file_rw_veg_dump; 5 | 6 | struct VegInstance 7 | { 8 | std::string model; 9 | std::string material; 10 | std::string pos; 11 | 12 | 13 | bool operator == (const VegInstance& obj) 14 | { 15 | return (model == obj.model) && (material == obj.material); 16 | } 17 | }; 18 | 19 | 20 | std::vector vec_veg_ents; 21 | std::vector vec_veg_groups; 22 | 23 | void VegetationDumpConvert(const std::string& path_in, const std::string& path_out) 24 | { 25 | std::string buf; 26 | 27 | if (fs::exists(path_in)) 28 | { 29 | file_r_dump_veg_read.open(path_in); 30 | file_rw_veg_dump.open(path_out, std::ofstream::out); 31 | std::getline(file_r_dump_veg_read, buf); //skip id 32 | VegInstance vg; 33 | while (!file_r_dump_veg_read.eof()) 34 | { 35 | std::getline(file_r_dump_veg_read, buf); 36 | vg.model = buf; 37 | std::getline(file_r_dump_veg_read, buf); 38 | vg.material = buf; 39 | std::getline(file_r_dump_veg_read, buf); 40 | vg.pos = buf; 41 | vec_veg_ents.push_back(vg); 42 | } 43 | 44 | for (unsigned int a = 0; a < vec_veg_ents.size(); a++) 45 | { 46 | if (std::find(vec_veg_groups.begin(), vec_veg_groups.end(), vec_veg_ents[a]) == vec_veg_groups.end()) 47 | { 48 | vec_veg_groups.push_back(vec_veg_ents[a]); 49 | } 50 | } 51 | 52 | file_rw_veg_dump << "\n"; 53 | 54 | for (unsigned int a = 0; a < vec_veg_groups.size(); a++) 55 | { 56 | std::string group_name = vec_veg_groups[a].model.substr(0, vec_veg_groups[a].model.size() - 4); 57 | file_rw_veg_dump << R"( \n"; 68 | } 69 | 70 | file_rw_veg_dump << "\n"; 71 | 72 | 73 | file_r_dump_veg_read.close(); 74 | file_rw_veg_dump.close(); 75 | 76 | std::cout << "[VegetationConvert] Converted vegetation dump to .veg file!\n"; 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /Converters/CustomFormat/VegetationDumpConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../../Utils/CLCHeaders.h" 5 | 6 | 7 | /// 8 | /// Warning! This thing is deprecated and should be used with special software which is private for now. Proceed with caution. 9 | /// 10 | void VegetationDumpConvert(const std::string& path_in, const std::string& path_out); -------------------------------------------------------------------------------- /Converters/XML/Converter.cpp: -------------------------------------------------------------------------------- 1 | #include "Converter.h" 2 | 3 | bool XMLConverter::ConvertFromDisk(const std::string& path_in) 4 | { 5 | if (!path_in.empty()) 6 | { 7 | xml_path = path_in; 8 | doc.load_file(path_in.c_str()); 9 | if (doc.load_file(path_in.c_str()).status != pugi::status_ok) 10 | { 11 | return false; 12 | std::cout << "[Converter] Can't reload xml\n"; 13 | } 14 | if (Convert()) 15 | { 16 | return true; 17 | } 18 | else 19 | { 20 | return false; 21 | } 22 | } 23 | else 24 | { 25 | return false; 26 | } 27 | } 28 | 29 | bool XMLConverter::ConvertFromDisk(const std::vector& paths_in) 30 | { 31 | bool success = true; 32 | for (auto& flist : paths_in) 33 | { 34 | if (!flist.empty()) 35 | { 36 | xml_path = flist; 37 | doc.load_file(flist.c_str()); 38 | if (doc.load_file(flist.c_str()).status != pugi::status_ok) 39 | { 40 | return false; 41 | std::cout << "[MissionConvert] Can't reload xml\n"; 42 | } 43 | } 44 | if (!Convert()) 45 | { 46 | success = false; 47 | } 48 | } 49 | return success; 50 | } 51 | 52 | bool XMLConverter::ConvertFromByteArray(std::vector& data) 53 | { 54 | doc.load_buffer_inplace((void*)data.data(), data.size()); 55 | if (Convert()) 56 | { 57 | return true; 58 | } 59 | else 60 | { 61 | return false; 62 | } 63 | } 64 | 65 | XMLConverter::XMLConverter(const std::string& path_in) 66 | { 67 | xml_path = path_in; 68 | res = doc.load_file(xml_path.c_str()); 69 | if (res.status != pugi::status_ok) 70 | { 71 | std::cout << "[Coverter] Can't properly parse XML!\n"; 72 | } 73 | } 74 | 75 | XMLConverter::XMLConverter() 76 | { 77 | 78 | } 79 | -------------------------------------------------------------------------------- /Converters/XML/Converter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CLCHeaders.h" 3 | #include 4 | #include 5 | 6 | /// 7 | ///Base class for all XML converters. 8 | /// 9 | class XMLConverter 10 | { 11 | protected: 12 | std::string class_name; 13 | pugi::xml_document doc; 14 | pugi::xml_parse_result res; 15 | std::string xml_path; 16 | inline virtual bool Convert() = 0; 17 | public: 18 | virtual bool ConvertFromDisk(const std::string& path_in); 19 | virtual bool ConvertFromDisk(const std::vector& paths_in); 20 | virtual bool ConvertFromByteArray(std::vector& data); 21 | XMLConverter(const std::string& path_in); 22 | XMLConverter(); 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /Converters/XML/EntArchetypeObtainer.cpp: -------------------------------------------------------------------------------- 1 | #include "EntArchetypeObtainer.h" 2 | 3 | EntArchetypeObtainer::EntArchetypeObtainer() 4 | { 5 | 6 | } 7 | 8 | int EntArchetypeObtainer::FillEntArcList() 9 | { 10 | if (!fs::exists(cry_fname::out::ENTARCCACHE)) //Not write cache if one already exists. 11 | { 12 | if (fs::exists(cry_fname::in::ENTARCFOLDER)) //How would you collect info about ent. archs without libs?? 13 | { 14 | m_ent_arc_cache_stream.open(cry_fname::out::ENTARCCACHE, std::fstream::out); 15 | 16 | for (auto& x : fs::recursive_directory_iterator(cry_fname::in::ENTARCFOLDER)) 17 | { 18 | if (doc.load_file(x.path().c_str())) 19 | { 20 | std::string lib_name = doc.child("EntityPrototypeLibrary").attribute("Name").value(); 21 | for (auto& n : doc.select_nodes("EntityPrototypeLibrary/EntityPrototype")) 22 | { 23 | if (strcmp(n.node().name(), "EntityPrototype") == 0) 24 | { 25 | m_ent_arc_cache_stream << lib_name << "." << n.node().attribute("Name").value() << "\n"; 26 | m_ent_arc_cache_stream << n.node().attribute("Id").value() << "\n"; 27 | m_ent_arc_map.emplace(lib_name + "." + n.node().attribute("Name").value(), n.node().attribute("Id").value()); 28 | } 29 | } 30 | } 31 | else 32 | { 33 | std::cout << "[EntArcObtain] Warning! Unable to parse " << x.path() << "\n"; 34 | } 35 | } 36 | std::cout << "[EntArcObtain] Got info about " << m_ent_arc_map.size() << " entity archetypes from EntityArchetype folder\n"; 37 | } 38 | else 39 | { 40 | std::cout << "[EntArcObtain] Warning! Unable to find EntityArchetype folder. This may cause missing entites!\n"; 41 | } 42 | } 43 | else 44 | { 45 | uint16_t cntr = 0; 46 | std::string buf,buf2; 47 | m_ent_arc_cache_stream.clear(); 48 | m_ent_arc_cache_stream.close(); 49 | m_ent_arc_cache_stream.open(cry_fname::out::ENTARCCACHE, std::fstream::in); 50 | while (!m_ent_arc_cache_stream.eof()) 51 | { 52 | if (cntr == 0) 53 | { 54 | std::getline(m_ent_arc_cache_stream, buf); 55 | cntr++; 56 | } 57 | else if (cntr == 1) 58 | { 59 | std::getline(m_ent_arc_cache_stream, buf2); 60 | m_ent_arc_map.emplace(buf,buf2); 61 | cntr = 0; 62 | } 63 | } 64 | std::cout << "[EntArcObtain] Got info about " << m_ent_arc_map.size() << " entity archetypes from cache\n"; 65 | } 66 | 67 | m_have_ent_data = true; 68 | return OK; 69 | } 70 | 71 | std::unordered_map& EntArchetypeObtainer::GetEntArcMap() 72 | { 73 | return m_ent_arc_map; 74 | } 75 | 76 | bool EntArchetypeObtainer::IsHavingData() 77 | { 78 | return m_have_ent_data; 79 | } 80 | 81 | //m_ent_arc_cache_stream << std::string_view(n.node().attribute("Id").value()).substr(1, 36) << "\n"; //.substr(1,36) removes first and last char of GUID (braces). Size of GUID is constant (38)! 82 | //m_ent_arc_map.emplace(n.node().attribute("Name").value(), std::string_view(n.node().attribute("Id").value()).substr(1, 36)); -------------------------------------------------------------------------------- /Converters/XML/EntArchetypeObtainer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CLCHeaders.h" 3 | #include 4 | #include 5 | 6 | enum ENT_ARC_CACHE_RES {OK, CACHE_EXISTS, WRITE_ERROR, READ_ERROR}; 7 | 8 | /// 9 | ///Entity archetype resolver is required for retriving all entity archetype id's from provided XML libraries (Libs folder) 10 | /// 11 | class EntArchetypeObtainer 12 | { 13 | private: 14 | std::unordered_map m_ent_arc_map; 15 | pugi::xml_document doc; 16 | std::fstream m_ent_arc_cache_stream; 17 | bool m_have_ent_data = false; 18 | public: 19 | EntArchetypeObtainer(); 20 | int FillEntArcList(); 21 | std::unordered_map& GetEntArcMap(); 22 | bool IsHavingData(); 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /Converters/XML/MissionConvert.cpp: -------------------------------------------------------------------------------- 1 | #include "MissionConvert.h" 2 | 3 | EntArchetypeObtainer MissionConvert::entarc = EntArchetypeObtainer();//Init archetype obtainer 4 | 5 | std::map map_ent = //Some entites have different classes in editor and in game! 6 | { 7 | { "EnvironmentLight","EnvironmentProbe" }, 8 | { "AreaShape","Shape" }, 9 | { "AITerritory", "Shape" }, 10 | { "LightShape", "Shape" }, 11 | { "RopeEntity", "Rope" } 12 | }; 13 | 14 | 15 | bool MissionConvert::Convert() 16 | { 17 | for (auto& fg_node : doc.select_node("/Mission/Objects/Entity/FlowGraph/Nodes").node().children()) //collect info about entity guids 18 | { 19 | if (strcmp(fg_node.attribute("EntityGUID").value(), "") != 0 || strcmp(fg_node.attribute("EntityGUID_64").value(), "") != 0) 20 | { 21 | m_map_guid_64.emplace(fg_node.attribute("EntityGUID").value(), fg_node.attribute("EntityGUID_64").value()); 22 | } 23 | } 24 | for (pugi::xml_node mission_node = doc.select_node("/Mission/Objects/Entity").node(); mission_node; mission_node = mission_node.next_sibling()) //for every entity 25 | { 26 | if (!mission_node.attribute("Type").empty()) 27 | { 28 | for (auto& x : map_ent) 29 | { 30 | if (strcmp(mission_node.attribute("EntityClass").value(), x.first.c_str()) == 0) 31 | { 32 | mission_node.attribute("Type").set_value(x.second.c_str()); 33 | } 34 | } 35 | } 36 | if (!mission_node.attribute("Layer").empty()) //collect info about layers of entities 37 | { 38 | m_layer_names.push_back(mission_node.attribute("Layer").value()); 39 | } 40 | if (!mission_node.attribute("EntityGuid").empty()) //in .lyr files EntityGuid is Id 41 | { 42 | pugi::xml_attribute attr = mission_node.attribute("EntityGuid"); 43 | attr.set_name("Id"); 44 | } 45 | if (strcmp(mission_node.name(), "Entity") == 0) 46 | { 47 | m_map_id_parent.emplace(mission_node.attribute("Id").value(), mission_node.attribute("EntityId").value()); 48 | mission_node.set_name("Object"); 49 | mission_node.append_attribute("Type").set_value("Entity"); 50 | if (mission_node.child("Area")) //Areas have additional data in .lyr 51 | { 52 | mission_node.append_copy(mission_node.child("Area").child("Points")); 53 | mission_node.append_copy(mission_node.child("Area").child("Entities")); 54 | mission_node.append_attribute("AreaId").set_value(mission_node.child("Area").attribute("Area Id").value()); 55 | mission_node.append_attribute("GroupId").set_value(mission_node.child("Area").attribute("Group").value()); 56 | mission_node.append_attribute("Priority").set_value(mission_node.child("Area").attribute("Priority").value()); 57 | mission_node.append_attribute("Height").set_value(mission_node.child("Area").attribute("Height").value()); 58 | mission_node.remove_child("Area"); 59 | 60 | } 61 | } 62 | if (!mission_node.attribute("Archetype").empty()) //replace archetype type (pun intended) with one from database 63 | { 64 | for (auto& arc_ent : entarc.GetEntArcMap()) 65 | { 66 | if (strcmp(arc_ent.first.c_str(), mission_node.attribute("Archetype").value()) == 0) 67 | { 68 | mission_node.attribute("Archetype").set_value(arc_ent.second.c_str()); 69 | break; 70 | } 71 | } 72 | } 73 | for (auto& val : map_ent) //some entities have different type name in game and in editor 74 | { 75 | if (mission_node.attribute("EntityClass").as_string() == val.first) 76 | { 77 | mission_node.attribute("EntityClass").set_value(val.second.c_str()); 78 | break; 79 | } 80 | } 81 | } 82 | //Next two loops are restoring links between entities based on data we collected in previous big loop 83 | for (auto& link : doc.select_nodes("/Mission/Objects/Object/EntityLinks/Link")) 84 | { 85 | for (auto& x : m_map_id_parent) 86 | { 87 | if (strcmp(link.node().attribute("TargetId").value(), x.second.c_str()) == 0) 88 | { 89 | link.node().attribute("TargetId").set_value(Util::GenerateGUIDFrom64(x.first, true).c_str()); 90 | break; 91 | } 92 | } 93 | } 94 | for (auto& ent : doc.select_nodes("/Mission/Objects/Object/Entities/Entity")) 95 | { 96 | for (auto& x : m_map_id_parent) 97 | { 98 | if (strcmp(ent.node().attribute("Id").value(), x.second.c_str()) == 0) 99 | { 100 | ent.node().attribute("Id").set_value(Util::GenerateGUIDFrom64(x.first, true).c_str()); 101 | break; 102 | } 103 | } 104 | } 105 | bool valid_val = false; 106 | for (pugi::xml_node mission_node = doc.select_node("/Mission/Objects/Object").node(); mission_node; mission_node = mission_node.next_sibling()) 107 | { 108 | for (auto& id_64 : m_map_guid_64) 109 | { 110 | if (strcmp(mission_node.attribute("Id").value(), id_64.second.c_str()) == 0) 111 | { 112 | mission_node.attribute("Id").set_value(id_64.first.c_str()); 113 | valid_val = true; 114 | break; 115 | } 116 | } 117 | if (!valid_val && !mission_node.attribute("Id").empty()) 118 | { 119 | mission_node.attribute("Id").set_value(Util::GenerateGUIDFrom64(mission_node.attribute("Id").value(), true).c_str()); //Editor requires GUID in it's special format. If it is not converted - editor will throw errors about duplicate objects. 120 | } 121 | valid_val = false; 122 | } 123 | 124 | //delete layer duplicates 125 | std::sort(m_layer_names.begin(), m_layer_names.end()); 126 | m_layer_names.erase(std::unique(m_layer_names.begin(), m_layer_names.end()), m_layer_names.end()); 127 | for (auto& x : m_layer_names) 128 | { 129 | pugi::xml_document dc = Util::CreateLayerFile(x); 130 | for (auto& a : doc.select_nodes("/Mission/Objects/Object")) 131 | { 132 | if (strcmp(a.node().attribute("Layer").value(), x.c_str()) == 0) 133 | { 134 | dc.select_node("ObjectLayer/Layer/LayerObjects").node().append_copy(a.node()); 135 | } 136 | } 137 | dc.save_file(std::string(Util::PathWithoutFilename(xml_path) + x + ".lyr").c_str(), " "); //We need to save .lyr files in folder of level in case of converting multiple levels in a row. 138 | } 139 | return true; 140 | } 141 | 142 | void MissionConvert::ExtractTOD() 143 | { 144 | tod_stream_out.close(); 145 | tod_stream_out.clear(); 146 | tod_stream_out.open(cry_fname::out::TOD, std::ofstream::out); 147 | 148 | if (!tod_stream_out.is_open()) 149 | { 150 | std::cout << "[TODExtract] Can't write TOD file to disk!\n"; 151 | } 152 | else 153 | { 154 | doc.select_node("/Mission/TimeOfDay").node().print(tod_stream_out); 155 | } 156 | tod_stream_out.close(); 157 | } 158 | 159 | MissionConvert::MissionConvert() 160 | { 161 | } 162 | -------------------------------------------------------------------------------- /Converters/XML/MissionConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Converter.h" 3 | #include "EntArchetypeObtainer.h" 4 | #include 5 | #include 6 | 7 | /// 8 | ///XML Converter classInput : mission_mission0.xml file from level.pak Output : .lyr files containing scripts/dynamic entities info 9 | /// 10 | class MissionConvert : public XMLConverter 11 | { 12 | private: 13 | static EntArchetypeObtainer entarc; //Database is shared across different instances of MissionConvert. 14 | std::vector m_layer_names; //if ent. contains lyr name - write to its own file! 15 | std::map m_map_guid_64; 16 | std::map m_map_id_parent; 17 | std::fstream tod_stream_out; 18 | inline bool Convert() override; 19 | std::mutex entarc_lock; 20 | public: 21 | void ExtractTOD(); 22 | static EntArchetypeObtainer& GetEntArcObtainter() { return entarc; }; 23 | MissionConvert(); 24 | using XMLConverter::XMLConverter; //Deriving constructor 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /Converters/XML/MovieDataConvert.cpp: -------------------------------------------------------------------------------- 1 | #include "MovieDataConvert.h" 2 | 3 | inline bool MovieDataConvert::Convert() 4 | { 5 | pugi::xml_document dc = Util::CreateLayerFile("CLC_MovieData"); 6 | for (pugi::xml_node mission_node = doc.select_node("/MovieData/Mission/SequenceData/Sequence").node(); mission_node; mission_node = mission_node.next_sibling()) 7 | { 8 | auto node = dc.select_node("ObjectLayer/Layer/LayerObjects").node().append_child("Object"); 9 | node.append_attribute("Type").set_value("SequenceObject"); 10 | node.append_attribute("Layer").set_value("CLC_MDATA"); 11 | node.append_attribute("LayerGUID").set_value(mdata_guid.c_str()); 12 | node.append_attribute("Id").set_value(Util::GenerateRandomGUID().c_str()); 13 | node.append_attribute("Name").set_value(mission_node.attribute("Name").as_string()); 14 | dc.select_node("ObjectLayer/Layer/LayerObjects").node().append_copy(mission_node); 15 | } 16 | dc.save_file("CLC_MovieData.lyr", " "); 17 | return true; 18 | } 19 | 20 | MovieDataConvert::MovieDataConvert() 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /Converters/XML/MovieDataConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Converter.h" 3 | /// 4 | ///XML Converter classInput : moviedata.xml file from level.pak Output : .lyr file containing trackview data 5 | /// 6 | class MovieDataConvert : public XMLConverter 7 | { 8 | private: 9 | inline bool Convert() override; 10 | std::string mdata_guid = Util::GenerateRandomGUID(); //we need only one guid for layer, because all data from moviedata is being stored in one layer! 11 | public: 12 | MovieDataConvert(); 13 | using XMLConverter::XMLConverter; //Deriving constructor 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /Converters/XML/TerrainLayerInfoConvert.cpp: -------------------------------------------------------------------------------- 1 | #include "TerrainLayerInfoConvert.h" 2 | 3 | std::regex rx_matname("Name=\"(.*?)\""); 4 | 5 | inline bool TerrainLayerInfoConvert::Convert() 6 | { 7 | terlay_fstream_in.open(xml_path); 8 | terlay_fstream_out.open(cry_fname::out::TERLAYDATA, std::ofstream::out | std::ofstream::binary); 9 | if (terlay_fstream_in.is_open()) 10 | { 11 | size_t before = terlay_fstream_out.tellp(); 12 | int layer_cntr = 0; 13 | std::string buffer; 14 | bool IsSurfDat = false; 15 | std::vector layers_mats; 16 | while (std::getline(terlay_fstream_in, buffer)) 17 | { 18 | if (buffer == " ") 19 | { 20 | IsSurfDat = true; 21 | break; 22 | } 23 | } 24 | if (IsSurfDat) 25 | { 26 | terlay_fstream_out << " \n"; 27 | terlay_fstream_out << " \n"; 28 | while (std::getline(terlay_fstream_in, buffer)) 29 | { 30 | if (buffer != " ") 31 | { 32 | std::regex_search(buffer, matname_match, rx_matname); 33 | layers_mats.push_back(matname_match.str().substr(6, matname_match.str().size() - 1)); 34 | terlay_fstream_out << buffer << "\n"; 35 | } 36 | else 37 | { 38 | break; 39 | } 40 | } 41 | terlay_fstream_out << " \n"; 42 | terlay_fstream_out << " \n"; 43 | for (auto& x : layers_mats) 44 | { 45 | layer_cntr++; 46 | terlay_fstream_out << " )" << "\n"; 47 | } 48 | terlay_fstream_out << " \n"; 49 | terlay_fstream_out << "\n"; 50 | 51 | //binary end 52 | for (int a = 0; a < 4; a++) 53 | { 54 | terlay_fstream_out.write(reinterpret_cast(&zero), sizeof(zero)); 55 | } 56 | 57 | size = terlay_fstream_out.tellp().operator-(before).operator-(std::streamoff(7)); 58 | terlay_fstream_out.seekp(std::ios_base::beg); 59 | terlay_fstream_out.write(reinterpret_cast(&holder), sizeof(holder)); 60 | terlay_fstream_out.write(reinterpret_cast(&size), sizeof(size)); 61 | 62 | std::cout << "[TerTexLayInfo] Exported terrain layers info to .lay!\n"; 63 | return true; 64 | } 65 | else 66 | { 67 | std::cout << "[TerTexLayInfo] Leveldata.xml doesn't have any information about terrain texture layers!\n"; 68 | return false; 69 | } 70 | } 71 | 72 | } 73 | 74 | TerrainLayerInfoConvert::TerrainLayerInfoConvert() 75 | { 76 | } 77 | -------------------------------------------------------------------------------- /Converters/XML/TerrainLayerInfoConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Converter.h" 3 | #include 4 | /// 5 | /// XML data converter class Input : levelinfo.xml from level Output : .lay file containing data about terrain layers. REFACTOR IN FUTURE! 6 | /// 7 | class TerrainLayerInfoConvert : public XMLConverter 8 | { 9 | private: 10 | uint16_t size; 11 | uint8_t zero = 0; 12 | uint8_t holder = 255; 13 | std::smatch matname_match; 14 | std::ifstream terlay_fstream_in; 15 | std::ofstream terlay_fstream_out; 16 | inline bool Convert() override; 17 | //bool Convert() override; 18 | public: 19 | TerrainLayerInfoConvert(); 20 | using XMLConverter::XMLConverter; 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Stanislav Potanin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CryLevelConvert 2 | 3 | [![CMake](https://github.com/prophetl33t/CryLevelConvert/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/prophetl33t/CryLevelConvert/actions/workflows/cmake.yml) 4 | 5 | 6 | Tool for conversion of level.pak files from Cryengine 3 game engine to format readable by Cryengine 3 Sandbox 7 | 8 | Download latest stable windows binary at 9 | https://github.com/prophetl33t/CryLevelConvert/releases/download/latest/CryLevelConvert.exe 10 | 11 | ## What is convertible? 12 | mission_mission0.xml -> .lyr files containing level Flow Graph and dynamic entities 13 | mission_mission0.xml -> .tod file containing Time Of Day data 14 | moviedata.xml -> .lyr containing Track View info 15 | leveldata.xml -> .lay containing terrain layers info 16 | 17 | ### How to use this tool? 18 | Just place .xml files in folder of CLC and run it. 19 | Drag-and-drop for level.pak and .xml files is also available. 20 | It is recommended to place EntityArchetypes folder in working directory of CLC for better conversion. 21 | 22 | #### Compiling CLC 23 | 24 | 1)Clone repo and init submodules 25 | ```shell 26 | git clone --recursive https://github.com/prophetl33t/CryLevelConvert.git 27 | git submodule update --init --recursive 28 | ``` 29 | 30 | 2)Create project file for your build system/IDE by using shell or cmake-gui. 31 | Run configure step two times for correct zlib static link params. 32 | ```shell 33 | cmake -S -B 34 | cmake -S -B 35 | ``` 36 | 37 | #### Libraries used: 38 | XML parser: 39 | pugixml https://github.com/zeux/pugixml 40 | Interactions with .pak files: 41 | zipper https://github.com/sebastiandev/zipper 42 | -------------------------------------------------------------------------------- /Utils/CLCHeaders.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "pugixml.hpp" 11 | #include "Util.h" 12 | 13 | namespace fs = std::filesystem; -------------------------------------------------------------------------------- /Utils/CryVersionDetect.cpp: -------------------------------------------------------------------------------- 1 | #include "CryVersionDetect.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifdef USE_PAK_INTERACTION 10 | #include "zipper/unzipper.h" 11 | #endif 12 | 13 | std::vector KNOWN_VERSIONS; 14 | 15 | size_t EngineVersionUtils::FindNthOccurence(std::string_view str, char ch, int num = 0) 16 | { 17 | size_t occ = 0; 18 | for (int a = 0; a < str.size(); a++) 19 | { 20 | if (str[a] == ch) 21 | { 22 | occ++; 23 | if (occ == num) 24 | { 25 | return a; 26 | } 27 | } 28 | } 29 | } 30 | 31 | EngineVersionInfo& EngineVersionUtils::FindInfoByVersion(std::string& ver) 32 | { 33 | for (auto& x : KNOWN_VERSIONS) 34 | { 35 | if (x.engine_version == ver) 36 | { 37 | return x; 38 | } 39 | } 40 | } 41 | 42 | void EngineVersionUtils::SetDefaultEngineVersionDB() 43 | { 44 | KNOWN_VERSIONS.emplace_back(CE3, "Generic internal version", "Unknown", "1.1.1.1", "Generic version for commercial editions of CE", true); 45 | } 46 | 47 | void EngineVersionUtils::LoadEngineVersionDB(std::string_view path) 48 | { 49 | pugi::xml_document doc; 50 | doc.load_file(path.data()); 51 | auto z = doc.child("DB"); 52 | for (auto& a : z.children()) 53 | { 54 | for (auto& x : a.children()) 55 | { 56 | KNOWN_VERSIONS.emplace_back(EngineGeneration(a.attribute("Gen").as_int()), a.attribute("Name").value(), x.attribute("Version").value(), x.attribute("Desc").value(), x.attribute("LevelName").value(), x.attribute("Internal").as_bool()); 57 | } 58 | } 59 | int x = 0; 60 | } 61 | 62 | void EngineVersionUtils::CreateEngineVersionDB(const char* path) 63 | { 64 | #ifdef USE_PAK_INTERACTION 65 | std::map vers; 66 | pugi::xml_document doc_write; 67 | std::string work_dir = std::filesystem::current_path().string() + "\\"; 68 | for (auto x : fs::recursive_directory_iterator(path)) 69 | { 70 | if (std::string fname = x.path().filename().string(); fname.find("level.pak") != std::string::npos) 71 | { 72 | pugi::xml_document doc_read; 73 | zipper::Unzipper pak(x.path().generic_string()); 74 | std::string xml_path = x.path().parent_path().string(); 75 | pak.extractEntry("leveldata.xml", xml_path); 76 | doc_read.load_file((xml_path + std::string("\\leveldata.xml")).c_str()); 77 | vers.emplace(doc_read.select_node("/LevelData").node().attribute("SandboxVersion").value(), doc_read.select_node("/LevelData/LevelInfo").node().attribute("Name").value()); 78 | } 79 | } 80 | for (auto& x : vers) 81 | { 82 | auto child = doc_write.append_child("VerData"); 83 | child.append_attribute("Version").set_value(x.first.c_str()); 84 | child.append_attribute("LevelName").set_value(x.second.c_str()); 85 | child.append_attribute("Desc").set_value("FarCry"); 86 | child.append_attribute("Internal").set_value(1); 87 | doc_write.save_file((work_dir + "VersionDB.clcdb").c_str()); 88 | } 89 | int x = 0; 90 | #endif 91 | } 92 | 93 | void EngineVersionUtils::CreateVersionInfoFromString(std::string_view str) 94 | { 95 | std::string sor = str.data(); 96 | size_t prev_point = 0; 97 | int point_cnt = 0; 98 | std::array arr; 99 | arr[0] = sor.substr(0, FindNthOccurence(sor, '.', 1)); 100 | arr[1] = sor.substr(FindNthOccurence(sor, '.', 1), FindNthOccurence(sor, '.', 2)); 101 | arr[2] = sor.substr(FindNthOccurence(sor, '.', 2), FindNthOccurence(sor, '.', 3)); 102 | arr[2] = sor.substr(FindNthOccurence(sor, '.', 2)); 103 | int x = 0; 104 | } 105 | 106 | EngineGeneration EngineVersionUtils::GetEngineGenFromMission(pugi::xml_document mission_xml) 107 | { 108 | if (mission_xml.attribute("MusicScript").empty() && mission_xml.attribute("Script").empty()); 109 | { 110 | return CE1; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Utils/CryVersionDetect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "pugixml.hpp" 12 | #include "CLCHeaders.h" 13 | 14 | namespace fs = std::filesystem; 15 | 16 | enum EngineGeneration { CE1 = 1, CE2, CE3, CE4, CE5 = 5 }; 17 | 18 | struct EngineVersionInfo 19 | { 20 | EngineGeneration engine_generation; //Generation of engine (e.g. CE3) 21 | std::string game_name; //Name of game (e.g. Warface) 22 | std::string engine_version; //Full version of engine in string format 23 | std::string desc; //Additional description 24 | std::string level_name; //Name of level where engine version was used 25 | bool is_crytek_build = false; //Flag is set to true if game is made by crytek or uses internal engine version 26 | 27 | EngineVersionInfo() {}; 28 | EngineVersionInfo(EngineGeneration eng_gen, const char* game_name, const char* eng_version, const char* desc, const char* levelname, bool crytek_build) : 29 | engine_generation(eng_gen), game_name(game_name), engine_version(eng_version), desc(desc), level_name(levelname), is_crytek_build(crytek_build) {}; 30 | 31 | void Print(bool verbose = false) 32 | { 33 | std::cout << "Engine version: " << engine_version << "\n"; 34 | if (verbose) 35 | { 36 | std::cout << "Engine generation: CE" << engine_generation << 37 | "\nGame name: " << game_name << 38 | "\nAdditional info: " << desc << 39 | (is_crytek_build ? "\nVersion of engine used internally by Crytek (for games like Crysis)" : "\nFreeSDK/EAAS/CE5 or other public version"); 40 | } 41 | } 42 | }; 43 | 44 | extern std::vector KNOWN_VERSIONS; 45 | 46 | namespace EngineVersionUtils 47 | { 48 | size_t FindNthOccurence(std::string_view str, char ch, int num); 49 | EngineVersionInfo& FindInfoByVersion(std::string& ver); 50 | void SetDefaultEngineVersionDB(); 51 | void LoadEngineVersionDB(std::string_view path); 52 | void CreateEngineVersionDB(const char* path); 53 | void CreateVersionInfoFromString(std::string_view str); 54 | EngineGeneration GetEngineGenFromMission(pugi::xml_document mission_xml); 55 | }; -------------------------------------------------------------------------------- /Utils/Util.cpp: -------------------------------------------------------------------------------- 1 | #include "Util.h" 2 | #include 3 | #include 4 | 5 | const std::string CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 6 | 7 | std::string Util::GenerateRandomGUID(bool add_brackets) 8 | { 9 | std::mt19937 rng; 10 | rng.seed(std::random_device()()); 11 | std::uniform_int_distribution rand_gen_guid(1, 9999999); 12 | std::string guid = std::string(36, ' '); 13 | int rnd = 0; 14 | int r = 0; 15 | 16 | guid[8] = '-'; 17 | guid[13] = '-'; 18 | guid[18] = '-'; 19 | guid[23] = '-'; 20 | guid[14] = '4'; 21 | 22 | for (int i = 0; i < 36; i++) { 23 | 24 | if (i != 8 && i != 13 && i != 18 && i != 14 && i != 23) { 25 | if (rnd <= 0x02) { 26 | rnd = 0x2000000 + rand_gen_guid(rng) * rand_gen_guid(rng) | 0; 27 | } 28 | rnd >>= 4; 29 | guid[i] = CHARS[(i == 19) ? ((rnd & 0xf) & 0x3) | 0x8 : rnd & 0xf]; 30 | } 31 | } 32 | if (add_brackets) 33 | { 34 | guid.insert(0, "{"); 35 | guid += '}'; 36 | } 37 | return guid; 38 | } 39 | 40 | std::string Util::GenerateGUIDFrom64(const std::string& guid_64, bool add_brackets) 41 | { 42 | return std::string((add_brackets ? "{" : "") + guid_64.substr(8) + "-" + guid_64.substr(4, 4) + "-" + guid_64.substr(0, 4) + "-" + guid_64.substr(0, 4) + "-" + guid_64.substr(8) + guid_64.substr(0, 4) + (add_brackets ? "}" : "")); 43 | } 44 | 45 | pugi::xml_document Util::CreateLayerFile(std::string_view name, std::string_view guid) 46 | { 47 | pugi::xml_document layer; 48 | pugi::xml_node lr_nd = layer.root().append_child("ObjectLayer").append_child("Layer"); 49 | lr_nd.append_attribute("Name").set_value(name.data()); 50 | lr_nd.append_attribute("GUID").set_value(guid.data()); 51 | lr_nd.append_attribute("FullName").set_value(name.data()); 52 | lr_nd.append_attribute("External").set_value(0); 53 | lr_nd.append_attribute("Exportable").set_value(1); 54 | lr_nd.append_attribute("ExportLayerPak").set_value(1); 55 | lr_nd.append_attribute("DefaultLoaded").set_value(0); 56 | lr_nd.append_attribute("HavePhysics").set_value(1); 57 | lr_nd.append_attribute("Expanded").set_value(0); 58 | lr_nd.append_attribute("IsDefaultColor").set_value(1); 59 | lr_nd.append_child("LayerObjects"); 60 | return layer; 61 | } 62 | 63 | Util::DATA_TYPES Util::DetectDataType(const std::string& path) 64 | { 65 | std::ifstream file_r_check_type; 66 | std::string buf_check; 67 | file_r_check_type.open(path); 68 | std::getline(file_r_check_type, buf_check); 69 | if (std::filesystem::is_directory(path)) 70 | { 71 | return Util::DATA_TYPES::DIR; 72 | } 73 | else if (buf_check == "") 74 | { 75 | return Util::DATA_TYPES::MDATA; 76 | } 77 | else if (buf_check.find("Mission Name") != std::string::npos) 78 | { 79 | return Util::DATA_TYPES::MISSION; 80 | } 81 | else if (std::filesystem::path(path).extension() == ".pak" || std::filesystem::path(path).extension() == ".zip") 82 | { 83 | return Util::DATA_TYPES::PAK; 84 | } 85 | else if (buf_check.find("AIPointsDump") != std::string::npos) 86 | { 87 | return Util::DATA_TYPES::AIDUMP; 88 | } 89 | else if (buf_check.find("Terrain") != std::string::npos) 90 | { 91 | return Util::DATA_TYPES::TERRAINDUMP; 92 | } 93 | else if (buf_check.find("VegDump") != std::string::npos) 94 | { 95 | return Util::DATA_TYPES::VEGDUMP; 96 | } 97 | else if (buf_check == "World") 98 | { 99 | return Util::DATA_TYPES::GEOMDUMP; 100 | } 101 | else if (buf_check.find("LevelData") != std::string::npos) 102 | { 103 | return Util::DATA_TYPES::TERLAYDUMP; 104 | } 105 | else 106 | { 107 | return Util::DATA_TYPES::UNKNOWN; 108 | } 109 | } 110 | 111 | std::string Util::PathWithoutFilename(std::string& path) 112 | { 113 | auto a = path.rfind('\\'); 114 | if (path.size() > 3) 115 | { 116 | if (a != std::string::npos) 117 | { 118 | path.erase(a, std::string::npos); 119 | return path + '\\'; 120 | } 121 | else 122 | { 123 | return ""; 124 | } 125 | } 126 | else 127 | { 128 | return ""; 129 | } 130 | } 131 | 132 | void Util::DelayedExit() 133 | { 134 | if (auto_exit != 1) 135 | { 136 | std::cout << "Press ENTER to exit...\n"; 137 | std::cin.get(); 138 | exit(0); 139 | } 140 | } 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /Utils/Util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pugixml.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | const int auto_exit = 0; 8 | 9 | namespace cry_fname 10 | { 11 | namespace in 12 | { 13 | const std::string MISSIONXML = "mission_mission0.xml"; 14 | const std::string AIDUMP = "ai_points_dump.txt"; 15 | const std::string MDATA = "moviedata.xml"; 16 | const std::string GEOMDUMP = "dump_ent.txt"; 17 | const std::string TERRAINDUMP = "dump_terrain.txt"; 18 | const std::string VEGDUMP = "veg_dump.txt"; 19 | const std::string LVLDATA = "leveldata.xml"; 20 | const std::string ENTARCFOLDER = "EntityArchetypes"; 21 | } 22 | namespace out 23 | { 24 | const std::string MISSIONXML = "Layer_Mission.lyr"; 25 | const std::string TOD = "TimeOfDay.tod"; 26 | const std::string AIDUMP = "Layer_AI_Markup.lyr"; 27 | const std::string MDATA = "Layer_MovieData.lyr"; 28 | const std::string GEOMDUMP = "Layer_Geom.lyr"; 29 | const std::string TERRAINDUMP = "terrain.raw"; 30 | const std::string VEGDUMP = "VegetationData.veg"; 31 | const std::string TERLAYDATA = "TerrainLayerTexInfo.lay"; 32 | const std::string ENTARCCACHE = "EntityArchetypeData.clcdb"; 33 | } 34 | const std::string LEVELPAK = "level.pak"; 35 | } 36 | 37 | namespace Util 38 | { 39 | //Original code of GenerateRandomGUID is taken from : https://gist.github.com/fernandomv3/46a6d7656f50ee8d39dc 40 | //Changed random to a better one and replaced "uuid" with "guid" 41 | 42 | std::string GenerateRandomGUID(bool add_brackets = 0); //Generate random GUID 43 | 44 | std::string GenerateGUIDFrom64(const std::string& guid_64, bool add_brackets = 0); //Generate EntityGuid from EntityGUID_64 being used as GUID in flowgraph 45 | 46 | pugi::xml_document CreateLayerFile(std::string_view name, std::string_view guid = Util::GenerateRandomGUID(true)); //Create layer xml document with pre-generated layer structure 47 | 48 | enum class DATA_TYPES { DIR, MDATA, MISSION, PAK, AIDUMP, TERRAINDUMP, VEGDUMP, TERLAYDUMP, GEOMDUMP, UNKNOWN }; 49 | 50 | DATA_TYPES DetectDataType(const std::string& path); //Detects data type of provided file. 51 | 52 | std::string PathWithoutFilename(std::string& path); //Trunc filename in path 53 | 54 | void DelayedExit(); //Replacement for system("pause") 55 | } 56 | -------------------------------------------------------------------------------- /VersionDBFinal.clcdb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /git: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prophetl33t/CryLevelConvert/d8df3bfa8860732a2044e1bd7877cc5b4b1a3622/git -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "CLCHeaders.h" 2 | #include "Converters/XML/MissionConvert.h" 3 | #include "Converters/XML/MovieDataConvert.h" 4 | #include "Converters/CustomFormat/VegetationDumpConvert.h" 5 | #include "XML/EntArchetypeObtainer.h" 6 | #include "Converters/XML/TerrainLayerInfoConvert.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #ifdef USE_PAK_INTERACTION 12 | #include "zipper/unzipper.h" 13 | #endif 14 | 15 | inline void PakConvert(const char* path) 16 | { 17 | #ifdef USE_PAK_INTERACTION 18 | MissionConvert c_m; 19 | MovieDataConvert m_m; 20 | TerrainLayerInfoConvert t_m; 21 | 22 | zipper::Unzipper arch(path); 23 | std::vector data; 24 | bool success = arch.extractEntryToMemory(cry_fname::in::MISSIONXML, data); 25 | if (success) 26 | { 27 | c_m.ConvertFromByteArray(data); 28 | c_m.ExtractTOD(); 29 | } 30 | 31 | success = arch.extractEntryToMemory(cry_fname::in::MDATA, data); 32 | if (success) m_m.ConvertFromByteArray(data); 33 | 34 | success = arch.extractEntryToMemory(cry_fname::in::LVLDATA, data); 35 | if (success) t_m.ConvertFromByteArray(data); 36 | 37 | std::cout << "[PakConvert] " << path << " converted\n"; 38 | #else 39 | std::cout << path << " was not processed because CLC was compiled without USE_PAK_INTERACTION\n"; 40 | #endif 41 | } 42 | 43 | inline void ConvertData(const char* path) 44 | { 45 | MissionConvert mis_convert; 46 | MovieDataConvert mdata_convert; 47 | TerrainLayerInfoConvert terlay_convert; 48 | if (!fs::exists(path)){ 49 | std::cout << path << " doesn't exist.\n"; 50 | return; 51 | } 52 | switch (Util::DetectDataType(path)) 53 | { 54 | case Util::DATA_TYPES::DIR: 55 | ConvertData(path); 56 | break; 57 | case Util::DATA_TYPES::MDATA: 58 | if (mdata_convert.ConvertFromDisk(path)) 59 | { 60 | std::cout << "[MovieData] Converted moviedata to .lyr!\n"; 61 | } 62 | break; 63 | case Util::DATA_TYPES::MISSION: 64 | if (mis_convert.ConvertFromDisk(path)) 65 | { 66 | std::cout << "[MissionConvert] Converted mission to .lyr!\n"; 67 | mis_convert.ExtractTOD(); 68 | } 69 | break; 70 | case Util::DATA_TYPES::PAK: 71 | PakConvert(path); 72 | break; 73 | case Util::DATA_TYPES::AIDUMP: 74 | std::cout << "Use CryDumper for AIDump.\n"; 75 | break; 76 | case Util::DATA_TYPES::TERRAINDUMP: 77 | std::cout << "Use CryDumper for TerrainDump.\n"; 78 | break; 79 | case Util::DATA_TYPES::VEGDUMP: 80 | VegetationDumpConvert(path, std::string(path) + "clc_veg.veg"); 81 | break; 82 | case Util::DATA_TYPES::GEOMDUMP: 83 | std::cout << "Use CryDumper for GeomDump.\n"; 84 | break; 85 | case Util::DATA_TYPES::TERLAYDUMP: 86 | terlay_convert.ConvertFromDisk(path); 87 | break; 88 | case Util::DATA_TYPES::UNKNOWN: 89 | std::cout << "Unknown file format.\n"; 90 | break; 91 | } 92 | } 93 | 94 | int main(int argc, char* argv[]) 95 | { 96 | auto begin = std::chrono::steady_clock::now(); 97 | std::ios_base::sync_with_stdio(false); 98 | 99 | std::cout << "CryLevelConvert V2 by Prophet\n"; 100 | MissionConvert::GetEntArcObtainter().FillEntArcList(); 101 | 102 | if (argc > 1) 103 | { 104 | for (int a = 1; a < argc; a++) 105 | { 106 | std::cout << argv[a] << "\n"; 107 | std::async(std::launch::async, ConvertData, argv[a]); 108 | } 109 | } 110 | else 111 | { 112 | ConvertData(cry_fname::in::MISSIONXML.data()); 113 | ConvertData(cry_fname::in::MDATA.data()); 114 | ConvertData(cry_fname::in::LVLDATA.data()); 115 | VegetationDumpConvert("veg_dump.txt", "clc_veg.veg"); 116 | } 117 | 118 | auto end = std::chrono::steady_clock::now(); 119 | auto elapsed_ms = std::chrono::duration_cast(end - begin); 120 | std::cout << "Elapsed time: " << elapsed_ms.count() << " ms\n"; 121 | Util::DelayedExit(); 122 | } 123 | --------------------------------------------------------------------------------