#include <stdio.h>
#include <stdlib.h>
main( int argc, char *argv[] )
{
int c = 0;
char String[255];
sprintf(String, "start ffplay -fs -autoexit %s ", argv[2]);
chdir(".\\movies");
int a = 1;
a = system(String);
do{;}while(a!=0);
return ;
}
And that's it. Compiled with gcc (freeware c compiler) from command line.#include <windows.h>
#include <cstdlib>
#include <string>
#include <unistd.h>
#include <tchar.h>
#include <fstream>
using namespace std;
const char g_szClassName[] = "smacxWindowFfplay";
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
FreeConsole();
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
::MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// getting command line
TCHAR cmdline[4096] ;
TCHAR* argv[4096] ;
int argc = 0 ;
_tcscpy( cmdline, GetCommandLine() ) ;
argv[argc] = _tcstok( cmdline, TEXT(" \t") ) ;
while( argv[argc] != 0 )
{
argc++ ;
argv[argc] = _tcstok( 0, TEXT(" \t") ) ;
}
// name of the movie to play
string sargv = argv[argc-1];
// saving the name to the file
std::ofstream ofs(".\\TERX_movie\\movie.txt",std::ofstream::out);
ofs << sargv;
ofs.close();
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"Playing ...",
WS_ICONIC,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, SW_SHOWMINIMIZED);
UpdateWindow(hwnd);
sleep(1);
// Step 3: The Message Loop
::PostMessage(hwnd, WM_CLOSE, 0, 0);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}// end of WinMain
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <psapi.h>
#include <sstream>
#include <fstream>
#include <vector>
using namespace std;
void suspend(DWORD processId);
void resume(DWORD processId);
string ExePath();
int main( int argc, char *argv[] )
{
FreeConsole();
HWND hWnd1 = NULL;
HWND hWnd2 = NULL;
vector<string> vNameShort;
vector<string> vNameFull;
string nameShort;
string nameFull;
bool replace = false;
std::size_t found;
//for testing and message boxes
std::stringstream os;
// read movie names from movies2
string folder = ExePath();
folder.append("\\movies2\\");
char search_path[200];
sprintf(search_path, "%s*.*", folder.c_str());
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE) {
do {
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
vNameFull.push_back(fd.cFileName);
}
}while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
// creating the second vector
for(int i=0;i<vNameFull.size();++i){
found= vNameFull[i].find('.');
if (found!=std::string::npos){
nameShort = vNameFull[i].substr(0,found);
}
vNameShort.push_back(nameShort);
}
// Starting terranx.exe
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
CreateProcess( "terranx.exe",
NULL,
NULL,
NULL,
FALSE,
0, //CREATE_NO_WINDOW
NULL,
NULL,
&si,
&pi ) ;
// Loop
while(true){
usleep(300*1000);
// finding working smacx
hWnd1 = FindWindow(NULL,"Sid Meier's Alpha Centauri");
if(hWnd1==NULL){
// no terranx.exe so quiting
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return 0;
}
else{
// check if playuv15 is launched
hWnd2 = FindWindow(NULL,"Playing ...");
if(hWnd2!=NULL){
usleep(100*1000);
DWORD pid;
// find state of smax window, save it
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
RECT rect1;
rect1.left = 0;
rect1.right = 0;
rect1.top = 800;
rect1.bottom = 600;
POINT p1,p2;
p1.x = 0;
p1.y = 0;
WINDOWPLACEMENT wp1;
wp1.length = sizeof(WINDOWPLACEMENT);
wp1.flags = WPF_ASYNCWINDOWPLACEMENT;
wp1.showCmd = 6;
wp1.ptMinPosition = p1;
wp1.ptMaxPosition = p1;
wp1.rcNormalPosition = rect1;
GetWindowPlacement(hWnd1,&wp);
// change to windowed, or normal state
SetWindowPlacement(hWnd1,&wp1);
SetWindowPos(hWnd1,HWND_BOTTOM,0,0,800,600,SWP_NOMOVE);
// find terranx process
// suspend the process
DWORD aProcesses[1024], cbNeeded, cProcesses;
bool ret = false;
ret = EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded );
if ( !ret )
{
::MessageBox(
NULL,
"Enumerating processes problem",
"Message",
MB_ICONEXCLAMATION | MB_OK
);
return 1;
}
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Print the name and process identifier for each process.
for ( int i = 0; i < cProcesses; i++ )
{
if( aProcesses[i] != 0 )
{
DWORD processID = aProcesses[i];
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
// Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID );
// Get the process name.
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod),&cbNeeded) )
{
GetModuleBaseName( hProcess, hMod, szProcessName,
sizeof(szProcessName)/sizeof(TCHAR) );
}
}
if(!strcmp(szProcessName,"terranx.exe")){
pid = processID;
suspend(processID);
}
}
}
// find name of the movie to play and play it, waiting for completion
string str1;
string stroptions;
string nameToPlay = "";
nameShort = "";
std::ifstream infile;
infile.open(".\\TERX_movie\\movie.txt",ios::in);
getline(infile,nameToPlay);
nameFull = nameToPlay;
found=nameFull.find('.');
if (found!=std::string::npos){
nameShort = nameFull.substr(0,found);
}
if(nameShort != ""){
// choosing which folder to play
replace = false;
for(int i=0;i<vNameShort.size();++i){
if((replace==false)&&(0==nameShort.compare(vNameShort[i]))){
replace = true;
nameToPlay = vNameFull[i];
}
}
}
infile.close();
std::ifstream infile2;
infile2.open(".\\TERX_movie\\ffplay_options.txt",ios::in);
getline(infile2,stroptions);
infile2.close();
str1 = " ";
str1.append(stroptions);
str1.append(" ");
str1.append(nameToPlay);
// trying to pass LPCTSTR...
char s[255] = "";
strcpy(s, str1.c_str());
LPCTSTR sls;
sls = s;
// and folders
LPCTSTR whichFolder;
if(replace){
whichFolder = _T(".\\movies2");
}else{
whichFolder = _T(".\\movies");
}
SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NO_CONSOLE ;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = "ffplay.exe";
ShExecInfo.lpParameters = sls;
ShExecInfo.lpDirectory = whichFolder;
ShExecInfo.nShow = SW_SHOWMAXIMIZED;
ShExecInfo.hInstApp = NULL;
if(!ShellExecuteEx(&ShExecInfo))
{
::MessageBox(
NULL,
"Failed to create process, and start ffplay",
"Message",
MB_ICONEXCLAMATION | MB_OK
);
return -1;
}
else{
WaitForSingleObject(ShExecInfo.hProcess,INFINITE);
}
// resume terranx process
resume(pid);
// restore window to its state
SetWindowPlacement(hWnd1,&wp);
SetFocus(hWnd1);
SetActiveWindow(hWnd1);
// end of playing procedure, delete movie name file.
}
}//end of if handle to window terranx found
}//end while true
}// end of main
void suspend(DWORD processId)
{
stringstream stros;
stros << processId;
HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 threadEntry;
threadEntry.dwSize = sizeof(THREADENTRY32);
Thread32First(hThreadSnapshot, &threadEntry);
do
{
if (threadEntry.th32OwnerProcessID == processId)
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE,
threadEntry.th32ThreadID);
SuspendThread(hThread);
CloseHandle(hThread);
}
} while (Thread32Next(hThreadSnapshot, &threadEntry));
CloseHandle(hThreadSnapshot);
}
void resume(DWORD processId)
{
HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 threadEntry;
threadEntry.dwSize = sizeof(THREADENTRY32);
Thread32First(hThreadSnapshot, &threadEntry);
do
{
if (threadEntry.th32OwnerProcessID == processId)
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE,
threadEntry.th32ThreadID);
ResumeThread(hThread);
CloseHandle(hThread);
}
} while (Thread32Next(hThreadSnapshot, &threadEntry));
CloseHandle(hThreadSnapshot);
}
string ExePath() {
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
string::size_type pos = string( buffer ).find_last_of( "\\/" );
return string( buffer ).substr( 0, pos);
}
As for movies, at present the biggest problem is, that the replacement executable, which is started by smacx does not have enough privileges to force it out of foreground for time of movie play... SMACX does seem to do it, though, with its original playuv15.exe.
The thing is, the replacement "playuv15.exe" would serve only as launcher/manager of new movies and ffplay.exe would actually play movies. So it is ffplay.exe that needs to make smacx(terranx.exe) to go out of foreground and let it play the movie.
The demo uses different solution, where there is yet another executable ("manager"), that launches smacx (terranx.exe), thus as parent process it can force it to go out of foreground and allow ffplay.exe to show the movie.
I do not consider this solution as the best, since one (person playing the game) actually must use it instead of terranx.exe to launch the game. Also, the manager must be active "observing" if terranx.exe launches playuv15.exe.
... I think Plotinus changed the movie playing behaviour and has them played by playuv directly, so you could look at what he's doing (source code is available but not commented very much) or just try replacing playuv15 with your own thing while running PRACX.
I got SMAC to call a batch file that called ffplay in a previous experiment, I think I got videos to be played OK and don't remember them being played in the background, but I'm not entirely sure. I can't remember exactly what I was doing, but here's the thread where I talked about it: http://alphacentauri2.info/index.php?topic=8500.0 (http://alphacentauri2.info/index.php?topic=8500.0)
...
...Yes, I will have to take a look into IPC, interprocess communication.
That doesn't sound like a particularly bad solution to me.
Yes, I can see it there. I have not read all the PRACX code, but Plotinus possibly took care of the smacx window going into background, or yielding "topmost" status to playuv15, since with the patch we can see the movies ok, even though they are really played by this external playuv15.exe?
And that gives me an idea of modifying pracx.dll in hex editor. That could work.
Are we talking about the same thing?
... Is there anything in particular you'd like me to look at when I try it out?Nothing in particular. Just overall experience: