我目前正在尝试重建和更新一个用Rust编写的项目(更具体地说,它是Skyrim的SKSE64插件:https://github.com/lukasaldersley/sse-mod-skyrim-search-se从qbx2分叉(我面临的最后一个问题是,该库现在需要从我们的库中导出一个结构来进行版本检查。
我尝试了很多可能很愚蠢的方法来实现这一点,但我无法实现。
c++代码如下:
struct SKSEPluginVersionData
{
enum
{
kVersion = 1,
};
enum
{
// set this if you are using a (potential at this time of writing) post-AE version of the Address Library
kVersionIndependent_AddressLibraryPostAE = 1 << 0,
// set this if you exclusively use signature matching to find your addresses and have NO HARDCODED ADDRESSES
kVersionIndependent_Signatures = 1 << 1,
};
UInt32 dataVersion; // set to kVersion
UInt32 pluginVersion; // version number of your plugin
char name[256]; // null-terminated ASCII plugin name
char author[256]; // null-terminated ASCII plugin author name (can be empty)
char supportEmail[256]; // null-terminated ASCII support email address (can be empty)
// version compatibility
UInt32 versionIndependence; // set to one of the kVersionIndependent_ enums or zero
UInt32 compatibleVersions[16]; // zero-terminated list of RUNTIME_VERSION_ defines your plugin is compatible with
UInt32 seVersionRequired; // minimum version of the script extender required, compared against PACKED_SKSE_VERSION
// you probably should just set this to 0 unless you know what you are doing
};
#define RUNTIME_VERSION_1_6_318 0x010613E0
extern "C" {
__declspec(dllexport) SKSEPluginVersionData SKSEPlugin_Version =
{
SKSEPluginVersionData::kVersion,
1,
"Skyrim Search",
"qbx2",
"",
0, // not version independent
{ RUNTIME_VERSION_1_6_318, 0 }, // RUNTIME_VERSION_1_6_318 is
0, // works with any version of the script extender. you probably do not need to put anything here
};
};
到目前为止,我在Rust中想到的是:
enum KVersionenum {
KVersion=1,
}
#[repr(C)]
pub struct SKSEPluginVersionData {
dataVersion: u32,
pluginVersion: u32,
name: [char;256],
author: [char;256],
supportEmail: [char;256],
versionIndependence: u32,
compatibleVersions: [u32;16],
seVersionRequired: u32,
}
//0x010613E0 is RUNTIME_VERSION_1_6_318
//how can I do this OUTSIDE of a method and how can I make it public to the dll? is that even possible?
let SKSEPlugin_Version = SKSEPluginVersionData {
dataVersion: KVersionenum::KVersion as u32,
pluginVersion: 1,
name: "Skyrim Search ", //this doesn't work, how can I fill this char array?
author: "qbx2 / lukasaldersley ", //same here
supportEmail: "something@something.something ", //and here
versionIndependence: 0,
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], //I'm sure this is a horrible way of doing this
seVersionRequired: 0,
};
当我试图在函数外使用let thingy时,编译器抱怨期望"item",但我的谷歌功能不够好,无法在那里找到任何有用的信息,因为我一直在视频游戏Rust中找到有关项目的信息。
对于car array/string问题,我遇到了std:ffi的东西,完全迷失在它的文档中,但据我所知,它只会处理指针,这不是我需要的。
现在的两个问题是如何填充这些char数组(我不能只传递指针(,以及如何创建这个结构的实例作为全局变量(或者Rust如何调用它(,因为let name=something{…}不起作用,所以我可以导出它。
据我所知,将函数导出到dll会是这样的,但我认为它对该结构的工作方式不同。
#[no_mangle]
pub extern "C" fn SKSEPlugin_Query(skse: *const SKSEInterface, info: *mut PluginInfo) -> bool {...}
有可能做到这一点吗?
有人能在这里帮我吗,或者至少给我指一个正确的方向?请注意,我是Rust的绝对初学者,显然我错误地认为添加一个结构不会那么复杂。
首先,Rust中的char
是32位值,而它在C++
中是8位值(严格来说这不是真的,但Rust不支持它不支持的体系结构(。所以name
、author
和supportEmail
字段应该是u8
数组。
您可以在公共static
变量上使用#[no_mangle]
导出全局值:
#[no_mangle]
pub static SKSEPlugin_Version: SKSEPluginVersionData = SKSEPluginVersionData {
...
};
请参阅:Rust常量/静态可以暴露在C中吗?
您可以使用字节字符串从文字初始化u8
数组并取消对其的引用:*b"..."
。不幸的是,没有像C++中那样的缩写来对数组中未确定的部分进行零填充,所以只剩下:
name: *b"Skyrim Search ",
author: *b"qbx2 / lukasaldersley ",
supportEmail: *b"something@something.something ",
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
老实说,这一点都不好。你可以用一些为你填充数组的函数来解决这个问题,然而,初始化static
变量需要const
函数,而这些函数在Rust中还相对不成熟,所以我们不能使用for
循环或特征来帮助我们:
const fn zero_pad_u8<const N: usize, const M: usize>(arr: &[u8; N]) -> [u8; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
const fn zero_pad_u32<const N: usize, const M: usize>(arr: &[u32; N]) -> [u32; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
...
name: zero_pad_u8(b"Skyrim Search"),
author: zero_pad_u8(b"qbx2 / lukasaldersley"),
supportEmail: zero_pad_u8(b"something@something.something"),
compatibleVersions: zero_pad_u32(&[0x010613E0]),
仍然不是那么好,但至少它是可控的。可能有一个板条箱可以为你做这件事。
最后,您不必使用与C++中使用的字段命名约定相同的字段命名惯例,因为这只是顺序和类型很重要,所以我建议使用snake_case
,但如果您确实希望保持相同的名称以保持一致性,则可以将#[allow(non_snake_case)]
属性放在SKSEPluginVersionData
上以抑制编译器警告。
我还建议为这个神奇的值设置一个常数,而不仅仅是一个注释:
const RUNTIME_VERSION_1_6_318: u32 = 0x010613E0;
在操场上看完整的东西。