|
|
(43 intermediate revisions by 3 users not shown) |
Line 1: |
Line 1: |
| Author: Leon
| | UCC is a command line utility for early Unreal engine games. Star Wars: Republic Commando ships without one, but a custom one has been made available as a result of reverse engineering. |
|
| |
|
| Used Tools: [[MS Visual Studio 2003]] | | Description: A custom UCC.exe for Republic Commando. Used for executing Unreal Commandlets. |
|
| |
|
| Description: A self written UCC for Republic Commando, still in development. Used for executing Unreal Commandlets.
| | Latest Build: [https://github.com/SWRC-Modding/CT/releases here] |
|
| |
|
| Github: [https://github.com/Leon280698/Republic-Commando-UCC here]
| |
|
| |
|
| | [[File:Uccshow.PNG]] |
|
| |
|
| ==UCC.cpp== | | ===== Batchexport Commandlet ===== |
|
| |
|
| <source lang="c++" line">
| | The batchexport commandlet is made available by UCC. This commandlet can parse the game's resource archives in order to export any exportable type in bulk. Here is the syntax for converting some texture package, called "exampletexturepackage.utx" from the game into a set of tga files in ExampleOutputFolder: |
| /*
| |
| * 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")
| | '''<sup>./ucc.exe batchexport exampletexturepackage.utx texture tga ".\\ExampleOutputFolder"</sup>''' |
| //#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case... | |
|
| |
|
| #include <windows.h> //VirtualProtect
| | In particular, UCC can extract batches of sounds from the UAX archives where Republic Commando's voice lines and sound effects are stored. The batchexport commandlet does this. See [[Extract_Game_Audio_Using_UCC|how to extract game audio using UCC]] for details. |
| | |
| #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
| |
| }
| |
| </source>
| |