Some help reverse engennering a piece of code?

All the inane chatter goes in here. If you're curious about whether we will support a game, post HERE not in General Discussion :)

Moderator: ScummVM Team

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Yeah only printing the success and return 0 (or NULL) on success appears to print the exception outside and do nothing on success.

But who should deallocate the object? If i am supposed to return the PyObject*
, logically it should be they. If it should only be NULL or not, i should be me.

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

This i think i understand
It prints the error if the output of the function is zero (ironically, it actually doesn't print anything if there is no error, so it might be a good idea to return 0 unconditionally to avoid the problem area).

Code: Select all

200DAB45  |.  FF15 90331720 CALL DWORD PTR DS&#58;&#91;<&vampire_python21.PyRun_ConsoleString>&#93;
200DAB4B  |.  83C4 14       ADD ESP,14
200DAB4E  |.  85C0          TEST EAX,EAX
200DAB50  |.  75 08         JNE SHORT 200DAB5A
200DAB52  |.  FF15 94331720 CALL DWORD PTR DS&#58;&#91;<&vampire_python21.PyErr_Print>&#93;
200DAB58  |.- EB 1E         JMP SHORT <JMP.&vampire_python21.Py_FlushConsoleOutput>          ; Jump to vampire_python21.Py_FlushConsoleOutput

And basically i don't understand this part of the assembly.
So there was no error, and so the code decreases the ... value pointed by the return? And if it is zero it flushes the line, if not it calls a unknown function with pointer after the pointer in the return?

Code: Select all

200DAB5A  |>  FF08          DEC DWORD PTR DS&#58;&#91;EAX&#93;
200DAB5C  |.  75 0A         JNE SHORT 200DAB68
200DAB5E  |.  8B48 04       MOV ECX,DWORD PTR DS&#58;&#91;EAX+4&#93;
200DAB61  |.  50            PUSH EAX
200DAB62  |.  FF51 18       CALL DWORD PTR DS&#58;&#91;ECX+18&#93;
200DAB65  |.  83C4 04       ADD ESP,4
200DAB68  |>  FF15 98331720 CALL DWORD PTR DS&#58;&#91;<&vampire_python21.Py_FlushLine>&#93;

I can't really attach the dbg to the running game to see where the call nds up. The game has a anti-debuger.

Qbix
Posts: 9
Joined: Wed Jul 08, 2009 2:25 pm

Post by Qbix »

could be a way to handle a variable amount of arguments ?
(flush when done)

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Dunno. It looks to me that the code path that crashes is the success path (returning a valid pointer). Maybe it is just the print i'm doing in the replacement bellow? Damn anti debuggers.

The original functions redirected the python stdout and stderr to the console too, so maybe that is all related to that.

Code: Select all

PyAPI_FUNC&#40;int&#41; PyRun_ConsoleString&#40;const char *, int, PyObject*, PyObject*&#41;;
int PyRun_ConsoleString&#40;const char *str, int typeOfExpression, PyObject* globals, PyObject* locals&#41;&#123;
	PyObject * code;

	//read this
	//http&#58;//docs.python.org/c-api/veryhigh.html#Py_eval_input
	//typeOfExpression is always equal to 256 or Py_single_input. 
	//So the input for the console is a series of statements, not expressions. BUT
	//one of the primitive python statements is the expression_stmt&#58;
	//http&#58;//docs.python.org/reference/simple_stmts.html#grammar-token-expression_stmt
	//that indeed evaluates expressions like 1+2 so this can be used for exec and output
	assert&#40;typeOfExpression == Py_single_input&#41;;
	code = PyRun_String&#40;str, typeOfExpression, globals, locals&#41;;
	if&#40;code&#41;&#123;
		//print the console evaluation result to std out TODO&#58; filter "None"
		PyObject_Print&#40;code, stdout, NULL&#41;; 
		printf&#40;"\r\n"&#41;;
		Py_DECREF&#40;code&#41;;
		code = NULL;
		//the original dll probably returned a pointer to something
		//It checks against NULL &#40;0&#41; and prints the error if it occurred, to stdout
		//if it is not null, it does something different that would need a running
		//dbg to find out &#40;bloodlines has anti dbg proctetion, and i dont know how
		//work around that&#41;
	&#125;
	return 0;
&#125;
Anyway, the console works more or less, modulo that uncertainty and printing to stdout.

Another problem is there are other (default) functions that are failing on map load, including what appears to be a bog standard import a directory (the others are related to modules i didn't bother to compile and include yet).

User avatar
md5
ScummVM Developer
Posts: 2261
Joined: Thu Nov 03, 2005 9:31 pm
Location: Athens, Greece

Post by md5 »

I think that the anti debugging code is part of the CD protection, not part of the actual game, right?

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Did a little "printf tracing"

Code: Select all

Called simple string&#58; import sys; sys.path.append&#40;"Z&#58;\\home\\paulo\\OS Images\\shared\\Vampire the Masquerade - Bloodlines\\Vampire the Masquerade - Bloodlines\\vampire\python"&#41;
This is ok

Code: Select all

sys&#58;1&#58; RuntimeWarning&#58; Python C API version mismatch for module vampire&#58; This Python has API version 1013, module vampire has version 1010.
This is expected

Code: Select all

Called simple string&#58; sys.path.append&#40;"Z&#58;/home/paulo/OS Images/shared/Vampire the Masquerade - Bloodlines/Vampire the Masquerade - Bloodlines/vampire/python/lib/"&#41;
Again ok.

Code: Select all

Called simple string&#58; import __main__
Called simple string&#58; from vampire import *
Ok

Code: Select all

Called simple string&#58; from vamputil import *
Traceback &#40;most recent call last&#41;&#58;
  File "<string>", line 1, in <module>
  File "Z&#58;\home\paulo\OS Images\shared\Vampire the Masquerade - Bloodlines\Vampire the Masquerade - Bloodlines\vampire\python\vamputil.py", line 6, in <module>
    from zvtool.zvtool import *
  File "Z&#58;\home\paulo\OS Images\shared\Vampire the Masquerade - Bloodlines\Vampire the Masquerade - Bloodlines\vampire\python\zvtool\zvtool.py", line 12, in <module>
    from zvtool_file import *
  File "Z&#58;\home\paulo\OS Images\shared\Vampire the Masquerade - Bloodlines\Vampire the Masquerade - Bloodlines\vampire\python\zvtool\zvtool_file.py", line 9, in <module>
    from struct import *
ImportError&#58; No module named struct
Not compiled yet modules, ok

Code: Select all

Called simple string&#58; sys.path.append&#40;"Z&#58;/home/paulo/OS Images/shared/Vampire the Masquerade - Bloodlines/Vampire the Masquerade - Bloodlines/vampire/python/santamonica"&#41;
AttributeError&#58; 'module' object has no attribute '_extension_registry'
Called simple string&#58; from santamonica import *
Traceback &#40;most recent call last&#41;&#58;
  File "<string>", line 1, in <module>
ImportError&#58; No module named santamonica
What? (santamonica is a dir in vampire\python\santamonica with a python file loaded during map load.) How can't it import it considering that it appended vampire\python\santamonica to the sys.path on the first lines? And what is that weird attribute error?

Note: i'm using wine. The game runs fine on it.
Last edited by Serious Callers Only on Sun Mar 06, 2011 9:06 pm, edited 3 times in total.

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

md5 wrote:I think that the anti debugging code is part of the CD protection, not part of the actual game, right?
Sure.

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Mmmm. Interesting. That santamonica error is a missing

"__init__.py" (empty) file on the directory.

It appears that to be recognized as a package they must have that file.

Also, i wasn't printing if the command was successful or not (stupidly). Of those above, the only successful import statements were __main__ and from vampire import * were successful. All others failed because of not having __init__.py.

What the game is trying and failing to do:

Code: Select all

AttributeError&#58; 'module' object has no attribute '_extension_registry'
Failed, Called simple string&#58; sys.path.append&#40;"Z&#58;/home/paulo/OS Images/shared/Vampire the Masquerade - Bloodlines/Vampire the Masquerade - Bloodlines/vampire/python/santamonica"&#41;
Putting the __init__.py just makes the later

Code: Select all

Success, Called simple string&#58; from santamonica import *
work, when it should if that first call worked. But the question is, why is it failing.

sys.path is a list!

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

This is some seriously broken compiler design. That error doesn't appear to have anything to do with the sys.

It is a simple symptom of a earlier error that was not "cleared". In the middle of the compiler, before compiling the new code, there is a call checking : "Oh, there is a exception, better bail out".

There is no such check at the interface borders.

A_call_from_the_foreign_function_interface_can_make_a_posterior_call_fail

:evil:

They (cpython guys) probably expected people to clear the exception after calling the code. (but, by god, they could have failed fast and tell a meaningful exception instead of misleading dross).
But troika probably put it IN the functions calls).

(i didn't edit the functions that have that error).
Last edited by Serious Callers Only on Mon Mar 07, 2011 3:33 am, edited 1 time in total.

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Which reminds me, a better design would be to wrap the dll and only export the functions used directly (and the additional code for they to work the "Troika way").

So i could just replace python by drag&drop if a new minor 2.7 version comes out (or i want more modules whatever).

Is this easy to do in c? Not exactly my sharpest tool as you can see.

There is one problem:

Code: Select all

/* for binary compatibility with 2.2 */
#undef _PyObject_Del
PyAPI_FUNC&#40;void&#41; _PyObject_Del&#40;PyObject *&#41;;
void
_PyObject_Del&#40;PyObject *op&#41;
&#123;
    PyObject_FREE&#40;op&#41;;
&#125;
I had to add the header in this function before Bloodlines accepted the compiled dll.
I don't really understand what that is calling. I tried to "go to definition/declaration in VS2003 and it says it can't find it. It isn't in the file either. Is this some kind of proprocessor shenanigans?

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Trying to wrap the dll dynamically with LoadLibrary didn't work somehow.

I was getting along inserting the functions (inserted 4 as you can see and had the list of the others, when:
Unhandled page fault on read access to 0x00000000 at address (nil) (thread 002b), starting debugger...
Unhandled exception: page fault on read access to 0x00000000 in 32-bit code (0x00000000).
Bizarre. I guess it's back to compiling python.

Code: Select all

#include <windows.h>
#include <stdio.h>
#include "Python.h"





extern "C" &#123;

	HMODULE pythonLib;
	//python functions the original game uses/modifies, dynamically import them
	PyObject*  &#40;*py_runstring&#41;       &#40;const char*, int, PyObject*, PyObject*&#41;;
	int        &#40;*py_runsimpleString&#41; &#40;const char*&#41;;
	void       &#40;*py_pyObjectDel&#41;     &#40;PyObject*&#41;;
	void       &#40;*py_setProgramName&#41;  &#40;char *&#41;;

	//GetProcAddresses
	//Argument1&#58; hLibrary - Handle for the Library Loaded
	//Argument2&#58; lpszLibrary - Library to Load
	//Argument3&#58; nCount - Number of functions to load
	//&#91;Arguments Format&#93;
	//Argument4&#58; Function Address - Function address we want to store
	//Argument5&#58; Function Name -  Name of the function we want
	//&#91;Repeat Format&#93;
	//
	//Returns&#58; FALSE if failure
	//Returns&#58; TRUE if successful
	BOOL GetProcAddresses&#40; HMODULE *hLibrary, 
		LPCSTR lpszLibrary, INT nCount, ... &#41;
	&#123;
		va_list va;
		va_start&#40; va, nCount &#41;;

		if &#40; &#40; *hLibrary = LoadLibrary&#40; lpszLibrary &#41; &#41; 
			!= NULL &#41;
		&#123;
			FARPROC * lpfProcFunction = NULL;
			LPSTR lpszFuncName = NULL;
			INT nIdxCount = 0;
			while &#40; nIdxCount < nCount &#41;
			&#123;
				lpfProcFunction = va_arg&#40; va, FARPROC* &#41;;
				lpszFuncName = va_arg&#40; va, LPSTR &#41;;
				if &#40; &#40; *lpfProcFunction = 
					GetProcAddress&#40; *hLibrary, 
					lpszFuncName &#41; &#41; == NULL &#41;
				&#123;
					lpfProcFunction = NULL;
					return FALSE;
				&#125;
				nIdxCount++;
			&#125;
		&#125;
		else
		&#123;
			va_end&#40; va &#41;;
			return FALSE;
		&#125;
		va_end&#40; va &#41;;
		return TRUE;
	&#125;


	int __declspec &#40;dllexport&#41; Initialize&#40;&#41;&#123;

		BOOL success = GetProcAddresses&#40;&pythonLib, "python.dll", 3,
			&py_runstring, "PyRun_String",
			&py_runsimpleString, "PyRun_SimpleString",
			&py_setProgramName, "Py_SetProgramName",
			&py_pyObjectDel, "_PyObject_Del" &#41;;

		if&#40;!success&#41;&#123;
			printf&#40;"Failed dynamically initializing python.dll in the vampire dir\r\n"&#41;;
		&#125;
		return success;
	&#125;


	int __declspec &#40;dllexport&#41; Terminate&#40;&#41;&#123;
		if&#40;pythonLib != NULL&#41;&#123;
			FreeLibrary&#40;pythonLib&#41;;
		&#125;
		return TRUE;
	&#125;

	void __declspec &#40;dllexport&#41; Py_SetGameInterface&#40;void&#41;&#123;
		//TODO, enable extensions
		//Py_NoSiteFlag = 1;
	&#125;

	void __declspec &#40;dllexport&#41; Py_FlushConsoleOutput&#40;void&#41;&#123;
		printf&#40;"\r\n"&#41;;
		return;
	&#125;


	int __declspec &#40;dllexport&#41; PyRun_ConsoleString&#40;const char *str, int typeOfExpression, PyObject* globals, PyObject* locals&#41;&#123;
		PyObject * code;
		if &#40;PyErr_Occurred&#40;&#41;&#41;&#123;
			//if there is a error saved the compiler will bail out while compiling.
			//this error can be bogus, simply because a previous error - on a previous 
			//execution was not cleared by thecaller. 
			//The cpython guys prefered to centralize the error checking to fail early. 
			//Probably they thought no one would change the api proper. Troikan comes along and 
			//decides to modify the api directly &#40;for no reason at all&#41;
			//Troika probably cleared previous exceptions here on all calls they used. 
			//Either after or before, but i choose before, because there might be some caller
			//checking too.
			printf&#40;"Warning, previous call c code didnt clear the failing exception\r\n"&#41;;
			PyErr_Clear&#40;&#41;;
		&#125;
		//read this
		//http&#58;//docs.python.org/c-api/veryhigh.html#Py_eval_input
		//typeOfExpression is always equal to 256 or Py_single_input. 
		//So the input for the console is a series of statements, not expressions. BUT
		//one of the primitive python statements is the expression_stmt&#58;
		//http&#58;//docs.python.org/reference/simple_stmts.html#grammar-token-expression_stmt
		//that indeed evaluates expressions like 1+2 so this can be used for exec and output
		assert&#40;typeOfExpression == Py_single_input&#41;;
		code = py_runstring&#40;str, typeOfExpression, globals, locals&#41;;
		printf&#40;"%sCalled console string&#58; %s\r\n", code ? "Success, " &#58; "Failed, ",  str&#41;;
		if&#40;code&#41;&#123;
			//TODO&#58; this should be printing to the game console instead
			//TODO&#58; filter "None"
			PyObject_Print&#40;code, stdout, NULL&#41;;
			Py_DECREF&#40;code&#41;;
			code = NULL;
			//the original dll probably returned a pointer to something
			//It checks against NULL &#40;0&#41; and prints the error if it occurred, to stdout
			//if it is not null, it does something different that would need a running
			//dbg to find out &#40;bloodlines has anti dbg protection, and i dont know how
			//work around that&#41;. Seems to work fine anyway?
		&#125;
		return 0;
	&#125;


	//Original python functions modified by troika


#undef PyRun_SimpleString
	int __declspec &#40;dllexport&#41; PyRun_SimpleString&#40;const char *st&#41;
	&#123; 
		int result;
		if &#40;PyErr_Occurred&#40;&#41;&#41;&#123;
			printf&#40;"Warning, previous call c code didnt clear the failing exception\r\n"&#41;;
			PyErr_Clear&#40;&#41;;
		&#125;
		result = py_runsimpleString&#40;st&#41;;
		printf&#40;"%sCalled simple string&#58; %s\r\n", result == 0 ? "Success, " &#58; "Failed, ",  st&#41;;
		return result;
	&#125;

	//this function was deprecated on python 2.3 check if it can still link

#undef _PyObject_Del
	void __declspec &#40;dllexport&#41; _PyObject_Del&#40;PyObject *op&#41;
	&#123;
		py_pyObjectDel&#40;op&#41;;
	&#125;

	//Original functions used unmodified by troika.

/**
#undef PyErr_Clear
void __declspec &#40;dllexport&#41; PyErr_Clear&#40;&#41;&#123;

&#125; 

#undef PyErr_Print
void __declspec &#40;dllexport&#41; PyErr_Print&#40;&#41;&#123;

&#125; 

#undef PyErr_SetString
void __declspec &#40;dllexport&#41; PyErr_SetString&#40;&#41;&#123;

&#125; 

#undef PyFloat_AsDouble
void __declspec &#40;dllexport&#41; PyFloat_AsDouble&#40;&#41;&#123;

&#125; 

#undef PyFloat_FromDouble
void __declspec &#40;dllexport&#41; PyFloat_FromDouble&#40;&#41;&#123;

&#125; 

#undef PyImport_AddModule
void __declspec &#40;dllexport&#41; PyImport_AddModule&#40;&#41;&#123;

&#125; 

#undef PyInt_AsLong
void __declspec &#40;dllexport&#41; PyInt_AsLong&#40;&#41;&#123;

&#125; 

#undef PyInt_FromLong
void __declspec &#40;dllexport&#41; PyInt_FromLong&#40;&#41;&#123;

&#125; 

#undef PyLong_AsDouble
void __declspec &#40;dllexport&#41; PyLong_AsDouble&#40;&#41;&#123;

&#125; 

#undef PyModule_AddObject
void __declspec &#40;dllexport&#41; PyModule_AddObject&#40;&#41;&#123;

&#125; 

#undef PyModule_GetDict
void __declspec &#40;dllexport&#41; PyModule_GetDict&#40;&#41;&#123;

&#125; 

#undef _PyObject_New
void __declspec &#40;dllexport&#41; _PyObject_New&#40;&#41;&#123;

&#125; 

#undef PyObject_Str
void __declspec &#40;dllexport&#41; PyObject_Str&#40;&#41;&#123;

&#125; 

#undef PyString_AsString
void __declspec &#40;dllexport&#41; PyString_AsString&#40;&#41;&#123;

&#125; 

#undef PyString_FromString
void __declspec &#40;dllexport&#41; PyString_FromString&#40;&#41;&#123;

&#125; 

#undef Py_Finalize
void __declspec &#40;dllexport&#41; Py_Finalize&#40;&#41;&#123;

&#125; 

#undef Py_FlushLine
void __declspec &#40;dllexport&#41; Py_FlushLine&#40;&#41;&#123;

&#125; 

#undef Py_Initialize
void __declspec &#40;dllexport&#41; Py_Initialize&#40;&#41;&#123;

&#125; 
**/

#undef Py_SetProgramName
void __declspec &#40;dllexport&#41; Py_SetProgramName&#40;char * name&#41;&#123;
	py_setProgramName&#40;name&#41;;
&#125; 

&#125;

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

Hey, the function point is null, wtf.

Edit: Not even wrong, the initialize is not being called. The number of function is wrong too.

Serious Callers Only
Got a warning
Posts: 166
Joined: Thu Feb 25, 2010 7:44 am

Post by Serious Callers Only »

This approach is kinda working, but VS is giving (rightfully i guess) this warning for each function redirected:

vampire_python21.cpp(207) : warning C4273: 'Py_Initialize' : inconsistent dll linkage

Any alternative or tips to solve the warning?

Code: Select all

#include "Python.h"
#include <windows.h>
#include <stdio.h>



HMODULE pythonLib;
//python functions the original game uses/modifies, dynamically import them
PyObject*  &#40;*py_runstring&#41;        &#40;const char*, int, PyObject*, PyObject*&#41;;
PyObject*  &#40;*py__PyObjectNew&#41;     &#40;PyTypeObject*&#41;;
int        &#40;*py_runsimpleString&#41;  &#40;const char*&#41;;
void       &#40;*py_initialize&#41;       &#40;void&#41;;
void       &#40;*py_setProgramName&#41;   &#40;char*&#41;;
PyObject*  &#40;*py_pyImportAddModule&#41;&#40;const char *&#41;;
int        &#40;*py_moduleAddObject&#41;  &#40;PyObject*, const char*, PyObject*&#41;;
PyObject*  &#40;*py_moduleGetDict&#41;    &#40;PyObject*&#41;;
void       &#40;*py_errorPrint&#41;       &#40;void&#41;;
PyObject*  &#40;*py_objectStr&#41;        &#40;PyObject*&#41;;
char*      &#40;*py_stringAsString&#41;   &#40;PyObject*&#41;;
PyObject*  &#40;*py_stringFromString&#41; &#40;const char*&#41;;


extern "C" &#123;

//GetProcAddresses
//Argument1&#58; hLibrary - Handle for the Library Loaded
//Argument2&#58; lpszLibrary - Library to Load
//Argument3&#58; nCount - Number of functions to load
//&#91;Arguments Format&#93;
//Argument4&#58; Function Address - Function address we want to store
//Argument5&#58; Function Name -  Name of the function we want
//&#91;Repeat Format&#93;
//
//Returns&#58; FALSE if failure
//Returns&#58; TRUE if successful
__declspec &#40;dllexport&#41; int GetProcAddresses&#40; HMODULE *hLibrary, 
					 LPCSTR lpszLibrary, INT nCount, ... &#41;
&#123;
	va_list va;
	va_start&#40; va, nCount &#41;;

	if &#40; &#40; *hLibrary = LoadLibrary&#40; lpszLibrary &#41; &#41; 
		!= NULL &#41;
	&#123;

		FARPROC * lpfProcFunction = NULL;
		LPSTR lpszFuncName = NULL;
		INT nIdxCount = 0;
		while &#40; nIdxCount < nCount &#41;
		&#123;
			lpfProcFunction = va_arg&#40; va, FARPROC* &#41;;
			lpszFuncName = va_arg&#40; va, LPSTR &#41;;
			printf&#40;"%s\r\n",lpszFuncName&#41;; 
			if &#40; &#40; *lpfProcFunction = 
				GetProcAddress&#40; *hLibrary, 
				lpszFuncName &#41; &#41; == NULL &#41;
			&#123;
				lpfProcFunction = NULL;
				return FALSE;
			&#125;
			nIdxCount++;
		&#125;
	&#125;
	else
	&#123;
		va_end&#40; va &#41;;
		return 0;
	&#125;
	va_end&#40; va &#41;;
	return 1;
&#125;


__declspec &#40;dllexport&#41; int APIENTRY DllMain&#40;HANDLE hModule, 
					 DWORD  ul_reason_for_call, 
					 LPVOID lpReserved&#41;
&#123;
	int success;
	char bufExePath&#91;MAX_PATH + 1&#93; = &#123; 0 &#125;;
	unsigned long nChars;

	switch&#40; ul_reason_for_call &#41; &#123;
	case DLL_PROCESS_ATTACH&#58;
		nChars = GetModuleFileName&#40;NULL, bufExePath, MAX_PATH&#41;;
		if&#40;nChars <= 0&#41;&#123;
			printf&#40;"Failed getting the vampire dir\r\n"&#41;;
			return 0;
		&#125;
		for&#40; ; nChars > 0; nChars--&#41;&#123;
			//think windows accepts both
			if&#40;bufExePath&#91;nChars&#93;=='\\' || bufExePath&#91;nChars&#93;=='/'&#41;&#123;
				break;
			&#125;
		&#125;
		if&#40;nChars == 0&#41;&#123;
			printf&#40;"Failed getting the vampire dir\r\n"&#41;;
			return 0;
		&#125;
		strcpy&#40;&bufExePath&#91;nChars+1&#93;,"Bin\\python.dll\0"&#41;;
		success = GetProcAddresses&#40;&pythonLib, bufExePath, 
			9,
			//&py_objectStr, "PyObject_Str",
			//&py_stringAsString, "PyString_AsString",
			//&py_stringFromString, "PyString_FromString",
			&py_errorPrint, "PyErr_Print",
			&py_moduleGetDict, "PyModule_GetDict",
			&py_moduleAddObject, "PyModule_AddObject",
			&py_initialize, "Py_Initialize",
			&py__PyObjectNew, "_PyObject_New",
			&py_runstring, "PyRun_String",
			&py_runsimpleString, "PyRun_SimpleString",
			&py_setProgramName, "Py_SetProgramName",
			&py_pyImportAddModule, "PyImport_AddModule"
			&#41;;
		if&#40;!success&#41;&#123;
			printf&#40;"Failed dynamically initializing python.dll on %s\r\n", bufExePath&#41;;
		&#125;
		return success;
	case DLL_PROCESS_DETACH&#58;
		if&#40;pythonLib != NULL&#41;&#123;
			FreeLibrary&#40;pythonLib&#41;;
		&#125;
	case DLL_THREAD_ATTACH&#58;
	case DLL_THREAD_DETACH&#58;
	default&#58;
		return 1;
	&#125;
&#125;


//functions added by troika

__declspec &#40;dllexport&#41; void  Py_SetGameInterface&#40;void&#41;&#123;
	//TODO, enable extensions
	Py_NoSiteFlag = 1;
&#125;

__declspec &#40;dllexport&#41; void Py_FlushConsoleOutput&#40;void&#41;&#123;
	printf&#40;"\r\n"&#41;;
	return;
&#125;


__declspec &#40;dllexport&#41; int PyRun_ConsoleString&#40;const char *str, int typeOfExpression, PyObject* globals, PyObject* locals&#41;&#123;
	PyObject * code;
	if &#40;PyErr_Occurred&#40;&#41;&#41;&#123;
		//if there is a error saved the compiler will bail out while compiling.
		//this error can be bogus, simply because a previous error - on a previous 
		//execution was not cleared by the caller. 
		//The cpython guys prefered to centralize the error checking to fail early. 
		//Probably they thought no one would change the api proper.
		//Troika probably cleared previous exceptions here on all calls they used. 
		//Either after or before, but i choose before, because there might be some caller
		//checking too.
		printf&#40;"Warning, previous call c code didnt clear the failing exception\r\n"&#41;;
		PyErr_Clear&#40;&#41;;
	&#125;
	//read this
	//http&#58;//docs.python.org/c-api/veryhigh.html#Py_eval_input
	//typeOfExpression is always equal to 256 or Py_single_input. 
	//So the input for the console is a series of statements, not expressions. BUT
	//one of the primitive python statements is the expression_stmt&#58;
	//http&#58;//docs.python.org/reference/simple_stmts.html#grammar-token-expression_stmt
	//that indeed evaluates expressions like 1+2 so this can be used for exec and output
	assert&#40;typeOfExpression == Py_single_input&#41;;
	code = py_runstring&#40;str, typeOfExpression, globals, locals&#41;;
	printf&#40;"%sCalled console string&#58; %s\r\n", code ? "Success, " &#58; "Failed, ",  str&#41;;
	if&#40;code&#41;&#123;
		//TODO&#58; this should be printing to the game console instead
		//TODO&#58; filter "None"
		PyObject_Print&#40;code, stdout, NULL&#41;;
		Py_DECREF&#40;code&#41;;
		code = NULL;
		//the original dll probably returned a pointer to something
		//It checks against NULL &#40;0&#41; and prints the error if it occurred, to stdout
		//if it is not null, it does something different that would need a running
		//dbg to find out &#40;bloodlines has anti dbg protection, and i dont know how
		//work around that&#41;. Seems to work fine anyway?
	&#125;
	return 0;
&#125;




//Original python functions used/modified by troika


#undef PyRun_SimpleString
	__declspec &#40;dllexport&#41; int PyRun_SimpleString&#40;const char *st&#41;
	&#123; 
		int result;
		if &#40;PyErr_Occurred&#40;&#41;&#41;&#123;
			printf&#40;"Warning, previous call c code didnt clear the failing exception\r\n"&#41;;
			PyErr_Clear&#40;&#41;;
		&#125;
		result = py_runsimpleString&#40;st&#41;;
		printf&#40;"%sCalled simple string&#58; %s\r\n", result == 0 ? "Success, " &#58; "Failed, ",  st&#41;;
		return result;
	&#125;


	//Original functions used unmodified by troika.

#undef Py_Initialize
	__declspec &#40;dllexport&#41; void Py_Initialize&#40;void&#41;&#123;
		py_initialize&#40;&#41;;
	&#125; 

	//this function was deprecated on python 2.3 check if this works with TRACEREFS
#undef _PyObject_Del
	__declspec &#40;dllexport&#41; void _PyObject_Del&#40;PyObject *op&#41;
	&#123;
		Py_DECREF&#40;op&#41;;
	&#125;

#undef _PyObject_New
	__declspec &#40;dllexport&#41; PyObject* _PyObject_New&#40;PyTypeObject *type&#41;&#123;
		return py__PyObjectNew&#40;type&#41;;
	&#125; 


#undef PyImport_AddModule
	__declspec &#40;dllexport&#41; PyObject* PyImport_AddModule&#40;const char *name&#41;&#123;
		return py_pyImportAddModule&#40;name&#41;;
	&#125; 

#undef PyModule_AddObject
	__declspec &#40;dllexport&#41; int PyModule_AddObject&#40;PyObject *module, const char *name, PyObject *value&#41;&#123;
		return py_moduleAddObject&#40;module,name,value&#41;;
	&#125; 

#undef PyModule_GetDict
	__declspec &#40;dllexport&#41; PyObject* PyModule_GetDict&#40;PyObject * module&#41;&#123;
		return py_moduleGetDict&#40;module&#41;;
	&#125;

#undef PyErr_Print
	__declspec &#40;dllexport&#41; void PyErr_Print&#40;&#41;&#123;
		py_errorPrint&#40;&#41;;
	&#125; 

	//#undef PyObject_Str
	//PyObject * __declspec &#40;dllexport&#41; PyObject_Str&#40;PyObject *o&#41;&#123;
	//	return py_objectStr&#40;o&#41;;
	//&#125; 

	//#undef PyString_AsString
	//char* __declspec &#40;dllexport&#41; PyString_AsString&#40;PyObject * o&#41;&#123;
	//	return py_stringAsString&#40;o&#41;;
	//&#125; 

	//#undef PyString_FromString
	//PyObject* __declspec &#40;dllexport&#41; PyString_FromString&#40;const char * s&#41;&#123;
	//	return py_stringFromString&#40;s&#41;;
	//&#125; 


	/**
	#undef PyErr_Clear
	void __declspec &#40;dllexport&#41; PyErr_Clear&#40;&#41;&#123;

	&#125; 



	#undef PyErr_SetString
	void __declspec &#40;dllexport&#41; PyErr_SetString&#40;&#41;&#123;

	&#125; 

	#undef PyFloat_AsDouble
	void __declspec &#40;dllexport&#41; PyFloat_AsDouble&#40;&#41;&#123;

	&#125; 

	#undef PyFloat_FromDouble
	void __declspec &#40;dllexport&#41; PyFloat_FromDouble&#40;&#41;&#123;

	&#125; 


	#undef PyInt_AsLong
	void __declspec &#40;dllexport&#41; PyInt_AsLong&#40;&#41;&#123;

	&#125; 

	#undef PyInt_FromLong
	void __declspec &#40;dllexport&#41; PyInt_FromLong&#40;&#41;&#123;

	&#125; 

	#undef PyLong_AsDouble
	void __declspec &#40;dllexport&#41; PyLong_AsDouble&#40;&#41;&#123;

	&#125; 







	#undef Py_Finalize
	void __declspec &#40;dllexport&#41; Py_Finalize&#40;&#41;&#123;

	&#125; 

	#undef Py_FlushLine
	void __declspec &#40;dllexport&#41; Py_FlushLine&#40;&#41;&#123;

	&#125; 
	**/



#undef Py_SetProgramName
	__declspec &#40;dllexport&#41; void Py_SetProgramName&#40;char * name&#41;&#123;
		py_setProgramName&#40;name&#41;;
	&#125; 

&#125;

fingolfin
Retired
Posts: 1466
Joined: Wed Sep 21, 2005 4:12 pm

Post by fingolfin »

I don't think this forum is entirely the right place for your monologue, and for loooong code excerpts. Please consider getting a blog, and posting there.

User avatar
sev
ScummVM Lead
Posts: 2043
Joined: Wed Sep 21, 2005 1:06 pm
Contact:

Post by sev »

I was thinking about posting something similar, but fingolfin did it earlier.


Eugene

Locked