If you want to help us maintaining this wiki, check out our discord server: https://discord.gg/3u69jMa
Difference between revisions of "Republic Commando UCC"
(Created page with "Author: Leon Used Tools: MS Visual Studio 2003 Description: A self written UCC for Republic Commando, still in development. Used for executing Unreal Commandlets. Githu...") |
|||
Line 11: | Line 11: | ||
<code lang=c++> | <code lang=c++> | ||
/* | |||
* This code compiles to a dll. In order to use it, SWRepublicCommando.exe needs to be modified so | |||
* that it calls this dll's 'uccInit' instead of 'appInit' from Core.dll | |||
* Everything compiles fine with 'Visual Studio .NET 2003' but it should also work with newer | |||
* versions (when making some adjustments to the headers) | |||
* The following settings are needed to compile everything without errors: | |||
* - Character Set = Not Set | |||
* - Struct Member Alignment = 4 Bytes | |||
* - Calling Convention = __fastcall | |||
*/ | |||
#pragma comment(lib, "Core.lib") | |||
//#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case... | |||
#include <windows.h> //VirtualProtect | |||
#include <iostream> | |||
#include <cstdlib> //__argc, __argv | |||
#include <cctype> //std::toupper | |||
#include <string> | |||
// Core and Engine | |||
//#include "Engine/inc/Engine.h" | |||
//Only need Core... | |||
#include "Core/inc/Core.h" | |||
//Helper function used for case-insensitive string comparisons | |||
std::string ToUpper(const std::string& s){ | |||
std::string result; | |||
result.reserve(s.size()); | |||
for(std::size_t i = 0; i < s.size(); i++) | |||
result += std::toupper(s[i]); | |||
return result; | |||
} | |||
/* | |||
* Replacement for Serialize function of 'InWarn', passed to appInit | |||
* Have to do this vtable hack because creating new FFeedbackContext and passing it results in crash | |||
*/ | |||
void __stdcall FFeedbackContextAnsiSerialize(const TCHAR* V, EName Event){ | |||
TCHAR Temp[1024]= ""; | |||
if(Event==NAME_Title){ | |||
return; | |||
}else if(Event==NAME_Heading){ | |||
appSprintf(Temp, "--------------------%s--------------------", V); | |||
V = Temp; | |||
}else if(Event==NAME_SubHeading){ | |||
appSprintf(Temp, "%s...", V); | |||
V = Temp; | |||
}else if(Event==NAME_Error || Event==NAME_Warning || Event==NAME_ExecWarning || Event==NAME_ScriptWarning){ | |||
appSprintf(Temp, "Warning : %s", V); | |||
V = Temp; | |||
}else if(Event==NAME_Progress){ | |||
appSprintf(Temp, "%s", V); | |||
std::cout << Temp << '\r'; | |||
return; | |||
} | |||
std::cout << V << '\n'; | |||
GLog->Serialize(V, Event); | |||
} | |||
/* | |||
* Helper class to redirect RC's log output to the system console when launching a server | |||
*/ | |||
class FOutputDeviceConsole : public FOutputDevice{ | |||
public: | |||
FOutputDeviceConsole(FOutputDevice& FileLog) : FileLog(FileLog){} | |||
void Serialize(const TCHAR* V, EName Event){ | |||
if(Event != NAME_Title){ //Prevents server from spamming the player count to the console | |||
std::cout << *FName(Event) << ": " << V << '\n'; | |||
FileLog.Serialize(V, Event); | |||
} | |||
} | |||
private: | |||
FOutputDevice& FileLog; | |||
}; | |||
//Entry point called by modified SWRepublicCommando.exe | |||
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError, | |||
FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){ | |||
int ExitVal = EXIT_SUCCESS; | |||
std::cout << "=======================================\n" | |||
"ucc.exe for Star Wars Republic Commando\n" | |||
"made by Leon0628\n" | |||
"=======================================\n\n"; | |||
//Small vtable hack to have InWarn call my own Serialize function | |||
//=============================================================== | |||
PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn); | |||
DWORD dwNull; | |||
VirtualProtect(&vtable[0], 4, PAGE_EXECUTE_READWRITE, &dwNull); | |||
vtable[0] = FFeedbackContextAnsiSerialize; | |||
//=============================================================== | |||
//Server stuff... | |||
for(int i = 1; i < __argc; i++){ | |||
std::string Temp = ToUpper(__argv[i]); | |||
//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe | |||
//Uses RC server command line syntax, not Engine.ServerCommandlet!!! | |||
if(Temp == "-SERVER"){ | |||
static FOutputDeviceConsole ConsoleLog(*InLog); //Static here just to have everything together in one place | |||
appInit(InPackage, InCmdLine, &ConsoleLog, InError, InWarn, ConfigFactory, RequireConfig); | |||
return; | |||
} | |||
} | |||
//Actual UCC stuff... | |||
try{ | |||
GIsStarted = 1; | |||
GIsGuarded = 1; | |||
appInit(InPackage, InCmdLine, InLog, InError, InWarn, ConfigFactory, 1); | |||
UObject::SetLanguage("int"); | |||
if(__argc > 1){ | |||
//Initializing global state | |||
GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1; | |||
std::string Token = ToUpper(__argv[1]); | |||
DWORD LoadFlags = 0; | |||
std::string ClassName; | |||
bool bIsUScript = false; | |||
if(Token == "MAKE" || Token == "EDITOR.MAKECOMMANDLET"){ | |||
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it too... | |||
ClassName = "Editor.MakeCommandlet"; | |||
}else if(Token == "USCRIPT"){ //When executing commandlets written in UnrealScript, like Core.HelloWorldCommandlet | |||
ClassName = __argc > 2 ? __argv[2] : Token; | |||
bIsUScript = true; | |||
}else{ | |||
ClassName = __argv[1]; | |||
} | |||
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL); | |||
if(Class){ | |||
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class); | |||
InWarn->Logf("Executing %s\n", Class->GetFullName()); | |||
Commandlet->InitExecution(); | |||
Commandlet->ParseParms(appCmdLine()); | |||
if(!bIsUScript){ | |||
Commandlet->Main(appCmdLine()); | |||
}else{ | |||
FOutputDevice* Log = GLog; | |||
FOutputDeviceConsole ConsoleLog(*GLog); //Redirecting UnrealScript log to console | |||
GLog = &ConsoleLog; | |||
Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used | |||
GLog = Log; | |||
} | |||
} | |||
}else{ | |||
std::cout << "Usage: ucc <command> <parameters>\n"; | |||
} | |||
appPreExit(); | |||
GIsGuarded = 0; | |||
}catch(...){ | |||
ExitVal = EXIT_FAILURE; | |||
GIsGuarded = 0; | |||
InError->HandleError(); | |||
} | |||
appExit(); | |||
std::exit(ExitVal); //Needed in order to prevent this function from returning to SWRepublicCommando.exe | |||
} | |||
</code> | </code> |
Revision as of 05:27, 21 November 2017
Author: Leon
Used Tools: MS Visual Studio 2003
Description: A self written UCC for Republic Commando, still in development. Used for executing Unreal Commandlets.
Github: here
UCC.cpp
/*
- This code compiles to a dll. In order to use it, SWRepublicCommando.exe needs to be modified so
- that it calls this dll's 'uccInit' instead of 'appInit' from Core.dll
- Everything compiles fine with 'Visual Studio .NET 2003' but it should also work with newer
- versions (when making some adjustments to the headers)
- The following settings are needed to compile everything without errors:
- - Character Set = Not Set
- - Struct Member Alignment = 4 Bytes
- - Calling Convention = __fastcall
- /
- pragma comment(lib, "Core.lib")
//#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case...
- include <windows.h> //VirtualProtect
- include <iostream>
- include <cstdlib> //__argc, __argv
- include <cctype> //std::toupper
- include <string>
// Core and Engine
//#include "Engine/inc/Engine.h"
//Only need Core...
- include "Core/inc/Core.h"
//Helper function used for case-insensitive string comparisons
std::string ToUpper(const std::string& s){
std::string result;
result.reserve(s.size());
for(std::size_t i = 0; i < s.size(); i++)
result += std::toupper(s[i]);
return result;
}
/*
- Replacement for Serialize function of 'InWarn', passed to appInit
- Have to do this vtable hack because creating new FFeedbackContext and passing it results in crash
- /
void __stdcall FFeedbackContextAnsiSerialize(const TCHAR* V, EName Event){
TCHAR Temp[1024]= "";
if(Event==NAME_Title){
return;
}else if(Event==NAME_Heading){
appSprintf(Temp, "--------------------%s--------------------", V);
V = Temp;
}else if(Event==NAME_SubHeading){
appSprintf(Temp, "%s...", V);
V = Temp;
}else if(Event==NAME_Error || Event==NAME_Warning || Event==NAME_ExecWarning || Event==NAME_ScriptWarning){
appSprintf(Temp, "Warning : %s", V);
V = Temp;
}else if(Event==NAME_Progress){
appSprintf(Temp, "%s", V);
std::cout << Temp << '\r';
return;
}
std::cout << V << '\n';
GLog->Serialize(V, Event);
}
/*
- Helper class to redirect RC's log output to the system console when launching a server
- /
class FOutputDeviceConsole : public FOutputDevice{
public:
FOutputDeviceConsole(FOutputDevice& FileLog) : FileLog(FileLog){}
void Serialize(const TCHAR* V, EName Event){
if(Event != NAME_Title){ //Prevents server from spamming the player count to the console
std::cout << *FName(Event) << ": " << V << '\n';
FileLog.Serialize(V, Event);
}
}
private:
FOutputDevice& FileLog;
};
//Entry point called by modified SWRepublicCommando.exe
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError,
FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){
int ExitVal = EXIT_SUCCESS;
std::cout << "=======================================\n"
"ucc.exe for Star Wars Republic Commando\n"
"made by Leon0628\n"
"=======================================\n\n";
//Small vtable hack to have InWarn call my own Serialize function
//===============================================================
PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn);
DWORD dwNull;
VirtualProtect(&vtable[0], 4, PAGE_EXECUTE_READWRITE, &dwNull);
vtable[0] = FFeedbackContextAnsiSerialize;
//===============================================================
//Server stuff...
for(int i = 1; i < __argc; i++){
std::string Temp = ToUpper(__argv[i]);
//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
if(Temp == "-SERVER"){
static FOutputDeviceConsole ConsoleLog(*InLog); //Static here just to have everything together in one place
appInit(InPackage, InCmdLine, &ConsoleLog, InError, InWarn, ConfigFactory, RequireConfig);
return;
}
}
//Actual UCC stuff...
try{
GIsStarted = 1;
GIsGuarded = 1;
appInit(InPackage, InCmdLine, InLog, InError, InWarn, ConfigFactory, 1);
UObject::SetLanguage("int");
if(__argc > 1){
//Initializing global state
GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1;
std::string Token = ToUpper(__argv[1]);
DWORD LoadFlags = 0;
std::string ClassName;
bool bIsUScript = false;
if(Token == "MAKE" || Token == "EDITOR.MAKECOMMANDLET"){
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it too...
ClassName = "Editor.MakeCommandlet";
}else if(Token == "USCRIPT"){ //When executing commandlets written in UnrealScript, like Core.HelloWorldCommandlet
ClassName = __argc > 2 ? __argv[2] : Token;
bIsUScript = true;
}else{
ClassName = __argv[1];
}
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
if(Class){
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
InWarn->Logf("Executing %s\n", Class->GetFullName());
Commandlet->InitExecution();
Commandlet->ParseParms(appCmdLine());
if(!bIsUScript){
Commandlet->Main(appCmdLine());
}else{
FOutputDevice* Log = GLog;
FOutputDeviceConsole ConsoleLog(*GLog); //Redirecting UnrealScript log to console
GLog = &ConsoleLog;
Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used
GLog = Log;
}
}
}else{
std::cout << "Usage: ucc <command> <parameters>\n";
}
appPreExit();
GIsGuarded = 0;
}catch(...){
ExitVal = EXIT_FAILURE;
GIsGuarded = 0;
InError->HandleError();
}
appExit();
std::exit(ExitVal); //Needed in order to prevent this function from returning to SWRepublicCommando.exe
}