Page 1 of 3 123 LastLast
Results 1 to 25 of 55

Thread: Export strings to pascal

  1. #1
    Join Date
    Jul 2009
    Posts
    166
    Mentioned
    5 Post(s)
    Quoted
    69 Post(s)

    Default Export strings to pascal

    I has setup like this:

    Code:
    char* Exports[] =
    {
    	(char*)"ReadUpTxt", (char*)"Function ReadUpTxt(): String;"
    };
    Code:
    extern "C"  __declspec(dllexport) string ReadUpTxt()
    {	
    	return  "p";
    }
    In simba:
    Code:
    Writeln(ReadUpTxt());
    It gives error of access violation. Is something missing? It should just print "p" on simba.

  2. #2
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by alar82 View Post
    I has setup like this:

    Code:
    char* Exports[] =
    {
    	(char*)"ReadUpTxt", (char*)"Function ReadUpTxt(): String;"
    };
    Code:
    extern "C"  __declspec(dllexport) string ReadUpTxt()
    {	
    	return  "p";
    }
    In simba:
    Code:
    Writeln(ReadUpTxt());
    It gives error of access violation. Is something missing? It should just print "p" on simba.


    You can't.. Strings in Simba have the length preprended before the data AND uses a different memory allocation scheme than C or C++ has.. You can see how it does it here: https://github.com/SRL/Lape/blob/mas...array.pas#L248

    Example in C:

    C Code:
    typedef struct
    {
        size_t length;
        char data[];
    } PSString;

    extern "C"  __declspec(dllexport) char* ReadUpTxt()
    {
        PSString *str = malloc(sizeof(PSString) + 1);
        str->length = 1;
        strcpy(str->data, "P");
        return str->data;
    }

    then Simba would print "P". However, since Simba uses a different allocator than C and C++, you can't use malloc because when Simba calls Free() on the data, it will crash..

    So to get around it, you have to do:

    Simba Code:
    function CAllocator(size: integer): Pointer;
    begin
      return AllocMem(size);
    end;

    begin
      Plugin_SetAllocator(@natify(CAlllocator));

      writeln(ReadUpTxt());
    end.

    C Code:
    void* (*simba_allocator)(int size) = NULL;

    void Plugin_SetAllocator(void*(*allocator)(int size))
    {
        simba_allocator = allocator;
    }

    char* make_simba_string(const char* txt)
    {
        size_t len = strlen(txt);
        PSString *str = simba_allocator(sizeof(PSString) + len + 1);
        str->length = len;
        strcpy(str->data, txt);
        return str;
    }

    char *ReadUpTxt()
    {
        return make_simba_string("Hello World!");
    }

    char* Exports[] =
    {
        (char*)"ReadUpTxt", (char*)"Function ReadUpTxt(): String;"
    };


    I had originally proposed that Simba pass allocator functions to plugins on init.. Not sure what became of it.. https://github.com/MerlijnWajer/Simba/issues/340
    It isn't possible to use TMemoryManager in C plugins unless you write TWO plugins (a dynamic one as a memory manager and then the one that does all your stuff).


    The other way is to return a "PChar" type and have Simba code use that PChar to create a string..

    Example:
    C Code:
    char *ReadUpTxt()
    {
        return make_simba_string("Hello World!");
    }

    char* Exports[] =
    {
        (char*)"ReadUpTxt", (char*)"Function ReadUpTxt(): PChar;"
    };

    Simba Code:
    function PCharToStr(P: PChar): String;
    var
      L: Integer;
      PP: PChar := P;
    begin
      while (PP^ <> #0) do
      begin
        Inc(PP); Inc(L);
      end;
      SetLength(Result, L + 1);
      MemMove(P^, Result[1], L);
    end;


    var
      str: String;
    begin
      str := PCharToStr(ReadUpTxt());
      writeln(str);
    end.
    Last edited by Brandon; 02-26-2017 at 05:47 PM.
    I am Ggzz..
    Hackintosher

  3. #3
    Join Date
    Oct 2011
    Posts
    805
    Mentioned
    21 Post(s)
    Quoted
    152 Post(s)

    Default

    Quote Originally Posted by Brandon
    then Simba would print "P". However, since Simba uses a different allocator than C and C++, you can't use malloc because when Simba calls Free() on the data, it will crash..
    I had this problem writing one of my plugin. I wasn't able to create array inside plugin and pass it to simba without crash or building up memory, so I failed. Good to know you can go around it with new Simba.

  4. #4
    Join Date
    Feb 2007
    Location
    Het ademt zwaar en moedeloos vannacht.
    Posts
    7,211
    Mentioned
    26 Post(s)
    Quoted
    72 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    It isn't possible to use TMemoryManager in C plugins unless you write TWO plugins (a dynamic one as a memory manager and then the one that does all your stuff).
    Hmm that shouldn't be true. The calling convention isn't the nicest but there is code out there doing just that (using the memory manager from C) with just 1 plugin. Just make sure the calling conventions are right.
    IIRC BenLand100 has some code taking care of just that. Might be hidden somewhere though.
    I made a new script, check it out!.

  5. #5
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Markus View Post
    Hmm that shouldn't be true. The calling convention isn't the nicest but there is code out there doing just that (using the memory manager from C) with just 1 plugin.
    I very much doubt that. Wrapping C allocation schemes in FPC is one thing. The other way around.. won't be happening.

    http://bugs.freepascal.org/view.php?id=22272

    1. FPC doesn't produce static libraries that are compatible with gcc or g++ or msvc and it never will as per the link above. This means you need to create a dynamic library that handles the memory manager and then your plugin will link to it (IE: 2 plugins pretty much).

    2. C cannot call GetMemoryManager or SetMemoryManager because those only exist in Free Pascal. In other words, even if you did create a memory manager, you have no way of replacing the one Simba uses: http://pastebin.com/S65R0MAW as shown on the github issue I linked in the previous post. The only way to do this is to compile your plugin to object files and then link it against an FPC wrapper into a single .dll/.so.

    3. Even though you can use allocators and malloc and whatever else, there is no way to tell Simba to use them at this point in time from a non-fpc plugin.


    I've asked on Stackoverflow and the FPC support mailing list and their forums. The answer everywhere was "no".
    Last edited by Brandon; 02-27-2017 at 12:57 AM.
    I am Ggzz..
    Hackintosher

  6. #6
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    ...
    Why wouldn't this work? (In c++ code...)
    Simba Code:
    GetMemoryManager(MemManager);
    Ptr := MemManager.AllocMem(16);
    It's the same as what you've said above with Plugin_SetAllocater? Plugin's only copy Simba's memory manager, they don't change it.
    Last edited by Olly; 02-27-2017 at 02:00 AM.

  7. #7
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Olly View Post
    Why wouldn't this work? (In c++ code...)
    Simba Code:
    GetMemoryManager(MemManager);
    Ptr := MemManager.AllocMem(16);
    It's the same as what you've said above with Plugin_SetAllocater? Plugin's only copy Simba's memory manager, they don't change it.
    Because no other language has such a function called "GetMemoryManager". No such thing exists.

    Plugin_SetAllocator is a function that you write in your plugin and it calls a Simba function with Natify in order to get Simba to allocate for you. Hence "workaround".. That is what I was saying in the github ticket.

    Right now, Simba has: procedure OnAttach(info: Pointer); where info is always null. This can be extended to be a pointer to the current memory manager without breaking any existing plugins or anything at all.
    Last edited by Brandon; 02-27-2017 at 02:26 AM.
    I am Ggzz..
    Hackintosher

  8. #8
    Join Date
    Feb 2007
    Location
    Het ademt zwaar en moedeloos vannacht.
    Posts
    7,211
    Mentioned
    26 Post(s)
    Quoted
    72 Post(s)

    Default

    True, but in C you control pretty much the entire memory layout yourself. Replace malloc by Simba's allocater for stuff you export to Simba and you should be fine, right?
    I made a new script, check it out!.

  9. #9
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Markus View Post
    True, but in C you control pretty much the entire memory layout yourself. Replace malloc by Simba's allocater for stuff you export to Simba and you should be fine, right?
    That is exactly what my first post is doing. Same thing is explained on the github issue: https://github.com/MerlijnWajer/Simba/issues/340

    I was just saying that this should be built into Simba's plugin interface so "users" do not have to set a memory allocator manually in their scripts. After all, this would have to be called by the user at the beginning of the main "begin - end." block and they'd have to natify it with a dummy function.
    Last edited by Brandon; 02-27-2017 at 02:40 AM.
    I am Ggzz..
    Hackintosher

  10. #10
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    Because no other language has such a function called "GetMemoryManager". No such thing exists.

    Plugin_SetAllocator is a function that you write in your plugin and it calls a Simba function with Natify in order to get Simba to allocate for you. Hence "workaround".. That is what I was saying in the github ticket.

    Right now, Simba has: procedure OnAttach(info: Pointer); where info is always null. This can be extended to be a pointer to the current memory manager without breaking any existing plugins or anything at all.
    Simba calls "SetPluginMemManager" if it's a exported on attach, there you can grab all the info you need if you can recreate the TMemoryManager struct in other langages.
    (Pascal example)
    Simba Code:
    procedure SetPluginMemManager(MemMgr : TMemoryManager); callconv export;
    begin
      Writeln('Simbas GetMem is ', HexStr(MemMgr.GetMem));
    end;
    MemMgr parameter will be filled with all Simba's memory methods.

    https://github.com/MerlijnWajer/Simb...ugins.pas#L166

    It's exactly what you want? Correct me if i'm wrong.

    e: I guess the naming is shit it should be GetPluginMemManager rather than SetPluginMemManager.
    Last edited by Olly; 02-27-2017 at 03:00 AM.

  11. #11
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Olly View Post
    Simba calls "SetPluginMemManager" if it's a exported on attach, there you can grab all the info you need if you can recreate the TMemoryManager struct in other langages.
    (Pascal example)
    Simba Code:
    procedure SetPluginMemManager(MemMgr : TMemoryManager); callconv export;
    begin
      Writeln('Simbas GetMem is ', HexStr(MemMgr.GetMem));
    end;
    MemMgr parameter will be filled with all Simba's memory methods.

    https://github.com/MerlijnWajer/Simb...ugins.pas#L166

    It's exactly what you want? Correct me if i'm wrong.

    e: I guess the naming is shit it should be GetPluginMemManager rather than SetPluginMemManager.

    I tried this before. I'll try it again. That was exactly what I wanted.
    I am Ggzz..
    Hackintosher

  12. #12
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    @Olly;

    That works. At least it works for arrays. I couldn't get it to work for Strings yet. Simba possibly allocates strings differently than arrays (I tried shortstring, ansistring, string: http://wiki.freepascal.org/Character_and_string_types). I used calling convention from: https://en.wikipedia.org/wiki/X86_ca...rland_register otherwise it crashes.

    Will have to look into why string doesn't work later.


    MemoryManager.hxx
    C++ Code:
    #ifndef MEMORYMANAGER_HXX_INCLUDED
    #define MEMORYMANAGER_HXX_INCLUDED

    #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 // MEMORYMANAGER_HXX_INCLUDED


    Exports.hxx:
    C++ Code:
    #ifndef EXPORTS_HXX_INCLUDED
    #define EXPORTS_HXX_INCLUDED

    #include <cstdint>
    #include <cstdio>
    #include <new>
    #include "MemoryManager.hxx"

    #if defined(_WIN32) || defined(_WIN64)
    #include <windows.h>

    #define DLL_FUNC __declspec(dllexport)
    #else
    #define DLL_FUNC
    #endif

    extern HMODULE module;

    //extern TMemoryManager PLUGIN_MEMORY_MANAGER;


    static const char* PascalExports[] =
    {
        "GetData", "Function GetPluginData(): array of char;" //array of char works. string, ansistring, shortstring does not.
    };

    static const char* PascalTypes[] =
    {
        "", ""
    };

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

    #ifdef __cplusplus
    extern "C"
    {
    #endif
    DLL_FUNC int GetPluginABIVersion();
    DLL_FUNC int GetFunctionCount();
    DLL_FUNC int GetTypeCount();
    DLL_FUNC int GetFunctionInfo(int Index, void** Address, char** Definition);
    DLL_FUNC int GetTypeInfo(int Index, char** Type, char** Definition);

    DLL_FUNC void SetPluginMemManager(TMemoryManager MemMgr);
    DLL_FUNC void OnAttach(void* info);
    DLL_FUNC void OnDetach();

    DLL_FUNC void* GetData();
    #ifdef __cplusplus
    }
    #endif


    #endif // EXPORTS_HXX_INCLUDED

    Exports.cxx
    C++ Code:
    #include "Exports.hxx"

    HMODULE module = nullptr;
    TMemoryManager PLUGIN_MEMORY_MANAGER = {0};

    int GetPluginABIVersion()
    {
        return 2;
    }

    int GetFunctionCount()
    {
        return PascalExportCount;
    }

    int GetTypeCount()
    {
        return PascalTypeCount;
    }

    int GetFunctionInfo(int Index, void** Address, char** Definition)
    {
        if (Index < PascalExportCount)
        {
            #if defined(_WIN32) || defined(_WIN64)
            *Address = (void*)GetProcAddress(module, PascalExports[Index * 2]);
            #else
            *Address = (void*)dlsym(RTLD_DEFAULT, PascalExports[Index * 2]);
            #endif
            strcpy(*Definition, PascalExports[Index * 2 + 1]);
            return Index;
        }
        return -1;
    }

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

    void SetPluginMemManager(TMemoryManager MemMgr)
    {
        PLUGIN_MEMORY_MANAGER = MemMgr;
    }

    void OnAttach(void* info)
    {
    }

    void OnDetach()
    {
    }

    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* Allocate(std::size_t size, std::size_t element_size = sizeof(T))
    {
        std::size_t new_size = (size * element_size) + (sizeof(int) * 2);
        std::int32_t* ptr = static_cast<std::int32_t*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size));
        *ptr++ = 1; //ref_count
        *ptr++ = size - 1; //data_size
        return reinterpret_cast<T*>(ptr);
    }

    void* GetData()
    {
        const char* res = "Hello World!";
        char* str = Allocate<char>(strlen(res));
        strcpy(&str[0], &res[0]);
        return &str[0];
    }
    Last edited by Brandon; 02-27-2017 at 05:47 AM.
    I am Ggzz..
    Hackintosher

  13. #13
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

  14. #14
    Join Date
    Sep 2006
    Posts
    6,089
    Mentioned
    77 Post(s)
    Quoted
    43 Post(s)

    Default

    Strings are null terminated, so reserve strlen+1 instead of strlen. Also, when Simba switches to FPC 3.0, you have to prepend the codepage of the string (next to length/refcount).
    Hup Holland Hup!

  15. #15
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by nielsie95 View Post
    Strings are null terminated, so reserve strlen+1 instead of strlen. Also, when Simba switches to FPC 3.0, you have to prepend the codepage of the string (next to length/refcount).
    That didn't work. It looks like I can't export "string" type. Instead, I found a workaround and exported "^string".


    Simba Code:
    {$loadlib SimbaPlugin}

    var
      str: ^string;
    begin
      str := GetPluginString();
      writeln(str^); //works fine. If this was NOT a pointer to a string, it would print "nothing".

      writeln(GetPluginArray());
    end.

    I do that because even if I return the correct structure and alignment, Simba sees it as nil no matter what. However, if I return a pointer to it, then Simba sees it as pString (pointer to string) and it isn't nil.

    For some reason, if I return an AnsiString directly, it will not work (the returned string is somehow nil). If I return PAnsiString, it will work. That's fine though.

    I got the correct structures from: https://github.com/graemeg/freepasca...trings.inc#L41


    Allocators in addition to the ones in my previous post.
    C++ Code:
    static const char* PascalExports[] =
    {
        "GetString", "Function GetPluginString(): ^string;",
        "GetArray", "Function GetPluginArray(): array of char;"
    };

    #ifdef __cplusplus
    extern "C"
    {
    #endif
    DLL_FUNC void* GetString();
    DLL_FUNC void* GetArray();
    #ifdef __cplusplus
    }
    #endif


    C++ Code:
    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))
    {
        typedef struct
        {
            std::int32_t refCount;
            std::int32_t length;
            char data[];
        } FPCArray;

        std::size_t new_size = (size * element_size) + sizeof(FPCArray);
        FPCArray* ptr = static_cast<FPCArray*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size));
        ptr->refCount = 1;
        ptr->length = size - 1;
        return reinterpret_cast<T*>(++ptr);
    }

    template<typename T, bool isFPC3 = false>
    T* AllocateString(std::size_t size, std::size_t element_size = sizeof(T))
    {
        if (isFPC3)
        {
            typedef struct
            {
                std::uint16_t codePage;
                std::uint16_t elementSize;
                #if defined(__x86_64__)
                std::uint32_t dummy;
                #endif
                std::int32_t refCount;
                std::int32_t length;
                char data[];
            } __attribute__((__packed__)) FPCAnsiString;

            std::size_t new_size = (size * element_size) + sizeof(FPCAnsiString);
            FPCAnsiString* ptr = static_cast<FPCAnsiString*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size + 1));
            ptr->codePage = CP_ACP;
            ptr->elementSize = element_size;
            ptr->refCount = 1;
            ptr->length = size;
            return reinterpret_cast<T*>(++ptr);
        }

        typedef struct
        {
            std::int32_t refCount;
            std::int32_t length;
            char data[];
        } __attribute__((__packed__)) FPCAnsiString;

        std::size_t new_size = (size * element_size) + sizeof(FPCAnsiString);
        FPCAnsiString* ptr = static_cast<FPCAnsiString*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size + 1));
        ptr->refCount = 1;
        ptr->length = size;
        return reinterpret_cast<T*>(++ptr);

    }

    void* GetString()
    {
        auto address_of_ptr = [](void** ptr){
            return static_cast<void*>(ptr); //GCC doesn't let you return the address of local variable (compiler optimizes out/removes the code). This workaround fixes that.
        };

        const char* cpy = "Hello World";
        char* data = AllocateString<char>(strlen(cpy) + 1);
        strcpy(data, cpy);
        return address_of_ptr(reinterpret_cast<void**>(&data));
    }

    void* GetArray()
    {
        const char* cpy = "Hello World";
        char* data = AllocateArray<char>(strlen(cpy));
        strcpy(data, cpy);
        return data;
    }


    Oh well. At least we can export strings.
    Last edited by Brandon; 02-28-2017 at 02:52 AM.
    I am Ggzz..
    Hackintosher

  16. #16
    Join Date
    Sep 2006
    Posts
    6,089
    Mentioned
    77 Post(s)
    Quoted
    43 Post(s)

    Default

    Simba Code:
    function AllocateString(Len: SizeInt): Pointer;
    begin
      if (Len < 1) then
        Exit(nil);
      Result := GetMem(Len + 1 + SizeOf(SizeInt)*2);
      SizeInt(Result^) := 1;
      Inc(Result, SizeOf(SizeInt));
      SizeInt(Result^) := Len;
      Inc(Result, SizeOf(SizeInt));
    end;

    const
      c = 'Hello World!';
    var
      s: String;
    begin
      s := String(AllocateString(Length(c)));
      WriteLn(Length(c), ' = ', Length(s), ' -> ', Length(c) = Length(s));
      MemMove(c[1], s[1], Length(c)+1);
      WriteLn(c, ' = ', s, ' -> ', c = s);
    end.

    It works for me. What problems are you running into?
    Hup Holland Hup!

  17. #17
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by nielsie95 View Post
    Simba Code:
    function AllocateString(Len: SizeInt): Pointer;
    begin
      if (Len < 1) then
        Exit(nil);
      Result := GetMem(Len + 1 + SizeOf(SizeInt)*2);
      SizeInt(Result^) := 1;
      Inc(Result, SizeOf(SizeInt));
      SizeInt(Result^) := Len;
      Inc(Result, SizeOf(SizeInt));
    end;

    const
      c = 'Hello World!';
    var
      s: String;
    begin
      s := String(AllocateString(Length(c)));
      WriteLn(Length(c), ' = ', Length(s), ' -> ', Length(c) = Length(s));
      MemMove(c[1], s[1], Length(c)+1);
      WriteLn(c, ' = ', s, ' -> ', c = s);
    end.

    It works for me. What problems are you running into?

    That is the exact equivalent of:
    C++ Code:
    template<typename T>
    T* AllocateString(std::size_t size, std::size_t element_size = sizeof(T))
    {
        typedef struct
        {
            std::int32_t refCount;
            std::int32_t length;
            char data[]; //0 length array.
        } __attribute__((__packed__)) FPCAnsiString;  //sizeof this entire struct is sizeof(refCount) + sizeof(length) = sizeof(int32_t) * 2.
       
        std::size_t new_size = (size * element_size) + sizeof(FPCAnsiString);  //new_size = refCount + length + stringLength
        FPCAnsiString* ptr = static_cast<FPCAnsiString*>(PLUGIN_MEMORY_MANAGER.AllocMem(new_size + 1));  //Plus 1 for null-terminator.
        ptr->refCount = 1; //refCount = 1.
        ptr->length = size; //length = length of string.
        return reinterpret_cast<T*>(++ptr);  //Point to:  mem_ptr + sizeof(refCount) + sizeof(length);
    }

    char* GetPluginString()
    {
        const char* c = "Hello World";
        char* str = AllocateString<char>(strlen(c)); //Allocate an FPC string.
        memcpy(&str[0], &c[0], strlen(c) + 1);  //Copy contents into that string.
        return str;
    }

    PASCAL_EXPORT("GetPluginString", "Function GetPluginString(): String;");  //Return string.
    PASCAL_EXPORT("GetPluginString", "Function GetPluginStringPtr(): Pointer;");  //Return pointer.

    Then in Simba:
    Simba Code:
    {$loadlib StringPlugin}

    var
      str: String;
    begin
      str := GetPluginString(); //Get String.
      writeln(str);  //Prints blank line.
      writeln(@str); //Prints NIL.

      str := string(GetPluginStringPtr());  //Get Pointer. (Same as above but different return type).
      writeln(str); //Prints "Hello World".
      writeln(@str); //Prints "str::0xXXXXXX (Hello World)"
    end.


    I'm not sure why. If I export "string" as the return type, it turns out nil 100% of the time. If I return "pointer" instead and then cast to string in Simba, it works 100% of the time. I gave up trying to figure out why because there's no logic behind it. That is why I returned address of the string instead and then on Simba side I dereference it.
    Last edited by Brandon; 02-28-2017 at 02:06 PM.
    I am Ggzz..
    Hackintosher

  18. #18
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    ...
    Can you upload SimbaPlugin.dll? I've got a feeling it's a FFI problem that was fixed in more recent (non released) Simba versions.

    edit:

    Or test with RC6, it will leak memory but that's a known issue.
    Last edited by Olly; 03-01-2017 at 03:33 AM.

  19. #19
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Olly View Post
    Can you upload SimbaPlugin.dll? I've got a feeling it's a FFI problem that was fixed in more recent (non released) Simba versions.

    edit:

    Or test with RC6, it will leak memory but that's a known issue.

    Everything: SimbaPlugin.zip

    Contains:
    - Simba script
    - Codeblocks project & source
    - Pre-built/Compiled Simba plugin/binary.

    Tested with Simba r1201, No cigar. The "GetString.simba" script will have 3 outputs: "String", "StringPointer", and "Array".
    Last edited by Brandon; 03-05-2017 at 03:30 PM.
    I am Ggzz..
    Hackintosher

  20. #20
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    Everything: SimbaPlugin.zip

    Contains:
    - Simba script
    - Codeblocks project & source
    - Pre-built/Compiled Simba plugin/binary.

    Tested with Simba r1201, No cigar. The "GetString.simba" script will have 3 outputs: "String", "StringPointer", and "Array".
    My final suggestion is that you can try without using FFI but with normal Lape exports (if a port is possible).

    https://github.com/Olly-/libLayer/bl...r/libLayer.lpr

    Note the "native;" at the end of the export header.

  21. #21
    Join Date
    Jul 2009
    Posts
    166
    Mentioned
    5 Post(s)
    Quoted
    69 Post(s)

    Default

    Does Simba support dynamic arrays? http://wiki.freepascal.org/Dynamic_array. As I understand with this we wouldn't need to tell array length before. Tried to get to get simple integers array into plugin, but I am not quite sure how :I
    Hm maybe not.
    Code:
    formWriteln: Error: Type expected at line 6
    Brandon code is assome but its bit to complicated.

  22. #22
    Join Date
    Feb 2012
    Location
    Norway
    Posts
    995
    Mentioned
    145 Post(s)
    Quoted
    596 Post(s)

    Default

    Quote Originally Posted by alar82 View Post
    Does Simba support dynamic arrays? http://wiki.freepascal.org/Dynamic_array. As I understand with this we wouldn't need to tell array length before. Tried to get to get simple integers array into plugin, but I am not quite sure how :I
    Hm maybe not.
    Code:
    formWriteln: Error: Type expected at line 6
    Brandon code is assome but its bit to complicated.
    Yes dynamic arrays are supported. I am not sure what you mean by "tell array length before", dynamic arrays are equal to how strings are stored, meaning it has a refcount field, and a length field before the contents.
    Dynamic arrays are what we mean when we say "array"... for the most of it. Static arrays (arrays that can't be resized) is the other array type `array [0..10] of Int32`.

    Edit: Someone tipped me off that you might have taken their example as actual working code. A dynarray would be declared like this:
    > var myArr: array of Int32;
    where you can replace Int32 with any declared type.
    Last edited by slacky; 05-25-2017 at 09:36 AM.
    !No priv. messages please

  23. #23
    Join Date
    Jul 2009
    Posts
    166
    Mentioned
    5 Post(s)
    Quoted
    69 Post(s)

    Default

    Yes array of int32 :P
    Anyway this is the cheapest hax I invented to get it working only with few lines:

    Simba Code:
    Findobj2([3279978496,3295100928,1,2,3,4,5,6],8,25);

    Code:
    char* PascalExports[] =
    {
    (char*)"Findobj2", (char*)"Function Findobj2(obj:array of Int32;size,d:Int32): Boolean;"
    };
    Code:
    extern "C"  __declspec(dllexport) BOOL Findobj2(DWORD* obj, DWORD size, DWORD d)
    {
    	vector<DWORD> objectsArray;
    	for (int i = 0; i < size; i++) { 
    		objectsArray.push_back(obj[i]);
    	}
    	return  FindSObj(objectsArray, d);;
    }
    obj - a pointer to write simba given array to somewhere into plugin memory? I think=?
    size - is array length of given array coming from simba.
    d - game stuff.
    Data is taken from "somewhere" by obj, as it came by sectors, it can read by sectors. Written then over to objectsArray 1 by 1. Findobj takes vector array and loops through it.

    Any issues with it?
    Can it be done better?

  24. #24
    Join Date
    Nov 2011
    Location
    England
    Posts
    3,072
    Mentioned
    296 Post(s)
    Quoted
    1094 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    Everything...
    Your code does actually work! A bug with FFI seems to stop the raw string method from working, I exported a method that bypasses FFI and it seems to work fine. Only issue is that the trailing #0 character is visible, but that's a simple fix.
    Code:
    FFI String: ""
    Native String: "Hello World"
    Added code:
    C++ Code:
    void GetString_Native(void** Params, void** Result)
    {
        const char* cpy = "Hello World";
        char* data = AllocateString<char>(strlen(cpy) + 1);
        strcpy(data, cpy);
        *Result = (void*) data;
    }

    static const char* PascalExports[] =
    {
        "GetString_Native", "Function GetPluginString_Native(): string; native;",
    };

    I have also attached the entire codeblocks project.
    Attached Files Attached Files
    Last edited by Olly; 06-03-2017 at 02:22 PM.

  25. #25
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Olly View Post
    Your code does actually work! A bug with FFI seems to stop the raw string method from working, I exported a method that bypasses FFI and it seems to work fine. Only issue is that the trailing #0 character is visible, but that's a simple fix.
    Code:
    FFI String: ""
    Native String: "Hello World"
    Added code:
    C++ Code:
    void GetString_Native(void** Params, void** Result)
    {
        const char* cpy = "Hello World";
        char* data = AllocateString<char>(strlen(cpy) + 1);
        strcpy(data, cpy);
        *Result = (void*) data;
    }

    static const char* PascalExports[] =
    {
        "GetString_Native", "Function GetPluginString_Native(): string; native;",
    };

    I have also attached the entire codeblocks project.
    Nice! Glad it worked! Yeah the trailing char shouldn't be too hard. We can just minus one from the AllocateString length reference within the function itself. Mistake on my part.

    So you bypass FFI by exporting the method with Params array and Results array? Looks similar to how Simba exports its internal functions. :O Actually didn't know you could do that..
    Last edited by Brandon; 06-03-2017 at 05:32 PM.
    I am Ggzz..
    Hackintosher

Page 1 of 3 123 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •