Jump to content


Photo

Gmaker C/c++ Library V. 2


  • Please log in to reply
177 replies to this topic

#1 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 22 February 2009 - 02:59 AM

GMaker C/C++ Library V. 2

What can I do with this library?

It's basically meant to be used with Game Maker DLLs period, but it's greatest feature is to easily use any Game Maker function within your DLL!

Can I use this with my DLL?

Of course! No restrictions, no credit required.

May I compile this library for other languages?

That is exactly why I made it open-source. I want people to add things to it, and make it greater than it really is.
If your going to make a topic with your library, and you used mine, you should probably link to this topic somewhere in it, and make sure the source to your library is available to anyone.

In this version forward, it will be open source for anybody to convert it to other languages.
Actually, you can probably just include the .cpp and .h file and use it in any compiler with some syntax changes.

DLLs that use this library (PM me if you want yours added):

system.dll: Threading, assembly, and more.
GMModelEx: GM Model Extender
D3D Particle System: Fast particle system for 3D!
GMFMODSimple


The download link for the compiler-specific library is below:
MingW (Dev-C++): Download

Edited by X-tra Fear, 12 April 2009 - 03:55 AM.

  • 0

#2 coolsmile

coolsmile

    Programmer

  • New Member
  • 1346 posts

Posted 22 February 2009 - 03:17 AM

That's pretty nice, but how does it work exactly?
  • 0

#3 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 22 February 2009 - 03:28 AM

That's pretty nice, but how does it work exactly?


How the library works, or how using it works?
  • 0

#4 coolsmile

coolsmile

    Programmer

  • New Member
  • 1346 posts

Posted 22 February 2009 - 03:45 AM

How does the library work exactly?
  • 0

#5 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 22 February 2009 - 03:47 AM

How does the library work exactly?


It takes advantage of a list of functions the game keeps in memory, then calls the function using it's function pointer (found in that list).

Probably need a lot more details, but I don't feel like explaining it right now. :D

Edited by X-tra Fear, 28 February 2009 - 02:37 PM.

  • 0

#6 coolsmile

coolsmile

    Programmer

  • New Member
  • 1346 posts

Posted 22 February 2009 - 04:01 AM

Sounds simple enough :D just some calls to GetProcAddress
I'm suprised people didn't think of that before
  • 0

#7 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 22 February 2009 - 04:40 AM

Sounds simple enough :D just some calls to GetProcAddress
I'm suprised people didn't think of that before


You'd think. More complex than that. :P
  • 0

#8 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14389 posts
  • Version:GM:Studio

Posted 22 February 2009 - 12:08 PM

Are you saying that GM has a the names of its script function in the clear so I can get its address as easilly as you show for show_message?

So I could do a

GMPROC* ds_list_add = (GMPROC*) GetGMProcAddress("ds_list_add");

or
GMPROC* d3d_mode_draw = (GMPROC*) GetGMProcAddress("d3d_mode_draw");


Anyway, a few tips

1) Have an init function which will get the prointers to all the functions when its called
2) define a function to match the gm function name
3) define the proper operators so the casts are seamless

example
GMPROC* pexecute_string;
GMPROC* pds_list_add;
GMPROC* pds_list_create;
GMPROC* pds_list_find_value;
GMPROC* pds_list_destroy;
...

export double DLLInit(char* exeName)
{ 
  GetSystemInfo(&systemInfo);
  asmPointer = 0;
  
  // Get Game Maker version
  GMVersion = GetGMVersion();
  if(!GMVersion)
  {
	MessageBox(0, "This dll is incompatible with this version of Game Maker!", "System", 16);
	return false;
  }
  
  // Get Game in debug mode
  debugMode = GetGMDebugMode();
  
  // Get the pointer to execute_string
  pexecute_string = (GMPROC*) GetGMProcAddress("execute_string");
  pds_list_add = (GMPROC*) GetGMProcAddress("pds_list_add");
  pds_list_create = (GMPROC*) GetGMProcAddress("pds_list_create");
  pds_list_find_value= (GMPROC*) GetGMProcAddress("pds_list_find_value");
  pds_list_destroy= (GMPROC*) GetGMProcAddress("pds_list_delete");
  
  return true;
}

GMVariable gm_ds_list_add(GMVariable l, GMVariable v)
{
  GMVariable args[2];
  args[0] = l;
  args[1] = v;
  return GMProcCall(pds_list_add, 2, args);
}

GMVariable gm_ds_find_value(GMVariable l,GMVariable ind)
{
   GMVariable args[1];
   args[0] = l;
   args[1] = ind;
   return GMProcCall(pds_find_value, 1, args);
}

GMVariable gm_ds_list_create()
{
  return GMProcCall(pds_list_create, 0, NULL);
}

GMVariable gm_ds_list_destroy(GMVariable l)
{
  GMVariable args[1];
  args[0] = GMVariableCreate(mess);
  return GMProcCall(pds_list_destroy, 1, args);
}


now, in my c++ code I call stuff looking close to what it looks like in GML

double lid = gm_ds_list_create();
gm_ds_list_add(lid,1);
gm_ds_list_add(lid,2.2);
gm_ds_list_add(lid,"Another Item");

double v1 = gm_ds_list_find_value(lid,0)
double v2 = gm_ds_list_find_value(lid,1)
char *v3 = gm_ds_list_find_value(lid,2)

gm_ds_list_destroy(lid);


Oh, BTW, I dont want to go through execute_string to do my fiddling... That would turn out to be just as slow as calling execute_string in GM which would nullify the use of the dll as far as using it to improve performance.

Edited by icuurd12b42, 22 February 2009 - 12:24 PM.

  • 0

#9 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 22 February 2009 - 04:47 PM

Well, you could probably simplify that more... did you see the example that came with it?

If you want to call them by their pointers, you could try this, althought it is a bit more messier.

GMPROC* ds_list_create = (GMPROC*) GetGMProcAddress("ds_list_create");
GMVariable ret = GMVariableCreate();
asm("mov $0x00, %ecx");
ds_list_create(&ret, 0, NULL);
return ret.real_part;

That would return a double... I don't see why you can't just use the GMProcCall function, since it handles everything for you and it works 100% of the time. Plus, it's written in full ASM and I realy don't want to make things harder for people, let alone them have 100 spots with 15 lines of the same asm everywhere.
  • 0

#10 score_under

score_under

    Least kawaii

  • GMC Member
  • 1319 posts

Posted 22 February 2009 - 05:59 PM

GMPROC* ds_list_create = (GMPROC*) GetGMProcAddress("ds_list_create");
GMVariable ret = GMVariableCreate();
asm("mov $0x00, %ecx");
ds_list_create(&ret, 0, NULL);
return ret.real_part;

Except that it will not work for execute_string() using this method :) And also, you've forgotten the order of the arguments again!
  • 0

#11 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14389 posts
  • Version:GM:Studio

Posted 22 February 2009 - 08:50 PM

I don't see why you can't just use the GMProcCall function, since it handles everything for you and it works 100% of the time. Plus, it's written in full ASM and I realy don't want to make things harder for people, let alone them have 100 spots with 15 lines of the same asm everywhere.


I don't understand why you said this from my post as my suggestion uses GMProcCall ...

I Asked
1) Do I have access to All GM Function?
2) Can you simplify it so it looks more like GM by providing the proper oveloaded operators so that the suggested function wrapper would look like those in my example...

I guess it's not a good idea to make the init create all the pointers since you will never need them all so an easy setup like my example definitions would help. All I would need is add the global variables in the init and add my function wrappers. Prety much like we do to define a external call in gm...

I see no reason to waste grabing the GMPointer GetGMProcAddress("ds_list_create"); each time you call a wrapper script. The wrapper script, a few examples covering each possibility (multi args, strings and so on) would help us define the appropriate scripts.

Edited by icuurd12b42, 22 February 2009 - 08:57 PM.

  • 0

#12 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 23 February 2009 - 12:27 AM

I don't see why you can't just use the GMProcCall function, since it handles everything for you and it works 100% of the time. Plus, it's written in full ASM and I realy don't want to make things harder for people, let alone them have 100 spots with 15 lines of the same asm everywhere.


I don't understand why you said this from my post as my suggestion uses GMProcCall ...

I Asked
1) Do I have access to All GM Function?
2) Can you simplify it so it looks more like GM by providing the proper oveloaded operators so that the suggested function wrapper would look like those in my example...

I guess it's not a good idea to make the init create all the pointers since you will never need them all so an easy setup like my example definitions would help. All I would need is add the global variables in the init and add my function wrappers. Prety much like we do to define a external call in gm...

I see no reason to waste grabing the GMPointer GetGMProcAddress("ds_list_create"); each time you call a wrapper script. The wrapper script, a few examples covering each possibility (multi args, strings and so on) would help us define the appropriate scripts.


1. Yes, you have access to all functions.
2. Huh? Explain a bit.

You probably should grab the pointer in an init... that was just an example, and I didn't feel like writing an init.


I think I understand a bit what you're trying to say, but at the same time, I don't. :D
Just give me a little code example to show what you mean... oh wait, like if you put:

GMVariable a = GMVariableCreate(5.5);
double b = a;

b would = 5.5? Instead of having to do everything else.

EDIT: read your post earlier wrong. Yeah, I see what you mean now, exactly what I said. :) (I think).

Edited by X-tra Fear, 23 February 2009 - 12:44 AM.

  • 0

#13 Yourself

Yourself

    The Ultimate Pronoun

  • Retired Staff
  • 7341 posts
  • Version:Unknown

Posted 23 February 2009 - 01:27 AM

If people are going to say something is C++, they should actually make use of some of the abstraction features of C++ to make the code actually easier to work with. If you're not using any features of C++, you're using C, period. You could pretty easily create a class which acts just like values in GM do (being able to assign strings to them and switch them to doubles later and have them automatically convert themselves into doubles or strings when retrieving values). C++ has some very powerful abstraction features and I'm disappointed that people who "use" C++ don't actually use the best of its features.
  • 0

#14 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14389 posts
  • Version:GM:Studio

Posted 23 February 2009 - 02:13 AM

If people are going to say something is C++, they should actually make use of some of the abstraction features of C++ to make the code actually easier to work with. If you're not using any features of C++, you're using C, period. You could pretty easily create a class which acts just like values in GM do (being able to assign strings to them and switch them to doubles later and have them automatically convert themselves into doubles or strings when retrieving values). C++ has some very powerful abstraction features and I'm disappointed that people who "use" C++ don't actually use the best of its features.


There you go X-tra Fear...


If you change your struct to an actual Class, you can overload the/a few assignment operator(s) (Though can you define operators whitout the need to define a class?) I'm a straight C guy myself so I cant help much on that when it come to the C++ tricks.. But I know you can set it up the way I mentioned

so you can have

GMVariable a = 1.5;
or
GMVariable a = "Hello"

and

GMVariable func(GMVariable arg1)

that can be called like so

double ret = func("Hello");

or

char *ret2 = func2(4.5);

I think I understand a bit what you're trying to say, but at the same time, I don't.
Just give me a little code example to show what you mean... oh wait, like if you put:


Yourself would be very much more qualified to show you how to setup your class so it works as I decribed in my prior post... All you need is about 10 extra lines of code in your gmhelper h file and your c++ file...


also, I think you can shortcut a few things

GMVariable gm_ds_find_value(GMVariable l,GMVariable ind)
{
GMVariable args[2];
args[0] = l;
args[1] = ind;
return GMProcCall(pds_find_value, 1, args);
}

is possibly the same as
GMVariable gm_ds_find_value(GMVariable l,GMVariable ind)
{
GMVariable args[] = {l,ind };
return GMProcCall(pds_find_value, 2, args);
}

and possibly
GMVariable gm_ds_find_value(GMVariable l,GMVariable ind)
{
return GMProcCall(pds_find_value, 2, {l,ind });
}

You can make GMProcCall take multiple arguments using some method I forgot... Just like sprintf takes multiple arguments

double l = GMProcCall(pds_ds_list_create);
GMProcCall(pds_add_value,l,"hello#how#are#you");
GMProcCall(pds_add_value,l,1.4);
double t = GMProcCall(pds_find_value,l,1);
char *s = GMProcCall(pds_find_value,l,0);
char *fs = GMProcCall(pstring_replace_all,s,"#","\r\n");

Edited by icuurd12b42, 23 February 2009 - 02:36 AM.

  • 0

#15 Yourself

Yourself

    The Ultimate Pronoun

  • Retired Staff
  • 7341 posts
  • Version:Unknown

Posted 23 February 2009 - 03:17 AM

(Though can you define operators whitout the need to define a class?)


Yes, overloaded operators don't have to be members of a class. This allows you to add operators on to classes that someone else has made without putzing around in their code. For example:

classA& operator=(classA& a, double val) {
	a.real_part = val;
	return a;
}

This will create an overloaded assignment operator for assigning a double to an instance of classA. It returns a reference to the first argument so you can chain up assignments. This applies to structs too (in C++ the only difference between structs and classes is that the default access modifier is private for classes and public for structs).

Now there are some "operators" which must be members. For example, if you want to make your class automatically convert itself to a native type you must create a member method.

class classA {
	// Other stuff
	double real_part;
public:
	operator double() { return real_part; }
};

Now when C++ encounters a situation where it expects a double but instead gets an instance of classA, it will invoke the operator double method (which could really do just about anything as long as it returns a double).

You should also write a class to encapsulate the calls to GM functions and return an instance of that instead of these pointers which have to be passed to a special calling function.
  • 0

#16 X-tra Fear

X-tra Fear

    Behemoth Creator

  • GMC Member
  • 430 posts
  • Version:GM8

Posted 23 February 2009 - 04:10 AM

Yeah, to late, got the code to work like how he said now...

only one thing left to go.

for some reason
GMVariable a = 5;

i suppose that could be like GMVariable::GMVariable(double a)

doesn't work, need to research on that...
going to switch to a class so ppl will stop *****ing.

You can make GMProcCall take multiple arguments using some method I forgot... Just like sprintf takes multiple arguments


va_list

Edited by X-tra Fear, 23 February 2009 - 04:15 AM.

  • 0

#17 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14389 posts
  • Version:GM:Studio

Posted 23 February 2009 - 05:38 AM

GMVariable a = 5;



That would be this in your h file
GMVariable & operator=(GMVariable & a, double val) {
a.real_part = val;
return a;
}
  • 0

#18 Yourself

Yourself

    The Ultimate Pronoun

  • Retired Staff
  • 7341 posts
  • Version:Unknown

Posted 23 February 2009 - 05:42 AM

If the class has a constructor taking a double, then the following will work:

classA myA = 5;

So will this.

classA myA;
myA = 5;

However, in many cases the former code will be slightly more efficient (assuming there is no appropriately overloaded assignment operator it will typically be even more efficient). The reason for this is a little bit complicated (and the following explanation leaves out some other details about what C++ is actually doing). When you declare the variable, C++ has to create an instance of the class. To do this it invokes the class constructor on the instance to initialize it. In the former piece of code the compiler can optimize the result so that the assignment ends up acting like an explicit construction (as in the value gets passed directly to the constructor). The latter however calls the constructor first which sets up the instance before seeing the assignment. If no assignment operator has been overloaded then C++ will try to convert the value 5 into a temporary instance of classA (for which a default assignment operator exists). It then copies from the temporary instance to the left side of the assignment. This is generally always slower than defining your own assignment operator (especially if the class has other data members that take time to construct/copy; a custom assignment operator will modify the instance in place rather than allowing C++ to create an unnecessary temporary object and copy over all of its data members).

You can make GMProcCall take multiple arguments using some method I forgot... Just like sprintf takes multiple arguments


You'll need to pass the number of arguments given to the function somehow. None of the standard calling conventions give a way for the called function to figure out the actual number of arguments that were passed (the caller has to pass some kind of indicator which the function knows to look for, in the case of sprintf the format string gives the function enough information to deduce the number and type of arguments, since each argument can be a different size in memory).
  • 0

#19 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14389 posts
  • Version:GM:Studio

Posted 23 February 2009 - 05:48 AM


Would the simple reason it does not work, assuming he tried and implemented it correclty, is that the compiler will not automatically cast a int (5) to a double? In which case he would need to add a int version for the operator or constuctor...

and 1.1, if used as a value, would that default to a float in the compiler... again the compiler would not know to cast to a double...
  • 0

#20 Yourself

Yourself

    The Ultimate Pronoun

  • Retired Staff
  • 7341 posts
  • Version:Unknown

Posted 23 February 2009 - 06:12 AM


Would the simple reason it does not work, assuming he tried and implemented it correclty, is that the compiler will not automatically cast a int (5) to a double? In which case he would need to add a int version for the operator or constuctor...

and 1.1, if used as a value, would that default to a float in the compiler... again the compiler would not know to cast to a double...


He'd have to be using a really ****ty compiler. Modern compilers are more than smart enough to figure out what type to turn a literal into.
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users