SRL Archive - C/C++ Help and Tutorials

Export strings to pascal


alar82

2020-01-28 18:07:06


What is going on here, some kind of error huh.

Brandon

2020-01-31 12:31:07

From: alar82

What is going on here, some kind of error huh.


As the author of this code, you need to use `regparm` which is only available in GCC, G++, Clang, Clang++.. Visual Studio does NOT support it..

So to fix this, you need to IFDEF that code for visual studio but then also use:


#ifndef TMEMORYMANAGER_HXX_INCLUDED
#define TMEMORYMANAGER_HXX_INCLUDED

#if !defined(_MSC_VER)
#define DELPHI_CALLING_CONVENTION __attribute__((regparm(3)))

typedef struct
{
std::uint32_t TotalAddressSpace;
std::uint32_t TotalUncommited;
std::uint32_t TotalCommited;
std::uint32_t TotalAllocated;
std::uint32_t TotalFree;
std::uint32_t FreeSmall;
std::uint32_t FreeBig;
std::uint32_t Unused;
std::uint32_t Overhead;
std::uint32_t HeapErrorCode;
} THeapStatus;

typedef struct
{
std::uintptr_t MaxHeapSize;
std::uintptr_t MaxHeapUsed;
std::uintptr_t CurrHeapSize;
std::uintptr_t CurrHeapUsed;
std::uintptr_t CurrHeapFree;
} TFPCHeapStatus;

typedef struct
{
bool NeedLock;
void* (*GetMem)(std::intptr_t size) DELPHI_CALLING_CONVENTION;
std::intptr_t (*FreeMem)(void* &ptr) DELPHI_CALLING_CONVENTION;
std::intptr_t (*FreeMemSize)(void* &ptr, std::intptr_t size) DELPHI_CALLING_CONVENTION;
void* (*AllocMem)(std::intptr_t size) DELPHI_CALLING_CONVENTION;
void* (*ReAllocMem)(void* &ptr, std::intptr_t size) DELPHI_CALLING_CONVENTION;
std::intptr_t (*MemSize)(void* ptr) DELPHI_CALLING_CONVENTION;

void (*InitThread)() DELPHI_CALLING_CONVENTION;
void (*DoneThread)() DELPHI_CALLING_CONVENTION;
void (*RelocateHeap)() DELPHI_CALLING_CONVENTION;
THeapStatus (*GetHeapStatus)() DELPHI_CALLING_CONVENTION;
TFPCHeapStatus (*GetFPCHeapStatus)() DELPHI_CALLING_CONVENTION;
} TMemoryManager;


typedef struct
{
bool NeedLock;
void* (*GetMem)(std::intptr_t size) DELPHI_CALLING_CONVENTION;
std::intptr_t (*FreeMem)(void* &ptr) DELPHI_CALLING_CONVENTION;
std::intptr_t (*FreeMemSize)(void* &ptr, std::intptr_t size) DELPHI_CALLING_CONVENTION;
void* (*AllocMem)(std::intptr_t size) DELPHI_CALLING_CONVENTION;
void* (*ReAllocMem)(void* &ptr, std::intptr_t size) DELPHI_CALLING_CONVENTION;
std::intptr_t (*MemSize)(void* ptr) DELPHI_CALLING_CONVENTION;

void (*InitThread)() DELPHI_CALLING_CONVENTION;
void (*DoneThread)() DELPHI_CALLING_CONVENTION;
void (*RelocateHeap)() DELPHI_CALLING_CONVENTION;
THeapStatus (*GetHeapStatus)() DELPHI_CALLING_CONVENTION;
TFPCHeapStatus (*GetFPCHeapStatus)() DELPHI_CALLING_CONVENTION;
} __attribute__((__packed__)) TCMemoryManager;
#endif


typedef struct
{
void (__cdecl *Sync)(void(__cdecl *synchronize_method)(void*), void* data);
} __attribute__((__packed__)) TSimbaMethods;


typedef struct
{
void* (__cdecl *GetMem)(std::size_t size);
void (__cdecl *FreeMem)(void* ptr);
} __attribute__((__packed__)) TSimbaMemoryAllocators;

#endif // TMEMORYMANAGER_HXX_INCLUDED


Then you have to do:


#if !defined(_MSC_VER)
TMemoryManager PLUGIN_MEMORY_MANAGER = {0};
#endif

TSimbaMethods PLUGIN_SYNC_METHODS = {0};
TSimbaMemoryAllocators PLUGIN_MEMORY_ALLOCATORS = {0};

#if !defined(_MSC_VER)
void SetPluginMemManager(TMemoryManager MemMgr)
{
PLUGIN_MEMORY_MANAGER = MemMgr;
}
#endif

void SetPluginSimbaMethods(TSimbaMethods Methods)
{
PLUGIN_SYNC_METHODS = Methods;
}

void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators)
{
PLUGIN_MEMORY_ALLOCATORS = Allocators;
}

Then to allocate a string or array, I do:


typedef struct
{
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
} __attribute__((__packed__)) PascalArray;

#if defined(FPC2)
typedef struct
{
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
} __attribute__((__packed__)) PascalString;
#else
typedef struct
{
std::uint16_t codePage;
std::uint16_t elementSize;
#if defined(__x86_64__)
std::uint32_t dummy;
#endif
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
} __attribute__((__packed__)) PascalString;
#endif // defined



template<typename T, std::size_t size>
struct __attribute__((__packed__)) StaticPascalArray
{
const std::make_signed_t<std::size_t> refCount = -1;
const std::make_signed_t<std::size_t> length = size;
T data[size] = {0};
};

template<typename T>
int PascalHigh(T* Arr)
{
return reinterpret_cast<int*>(Arr)[-1];
}

template<typename T>
int PascalLength(T* Arr)
{
return PascalHigh<T>(Arr) + 1;
}

template<typename T>
T* AllocateArray(std::size_t size, std::size_t element_size = sizeof(T))
{
std::size_t new_size = (size * element_size) + sizeof(PascalArray);
#if !defined(_MSC_VER)
PascalArray* ptr = static_cast<PascalArray*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size));
#else
PascalArray* ptr = static_cast<PascalArray*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));
#endif
ptr->refCount = 1;
ptr->length = size - 1;
return reinterpret_cast<T*>(++ptr);
}

template<typename T>
T* AllocateString(std::size_t size, std::size_t element_size = sizeof(T))
{
std::size_t new_size = (size * element_size) + sizeof(PascalString);
#if !defined(_MSC_VER)
PascalString* ptr = static_cast<PascalString*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size + 1));
#else
PascalString* ptr = static_cast<PascalString*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size + 1));
#endif
ptr->refCount = 1;
ptr->length = size;
return reinterpret_cast<T*>(++ptr);
}

template<typename T>
T* GetArray(void* ptr, std::size_t* size)
{
PascalArray* mem = static_cast<PascalArray*>(ptr);
return reinterpret_cast<T*>((--mem)->data);
}

template<typename T>
T* GetString(void* ptr)
{
PascalString* mem = static_cast<PascalString*>(ptr);
return reinterpret_cast<T*>((--mem)->data);
}


Usage then becomes:


int* IntArray = AllocateArray(100); //equivalent to SetLength(TIntegerArray, 100);
char* CharArray = AllocateArray(100); //equivalent to SetLength(TCharArray, 100);

char* StrArray = AllocateString(5); //Allocates a string of length 5 in pascal.
strcpy(StrArray, "Hello");

//Array of array:
int** Array2D = AllocateArray(4, sizeof(int)); //explicitly specify we want an array of array of integers (in this example, 4 x 5).
for (int i = 0; i < 4; ++i)
{
Array2D[i] = AllocateArray(5);
Array2D[i][0] = 1;
Array2D[i][1] = 2;
Array2D[i][2] = 3;
Array2D[i][3] = 4;
Array2D[i][4] = 5; //Can also use memcpy or memset to set values..
}

//Alternatively:
Array2D[0][0] = 0;
Array2D[0][1] = 1;
Array2D[0][2] = 2;
//etc..

Array2D[1][0] = 0;
Array2D[1][1] = 1;
Array2D[1][2] = 2;
//etc..

Array2D[2][0] = 0;
Array2D[2][1] = 1;
Array2D[2][2] = 2;
//etc..

You can always specialize the template to allocate array of arrays without specifying `sizeof(whateverType)` by doing: `sizeof(typename std::remove_pointer<T>::type)` instead.. but that's for another day.. Too lazy and most of my code works with this already so..


To export functions, I use Simba's "NATIVE" keyword:


static const char* PascalExports[] =
{
"SomeFunc", "Function SomeFunc(A: Int32; B: String; C: Char): Boolean; native;"
}

void SomeFunc(void** Params, void** Result)
{
int32_t A = ReadPointer(Params[0]); //*static_cast<int32_t*>(Params[0])
char* B = ReadPointer(Params[1]); //*static_cast<char**>(Params[1])
char C = ReadPointer(Params[2]); //*static_cast<char*>(Params[2])

WritePointer(Result, true); //*static_cast<bool*>(Result) = true;
}

alar82

2020-02-01 02:14:36

if !defined(_MSC_VER)
#define DELPHI_CALLING_CONVENTION __attribute__((regparm(3)))

#endif
Between those lines code is grayed out and then goes into red again. Do I have to do something extra?

Brandon

2020-02-01 09:36:09

From: alar82
if !defined(_MSC_VER)
#define DELPHI_CALLING_CONVENTION __attribute__((regparm(3)))

#endif
Between those lines code is grayed out and then goes into red again. Do I have to do something extra?



It's greyed out on purpose because you won't be able to use those structs UNLESS you write assembly to call the functions using the `regparm` calling convention..

So the code that's "outside" of the IFDEF is the new code to use. @Olly; exported it for us to use without having to use `TMemoryManager`.

IE: https://github.com/MerlijnWajer/Simba/issues/340#issuecomment-436310146

The code outside of that is using this.. instead of the TMemoryManager when it's available.

alar82

2020-05-04 07:53:52

Hey Brandon. Now I had time to look at it again. I has put it all together into header file.
In main.

TSimbaMethods PLUGIN_SYNC_METHODS = { 0 };
TSimbaMemoryAllocators PLUGIN_MEMORY_ALLOCATORS = { 0 };


void SetPluginSimbaMethods(TSimbaMethods Methods)
{
PLUGIN_SYNC_METHODS = Methods;
}

void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators)
{
PLUGIN_MEMORY_ALLOCATORS = Allocators;
}
Other header file.

typedef struct
{
void(__cdecl* Sync)(void(__cdecl* synchronize_method)(void*), void* data);
}TSimbaMethods;


typedef struct
{
void* (__cdecl* GetMem)(std::size_t size);
void(__cdecl* FreeMem)(void* ptr);
}TSimbaMemoryAllocators;


typedef struct
{
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
}PascalArray;

#if defined(FPC2)
typedef struct
{
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
}PascalString;
#else
typedef struct
{
std::uint16_t codePage;
std::uint16_t elementSize;
#if defined(__x86_64__)
std::uint32_t dummy;
#endif
std::make_signed_t<std::size_t> refCount;
std::make_signed_t<std::size_t> length;
char data[];
}PascalString;
#endif // defined


template<typename T, std::size_t size>
struct StaticPascalArray
{
const std::make_signed_t<std::size_t> refCount = -1;
const std::make_signed_t<std::size_t> length = size;
T data[size] = { 0 };
};

template<typename T>
int PascalHigh(T* Arr)
{
return reinterpret_cast<int*>(Arr)[-1];
}

template<typename T>
int PascalLength(T* Arr)
{
return PascalHigh<T>(Arr) + 1;
}

template<typename T>
T* AllocateArray(std::size_t size, std::size_t element_size = sizeof(T))
{
std::size_t new_size = (size * element_size) + sizeof(PascalArray);
PascalArray* ptr = static_cast<PascalArray*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));
ptr->refCount = 1;
ptr->length = size - 1;
return reinterpret_cast<T*>(++ptr);
}

template<typename T>
T* AllocateString(std::size_t size, std::size_t element_size = sizeof(T))
{
std::size_t new_size = (size * element_size) + sizeof(PascalString);
PascalString* ptr = static_cast<PascalString*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size + 1));
ptr->refCount = 1;
ptr->length = size;
return reinterpret_cast<T*>(++ptr);
}

template<typename T>
T* GetArray(void* ptr, std::size_t* size)
{
PascalArray* mem = static_cast<PascalArray*>(ptr);
return reinterpret_cast<T*>((--mem)->data);
}

template<typename T>
T* GetString(void* ptr)
{
PascalString* mem = static_cast<PascalString*>(ptr);
return reinterpret_cast<T*>((--mem)->data);
}
It does compile.
But when I try to call AllocateArray(100);
It complains that int type doesn't fit there.

Brandon

2020-05-04 09:14:58

From: alar82

It does compile.
But when I try to call AllocateArray(100);
It complains that int type doesn't fit there.



Hey, it seems MSVC is unable to infer the type of int (template<typename T>) for the template. The error message is just vague. It should have told you that it can't determine the type of `T` for the templates.

So just do:


//Allocating
int* foo = AllocateArray<int>(100); //Explicitly specify to `AllocateArray` that you want `int` type.
float* something = AllocateArray<float>(100); //returns space for simba array with 100 floats.
char* string = AllocateString<char>(100); //returns space for simba string with 100 characters.

//testing..
for (int i = 0; i < 100; ++i)
{
foo[i] = i;
}

Also make sure that SetPluginSimbaMemoryAllocators is actually getting called by your version of Simba :) That's about it. I am using these in my own plugins so I know they work :D



Example: https://gist.github.com/Brandon-T/5e62785acbc949728acef4933c66a319

Hop on the srl discord :D I check it more frequently than the forums.

alar82

2020-05-04 10:54:14

Thanks for helping. Nows it does somethin.

void Testt()
{
int* IntArray = AllocateArray <int>(100);
for (int i = 0; i < 100; ++i)
{
IntArray[i] = i + 1;
}
for (int i = 0; i < 100; ++i)
{
cout << IntArray[i] << endl;
}
}
Is this now stored in simbas memory not just plugins?
Edit:
Now I have changed my main code to.

TSimbaMethods PLUGIN_SYNC_METHODS = { 0 };
TSimbaMemoryAllocators PLUGIN_MEMORY_ALLOCATORS = { 0 };

extern "C" __declspec(dllexport) void SetPluginSimbaMethods(TSimbaMethods Methods)
{
PLUGIN_SYNC_METHODS = Methods;
}

extern "C" __declspec(dllexport) void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators)
{
cout << "SetPluginSimbaMemoryAllocators called" <<endl;
PLUGIN_MEMORY_ALLOCATORS = Allocators;
}

Now SetPluginSimbaMemoryAllocators is definitely being called when plugin is loaded in. Text is printed.

Nows using it, How=?

Added this to pascal exports:

CHAR* PascalExports[] =
{
(CHAR*)"SomeFunc", (CHAR*)"Procedure SomeFunc(A: Int32) native;"
}

Edit44:
It doesn't even matter. It crashes if i try to access that memory. I have attached visual studio debugger thing to see what is happening. Any ideas?
Do I have to declare array before?

Brandon

2020-05-06 11:52:37

From: alar82
Thanks for helping. Nows it does somethin.
Edit44:
It doesn't even matter. It crashes if i try to access that memory. I have attached visual studio debugger thing to see what is happening. Any ideas?
Do I have to declare array before?


Code is good and you've gotten it working for the most part :D. Look at the debugger though. It shows the value 0x63 was passed by value? Without seeing your Simba code, I can't figure out what is missing.
The first thing I noticed is that you are missing a `Result` parameter natively (without this parameter, you will have stack corruption due to invalid function signature).

I wrote some helpers that will read/write automatically for you:



template<typename T>
T PascalRead(void* ptr)
{
return *static_cast<T*>(ptr);
}

template<typename T>
void PascalWrite(void* result, T value)
{
*static_cast<T*>(result) = value;
}



CHAR* PascalExports[] =
{
(CHAR*)"SomeFunc", (CHAR*)"Procedure SomeFunc(A: Int32); native;",
(CHAR*)"SomeFunc2", (CHAR*)"Function SomeFunc2(size: Int32): Array of Int32; native;",
}

void SomeFunc(void** Params, void** Result) //YOU MUST have a Result parameter even if you aren't going to use it (for procedures and functions).
{
int32_t a = PascalRead<int32_t>(Params[0]);
//int32_t b = PascalRead<int32_t>(Params[1]);

//size_t length = 0;
//int32_t* indices = GetArray<int32_t>(PascalRead<void*>(Params[2]), &length);

printf("%d\n", a); //or std::cout<<a<<"\n";
}

void SomeFunc2(void** Params, void** Result)
{
//retrieve parameter..
int32_t size = PascalRead<int32_t>(Params[0]);

//allocate int32 array..
int32_t* arr = AllocateArray<int32_t>(size);
for (int i = 0; i < arr; ++i)
{
arr[i] = i + 1;
}

//Return arr..
PascalWrite(Result, arr);
}

Are you also using: https://github.com/ollydev/SimbaDevelopment/releases
@alar82;

alar82

2020-05-06 17:49:20

From: Brandon

Are you also using: https://github.com/ollydev/SimbaDevelopment/releases
@alar82;

Omg with 1400 build it works!!
I had 1302.

Brandon

2020-05-07 00:07:17

From: alar82
Omg with 1400 build it works!!
I had 1302.



Lol. Yay. Now you can take advantage of Simba's automatic memory management for Strings, Arrays, and Objects (in any language) :)
Primitives are already managed :D

Enjoy. :)

BeatDat

2020-05-07 08:32:01

Which IDE are you using for development/testing?

alar82

2020-05-08 00:15:25

That is a funny question. If you look at this thread carefully u will find out.

BeatDat

2020-05-08 10:32:44

Is it MSVC (Microsoft Visual Stuido with C++)?

alar82

2020-05-08 10:37:43

Hey Olly.
I has noticed some different things using development version.

First plug-in is ejected when using stop button. I am sure that should not be happening. I mean plug-in functions still stay at side list. But if it is intentional then make it optional.
Pic:

Hey Brandon trying to use vector arrays gives no results.
PascalRead<vector<int>>(Params[0]);
First params[0] is declared at pascal exports as array of INT32.
Also noticed this code:
	
PascalArray* mem = static_cast<PascalArray*>(Params[0]);
cout << mem->length << endl;
Give some kind of big number. If it would be standard array length. I could Insert one array into those vector arrays, if I would know size that is.


Edit: @BeatDat yes it is :D Most simplistic IDE there is in my opinion. Community edition 2019, it is free.

Brandon

2020-05-09 17:36:51

From: alar82
….




Hey, because of the error you got, I discovered a bug in my code. It is fixed now.

Change to:


template<typename T>
T* GetArray(void* ptr, std::size_t* size) noexcept
{
PascalArray* mem = static_cast<PascalArray*>(ptr) - 1;
*size = mem->length;
return reinterpret_cast<T*>(mem)->data);
}

It was just a copy paste error. It was reading the length from the raw pointer instead of offsetting to the pascal pointer.
The above should fix it.


Now for the vector stuff, Vector is a container and it's not a raw pointer to int32_t array as you've already mentioned. So the code as it is, will NOT work with vector. You can't do `PascalRead<vector<int>>` because `PascalRead` literally just dereferences and casts the result to template type T.

For any container that you want to read, you have to overload OR specialize the templates using template overloads or specialization..

Add this to your file and now it'll automatically covert a pascal array of type T to a vector of type T where T is primitive. I don't think you need T to be another container or generic?


template<typename T>
std::vector<T> GetArray(void* ptr) noexcept
{
std::size_t size = 0;
T* raw_data = GetArray<T>(ptr, &size);
return std::vector<T>{raw_data, raw_data + size};
}

std::string GetString(void* ptr) noexcept
{
return GetString<char>(ptr);
}

template<typename T>
void PascalWrite(void* result, const std::vector<T> &value)
{
T* array = AllocateArray<T>(value.size());
std::memcpy(array, value.data(), value.size() * sizeof(T));
PascalWrite(result, array);
}

template<typename T>
void PascalWrite(void* result, const std::string &value)
{
T* str = AllocateString<char>(value.size());
std::memcpy(str, value.data(), value.size() * sizeof(T));
PascalWrite(result, str);
}

void Usage_Example(void** Params, void** Result)
{
std::vector<int> vec = GetArray<int>(Params[0]);
std::string str = GetString<char>(Params[1]);
}


Also, the plugin unloading thing is intentional. Don't worry, it's not unloading forever. It's just that scripts are now ran in their own process so if a plugin or script crashes, it will only crash that process and won't crash your Simba itself. The next time you run the script, it will just load the plugin again.
This shouldn't cause you any problems.

alar82

2020-05-10 04:24:31

From: Brandon


Also, the plugin unloading thing is intentional. Don't worry, it's not unloading forever. It's just that scripts are now ran in their own process so if a plugin or script crashes, it will only crash that process and won't crash your Simba itself. The next time you run the script, it will just load the plugin again.
This shouldn't cause you any problems.

My global variables are lost:I

Brandon

2020-05-10 12:48:19

From: alar82
My global variables are lost:I



Global variables in the plugin? They will be re-created since it's a brand new process being spawned.
It's like loading your plugin into 5 Simbas or Simba tabs. They will each have their own memory and variables and stuff.

If you put a `print` in `DllMain`, it you will see that it gets called every time because Simba reloads your plugin.

Advantage: You can overwrite your plugin in the ./Simba/Plugin folder without having to restart Simba every time. If your plugin crashes or access-violates, you do not lose your script or anything.

What exactly do you mean by global variables are lost?

alar82

2020-05-10 14:35:15

From: Brandon

What exactly do you mean by global variables are lost?


Old version:
I has plainly declared variables. Vars like AOB scan results, so I wouldn't need to re-search them. I stop the script they stay there, re-start script instant go. Open different script tab, amazingly it uses those previous vars instantly.
New version:
It also kills my detached debug thread, which is just annoying, I have to keep script running to display it. Loses all vars.
Plain vars:

Although new Simba feels faster than previous.

Brandon

2020-05-11 20:59:54

From: alar82
Old version:
I has plainly declared variables. Vars like AOB scan results, so I wouldn't need to re-search them. I stop the script they stay there, re-start script instant go. Open different script tab, amazingly it uses those previous vars instantly.
New version:
It also kills my detached debug thread, which is just annoying, I have to keep script running to display it. Loses all vars.
Plain vars:

Although new Simba feels faster than previous.


In all honesty, you should be saving them so shm or something and not rely on the process staying alive. MAYBE one day, Simba will implement some communication between tabs or processes.. Alternatively, you can inject into the process itself :D and keep your variables that way.. but you are doing RPM (ReadProcessMemory) right?

Maybe with some preprocessor magic in the future we'd be able to specify that stuff shouldn't unload but for now, I think that's how it'll be (like Chrome where if a tab crashes it doesn't crash the entire process and kill all tabs).

Brandon

2020-05-14 11:33:58

@alar82;

I made a change to my own plugins to support UTF-8. Not sure if you care, but I do suggest using it instead of the default code-page:


template<typename T>
T* AllocateString(std::size_t size, std::size_t element_size) noexcept
{
std::size_t new_size = (size * element_size) + sizeof(PascalString) + sizeof(T);
PascalString* ptr = static_cast<PascalString*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));

ptr->refCount = 1;
ptr->length = size;
ptr->codePage = 65001; //CP_UTF8
ptr->elementSize = sizeof(T);
*reinterpret_cast<char*>(ptr + 1) = '\0';
return reinterpret_cast<T*>(++ptr);
}

This will technically serve you better in the long run. As a side note, if you still want to use Simba 1.3, you can compile your plugin with Mingw instead btw. Then it'll be compatible with 1.3 and you'd still get to keep your global variables.

alar82

2020-05-15 05:19:14

Erm yes.
Anyhow I did find funny video that explains void* usage. https://www.youtube.com/watch?v=j0_u26Vpb4w @16:20. I think this video contains generally useful stuff for beginners.

alar82

2020-06-22 01:18:51

I must ask, can that (void** Result) return multi value. Like c++ structs. So I could send several values in one go, like npc hp and location.
Edit: I has discovered that AllocateArray can take almost any struct and make array out of it, only problem seems to be strings.
Now I has thinkin to return multi values to simba. It is possible by custom record. It has to be declared before loading plugin or simba will be very confused.
Struct in c++:

//for
struct intPOINTtest {
int x;
int y;
//string txt;
};
Call.

(CHAR*)"SomeFunc", (CHAR*)"Function SomeFunc(A: Int32; B: String; C: Char): Array of dataP; native;"


program Test;
//custom data type
type
dataP= record
x: Int32;
y: Int32;
// txt: string;
end;
{$loadlib MemoryError}

begin
wait(199);
SetupRSReading(True,0);
wait(100+random(1099));
repeat
wait(5000+random(1099));

Writeln(SomeFunc(99,"test","t"));

until(false)
end.

Main problem is that it gives runtime error when I use string there, anything else is fine. It allocates fine any struct but when it comes sending it all back to simba it flops.

alar82

2020-06-29 08:13:29

Well this is what I have came up so far:

template<typename T>
T* AllocateArrayTest(std::vector<intPOINTtest> value)
{
std::size_t new_size = (1 * sizeof(UINT32)) + sizeof(PascalArray);
//x
PascalArray* ptr1 = static_cast<PascalArray*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));
ptr1->refCount = 1;
ptr1->length = 1;
std::memcpy(ptr1, value.data(), value.size());
//y
PascalArray* ptr2 = static_cast<PascalArray*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));
ptr2->refCount = 1;
ptr2->length = 1;
std::memcpy(ptr2, value.data(), value.size());
//txt
PascalString* ptr3 = static_cast<PascalString*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));
ptr3->refCount = 1;
ptr3->length = 1;
ptr3->codePage = 65001; //CP_UTF8
ptr3->elementSize = sizeof(T);
*reinterpret_cast<char*>(ptr3 + 1) = '\0';
std::memcpy(ptr3, value.data(), value.size());

cout <<"x:"<< *(int*)ptr1 << endl;
cout <<"y:"<< *(int*)ptr2 << endl;
cout << "txt:" << *(char*)ptr3 << endl;

return 0;
}

1)That memcpy copy's all values, but I need to make it copy only needed values and GetMem(new_size) reserve proper size.

2)Somehow pass it onto each separate dataP member on simba.

3)Can this type record(dataP) made in plugin? I mean functions can be declared. Why not this.

Brandon

2020-07-02 10:17:21

@alar82; Sorry for the late reply.. @Olly; alerted me about the issue a while back.. just haven't had time to reply.. but here goes:


PascalString* ptr3 = static_cast<PascalString*>(PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size));

Does NOT allocate a string! You are casting the memory to a string but that ain't going to work.


#define EXPORT __declspec(dllexport)

static const char* PascalExports[] =
{
"Pascal_Alloc_Struct", "Function Pascal_Alloc_Struct(): Array of SomeRecord; native;"
};

static const char* PascalTypes[] =
{
//You don't have to use multi-line string..
//You can just make it all one string: "packed record x, y: Int32; txt: String; end;".. etc.. if you wanted.
"SomeRecord", "SomeRecord", R"(
packed record
x, y: Int32;
txt: String;
end;
)"
};

static const long int PascalExportCount = sizeof(PascalExports) / (sizeof(PascalExports[0]) * 2);
static const long int PascalTypeCount = sizeof(PascalTypes) / (sizeof(PascalTypes[0]) * 2);

int GetPluginABIVersion() noexcept
{
return 2;
}

int GetFunctionCount() noexcept
{
return PascalExportCount;
}

int GetTypeCount() noexcept
{
return PascalTypeCount;
}

int GetFunctionInfo(int Index, void** Address, char** Definition) noexcept
{
if (Index < PascalExportCount)
{
*Address = (void*)GetProcAddress(module, PascalExports[Index * 2]);
strcpy(*Definition, PascalExports[Index * 2 + 1]);
return Index;
}
return -1;
}

int GetTypeInfo(int Index, char** Type, char** Definition) noexcept
{
if (Index < PascalTypeCount)
{
strcpy(*Type, PascalTypes[Index * 2 + 0]);
strcpy(*Definition, PascalTypes[Index * 2 + 1]);
return Index;
}
return -1;
}


extern "C" {
void Pascal_Alloc_Struct(void** Params, void** Result);
}


typedef struct
{
std::int32_t x;
std::int32_t y;
char* txt;
} __attribute__((__packed__)) SomeRecord; //#pragma push(1) //#pragma pack(1)



void Pascal_Alloc_Struct(void** Params, void** Result)
{
SomeRecord* arr = AllocateArray<SomeRecord>(4); //allocate however many you want..
for (std::uint32_t i = 0; i < 4; ++i) //fill them..
{
arr[i].x = i + 1;
arr[i].y = i + 1;
arr[i].txt = AllocateString<char>(11); //string length..
strcpy(arr[i].txt, "Hello World"); //copy string..
}
PascalWrite(Result, arr);
}

Result:

Succesfully compiled in 121 milliseconds.
[{X = 1, Y = 1, TXT = Hello World}, {X = 2, Y = 2, TXT = Hello World}, {X = 3, Y = 3, TXT = Hello World}, {X = 4, Y = 4, TXT = Hello World}]

alar82

2020-07-03 01:44:17

Hah you are too smart. I could have never figured this stuff out.

alar82

2020-07-04 09:58:09

Hey Brandon, today I tried your code out. It keeps complaining that "SomeRecord" is unknown declaration. Then I decided to do some analysis of Simbas calls.
Setup.

////////////////////////plugincalls////////////////////////////////////
extern "C" int __declspec(dllexport) GetPluginABIVersion()
{
cout << "1" << endl;
return 2;
}

extern "C" int __declspec(dllexport) GetTypeInfo(int Index, char** Type, char** Definition)
{
cout << "2" << endl;
if (Index < PascalTypeCount)
{
strcpy(*Type, PascalTypes[Index * 2 + 0]);
strcpy(*Definition, PascalTypes[Index * 2 + 1]);
return Index;
}
return 0;
}

extern "C" int __declspec(dllexport) GetTypeCount()
{
cout << "3" << endl;
return PascalTypeCount;
}

extern "C" int __declspec(dllexport) GetFunctionInfo(int Index, VOID** Address, CHAR** Definition)
{
cout << "4" << endl;
if (Index < PascalExportCount)
{
*Address = (void*)GetProcAddress(module, PascalExports[Index * 2]);
strcpy(*Definition, PascalExports[Index * 2 + 1]);
return Index;
}
return -1;
}

extern "C" int __declspec(dllexport) GetFunctionCount()
{
cout << "5" << endl;
return PascalExportCount;
}

extern "C" __declspec(dllexport) void SetPluginSimbaMethods(TSimbaMethods Methods)
{
PLUGIN_SYNC_METHODS = Methods;
}

extern "C" __declspec(dllexport) void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators)
{
cout << "SetPluginSimbaMemoryAllocators called" << endl;
PLUGIN_MEMORY_ALLOCATORS = Allocators;
}
///////////////////////////////////////////////////////////////////////
I have added couts to get order, output:

STARTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
5
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
3
2
SetPluginSimbaMemoryAllocators called
Unknown declaration "SomeRecord" at line 1, column 14 in file "!addGlobalType::SomeRecord" at line 3, column 11 in file "C:\Users\Al\Documents\Simba64\Scripts\serenspiritwatch.simba"

I don't know, shouldn't variables to be called before functions trying to use them :P
Edit:Removed function that uses "SomeRecord". Still gives that same error.
It is all copy paste, how it could not work.
static const char* PascalTypes[] =
{
"SomeRecord", "SomeRecord", R"(
packed record
x, y: Int32;
txt: String;
end;
)"
};
Edit:
Changed code into:
static const char* PascalTypes[] =
{
"SomeRecord", "record x, y: Int32; txt: String; end;"

};
Now it works. When clicking in Simbas plugin record thing it shows:
Declaration:
SomeRecord = record x, y: Int32; txt: String; end
But thanks. What is difference between packed record and just record? Btw GetPluginABIVersion is never called/used.

Brandon

2020-07-12 22:49:01

From: alar82

Now it works. When clicking in Simbas plugin record thing it shows:
Declaration:
SomeRecord = record x, y: Int32; txt: String; end
But thanks. What is difference between packed record and just record? Btw GetPluginABIVersion is never called/used.


:o yeah what you have is correct.
The function should be called. It gets called for me :D

The difference between a packed record and regular record is the alignment.

For example:


struct Foo
{
int a;
int b;
char c;
int d;
};

The size of this structure in my compiler is `sizeof(int) * 4 => 16`. WHY though? There are only 3 integers and a single char! Well.. turns out for various reasons, the compiler will `PAD` the structure to align on the nearest DWORD boundary.. IE: That char will be padded to an int..

IF you pack the structure or specify that it should be packed or aligned on a boundary of your choice (IE: 1).. then the compiler will NOT do this padding..

So in order to guarantee that Simba has the same padding as your plugin, we ask Simba to pack the record/struct tightly AND we ask our compiler to do the same for our struct.. Therefore they both have the same alignment and same size and we access the fields at the same offset :)


See: https://stackoverflow.com/questions/4306186/structure-padding-and-packing

alar82

2020-07-13 18:31:33

Hey Brandon I noticed that strcpy wants char* type to copy but in my structs there are strings not chars.
Edit: Made struct specially for exporting out. Now i can:
	 arr[i].Action = AllocateString<char>(AllNPC[i].Name.length());
strcpy(arr[i].Action, AllNPC[i].Name.c_str());
Edit:Nvm.

Brandon

2020-07-25 17:35:29

@alar82 and @here, change the `refCount` to 0 by default (as shown below):


template<typename T>
T* AllocateArray(std::size_t size, std::size_t element_size) noexcept
{
//other code here..
ptr->refCount = 0; //CHANGE THIS LINE TO 0 IN YOUR CODE
//other code here..
}

template<typename T>
T* AllocateString(std::size_t size, std::size_t element_size) noexcept
{
//other code here..
ptr->refCount = 0; //CHANGE THIS LINE TO 0 IN YOUR CODE
//other code here..
}