1
0

3 Commits

Author SHA1 Message Date
af6a50c2f9 feat: use cmdline args as the args of BMap bindings.
- update testbench of PyBMap and BMapSharp. use command line arguments as the arguments of testbench, instead of hardcoded variables in code.
2025-01-02 10:59:16 +08:00
0bf0519c4c fix: fix linux build issue
- use std::cos and std::sin instead of std::cosf and std::sinf. it seems that some linux environment do not have these 2 functions in std namespace.
2024-12-31 18:25:44 +08:00
c18ff8f2e3 fix: fix various issues.
- fix convertion loss in CKCamera.
- bump up version to 0.3.0
- use CMake to generate version info header.
- fix annotation about Dassault ComputeCRC error.
- change member field initialization value in CKLight.
2024-12-31 17:48:24 +08:00
16 changed files with 143 additions and 28 deletions

3
.gitignore vendored
View File

@@ -5,6 +5,9 @@
*.nms *.nms
*.vmo *.vmo
# Ignore CMake generated version header
LibCmo/VTVersion.hpp
# Ignore temporary Visual Studio files and folders # Ignore temporary Visual Studio files and folders
temp/ temp/
out/ out/

View File

@@ -4,6 +4,10 @@
<ProjectReference Include="..\BMapSharp\BMapSharp.csproj" /> <ProjectReference Include="..\BMapSharp\BMapSharp.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>

View File

@@ -3,11 +3,19 @@ using System;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.CommandLine;
namespace BMapSharpTestbench { namespace BMapSharpTestbench {
internal class Program { internal class Program {
static void Main(string[] args) { static void Main(string[] args) {
// Parse arguments
var resolved_args = ResolveArguments(args);
if (resolved_args is null) {
// just silent quit
Environment.Exit(0);
}
// Check environment // Check environment
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
if (!BMapSharp.BMapWrapper.Utils.IsBMapAvailable()) { if (!BMapSharp.BMapWrapper.Utils.IsBMapAvailable()) {
@@ -21,10 +29,10 @@ namespace BMapSharpTestbench {
Console.ReadKey(true); Console.ReadKey(true);
// Start testbench // Start testbench
string file_name = "LightCameraTest.nmo"; string file_name = resolved_args.mFileName; // "LightCameraTest.nmo";
string temp_folder = "Temp"; string temp_folder = resolved_args.mTempFolder; // "Temp";
string texture_folder = "F:\\Ballance\\Ballance\\Textures"; string texture_folder = resolved_args.mTextureFolder; // "F:\\Ballance\\Ballance\\Textures";
string[] encodings = ["cp1252", "gb2312"]; string[] encodings = resolved_args.mEncodings; // ["cp1252", "gb2312"];
using (var reader = new BMapSharp.BMapWrapper.BMFileReader(file_name, temp_folder, texture_folder, encodings)) { using (var reader = new BMapSharp.BMapWrapper.BMFileReader(file_name, temp_folder, texture_folder, encodings)) {
TestCommon(reader); TestCommon(reader);
@@ -36,6 +44,53 @@ namespace BMapSharpTestbench {
} }
class BMapSharpArguments {
public string mFileName;
public string mTempFolder;
public string mTextureFolder;
public string[] mEncodings;
}
static BMapSharpArguments ResolveArguments(string[] args) {
// define arguments
var fileNameOpt = new Option<string>
("--file-path", "The path to input Virtools file.");
fileNameOpt.IsRequired = true;
var tempFolderOpt = new Option<string>
("--temp-dir", "The temp folder used by BMap.");
tempFolderOpt.IsRequired = true;
var textureFolderOpt = new Option<string>
("--texture-dir", "The texture folder containing Ballance texture resources.");
textureFolderOpt.IsRequired = true;
var encodingsOpt = new Option<IEnumerable<string>>
("--encodings", "The encodings used to parse the names stroed in input Virtools file.");
encodingsOpt.IsRequired = true;
encodingsOpt.Arity = ArgumentArity.OneOrMore;
encodingsOpt.AllowMultipleArgumentsPerToken = true;
// init root command
var rootCommand = new RootCommand("The testbench of BMapSharp.");
rootCommand.Add(fileNameOpt);
rootCommand.Add(tempFolderOpt);
rootCommand.Add(textureFolderOpt);
rootCommand.Add(encodingsOpt);
// init result container
BMapSharpArguments ret = new BMapSharpArguments();
// set handler
rootCommand.SetHandler((context) => {
ret.mFileName = context.ParseResult.GetValueForOption(fileNameOpt);
ret.mTempFolder = context.ParseResult.GetValueForOption(tempFolderOpt);
ret.mTextureFolder = context.ParseResult.GetValueForOption(textureFolderOpt);
ret.mEncodings = context.ParseResult.GetValueForOption(encodingsOpt).ToArray();
context.ExitCode = 61;
});
// execute root command and return value.
if (rootCommand.Invoke(args) != 61) return null;
return ret;
}
static void TestCommon(BMapSharp.BMapWrapper.BMFileReader reader) { static void TestCommon(BMapSharp.BMapWrapper.BMFileReader reader) {
// Console.WriteLine("===== Groups ====="); // Console.WriteLine("===== Groups =====");
// foreach (var gp in reader.GetGroups()) { // foreach (var gp in reader.GetGroups()) {

View File

@@ -5,3 +5,5 @@ The core of BMapSharp project is placed within `BMapSharp` subdirectory. This di
The native BMap library should be placed together with managed `BMapSharp` dynamic library. I use gitignore file to filter all native binary so you need put them manually. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`. The native BMap library should be placed together with managed `BMapSharp` dynamic library. I use gitignore file to filter all native binary so you need put them manually. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`.
The most content of `VirtoolsTypes.cs` is generated by EnumsMigration, and the most content of `BMap.cs` is generated by BMapBindings. You should watch these file changes if corresponding C++ code or structures are changed. The most content of `VirtoolsTypes.cs` is generated by EnumsMigration, and the most content of `BMap.cs` is generated by BMapBindings. You should watch these file changes if corresponding C++ code or structures are changed.
Since BMap 0.3.0, testbench use command line arguments, instead of hardcode variables in code, as the arguments of BMap. It is convenient that debug BMapSharp without any modification of source code. For a brief instruction, you may need to launch BMapSharpTestbench in following command (just an example. you can modify it as you wished): `dotnet run -- --file-path "LightCameraTest.nmo" --temp-dir "Temp" --texture-dir "F:/Ballance/Ballance/Textures" --encodings cp1252 gb2312`.

View File

@@ -5,3 +5,5 @@ The real scripts are placed in sub PyBMap folder. This folder is served for test
The native BMap library should be placed in sub PyBMap folder, and I have used gitignore file to filter them. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`. The native BMap library should be placed in sub PyBMap folder, and I have used gitignore file to filter them. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`.
Please note the most content of `virtools_types.py` are generated by EnumsMigration sub-project. Additionally the most content of `bmap.py` is generated by BMapBindings. So if some structs are updated, do not forget checking these files. Please note the most content of `virtools_types.py` are generated by EnumsMigration sub-project. Additionally the most content of `bmap.py` is generated by BMapBindings. So if some structs are updated, do not forget checking these files.
Since BMap 0.3.0, testbench use command line arguments, instead of hardcode variables in code, as the arguments of BMap. It is convenient that debug BMapSharp without any modification of source code. For a brief instruction, you may need to launch BMapSharpTestbench in following command (just an example. you can modify it as you wished): `py testbench.py --file-path "LightCameraTest.nmo" --temp-dir "Temp" --texture-dir "F:/Ballance/Ballance/Textures" --encodings cp1252 gb2312`.

View File

@@ -1,13 +1,14 @@
import os import os
import argparse
import PyBMap.bmap_wrapper as bmap import PyBMap.bmap_wrapper as bmap
def main() -> None: def main(file_name: str, temp_folder: str, texture_folder: str, encodings: tuple[str, ...]) -> None:
input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...') input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...')
file_name: str = 'LightCameraTest.nmo' # file_name: str = 'LightCameraTest.nmo'
temp_folder: str = 'Temp' # temp_folder: str = 'Temp'
texture_folder: str = 'F:\\Ballance\\Ballance\\Textures' # texture_folder: str = 'F:\\Ballance\\Ballance\\Textures'
encodings: tuple[str, ...] = ('cp1252', ) # encodings: tuple[str, ...] = ('cp1252', )
with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader: with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader:
test_common(reader) test_common(reader)
test_equatable(reader) test_equatable(reader)
@@ -146,4 +147,32 @@ def test_equatable(reader: bmap.BMFileReader):
assert second_3dobj in test_dict assert second_3dobj in test_dict
if __name__ == '__main__': if __name__ == '__main__':
main() # parse argument
parser = argparse.ArgumentParser(
prog='PyBMap Testbench',
description='The testbench of PyBMap.'
)
parser.add_argument(
'--file-path',
action='store', dest='file_path', required=True,
help='The path to input Virtools file.'
)
parser.add_argument(
'--temp-dir',
action='store', dest='temp_dir', required=True,
help='The temp folder used by BMap.'
)
parser.add_argument(
'--texture-dir',
action='store', dest='texture_dir', required=True,
help='The texture folder containing Ballance texture resources.'
)
parser.add_argument(
'--encodings',
action='extend', nargs='+', dest='encodings', required=True,
help='The encodings used to parse the names stroed in input Virtools file.'
)
args = parser.parse_args()
# run main function
main(args.file_path, args.temp_dir, args.texture_dir, tuple(args.encodings))

6
CMake/VTVersion.hpp.in Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#define LIBCMO_VER_MAJOR @PROJECT_VERSION_MAJOR@
#define LIBCMO_VER_MINOR @PROJECT_VERSION_MINOR@
#define LIBCMO_VER_PATCH @PROJECT_VERSION_PATCH@
#define LIBCMO_VER_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.23) cmake_minimum_required(VERSION 3.23)
project(NeMo project(NeMo
VERSION 0.2.0 VERSION 0.3.0
LANGUAGES CXX LANGUAGES CXX
) )

View File

@@ -102,24 +102,27 @@ namespace LibCmo::CK2 {
// This is a patch for Dassault stupid programmer. // This is a patch for Dassault stupid programmer.
// //
// After Virtools 4.0, Dassault use a new way to compute the CRC of file. // After Virtools 4.0, Dassault use a new way to compute the CRC of file.
// Dassault introduce a new class called CKMemoryBufferWriter which use file and memory map to handle big file properly. // Dassault introduces a new class called CKMemoryBufferWriter which use file and memory map to handle big file properly.
// This algorithm splits the whole data body into 8 MB chunks and calculate them one by one to avoid instantaneous memory occupation.
// However, there is a bug in virtual function CKMemoryBufferWriter::ComputeCRC. // However, there is a bug in virtual function CKMemoryBufferWriter::ComputeCRC.
// It takes `PreviousCRC` as argument but never use it in function. In this function, the start value of CRC compution is hardcoded 0. // It takes `PreviousCRC` as argument but never use it in function.
// In this function, the start value of CRC compution is hardcoded 0.
// So, although Dassault programmer try to compute CRC for file header, header part and daat part in code, it actually only compute CRC for data part! // So, although Dassault programmer try to compute CRC for file header, header part and daat part in code, it actually only compute CRC for data part!
// I 100% sure this is the mistake of Dassault stupid programmer and this bug cause horrible result. // I 100% sure this is the mistake of Dassault stupid programmer and this bug cause more horrible result.
// //
// In Virtools 2.1, engine will check CRC of file first. If no matched CRC, engine will reject loading file. // In Virtools 2.1, engine will check CRC of file first. If no matched CRC, engine will reject loading file.
// So the obvious result is that we can not load file saved by Virtools 4.0 in Virtools 2.1. // So the obvious result is that we can not load file saved by Virtools 4.0 in Virtools 2.1.
// But this is not the point which makes me indignant. // But this is not the point which makes me indignant.
// The real weird point is that we can use Virtools 3.5 to open file saved by Virtools 4.0 but why? // The real weird point is that we can use Virtools 3.5 to open file saved by Virtools 4.0, but why?
// After some research, I found that the programmer of Dassault totally removed CRC check when loading file since some version which I don't know! // After some researches, I found that the programmer of Dassault totally removed CRC check when loading file, since some version which I don't know, to suppress this bug!
// This is totally cheat and commercial-oriented behavior! // This is totally cheat and commercial-oriented behavior!
// I guess Dassault programmer also find that they can not load new created file in old Virtools. // I guess Dassault programmer also found that they can not load new created file in old Virtools.
// But they entirely don't know how to resolve it. So they just directly remove the whole of CRC checker! // But they didn't find out what cause this bug, and just directly remove the whole of CRC checker to resolve this bug!
// That's the point which makes me indignant. // I can't believe that this thing happens on such official software.
// This is the point which makes me indignant.
gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, 0u); gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, 0u);
// Both CRC compute methods are failed, this file may be really broken. // Both CRC compute methods are failed. This file may be really broken.
// Report exception directly. // Report exception directly.
if (gotten_crc != this->m_FileInfo.Crc) { if (gotten_crc != this->m_FileInfo.Crc) {
this->m_Ctx->OutputToConsole(u8"Virtools file CRC error."); this->m_Ctx->OutputToConsole(u8"Virtools file CRC error.");

View File

@@ -128,7 +128,7 @@ SetObjectFlags(obj_flags); \
} }
void CKCamera::ComputeProjectionMatrix(VxMath::VxMatrix& mat) const { void CKCamera::ComputeProjectionMatrix(VxMath::VxMatrix& mat) const {
CKFLOAT aspect = m_Width / m_Height; CKFLOAT aspect = static_cast<CKFLOAT>(m_Width) / m_Height;
if (m_ProjectType == CK_CAMERA_PROJECTION::CK_PERSPECTIVEPROJECTION) { if (m_ProjectType == CK_CAMERA_PROJECTION::CK_PERSPECTIVEPROJECTION) {
mat.Perspective(m_Fov, aspect, m_FrontPlane, m_BackPlane); mat.Perspective(m_Fov, aspect, m_FrontPlane, m_BackPlane);
} else { } else {

View File

@@ -1,5 +1,6 @@
#include "CKLight.hpp" #include "CKLight.hpp"
#include "../CKStateChunk.hpp" #include "../CKStateChunk.hpp"
#include <numbers>
namespace LibCmo::CK2::ObjImpls { namespace LibCmo::CK2::ObjImpls {
@@ -16,8 +17,8 @@ namespace LibCmo::CK2::ObjImpls {
m_LightData.m_Attenuation0 = 1.0f; m_LightData.m_Attenuation0 = 1.0f;
m_LightData.m_Attenuation1 = 0.0f; m_LightData.m_Attenuation1 = 0.0f;
m_LightData.m_Attenuation2 = 0.0f; m_LightData.m_Attenuation2 = 0.0f;
m_LightData.m_InnerSpotCone = 0.69813174f; // MARK: Perhaps 40 deg in rad. m_LightData.m_InnerSpotCone = 40.0f / 180.0f * std::numbers::pi_v<float>; // MARK: Original value is 0.69813174f. Perhaps 40 deg in rad.
m_LightData.m_OuterSpotCone = 0.78539819f; // MARK: Perhaps 45 deg in rad. m_LightData.m_OuterSpotCone = 45.0f / 180.0f * std::numbers::pi_v<float>; // MARK: Original value is 0.78539819f. Perhaps 45 deg in rad.
} }
CKLight::~CKLight() {} CKLight::~CKLight() {}

View File

@@ -1,3 +1,10 @@
# Configure version file
configure_file(
${CMAKE_CURRENT_LIST_DIR}/../CMake/VTVersion.hpp.in
${CMAKE_CURRENT_LIST_DIR}/VTVersion.hpp
@ONLY
)
# Create static library # Create static library
add_library(LibCmo STATIC "") add_library(LibCmo STATIC "")
# Setup static library sources # Setup static library sources
@@ -47,6 +54,7 @@ PUBLIC
FILE_SET HEADERS FILE_SET HEADERS
FILES FILES
# Asststant header files # Asststant header files
VTVersion.hpp
VTInternal.hpp VTInternal.hpp
VTEncoding.hpp VTEncoding.hpp
VTUtils.hpp VTUtils.hpp

View File

@@ -20,6 +20,8 @@
* They should use Virtools type anywhere, except that Virtools type can not fulfill their requirements. * They should use Virtools type anywhere, except that Virtools type can not fulfill their requirements.
*/ */
// The version info header of LibCmo
#include "VTVersion.hpp"
// The base header of LibCmo. // The base header of LibCmo.
// It provides various convenient stuff, for example: // It provides various convenient stuff, for example:
// - General LibCmo specific custom exception. // - General LibCmo specific custom exception.

View File

@@ -444,7 +444,7 @@ namespace LibCmo::VxMath {
} }
void VxMatrix::Perspective(CKFLOAT Fov, CKFLOAT Aspect, CKFLOAT Near_plane, CKFLOAT Far_plane) { void VxMatrix::Perspective(CKFLOAT Fov, CKFLOAT Aspect, CKFLOAT Near_plane, CKFLOAT Far_plane) {
Clear(); Clear();
m_Data[0][0] = std::cosf(Fov * 0.5f) / std::sinf(Fov * 0.5f); m_Data[0][0] = std::cos(Fov * 0.5f) / std::sin(Fov * 0.5f);
m_Data[1][1] = m_Data[0][0] * Aspect; m_Data[1][1] = m_Data[0][0] * Aspect;
m_Data[2][2] = Far_plane / (Far_plane - Near_plane); m_Data[2][2] = Far_plane / (Far_plane - Near_plane);
m_Data[3][2] = -m_Data[2][2] * Near_plane; m_Data[3][2] = -m_Data[2][2] * Near_plane;

View File

@@ -362,8 +362,8 @@ namespace Unvirt::CmdHelper {
throw std::invalid_argument("root node should not be inserted as child node."); throw std::invalid_argument("root node should not be inserted as child node.");
// check conflict // check conflict
const auto& new_node_set = new_node_ptr->GetConflictSet(); const auto& new_node_set = new_node_ptr->GetConflictSet();
for (auto& node : m_Nodes) { for (auto& child_node : m_Nodes) {
const auto& node_set = node->GetConflictSet(); const auto& node_set = child_node->GetConflictSet();
if (new_node_set.IsConflictWith(node_set)) if (new_node_set.IsConflictWith(node_set))
throw std::invalid_argument("try to add a conflict node. please check your code."); throw std::invalid_argument("try to add a conflict node. please check your code.");
} }

View File

@@ -281,7 +281,7 @@ namespace Unvirt::Context {
YYCC::ConsoleHelper::EnableColorfulConsole(); YYCC::ConsoleHelper::EnableColorfulConsole();
// Show banner // Show banner
YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Unvirt 0.2.0") " built at " __DATE__ " " __TIME__); YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Unvirt") " (based on LibCmo " LIBCMO_VER_STR ") built at " __DATE__ " " __TIME__);
YYCC::ConsoleHelper::WriteLine(u8"Type 'help' for more infomation. Type 'exit' to quit."); YYCC::ConsoleHelper::WriteLine(u8"Type 'help' for more infomation. Type 'exit' to quit.");
// start process loop // start process loop