D
Deno

help

FFI string corrupted?

KKyiro7/11/2023
Is there anything i'm doing wrong here? I'm confused on why it's crashing on setVerisonUE5 in particular. The code without FFI in C++ works fine
import { Dataminer } from "./mod.ts";

Dataminer.withLogs(true);
Dataminer.setOodle("...");

const core = new Dataminer("...");
console.log(core); // printed
core.setVersionUE5(1008); // crashes here?
console.log("set version"); // not printed
import { Dataminer } from "./mod.ts";

Dataminer.withLogs(true);
Dataminer.setOodle("...");

const core = new Dataminer("...");
console.log(core); // printed
core.setVersionUE5(1008); // crashes here?
console.log("set version"); // not printed
const DLL_PATH = "...";

const library = Deno.dlopen(DLL_PATH, {
"dataminer_options_with_oodle": {
parameters: ["buffer"],
result: "void"
},
"dataminer_with_logging": {
parameters: ["bool"],
result: "void"
},
"dataminer_construct": {
parameters: ["buffer"],
result: "pointer"
},
"dataminer_set_version_ue5": {
parameters: ["pointer", "i32"],
result: "void"
}
});

function encode(text: string) {
return new TextEncoder().encode(text);
}

export class Dataminer {
pointer: Deno.PointerValue;

constructor(paksDir: string) {
this.pointer = library.symbols.dataminer_construct(encode(paksDir));
}

checkPointer() {
if (!this.pointer) throw new Error("pointer is null");
}

static setOodle(oodlePath: string) {
library.symbols.dataminer_options_with_oodle(encode(oodlePath));
}

static withLogs(shouldLog: boolean) {
library.symbols.dataminer_with_logging(shouldLog);
}

setVersionUE5(version: number) {
this.checkPointer();
library.symbols.dataminer_set_version_ue5(this.pointer, version);
}
}
const DLL_PATH = "...";

const library = Deno.dlopen(DLL_PATH, {
"dataminer_options_with_oodle": {
parameters: ["buffer"],
result: "void"
},
"dataminer_with_logging": {
parameters: ["bool"],
result: "void"
},
"dataminer_construct": {
parameters: ["buffer"],
result: "pointer"
},
"dataminer_set_version_ue5": {
parameters: ["pointer", "i32"],
result: "void"
}
});

function encode(text: string) {
return new TextEncoder().encode(text);
}

export class Dataminer {
pointer: Deno.PointerValue;

constructor(paksDir: string) {
this.pointer = library.symbols.dataminer_construct(encode(paksDir));
}

checkPointer() {
if (!this.pointer) throw new Error("pointer is null");
}

static setOodle(oodlePath: string) {
library.symbols.dataminer_options_with_oodle(encode(oodlePath));
}

static withLogs(shouldLog: boolean) {
library.symbols.dataminer_with_logging(shouldLog);
}

setVersionUE5(version: number) {
this.checkPointer();
library.symbols.dataminer_set_version_ue5(this.pointer, version);
}
}
#include "Dataminer/Dataminer.h"

#define CPAKPARSER_API extern "C" __declspec(dllexport)

CPAKPARSER_API void dataminer_options_with_oodle(const char* OodleDllPath)
{
Dataminer::Options::WithOodleDecompressor(OodleDllPath);
}

CPAKPARSER_API void dataminer_with_logging(bool bEnableLogging)
{
Dataminer::Options::WithLogging(bEnableLogging);
}

CPAKPARSER_API Dataminer* dataminer_construct(const char* PaksFolderDir)
{
auto Result = Dataminer(PaksFolderDir);
return &Result;
}

CPAKPARSER_API void dataminer_set_version_ue5(Dataminer* This, int Version)
{
This->SetVersionUE5(Version);
}
#include "Dataminer/Dataminer.h"

#define CPAKPARSER_API extern "C" __declspec(dllexport)

CPAKPARSER_API void dataminer_options_with_oodle(const char* OodleDllPath)
{
Dataminer::Options::WithOodleDecompressor(OodleDllPath);
}

CPAKPARSER_API void dataminer_with_logging(bool bEnableLogging)
{
Dataminer::Options::WithLogging(bEnableLogging);
}

CPAKPARSER_API Dataminer* dataminer_construct(const char* PaksFolderDir)
{
auto Result = Dataminer(PaksFolderDir);
return &Result;
}

CPAKPARSER_API void dataminer_set_version_ue5(Dataminer* This, int Version)
{
This->SetVersionUE5(Version);
}
I think it only happens when dealing with numbers? what's up with that I managed to fix it but now my strings end up corrupted?
CPAKPARSER_API bool dataminer_load_type_mappings(Dataminer* This, const char* UsmapFilePath)
{
return This->LoadTypeMappings(UsmapFilePath);
}
CPAKPARSER_API bool dataminer_load_type_mappings(Dataminer* This, const char* UsmapFilePath)
{
return This->LoadTypeMappings(UsmapFilePath);
}
[CPakParser] ERR Invalid paks directory Content\Paksď`☻
[CPakParser] ERR Invalid paks directory Content\Paksď`☻
const library = Deno.dlopen(DLL_PATH, {
// other FFI exports here
"dataminer_load_type_mappings": {
parameters: ["pointer", "buffer"],
result: "bool"
}
});

function encode(text: string) {
return new TextEncoder().encode(text);
}

// function in the class that wraps it
loadTypeMappings(usmapFileDir: string) {
return library.symbols.dataminer_load_type_mappings(this.pointer, encode(usmapFileDir));
}
const library = Deno.dlopen(DLL_PATH, {
// other FFI exports here
"dataminer_load_type_mappings": {
parameters: ["pointer", "buffer"],
result: "bool"
}
});

function encode(text: string) {
return new TextEncoder().encode(text);
}

// function in the class that wraps it
loadTypeMappings(usmapFileDir: string) {
return library.symbols.dataminer_load_type_mappings(this.pointer, encode(usmapFileDir));
}
Got it to work by doing uint32_t UsmapLength, const char* UsmapFilePath but idk if that's the way to go without leaking memory
AAapoAlas7/12/2023
You at least don't seem to be null-terminating your strings. That's going to be an issue since then C++ won't know when the strings end and odd things happen. Giving the length explicitly will then fix that issue since now C++ can rely on the length parameter to stop reading the string and doesn't need to look for a null byte.
KKyiro7/12/2023
How do I pass a null terminated string to C++ from Deno? do i just add a null byte at the end myself?
AAapoAlas7/12/2023
Yeah, easiest and probably fastest is to do
new TextEncoder().encode(`${myString}\0`)
new TextEncoder().encode(`${myString}\0`)
KKyiro7/12/2023
alr ty will try
AAapoAlas7/12/2023
If you know you're dealing with ASCII strings then you could also use encodeInto with a buffer you create yourself with its length being one longer than the length of your string. The extra byte is then your null byte. That could lead to a faster call, but it could also be slower since the buffer is initially zeroes which is not needed in the above version.
KKyiro7/12/2023
seems like this worked i never expected to deal with pointers in javascript lmfao but it's pretty nice
AAapoAlas7/12/2023
Yeah 🙂 FFI pretty much drew me into Deno in the first place, and by god it's been fun to deal with bytes and pointers and whatnot in JS. It feels so dirty but oh so fun 🙂 Just in case you haven't seen this, do check out Denonomicon: https://denonomicon.deno.dev/introduction I've written up at least some of the internals and intricacies of FFI there.
KKyiro7/12/2023
never heard about the denomicon thank you!
AAapoAlas7/12/2023
Also in YouTube we did a three part podcasty video series on FFI with Andy. It's on Deno's channel with the name "WTF is FFI"
KKyiro7/12/2023
last question, the "V8 Fast API" in the denomicon is just used internally within Deno and is not usable via the FFI APIs right?
AAapoAlas7/12/2023
Yes. It gets used automatically with all FFI calls except calls that use struct-by-value parameters or return values ({ struct: [...] }) And feel free to ask as many questions as you feel like, I'm happy to see FFI being used and enjoyed! 🙂

Looking for more? Join the community!

Recommended Posts
Typescript Conditional Type ReturnsI don't know why but this conditional type return is just causing me grief. ```ts type InnerType<T>Parsing Hostname for TLD, domain name, and SLDdoes anyone know of any packages to parse a URL's hostname for the top level domain, domain name, anMigrating large Node project to DenoIs there any reliable way to migrate a Node project to Deno? Currently working on a large project thWhy does Deno.serve not return a promise anymore?Doesn't this cause problems for error handling?Changing GitHub username – what about deno.land/x?If I change my GitHub username and I have a couple of modules on deno.land/x, what will happen to thAbsolute Imports with OakI would like to have absolute imports in my Oak-API ... 😉Passing initial data to WorkerDetault `Worker` doesnt seem to support `WorkerData` as params. Although using `node:worker_threads`Adding npm specifiers to deno.jsonHow do i add a specific version of an npm module to the cache?How do I enqueue_microtask from other threadso `Isolate.enqueue_microtask` is obviously not send/sync so I think the only option is to use` std:how to import svg in vite without type errors?Can't connect to http server on official alpine deno with std serve.I'm using the official alpine docker container to run deno, listening on port 8080, but a simple curcalling a javascript function and waiting for the promise with `rusty_v8````rust let result = function.call(scope, recv, &[]).expect("couldnt run"); /*i got this result whicDeno KV: subtract with `sum` operation?If I do `.sum([key], 1n)` the value is increased by 1. How can I decrease it? I tried `.sum([key], Proxying a Rust API using FFI?I have a rust application that serves a rest api that runs in a container. I also have a deno cli thHow to use declarations from `vite/client`?There are declarations: https://esm.sh/v126/vite@4.3.9/client.d.ts According to documentation, I neegetting errno with ffiI am using ffi to call `execv` and need to get the value of `errno` for failures, but can’t see how Purging Kv keysI have a logger which write logs to a kv store with dual keys, something like ```ts kv.set(["logs_bWhat is the right way to bundle native modules?If I'm not able to import a native module with the built-in "import" keyword, what is the ideal way HandleScope in async opIs there a way to get v8::HandleScope inside async op if I add it as ``` scope: &mut v8::HandleScopDeno LSP randomly breaks until a restart (unable to send result to client)This wasnt an issue until recently. In vscode randomly the lsp stops working. Trying to use the auto