Program Listing for File main.cpp

Return to documentation for file (blam\main.cpp)

// Blamite Engine - Entry point for Blamite engine  //
//   (c) Elaztek Studios 2016-2018                 //

#include "core.h"

// External includes
#include <thread>
#include <Uxtheme.h>

// Internal includes
#include "components/content/fonts/fonts.h"
#include "components/core/blam_ui/blam_ui.h"
#include "components/core/cache/cache.h"
#include "components/core/config/config.h"
#include "components/core/debug_ui/debug_colors.h"
#include "components/core/debug_ui/debug_ui.h"
#include "components/core/dialogs/dialogs.h"
#include "components/core/engine_definitions/engine_definitions.h"
#include "components/core/error/error.h"
#include "components/core/logger/logger.h"
#include "components/core/placeholders/placeholders.h"
#include "components/core/utils/converters/converters.h"
#include "components/core/utils/io/io.h"
#include "components/core/utils/res/res.h"
#include "components/core/utils/utilities.h"
#include "components/discord/m_discord.h"
#include "components/haloscript/haloscript.h"
#include "components/input/keyboard.h"
#include "components/networking/messages.h"
#include "components/networking/socket.h"
#include "components/rendering/directx11/render_stack/render_stack.h"
#include "components/rendering/directx11/render_stack/stack_types/blam_ui/console.hpp"
#include "components/rendering/directx11/render_stack/stack_types/blam_ui/debug_menu.hpp"
#include "components/rendering/directx11/render_stack/stack_types/blam_ui/fpscounter.hpp"
#include "components/rendering/rendering.h"
#include "version_data.h"
#include "components/3rdparty/imgui/formats/imgui_impl_win32.h"
#include "components/3rdparty/imgui/formats/dx11/imgui_impl_dx11.h"

//extern "C" int __cdecl myPuts(const char* message);

// ImGUI data
BlamRendering::RenderStack::ImGUIObject* imgui;
bool imgui_initialized = false;

// Blam UI data
BlamRendering::RenderStack::ConsoleUI* console;

// Window data
WNDCLASSEX wc;
HWND hwndGoto = NULL;
HWND main_hwnd = NULL;
HINSTANCE hInst;
HCURSOR cursor;
const wchar_t* window_name;

// Socket data
UINT8* socketBuffer;
Blam::Network::Socket sock;
Blam::LinearAllocator allocator;
UINT32 bytesReceived;
Blam::Endpoint from;
extern bool isConnected;

// Threading data
std::thread render_thread;
bool use_separate_render_thread = false;

// Forward declarations
INT_PTR CALLBACK FirstbootDialog(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void cleanup_resources(LPCWSTR ver_conv, WNDCLASSEX wc);
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT WINAPI MainWindowProcedure(HWND window_handle, UINT message, WPARAM wParam, LPARAM lParam)
{
    //handle imgui input
    if (ImGui_ImplWin32_WndProcHandler(window_handle, message, wParam, lParam))
    {
        return true;
    }

    //if imgui doesnt handle input, then its our job to do so
    switch (message)
    {
        case WM_QUIT:
        {
            cleanup_resources(window_name, wc);
            return 0;
        }
        case WM_SIZE:
        {
            //resize Direct3D render target upon window resize
            if (BlamRendering::DirectX::GetD3DDevice() != NULL && wParam != SIZE_MINIMIZED)
            {
                BlamRendering::DirectX::HandleWindowResize(lParam);
            }

            return 0;
        }
        case WM_KEYDOWN:
        {
            return Blam::Input::HandleKeyPress(wParam);
        }
        case WM_CHAR:
        {
            return Blam::Input::HandleCharacterInput(wParam);
        }
        case WM_KEYUP:
        {
            //key released
            return 0;
        }
        case WM_SYSCOMMAND:
        {
            // Disable ALT application menu
            if ((wParam & 0xfff0) == SC_KEYMENU)
            {
                return 0;
            }
            break;
        }
        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case ID_HELP_CREDITS:
                {
                    if (!IsWindow(hwndGoto))
                    {
                        hwndGoto = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROPPAGE_MEDIUM), window_handle, NULL);
                        ShowWindow(hwndGoto, SW_SHOW);
                    }
                    break;
                }
                case ID_HELP_ABOUT:
                {
                    Blam::Dialogs::AboutDialogProcedure(hInst, window_handle);
                    break;
                }
                case ID_TEST_SHOW_FIRSTBOOT:
                {
                    Blam::Dialogs::FirstbootDialogProcedure(hInst, window_handle);
                    break;
                }
                case ID_TEST_ERROR:
                {
                    Blam::Dialogs::ErrorDialogProcedure(hInst, window_handle, "This is a test error.");
                    break;
                }
                case ID_TEST_FONTTEST:
                {
                    Blam::Dialogs::FontTestDialogProcedure(hInst, window_handle);
                    break;
                }
                case ID_TEST_IDD:
                {
                    Blam::Dialogs::ErrorDialogProcedure(hInst, window_handle, "this shit was literally fucking garbage (tell halo to replace with IDD_PROPPAGE_MEDIUM thing just to finally put it in it's place)");
                    break;
                }
                case ID_TEST_RESTOREIMGUI:
                {
                    if (!BlamRendering::RenderStack::ContainsImGUIObject())
                    {
                        BlamRendering::RenderStack::ImGUIObject* imgui_obj = new BlamRendering::RenderStack::ImGUIObject();

                        BlamRendering::RenderStack::AddToStack("imgui", imgui_obj);
                    }
                    else
                    {
                        Blam::DebugUI::ShowErrorDialog("imgui already present", "imgui is already present in the render stack");
                    }

                    break;
                }
                case ID_FILE_EXIT:
                {
                    DestroyWindow(window_handle);
                    break;
                }
                default:
                {
                    return DefWindowProc(window_handle, message, wParam, lParam);
                }
            }
            return 0L;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }

    return DefWindowProc(window_handle, message, wParam, lParam);
}

int main(int args_count, char *args[])
{
    //assign hInstance
    hInst = GetModuleHandle(NULL);

    //int e = myPuts("are we connected");

    Blam::Config::LoadConfig();

    //load console color configuration
    {
        if (Blam::Config::GetConfig()->GetString("console_color_set") == "classic")
        {
            Blam::Utils::IO::CreateFileFromResource(CONFIG_COLOR_NAME, IDR_CON_COL_BUNGIE, "CFG", false);
        }
        else if (Blam::Config::GetConfig()->GetString("console_color_set") == "new")
        {
            Blam::Utils::IO::CreateFileFromResource(CONFIG_COLOR_NAME, IDR_CON_COL_NEW, "CFG", false);
        }
        else
        {
            if (!Blam::Utils::IO::file_exists(CONFIG_COLOR_NAME))
            {
                Blam::Utils::IO::CreateFileFromResource(CONFIG_COLOR_NAME, IDR_CON_COL_NEW, "CFG", false);
            }
        }

        Blam::Config::LoadConfig(CONFIG_COLOR_NAME);
    }

    // Start logger
    {
        //Load configuration placeholders
        Blam::Placeholders::LoadPlaceholders();

        //Initialize logger
        InternalUI::Colors::RegisterDefaultColors();

        Blam::Logger::PrepareLogger();
        Blam::Logger::PrintStartupHeader();
        Blam::Logger::Queue::Flush();

        Blam::LogEvent("Engine root set to '" + Blam::Config::GetConfig()->GetString("game_data_root") + "'");
        Blam::LogEvent("User data root set to '" + Blam::Config::GetConfig()->GetString("user_data_root") + "'");


        Blam::Logger::LogEvent("color log test", true, WSV_NONE, 255, 76, 255);
    }

    // Create gvars.xml then load it
    {
        Blam::Utils::IO::CreateFileFromResource(Blam::Config::GetConfig()->GetString("game_data_root") + GVARS_FILE, IDR_GVARS_XML, "XML", false);

        BlamScript::Globals::LoadGlobalsFromFile();
    }

    // Initialize debug menu
    {
        std::string debug_menu_path = Blam::Config::GetConfig()->GetString("game_data_root") + "" + "./" + DEBUG_MENU_FILE + ".xml";

        if (!Blam::Utils::IO::file_exists(debug_menu_path))
        {
            if (Blam::Config::GetConfig()->GetBoolean("autoGenerateNewDebugMenuFile"))
            {
                Blam::Utils::IO::CreateFileFromResource(debug_menu_path, IDR_DEBUG_MENU_INIT_XML, "XML", false);
            }
            else
            {
                Blam::Logger::LogEvent("### WARNING unable to find debug_menu_init.xml, and automatic generation is disabled in config! this may result in the debug menu not being functional.", WSV_WARNING);
            }
        }

        Blam::DebugMenu::InitializeDebugMenu();
    }

    // Check whether to show firstboot dialog
    if (Blam::Config::GetConfig()->GetBoolean("show_firstboot") == true)
    {
        //show firstboot dialog
    }


    // Create application window
    {
        std::wstring version = Blam::Converters::ConvertStringToWstring(Blam::EngineDefs::GetVersion());
        window_name = version.c_str();

        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_CLASSDC;
        wc.lpfnWndProc = MainWindowProcedure;
        wc.cbClsExtra = 0L;
        wc.cbWndExtra = 0L;
        wc.hInstance = hInst;
        wc.hIcon = NULL;
        wc.hCursor = NULL;
        wc.hbrBackground = NULL;
        wc.lpszMenuName = NULL;
        wc.lpszClassName = window_name;
        wc.hIconSm = NULL;

        //check whether or not to enable windows menubar
        if (Blam::Config::GetConfig()->GetBoolean("show_windows_menubar") == true)
        {
            wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
        }

        //check whether or not to use a custom cursor
        if (Blam::Config::GetConfig()->GetBoolean("use_custom_cursor"))
        {
            cursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER));

            if (cursor)
            {
                wc.hCursor = cursor;
                SetCursor(cursor);
            }
            else
            {
                Blam::LogEvent("### WARNING failed to load cursor resource");
                BL_LOGEVT_RGB("", true, WSV_WARNING, 255, 255, 0);
                Blam::Error::ShowLastErrorInfo("LoadCursorW", Blam::Error::ErrorDisplayType::Log);
            }
        }

        RegisterClassEx(&wc);
        main_hwnd = CreateWindow(window_name, window_name, WS_OVERLAPPEDWINDOW, 0, 0, 1920, 1040, NULL, NULL, wc.hInstance, NULL);

        if (Blam::Config::GetConfig()->GetBoolean("use_themed_windows") == false)
        {
            SetWindowTheme(main_hwnd, L" ", L" ");
        }
    }


    //initialize netcode if enabled
    if (Blam::Config::GetConfig()->GetBoolean("use_netcode") == true)
    {
        Blam::linearAllocCreate(&allocator, 16 * 1024 * 1024);

        Blam::LogEvent("### WARNING netcode is experimental!");

        if (Blam::Network::Init())
        {
            Blam::Network::Start(&sock, &allocator);

            Blam::LogEvent("Blam::Network - Initialized!");

            socketBuffer = Blam::linearAlloc(&allocator, 2048);

            Blam::Endpoint endpoint = {};
            endpoint.address = (127 << 24) | (0 << 16) | (0 << 8) | 1;
            endpoint.port = 6536;

            UINT32 ClientHelloSize = Blam::Network::ClientHelloMessage(socketBuffer);

            if (Blam::Network::Send(&sock, socketBuffer, ClientHelloSize, &endpoint))
            {
                Blam::LogEvent("Successfully sent message!");

                isConnected = true;
            }
        }
    }


    // Initialize DirectX
    {
        HRESULT hr = BlamRendering::DirectX::Initialize(main_hwnd);

        if (FAILED(hr))
        {
            Blam::Logger::LogEvent("### ERROR failed to initialize DirectX! details: '" + std::string(Blam::Error::GetStringFromHResult(hr)) + "'", WSV_CRITICAL);
            cleanup_resources(window_name, wc);
            return -45;
        }
    }


    //Show application window
    ShowWindow(main_hwnd, SW_MAXIMIZE);
    UpdateWindow(main_hwnd);


    //Setup ImGUI
    {
        IMGUI_CHECKVERSION();

        // Setup ImGui binding
        ImGui::CreateContext();


        Blam::LogEvent("Getting I/O for ImGUI");
        // Load Fonts
        // (there is a default font, this is only if you want to change it. see extra_fonts/README.txt for more details)
        ImGuiIO& io = ImGui::GetIO(); //(void)io;

        ImGui_ImplWin32_Init(main_hwnd);
        imgui_initialized = ImGui_ImplDX11_Init(BlamRendering::DirectX::GetD3DDevice(), BlamRendering::DirectX::GetD3DContext());

        if (Blam::Config::GetConfig()->GetBoolean("enable_imgui_gamepad_nav"))
        {
            Blam::LogEvent("imgui gamepad navigation enabled");
            io.ConfigFlags += ImGuiConfigFlags_NavEnableGamepad;
        }

        //load font for imgui
        {
            float font_size = Blam::Config::GetConfig()->GetFloat("sys_font_size");

            std::string font_path_str = "./maps/fonts/" + Blam::Config::GetConfig()->GetString("sys_font");

            if (Blam::Config::GetConfig()->GetBoolean("sys_font_force_extension") == true)
            {
                font_path_str = font_path_str + ".bin";
            }

            const char* font_path = font_path_str.c_str();

            Blam::Logger::LogEvent("Loading standard front from " + font_path_str);

            ImFont* font0 = io.Fonts->AddFontFromFileTTF(font_path_str.c_str(), font_size);
        }


        Blam::LogEvent("Setting UI theme: Elaztek");
        InternalUI::load_elaztek_styles();

        if (BlamRendering::DirectX::RenderTargetClearing() == false)
        {
            Blam::LogEvent("############################################");
            Blam::LogEvent("#   Render target clearing is DISABLED!    #");
            Blam::LogEvent("#  You will notice some visual distortion  #");
            Blam::LogEvent("#          and other visual errors!        #");
            Blam::LogEvent("############################################");
        }

        BlamRendering::SetClearColor(ImColor(114, 144, 154));

        imgui = new BlamRendering::RenderStack::ImGUIObject();

        BlamRendering::RenderStack::AddToStack("imgui", imgui);

        InternalUI::Initialize();
    }



    Blam::LogEvent("ENGINE_VERSION: " + std::string(Blam::EngineDefs::GetVersion()));
    Blam::LogEvent("UI_VERSION: " + std::string(InternalUI::GetVersion()));
    Blam::LogEvent("IMGUI_VERSION: " + std::string(ImGui::GetVersion()));

    //Blam::LogEvent("Beginning init");

    //Load blamscript (this shit really sucks and is useless)
    {
        Blam::Logger::LogEvent("Initializing blamscript (TODO: rewrite all this shit)");
        BlamScript::InitCSC();
        BlamScript::InitHSC();
    }


    //load content
    {
        //Blam::Content::Fonts::LoadFont("./maps/fonts/" + Blam::Config::GetConfig()->GetString("ui_default_font"));
        //Blam::Content::Fonts::LoadFont("./maps/fonts/conduit");
        //Blam::Content::Fonts::LoadFont("./maps/fonts/arial-12");
        //Blam::Content::Fonts::LoadFont("./maps/fonts/denmark-80");

        Blam::Content::Fonts::LoadAllFonts();
    }

    //Initialize Discord Rich Presence
    {
        Blam::DiscordRPC::InitDiscord();
        Blam::DiscordRPC::UpdatePresence(ENGINE_VERSION, "Using the Blamite Engine");
    }

    bool debug = Blam::Config::GetConfig()->GetBoolean("debugMode");

    use_separate_render_thread = Blam::Config::GetConfig()->GetBoolean("use_separate_render_thread");

    if (use_separate_render_thread)
    {
        Blam::Logger::LogEvent("### NOTICE rendering is being handled on a separate thread!");
        render_thread = std::thread(BlamRendering::DirectX::RenderThread, debug);
        render_thread.detach();
    }

    // Load built-in console commands
    Blam::Console::RegisterBuiltinCommands();

    // Create custom ui elements (console, fps counter)
    {
        BlamRendering::RenderStack::FPSCounter* fpscounter = new BlamRendering::RenderStack::FPSCounter();
        BlamRendering::RenderStack::AddToStack("fps_counter", fpscounter);

        console = new BlamRendering::RenderStack::ConsoleUI();
        BlamRendering::RenderStack::AddToStack("console", console);

        BlamRendering::RenderStack::DebugMenu* debug_menu = new BlamRendering::RenderStack::DebugMenu();
        BlamRendering::RenderStack::AddToStack("debug_menu", debug_menu);
    }

    // Run any startup commands in init.txt
    {
        if (Blam::Utils::IO::file_exists("./init.txt"))
        {
            std::vector<std::string> startup_commands = Blam::Utils::IO::GetFileContentsAsLines("./init.txt");

            for (int i = 0; i < startup_commands.size(); i++)
            {
                Blam::Console::RunCommandLine(startup_commands.at(i));
            }
        }
    }

    // Main application loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));

    while (msg.message != WM_QUIT)
    {
        Blam::Tick::tick();

        if (cursor)
        {
            //SetCursor(cursor);
        }

        if (debug == true)
        {
            //Blam::DebugUIKeyListener();
        }

        if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            continue;
        }

        if (!use_separate_render_thread)
        {
            BlamRendering::DirectX::RenderLoop(debug);

            BlamRendering::DirectX::GetDXGISwapChain()->Present(1, 0);
        }

        Blam::Network::HandleReceive(&sock, socketBuffer, 2048, &bytesReceived, &from);

        Blam::Logger::Queue::Flush();
    }

    cleanup_resources(window_name, wc);

    return 0;
}

void cleanup_resources(LPCWSTR ver_conv, WNDCLASSEX wc)
{
    if (use_separate_render_thread)
    {
        Blam::Logger::LogEvent("stopping render thread");

        BlamRendering::DirectX::ShutdownRenderThread();

        while (BlamRendering::DirectX::HasRenderThreadStopped() == false)
        {
            //wait for render thread to stop
        }
    }

    BlamRendering::RenderStack::Cleanup();

    Blam::LogEvent("Shutting down discord");
    Blam::DiscordRPC::ShutdownDiscord();

    if (imgui_initialized)
    {
        Blam::LogEvent("Shutting down ImGUI and cleaning up D3D device");
        ImGui_ImplDX11_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();
    }
    else
    {
        Blam::LogEvent("### INFO engine shutting down before imgui init");
    }

    InternalUI::Shutdown();

    //delete imgui;
    //delete console;

    Blam::Console::Cleanup();

    Blam::Content::Fonts::Cleanup();

    Blam::Cache::ClearCachedFiles();

    BlamRendering::DirectX::Cleanup();

    Blam::Logger::Queue::Flush();

    DestroyWindow(main_hwnd);
    UnregisterClass(ver_conv, wc.hInstance);
}

HWND Blam::GetMainWindowHandle()
{
    return main_hwnd;
}