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"
Jump to navigation
Jump to search
Line 36: | Line 36: | ||
#include "Core/inc/Core.h" | #include "Core/inc/Core.h" | ||
namespace{ | |||
//Global variables | |||
// | FOutputDevice* GFileLog; //OutputDevice that writes to a log file | ||
struct CommandletName{ | |||
std::string Package, Commandlet; | |||
}; | |||
//All commandlets shipped with RC | |||
const CommandletName DefaultCommandlets[] = { | |||
{"Editor", "AnalyzeContent"}, | |||
{"Editor", "BatchExport"}, | |||
{"Editor", "ChecksumPackage"}, | |||
{"Editor", "CheckUnicode"}, | |||
{"Editor", "CompareInt"}, | |||
{"IpDrv", "Compress"}, | |||
{"Editor", "CompressToDXT"}, | |||
{"Editor", "Conform"}, | |||
{"Editor", "ConvertMaterial"}, | |||
{"Editor", "CutdownContent"}, | |||
{"Editor", "DataRip"}, | |||
{"IpDrv", "Decompress"}, | |||
{"XGame", "DumpGameList"}, | |||
{"Editor", "DumpInt"}, | |||
{"XGame", "DumpMapList"}, | |||
{"XGame", "DumpMutatorList"}, | |||
{"Editor", "DumpSoundParams"}, | |||
{"Editor", "DumpSoundPropLog"}, | |||
{"XGame", "DumpWeaponList"}, | |||
{"Editor", "DXTConvert"}, | |||
{"Editor", "Exec"}, | |||
{"Editor", "FontUpdate"}, | |||
{"Core", "HelloWorld"}, | |||
{"Editor", "ImportAse"}, | |||
{"Editor", "ImportTexture"}, | |||
{"Editor", "ListPackageContents"}, | |||
{"Editor", "Make"}, | |||
{"Editor", "MapConvert"}, | |||
{"Editor", "Master"}, | |||
{"Editor", "MergeInt"}, | |||
{"Editor", "ModifyPackageFlags"}, | |||
{"Editor", "PackageFlag"}, | |||
{"Editor", "Pkg"}, | |||
{"Editor", "RearrangeInt"}, | |||
{"Editor", "ResavePackage"}, | |||
{"Engine", "Server"}, | |||
{"Editor", "StripSource"}, | |||
{"Editor", "TestMath"}, | |||
{"Editor", "UpdateUMod"}, | |||
{"Editor", "XACTExport"} | |||
}; | |||
return result; | //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 | * 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 | * Have to do this vtable hack because creating new FFeedbackContext and passing it results in crash | ||
*/ | */ | ||
void __stdcall FFeedbackContextAnsiSerialize(const TCHAR* V, EName Event){ | void __stdcall FFeedbackContextAnsiSerialize(const TCHAR* V, EName Event){ | ||
TCHAR Temp[1024]= ""; | |||
if(Event==NAME_Title){ | |||
return; | |||
}else if(Event==NAME_Heading){ | |||
appSprintf(Temp, "\n--------------------%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 || Event == NAME_Critical){ | |||
appSprintf(Temp, "%s: %s", *FName(Event), V); | |||
V = Temp; | |||
} | |||
std::puts(V); | |||
GFileLog->Serialize(V, Event); | |||
} | |||
} | } | ||
Line 95: | Line 147: | ||
//=============================================================== | //=============================================================== | ||
InWarn->Log("======================================= | InWarn->Log("======================================="); | ||
InWarn->Log("ucc.exe for Star Wars Republic Commando"); | |||
InWarn->Log("made by Leon0628"); | |||
InWarn->Log("=======================================\n"); | |||
//Server stuff... | //Server stuff... | ||
Line 113: | Line 165: | ||
} | } | ||
} | } | ||
//Actual UCC stuff... | //Actual UCC stuff... | ||
Line 127: | Line 179: | ||
GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1; | GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1; | ||
std::string | std::string ClassName = __argv[1]; | ||
std::string Upper = ToUpper(ClassName); | |||
DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet; | DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet; | ||
bool bIsUScript = true; //Whether the commandlet is written in UnrealScript or native code | |||
bool bIsUScript = | |||
if( | if(Upper == "MAKE" || Upper == "EDITOR.MAKE" || Upper == "EDITOR.MAKECOMMANDLET"){ | ||
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it | LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it aswell... | ||
ClassName = "Editor.MakeCommandlet"; | ClassName = "Editor.MakeCommandlet"; | ||
bIsUScript = false; | |||
bIsUScript = | |||
}else{ | }else{ | ||
ClassName = | //Looking it up in list of default commandlets | ||
for(int i = 0; i < ARRAY_COUNT(DefaultCommandlets); i++){ | |||
if(Upper == ToUpper(DefaultCommandlets[i].Commandlet) || | |||
Upper == ToUpper(DefaultCommandlets[i].Commandlet) + "COMMANDLET"){ | |||
ClassName = DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet + "Commandlet"; | |||
//HelloWorld is the only standard commandlet written in UnrealScript | |||
bIsUScript = ClassName == "Core.HelloWorldCommandlet"; | |||
break; | |||
}else if(Upper == ToUpper(DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet) || | |||
Upper == ToUpper(DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet) + "COMMANDLET"){ | |||
//HelloWorld is the only standard commandlet written in UnrealScript | |||
bIsUScript = Upper == "CORE.HELLOWORLD" || Upper == "CORE.HELLOWORLDCOMMANDLET"; | |||
break; | |||
} | |||
} | |||
} | } | ||
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL); | UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL); | ||
if(!Class) //If class failed to load appending "Commandlet" and trying again | if(!Class) //If class failed to load, appending "Commandlet" and trying again | ||
Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, (ClassName + "Commandlet").c_str(), NULL, LoadFlags, NULL); | Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, (ClassName + "Commandlet").c_str(), NULL, LoadFlags, NULL); | ||
Line 167: | Line 234: | ||
} | } | ||
}else{ | }else{ | ||
InWarn->Log("Usage: | InWarn->Log("Usage:"); | ||
InWarn->Log(" ucc PackageName.CommandletName <parameters>"); | |||
InWarn->Log("OR"); | |||
InWarn->Log(" ucc uscript PackageName.CommandletName <parameters>"); | |||
InWarn->Log(" for commandlets written in UnrealScript"); | |||
} | } | ||
Revision as of 11:32, 22 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
Latest Build: 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
* Also it's subsystem should be changed from window to console
* 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")
#include <windows.h> //VirtualProtect
#include <cstdio> //std::puts
#include <cstdlib> //__argc, __argv
#include <cctype> //std::toupper
#include <string>
#include "Core/inc/Core.h"
namespace{
//Global variables
FOutputDevice* GFileLog; //OutputDevice that writes to a log file
struct CommandletName{
std::string Package, Commandlet;
};
//All commandlets shipped with RC
const CommandletName DefaultCommandlets[] = {
{"Editor", "AnalyzeContent"},
{"Editor", "BatchExport"},
{"Editor", "ChecksumPackage"},
{"Editor", "CheckUnicode"},
{"Editor", "CompareInt"},
{"IpDrv", "Compress"},
{"Editor", "CompressToDXT"},
{"Editor", "Conform"},
{"Editor", "ConvertMaterial"},
{"Editor", "CutdownContent"},
{"Editor", "DataRip"},
{"IpDrv", "Decompress"},
{"XGame", "DumpGameList"},
{"Editor", "DumpInt"},
{"XGame", "DumpMapList"},
{"XGame", "DumpMutatorList"},
{"Editor", "DumpSoundParams"},
{"Editor", "DumpSoundPropLog"},
{"XGame", "DumpWeaponList"},
{"Editor", "DXTConvert"},
{"Editor", "Exec"},
{"Editor", "FontUpdate"},
{"Core", "HelloWorld"},
{"Editor", "ImportAse"},
{"Editor", "ImportTexture"},
{"Editor", "ListPackageContents"},
{"Editor", "Make"},
{"Editor", "MapConvert"},
{"Editor", "Master"},
{"Editor", "MergeInt"},
{"Editor", "ModifyPackageFlags"},
{"Editor", "PackageFlag"},
{"Editor", "Pkg"},
{"Editor", "RearrangeInt"},
{"Editor", "ResavePackage"},
{"Engine", "Server"},
{"Editor", "StripSource"},
{"Editor", "TestMath"},
{"Editor", "UpdateUMod"},
{"Editor", "XACTExport"}
};
//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, "\n--------------------%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 || Event == NAME_Critical){
appSprintf(Temp, "%s: %s", *FName(Event), V);
V = Temp;
}
std::puts(V);
GFileLog->Serialize(V, Event);
}
}
//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;
GFileLog = InLog;
//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;
//===============================================================
InWarn->Log("=======================================");
InWarn->Log("ucc.exe for Star Wars Republic Commando");
InWarn->Log("made by Leon0628");
InWarn->Log("=======================================\n");
//Server stuff...
for(int i = 1; i < __argc; i++){
std::string Temp = ToUpper(__argv[i]);
//In case user wants to start a server, redirecting log to console and returning to SWRepublicCommando.exe
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
if(Temp == "-SERVER"){
appInit(InPackage, InCmdLine, InWarn, 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 ClassName = __argv[1];
std::string Upper = ToUpper(ClassName);
DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet;
bool bIsUScript = true; //Whether the commandlet is written in UnrealScript or native code
if(Upper == "MAKE" || Upper == "EDITOR.MAKE" || Upper == "EDITOR.MAKECOMMANDLET"){
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it aswell...
ClassName = "Editor.MakeCommandlet";
bIsUScript = false;
}else{
//Looking it up in list of default commandlets
for(int i = 0; i < ARRAY_COUNT(DefaultCommandlets); i++){
if(Upper == ToUpper(DefaultCommandlets[i].Commandlet) ||
Upper == ToUpper(DefaultCommandlets[i].Commandlet) + "COMMANDLET"){
ClassName = DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet + "Commandlet";
//HelloWorld is the only standard commandlet written in UnrealScript
bIsUScript = ClassName == "Core.HelloWorldCommandlet";
break;
}else if(Upper == ToUpper(DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet) ||
Upper == ToUpper(DefaultCommandlets[i].Package + "." + DefaultCommandlets[i].Commandlet) + "COMMANDLET"){
//HelloWorld is the only standard commandlet written in UnrealScript
bIsUScript = Upper == "CORE.HELLOWORLD" || Upper == "CORE.HELLOWORLDCOMMANDLET";
break;
}
}
}
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
if(!Class) //If class failed to load, appending "Commandlet" and trying again
Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, (ClassName + "Commandlet").c_str(), NULL, LoadFlags, NULL);
if(Class){
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
InWarn->Logf("Executing %s\n", Class->GetFullName());
Commandlet->InitExecution();
Commandlet->ParseParms(appCmdLine());
GLog = InWarn; //Redirecting commandlet output to console
if(!bIsUScript)
Commandlet->Main(appCmdLine());
else
Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used
GLog = GFileLog;
}else{
InWarn->Logf("Commandlet %s not found", ClassName.c_str());
}
}else{
InWarn->Log("Usage:");
InWarn->Log(" ucc PackageName.CommandletName <parameters>");
InWarn->Log("OR");
InWarn->Log(" ucc uscript PackageName.CommandletName <parameters>");
InWarn->Log(" for commandlets written in UnrealScript");
}
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
}