Announcement

Collapse
No announcement yet.

Bizzare 'C' UDT struct query...

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Bizzare 'C' UDT struct query...

    Hey there! Been "off-line" for a bit, but have recently been
    afforded the opportunity to pick back up on the LightWave 3D
    SDK "C to PB" conversion process after a 6-month hiatus. I've
    gotten several hundred functions & UDTs already converted from
    before, and now remember why I set this particular piece of code
    aside...

    Instead of clouding this message with my best-guesses as to what
    in the snarf this is trying to accomplish, I thought I'd post
    just the original 'C' UDT type-struct here, and post my
    "guess(es)" shortly in a response.

    Normally a UDT would be defined in a Type/EndType block, and in
    this case containing a number of pointers, but the first line and
    the "orphaned" pointers (ie;"const char *" translated to "<???>
    AS ASCIIZ PTR" are really making my brain hurt!

    Where I am right now, I could convince myself that part, if not
    all of this may a funky sort of "type-mismatched" union, but I've
    been looking at these few lines for so long, that I've lost the
    ability to tell the forest from the trees. For argument's sake,
    take it as given that the "info/error/warning/yesno/ttl/etc."
    pointers have already been established elsewhere in the code.

    One thing I should mention is that the plugin SDK files are
    ultimately compiled into a .dll file (extention changed to .p)

    If anyone has some insight into what this is "saying" (psudo-code's
    just fine!), I'd really appreciate the input!

    As this progresses to a point where it is presentable, I will
    make the converted SDK available to any and all interested.

    Thanks in advance for your suggestions!

    ----- 'C' Source ------

    /****
    * Message Functions. This block of functions is returned as the
    * "Info Messages 2" global service. They display various info
    * and other confirmation dialogs to the user.
    * The return codes are as follows:
    * OKCancel ok(1) cancel(0)
    * YesNo yes(1) no(0)
    * YesNoCancel yes(2) no(1) cancel(0)
    * YesNoAll yesAll(3) yes(2) no(1) cancel(0)
    ****
    */
    #define LWMESSAGEFUNCS_GLOBAL "Info Messages 2"

    typedef struct st_LWMessageFuncs {
    void (*info) (const char *, const char *);
    void (*error) (const char *, const char *);
    void (*warning) (const char *, const char *);
    int (*okCancel) (const char *ttl, const char *, const char *);
    int (*yesNo) (const char *ttl, const char *, const char *);
    int (*yesNoCan) (const char *ttl, const char *, const char *);
    int (*yesNoAll) (const char *ttl, const char *, const char *);
    } LWMessageFuncs;


    ------------------
    Scott Martindale
    [email protected]


    ------------------
    Scott Martindale
    [email protected]



    [This message has been edited by Scott J. Martindale (edited December 29, 2001).]
    Scott Martindale
    [email protected]

  • #2
    you need PBDLL 6.1 (BYVAL ... AS ... PTR)
    If this not work you should change the
    TYPE and remove the PTR. Due there are very
    less information i cannot interpret the original
    struct correct.

    Code:
    TYPE st_LWMessageFuncs
        p_info AS DWORD PTR
        p_error AS DWORD PTR
        p_warning AS DWORD PTR
        p_okCancel  AS DWORD PTR
        p_yesNo AS DWORD PTR
        p_yesNoCan AS DWORD PTR
        p_yesNoAll  AS DWORD PTR
    END TYPE
    
    GLOBAL p_MsgFuncs AS st_LWMessageFuncs
    
    DECLARE SUB s_info(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
    DECLARE SUB s_error(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
    DECLARE SUB s_warning(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
    DECLARE FUNCTION f_okCancel(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
    DECLARE FUNCTION f_yesNo(BYVAL ttl AS ASCIIZ PTR, BYVAL char1 AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
    DECLARE FUNCTION f_yesNoCan(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
    DECLARE FUNCTION f_yesNoAll(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
    
    SUB s_info(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
        CALL DWORD [email protected]_info USING s_info(char1, char2)
    END SUB
    
    SUB s_error(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
        CALL DWORD [email protected]_error USING s_error(char1, char2)
    END SUB
    
    SUB s_warning(BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR)
        CALL DWORD [email protected]_warning USING s_warning(char1, char2)
    END SUB
    
    FUNCTION f_okCancel(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
        DIM lResult AS LONG
        CALL DWORD [email protected]_okCancel USING f_okCancel(ttl, char1, char2) TO lResult
        FUNCTION = lResult
    END FUNCTION
    
    FUNCTION f_yesNo(BYVAL ttl AS ASCIIZ PTR, BYVAL char1 AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
        DIM lResult AS LONG
        CALL DWORD [email protected]_yesNo USING f_yesNo(ttl, char1, char2) TO lResult
        FUNCTION = lResult
    END FUNCTION
    
    FUNCTION f_yesNoCan(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
        DIM lResult AS LONG
        CALL DWORD [email protected]_yesNoCan USING f_yesNoCan(ttl, char1, char2) TO lResult
        FUNCTION = lResult
    END FUNCTION
    
    FUNCTION f_yesNoAll(BYVAL ttl AS ASCIIZ PTR, BYVAL char1  AS ASCIIZ PTR, BYVAL char2 AS ASCIIZ PTR) AS LONG
        DIM lResult AS LONG
        CALL DWORD [email protected]_yesNoAll USING f_yesNoAll(ttl, char1, char2) TO lResult
        FUNCTION = lResult
    END FUNCTION
    
    
    FUNCTION PBMAIN
        'your code goes here
        'call into the third party module to fill p_MsgFuncs IMPORTANT!!!!
        'after that you can use the declarations above to
        'call the p_ function in the third party module
    END FUNCTION

    ------------------

    Comment


    • #3
      Wow Torsten, thanks for the lengthy response! This will take a little while to pour over.

      Processing...



      ------------------
      Scott Martindale
      [email protected]
      Scott Martindale
      [email protected]

      Comment


      • #4
        I didn't want to post too much code in the initial post, but if you'd like a well-documented overview of what it is that I'm trying to port to PB, the following url covers it in as much, or as little detail as you'd like-- http://members.home.net/erniew2/lwsdk/docs/index.html

        I'll field any questions about it I can, and you'll know where I'm getting my info from. If any interested parties would like the 'C' "hello world" example source & headers (pretty small files), I'd be happy to send it; just let me know.

        Thanks again!

        ------------------
        Scott Martindale
        [email protected]

        [This message has been edited by Scott J. Martindale (edited December 30, 2001).]
        Scott Martindale
        [email protected]

        Comment


        • #5
          It's good that you have posted the url.

          It seems that you want to create a "plugin" for an other software.
          In this case we have to "reverse" the function pointer work.
          Not the "server" fills the "obscure" struct, but you.

          The server is calling a default function in your library with
          a pointer to the "struct" as a paramter. Your module has implemented the functions. You are filling the struct with the "CODEPTR"'s to your functions and the "server" will know the adresses to call "your" functions.
          BTW, sometimes i have seen some parts that would cause me to think
          variables should be passed "BYREF". You should not use BYREF but always BYVAL ... AS ... PTR. This is in fact the same but you cannot mistake and the code reads much better.

          MyFunc(BYREF MyString as ASCIIZ) as LONG
          should be converted to
          MyFunc(BYVAL MyString AS ASCIIZ PTR) as LONG

          Because you have to find out, who is allocating memory for MyString, and who is responsable for deallocating the memory it is
          easier to deal with such situations. M$ has some "specialists" who made a "plugin" work and
          said "Our Server is calling into your Plugin passing a pointer to a string. After dealing with our string your Plugin should deallocate the memory for the string."

          Regards,

          Torsten Rienow


          ------------------

          Comment


          • #6
            Hi Torsten.

            Thank you again for taking the time and effort to help explain this to me. I know I have my work cut out for me. Though I'm actually looking forward to doing the "grunt-work" (what can I say, I'm a nerd!), it's the C-induced "head scratching" that makes my head hurt! I know I'm preaching to the choir saying this, but the more I delve into C, the more I wonder just what the big fuss about it is? At best, it seems like a huge intuitive step backwards on most fronts...

            On this c-intrepretation topic, the following is part of a recent thread I had with one of the guys who wrote the SDK in the first place. Picking-up at a point where I asked him a near-proverbial "what the heck is this?" C-question. Not completely sure just yet what could be widely useful or relevent here, and what is extraneous, I've left the core of the thread intact-- I think there are more than a few useful "C-Nuggets" to be gleaned from this:

            --------
            <mid-thread, starting with the "what's this? (me writing)>

            ----------- 'C Snippet' -----------

            #define LWMESSAGEFUNCS_GLOBAL "Info Messages 2" /*<< shouldn't there be a ";" here?!*\

            typedef struct st_LWMessageFuncs {
            void (*info) (const char *, const char *);
            void (*error) (const char *, const char *);
            void (*warning) (const char *, const char *);
            int (*okCancel) (const char *ttl, const char *, const char *);
            int (*yesNo) (const char *ttl, const char *, const char *);
            int (*yesNoCan) (const char *ttl, const char *, const char *);
            int (*yesNoAll) (const char *ttl, const char *, const char *);
            } LWMessageFuncs;

            ------- End of Original Code Snippet (still me)-------

            At a very basic level, I get C's basic UDT structure conventions, but things begin to fall apart (assuming that the "define" and "typedef " aren't part of the same structure), but things go south starting at "void (*info) (const char *,const char *);", and are completely fuzzy by "void (*warning) (const char *, const char *);". The "big" one, at least initially, is what (or which) in the snarf is the "const char *" referencing? Some unnamed character string (pointer)?!

            ////////////
            <response from SDK Developer>

            > #define LWMESSAGEFUNCS_GLOBAL "Info Messages 2" /*<< shouldn't there be a ";" here?!*\

            Nope. "#define" is a preprocessor directive. What follows it isn't actual C code. It's an instruction to the preprocessor, kinda like a word processing macro. #define says "Replace all occurrences of this symbol with what follows."

            So everywhere that the code has LWMESSAGEFUNCS_GLOBAL, the preprocessor replaces it with "Info Messages 2".

            "#include" is another preprocessor directive.

            You don't need a ";" at the end of these because they end at the end of the line (although you can have multiline preprocessor directives if you use the "\" continuation character at the end of each line).

            > typedef struct st_LWMessageFuncs {

            "typedef" isn't a preprocessor directive. It's a C keyword, and it's used to create names for types. The following is equivalent to what the SDK headers have.

            struct st_LWMessageFuncs {
            void (*info) (const char *, const char *);
            void (*error) (const char *, const char *);
            void (*warning) (const char *, const char *);
            int (*okCancel) (const char *ttl, const char *, const char *);
            int (*yesNo) (const char *ttl, const char *, const char *);
            int (*yesNoCan) (const char *ttl, const char *, const char *);
            int (*yesNoAll) (const char *ttl, const char *, const char *);
            };

            typedef struct st_LWMessageFuncs LWMessageFuncs;

            This first defines a structure, called st_LWMessageFuncs, and then creates a typedef based on that definition, called LWMessageFuncs. The SDK headers just do it all at once.

            The reasons for using typedef aren't obvious until you go to declare a variable of a structure type. Without the typedef, you'd need

            struct st_LWMessageFuncs *msgf;

            In other words, the "struct" keyword would be required. But since we created the typedef, we can declare msgf this way instead,

            LWMessageFuncs *msgf;

            Saves a little typing.

            > but things go south starting at "void (*info) (const char *, const char *);", and are completely fuzzy by "void (*warning) (const char *, const char *);". The "big" one, at least initially, is what (or which) in the snarf is the "const char *" referencing? Some unnamed character string (pointer)?!

            LWMessageFuncs is a structure containing seven function pointers. Each function pointer is declared with a prototype that specifies the types of the arguments and the function's return value.

            I don't know how PB handles function definitions, but in QB and VB you'd create what they call a "forward" function declaration with something like

            Declare Function Add%( i as integer, j as integer )

            Add%() takes two integer arguments and returns and integer. This is a forward declaration because the function itself isn't being defined here. You're just telling the compiler what the function's arguments and return value are. In C, the prototype of the same function would look like

            int Add( int i, int j );

            In C, though, "i" and "j" are optional in the prototype. So you could write

            int Add( int, int );

            Now, if you need to declare a pointer to the same function, it would look like

            int ( *add )( int, int );

            This says that "add" is a pointer to a function that takes two integer arguments and returns an integer. The "*" is the pointer-to operator, and you put parentheses around "*add" so that the "*" binds to the "add" rather than the preceeding "int". In other words, this isn't a function that returns an int * (pointer to int).

            This,

            void (*info) (const char *, const char *);

            is a pointer to a function that takes two constant strings as arguments and returns nothing. A C function that returns nothing is equivalent to a BASIC subroutine.

            ------ End of Thread ------

            Given what you've shared with me, and what he's passed along, I'm going to "process" this, and will post anything useful I may be able to come across.

            Thanks again, and I'll be reporting-in regularly!


            ------------------
            Scott Martindale
            [email protected]

            [This message has been edited by Scott J. Martindale (edited December 30, 2001).]
            Scott Martindale
            [email protected]

            Comment


            • #7
              I've a question about the code presented in this post. In PBMAIN it states:

              FUNCTION PBMAIN
              'your code goes here
              'call into the third party module to fill p_MsgFuncs IMPORTANT!!!!
              'after that you can use the declarations above to
              'call the p_ function in the third party module
              END FUNCTION
              How would one fill p_MsgFuncs? In the quake2 to powerBASIC conversion I need to do something similar. The main engine (quake2.exe) passes a bunch of functions onto the game engine dll via a type (game_import_t). Below is some of the code for a powerBASIC dll that is linked to the game dll. It shows the type definition with main engine functions defined as dword pointers within the type. I've commented out the c-definitions in the original struct. The subroutine pbCall is called from the game dll. This provides a pointer to the type defined. My question is how I can use this pointer to fill the type structure. Any suggestions?

              TIA

              Code:
              #COMPILE DLL "q2pb.dll"
              #DIM ALL
              
              macro WINAPI             = 1
              macro DLL_PROCESS_DETACH = 0
              macro DLL_PROCESS_ATTACH = 1
              macro DLL_THREAD_ATTACH  = 2
              macro DLL_THREAD_DETACH  = 3
              
              type game_import_t
              
              	'// special messages
              	'void	(*bprintf) (int printlevel, char *fmt, ...);
              	p_bprintf as dword ptr
              	'void	(*dprintf) (char *fmt, ...);
              	p_dprintf as dword ptr
              	'void	(*cprintf) (edict_t *ent, int printlevel, char *fmt, ...);
              	p_cprintf as dword ptr
              	'void	(*centerprintf) (edict_t *ent, char *fmt, ...);
              	p_centerprintf as dword ptr
              	'void	(*sound) (edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs);
              	p_sound as dword ptr
              	'void	(*positioned_sound) (vec3_t origin, edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs);
              	p_positioned_sound as dword ptr
              	'// config strings hold all the index strings, the lightstyles,
              	'// and misc data like the sky definition and cdtrack.
              	'// All of the current configstrings are sent to clients when
              	'// they connect, and changes are sent to all connected clients.
              	'void	(*configstring) (int num, char *string);
              	p_configstring as dword ptr
              	'void	(*error) (char *fmt, ...);
              	p_error as dword ptr
              	'// the *index functions create configstrings and some internal server state
              	'int		(*modelindex) (char *name);
              	p_modelindex as dword ptr
              	'int		(*soundindex) (char *name);
              	p_soundindex as dword ptr
              	'int		(*imageindex) (char *name);
              	p_imageindex as dword ptr
              	'void	(*setmodel) (edict_t *ent, char *name);
              	p_setmodel as dword ptr
              	'// collision detection
              	'trace_t	(*trace) (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passent, int contentmask);
              	p_trace as dword ptr
              	'int		(*pointcontents) (vec3_t point);
              	p_pointcontents as dword ptr
              	'qboolean	(*inPVS) (vec3_t p1, vec3_t p2);
              	p_inPVS as dword ptr
              	'qboolean	(*inPHS) (vec3_t p1, vec3_t p2);
              	p_inPHS as dword ptr
              	'void		(*SetAreaPortalState) (int portalnum, qboolean open);
              	p_SetAreaPortalState as dword ptr
              	'qboolean	(*AreasConnected) (int area1, int area2);
              	p_AreasConnected as dword ptr
              	'// an entity will never be sent to a client or used for collision
              	'// if it is not passed to linkentity.  If the size, position, or
              	'// solidity changes, it must be relinked.
              	'void	(*linkentity) (edict_t *ent);
              	p_linkentity as dword ptr
              	'void	(*unlinkentity) (edict_t *ent);		// call before removing an interactive edict
              	p_unlinkentity as dword ptr
              	'int		(*BoxEdicts) (vec3_t mins, vec3_t maxs, edict_t **list,	int maxcount, int areatype);
              	p_BoxEdicts as dword ptr
              	'void	(*Pmove) (pmove_t *pmove);		// player movement code common with client prediction
              	p_Pmove as dword ptr
              	'// network messaging
              	'void	(*multicast) (vec3_t origin, multicast_t to);
              	p_multicast as dword ptr
              	'void	(*unicast) (edict_t *ent, qboolean reliable);
              	p_unicast as dword ptr
              	'void	(*WriteChar) (int c);
              	p_WriteChar as dword ptr
              	'void	(*WriteByte) (int c);
              	p_WriteByte as dword ptr
              	'void	(*WriteShort) (int c);
              	p_WriteShort as dword ptr
              	'void	(*WriteLong) (int c);
              	p_WriteLong as dword ptr
              	'void	(*WriteFloat) (float f);
              	p_WriteFloat as dword ptr
              	'void	(*WriteString) (char *s);
              	p_WriteString as dword ptr
              	'void	(*WritePosition) (vec3_t pos);	// some fractional bits
              	p_WritePosition as dword ptr
              	'void	(*WriteDir) (vec3_t pos);		// single byte encoded, very coarse
              	p_WriteDir as dword ptr
              	'void	(*WriteAngle) (float f);
              	p_WriteAngle as dword ptr
              	'// managed memory allocation
              	'void	*(*TagMalloc) (int size, int tag);
              	p_TagMalloc as dword ptr
              	'void	(*TagFree) (void *block);
              	p_TagFree as dword ptr
              	'void	(*FreeTags) (int tag);
              	p_FreeTags as dword ptr
              	'// console variable interaction
              '	cvar_t	*(*cvar) (char *var_name, char *value, int flags);
              	p_cvar as dword ptr
              '	cvar_t	*(*cvar_set) (char *var_name, char *value);
              	p_cvar_set as dword ptr
              '	cvar_t	*(*cvar_forceset) (char *var_name, char *value);
              	p_cvar_forceset as dword ptr
              	'// ClientCommand and ServerCommand parameter access
              '	int		(*argc) (void);
              	p_argc as dword ptr
              '	char	*(*argv) (int n);
              	p_argv as dword ptr
              '	char	*(*args) (void);	// concatenation of all argv >= 1
              	p_args as dword ptr
              	'// add commands to the server console as if they were typed in
              	'// for map changing, etc
              	'void	(*AddCommandString) (char *text);
              	p_AddCommandString as dword ptr
              	'void	(*DebugGraph) (float value, int color);
              	p_DebugGraph as dword ptr
              
              end type
              
              GLOBAL p_imports AS game_import_t
              
              sub q_bprintf (byval printlevel as long, byval fmt as asciiz ptr)
              	CALL DWORD [email protected]_bprintf USING q_bprintf(printlevel,fmt)
              end sub
              
              sub q_dprintf(byval fmt as asciiz ptr) 
              	CALL DWORD [email protected]_dprintf USING q_dprintf(fmt)
              end sub
              
              sub q_cprintf (byval ent as dword ptr, byval printlevel as long, byval fmt as asciiz ptr)
              	CALL DWORD [email protected]_cprintf USING q_cprintf(ent,printlevel,fmt)
              end sub
              
              sub q_sound (byval ent as dword ptr, channel as long, soundindex as long, volume as single, attenuation as single, timeofs as single)
              	CALL DWORD [email protected]_sound USING q_sound (ent, channel , soundindex , volume , attenuation , timeofs )
              end sub
              
              FUNCTION LibMain(BYVAL hInstance AS LONG, BYVAL fwdReason AS LONG, BYVAL Reserved AS LONG) EXPORT AS LONG
              
              	SELECT CASE fwdReason
              		CASE DLL_PROCESS_ATTACH
              			FUNCTION = 1
              			EXIT FUNCTION
              		CASE DLL_PROCESS_DETACH
              			EXIT FUNCTION
              		CASE DLL_THREAD_ATTACH
              			EXIT FUNCTION
              		CASE DLL_THREAD_DETACH
              			EXIT FUNCTION
              	END SELECT
              
              END FUNCTION
              
              sub pbCall cdecl alias "pbCall" (byval import as dword ptr) export
              
              
              end sub


              ------------------
              Andre Smit

              Comment

              Working...
              X