code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h
#define SDL_MAIN_HANDLED
#include <SDL.h>

#include "common/logging/log.h"
#include "input_common/main.h"
#include "sdl_config.h"

const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = {
    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
    SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
    SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
};

const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = {
    SDL_SCANCODE_7,
    SDL_SCANCODE_8,
};

const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{
    {
        {
            SDL_SCANCODE_UP,
            SDL_SCANCODE_DOWN,
            SDL_SCANCODE_LEFT,
            SDL_SCANCODE_RIGHT,
        },
        {
            SDL_SCANCODE_I,
            SDL_SCANCODE_K,
            SDL_SCANCODE_J,
            SDL_SCANCODE_L,
        },
    }};

const std::array<int, 2> SdlConfig::default_stick_mod = {
    SDL_SCANCODE_D,
    0,
};

const std::array<int, 2> SdlConfig::default_ringcon_analogs{{
    0,
    0,
}};

SdlConfig::SdlConfig(const std::optional<std::string> config_path) {
    Initialize(config_path);
    ReadSdlValues();
    SaveSdlValues();
}

SdlConfig::~SdlConfig() {
    if (global) {
        SdlConfig::SaveAllValues();
    }
}

void SdlConfig::ReloadAllValues() {
    Reload();
    ReadSdlValues();
    SaveSdlValues();
}

void SdlConfig::SaveAllValues() {
    SaveValues();
    SaveSdlValues();
}

void SdlConfig::ReadSdlValues() {
    ReadSdlControlValues();
}

void SdlConfig::ReadSdlControlValues() {
    BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));

    Settings::values.players.SetGlobal(!IsCustomConfig());
    for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
        ReadSdlPlayerValues(p);
    }
    if (IsCustomConfig()) {
        EndGroup();
        return;
    }
    ReadDebugControlValues();
    ReadHidbusValues();

    EndGroup();
}

void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
    std::string player_prefix;
    if (type != ConfigType::InputProfile) {
        player_prefix.append("player_").append(ToString(player_index)).append("_");
    }

    auto& player = Settings::values.players.GetValue()[player_index];
    if (IsCustomConfig()) {
        const auto profile_name =
            ReadStringSetting(std::string(player_prefix).append("profile_name"));
        if (profile_name.empty()) {
            // Use the global input config
            player = Settings::values.players.GetValue(true)[player_index];
            player.profile_name = "";
            return;
        }
    }

    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
        auto& player_buttons = player.buttons[i];

        player_buttons = ReadStringSetting(
            std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
        if (player_buttons.empty()) {
            player_buttons = default_param;
        }
    }

    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
        const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
            default_analogs[i][3], default_stick_mod[i], 0.5f);
        auto& player_analogs = player.analogs[i];

        player_analogs = ReadStringSetting(
            std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
        if (player_analogs.empty()) {
            player_analogs = default_param;
        }
    }

    for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
        auto& player_motions = player.motions[i];

        player_motions = ReadStringSetting(
            std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
        if (player_motions.empty()) {
            player_motions = default_param;
        }
    }
}

void SdlConfig::ReadDebugControlValues() {
    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
        auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
        debug_pad_buttons = ReadStringSetting(
            std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
        if (debug_pad_buttons.empty()) {
            debug_pad_buttons = default_param;
        }
    }
    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
        const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
            default_analogs[i][3], default_stick_mod[i], 0.5f);
        auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
        debug_pad_analogs = ReadStringSetting(
            std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
        if (debug_pad_analogs.empty()) {
            debug_pad_analogs = default_param;
        }
    }
}

void SdlConfig::ReadHidbusValues() {
    const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
        0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
    auto& ringcon_analogs = Settings::values.ringcon_analogs;

    ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
    if (ringcon_analogs.empty()) {
        ringcon_analogs = default_param;
    }
}

void SdlConfig::SaveSdlValues() {
    LOG_DEBUG(Config, "Saving SDL configuration values");
    SaveSdlControlValues();

    WriteToIni();
}

void SdlConfig::SaveSdlControlValues() {
    BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));

    Settings::values.players.SetGlobal(!IsCustomConfig());
    for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
        SaveSdlPlayerValues(p);
    }
    if (IsCustomConfig()) {
        EndGroup();
        return;
    }
    SaveDebugControlValues();
    SaveHidbusValues();

    EndGroup();
}

void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
    std::string player_prefix;
    if (type != ConfigType::InputProfile) {
        player_prefix = std::string("player_").append(ToString(player_index)).append("_");
    }

    const auto& player = Settings::values.players.GetValue()[player_index];
    if (IsCustomConfig() && player.profile_name.empty()) {
        // No custom profile selected
        return;
    }

    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
        WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
                           player.buttons[i], std::make_optional(default_param));
    }
    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
        const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
            default_analogs[i][3], default_stick_mod[i], 0.5f);
        WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
                           player.analogs[i], std::make_optional(default_param));
    }
    for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
        WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
                           player.motions[i], std::make_optional(default_param));
    }
}

void SdlConfig::SaveDebugControlValues() {
    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
        const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
        WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
                           Settings::values.debug_pad_buttons[i],
                           std::make_optional(default_param));
    }
    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
        const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
            default_analogs[i][3], default_stick_mod[i], 0.5f);
        WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
                           Settings::values.debug_pad_analogs[i],
                           std::make_optional(default_param));
    }
}

void SdlConfig::SaveHidbusValues() {
    const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
        0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
    WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
                       std::make_optional(default_param));
}

std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {
    return Settings::values.linkage.by_category[category];
}


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "frontend_common/config.h"

class SdlConfig final : public Config {
public:
    explicit SdlConfig(std::optional<std::string> config_path);
    ~SdlConfig() override;

    void ReloadAllValues() override;
    void SaveAllValues() override;

protected:
    void ReadSdlValues();
    void ReadSdlPlayerValues(std::size_t player_index);
    void ReadSdlControlValues();
    void ReadHidbusValues() override;
    void ReadDebugControlValues() override;
    void ReadPathValues() override {}
    void ReadShortcutValues() override {}
    void ReadUIValues() override {}
    void ReadUIGamelistValues() override {}
    void ReadUILayoutValues() override {}
    void ReadMultiplayerValues() override {}

    void SaveSdlValues();
    void SaveSdlPlayerValues(std::size_t player_index);
    void SaveSdlControlValues();
    void SaveHidbusValues() override;
    void SaveDebugControlValues() override;
    void SavePathValues() override {}
    void SaveShortcutValues() override {}
    void SaveUIValues() override {}
    void SaveUIGamelistValues() override {}
    void SaveUILayoutValues() override {}
    void SaveMultiplayerValues() override {}

    std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;

public:
    static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
    static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
    static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
    static const std::array<int, 2> default_stick_mod;
    static const std::array<int, 2> default_ringcon_analogs;
};

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <chrono>
#include <iostream>
#include <memory>
#include <regex>
#include <string>
#include <thread>

#include <fmt/ostream.h>

#include "common/detached_tasks.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/nvidia_flags.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "common/telemetry.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs/vfs_real.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/telemetry_session.h"
#include "frontend_common/config.h"
#include "input_common/main.h"
#include "network/network.h"
#include "sdl_config.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"

#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>

#include <shellapi.h>

#include "common/windows/timer_resolution.h"
#endif

#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif

#ifdef _WIN32
extern "C" {
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
// graphics
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif

#ifdef __unix__
#include "common/linux/gamemode.h"
#endif

static void PrintHelp(const char* argv0) {
    std::cout << "Usage: " << argv0
              << " [options] <filename>\n"
                 "-c, --config          Load the specified configuration file\n"
                 "-f, --fullscreen      Start in fullscreen mode\n"
                 "-g, --game            File path of the game to load\n"
                 "-h, --help            Display this help and exit\n"
                 "-m, --multiplayer=nick:password@address:port"
                 " Nickname, password, address and port for multiplayer\n"
                 "-p, --program         Pass following string as arguments to executable\n"
                 "-u, --user            Select a specific user profile from 0 to 7\n"
                 "-v, --version         Output version information and exit\n";
}

static void PrintVersion() {
    std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}

static void OnStateChanged(const Network::RoomMember::State& state) {
    switch (state) {
    case Network::RoomMember::State::Idle:
        LOG_DEBUG(Network, "Network is idle");
        break;
    case Network::RoomMember::State::Joining:
        LOG_DEBUG(Network, "Connection sequence to room started");
        break;
    case Network::RoomMember::State::Joined:
        LOG_DEBUG(Network, "Successfully joined to the room");
        break;
    case Network::RoomMember::State::Moderator:
        LOG_DEBUG(Network, "Successfully joined the room as a moderator");
        break;
    default:
        break;
    }
}

static void OnNetworkError(const Network::RoomMember::Error& error) {
    switch (error) {
    case Network::RoomMember::Error::LostConnection:
        LOG_DEBUG(Network, "Lost connection to the room");
        break;
    case Network::RoomMember::Error::CouldNotConnect:
        LOG_ERROR(Network, "Error: Could not connect");
        exit(1);
        break;
    case Network::RoomMember::Error::NameCollision:
        LOG_ERROR(
            Network,
            "You tried to use the same nickname as another user that is connected to the Room");
        exit(1);
        break;
    case Network::RoomMember::Error::IpCollision:
        LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is "
                           "connected to the Room");
        exit(1);
        break;
    case Network::RoomMember::Error::WrongPassword:
        LOG_ERROR(Network, "Room replied with: Wrong password");
        exit(1);
        break;
    case Network::RoomMember::Error::WrongVersion:
        LOG_ERROR(Network,
                  "You are using a different version than the room you are trying to connect to");
        exit(1);
        break;
    case Network::RoomMember::Error::RoomIsFull:
        LOG_ERROR(Network, "The room is full");
        exit(1);
        break;
    case Network::RoomMember::Error::HostKicked:
        LOG_ERROR(Network, "You have been kicked by the host");
        break;
    case Network::RoomMember::Error::HostBanned:
        LOG_ERROR(Network, "You have been banned by the host");
        break;
    case Network::RoomMember::Error::UnknownError:
        LOG_ERROR(Network, "UnknownError");
        break;
    case Network::RoomMember::Error::PermissionDenied:
        LOG_ERROR(Network, "PermissionDenied");
        break;
    case Network::RoomMember::Error::NoSuchUser:
        LOG_ERROR(Network, "NoSuchUser");
        break;
    }
}

static void OnMessageReceived(const Network::ChatEntry& msg) {
    std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl;
}

static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
    std::string message;
    switch (msg.type) {
    case Network::IdMemberJoin:
        message = fmt::format("{} has joined", msg.nickname);
        break;
    case Network::IdMemberLeave:
        message = fmt::format("{} has left", msg.nickname);
        break;
    case Network::IdMemberKicked:
        message = fmt::format("{} has been kicked", msg.nickname);
        break;
    case Network::IdMemberBanned:
        message = fmt::format("{} has been banned", msg.nickname);
        break;
    case Network::IdAddressUnbanned:
        message = fmt::format("{} has been unbanned", msg.nickname);
        break;
    }
    if (!message.empty())
        std::cout << std::endl << "* " << message << std::endl << std::endl;
}

/// Application entry point
int main(int argc, char** argv) {
#ifdef _WIN32
    if (AttachConsole(ATTACH_PARENT_PROCESS)) {
        freopen("CONOUT$", "wb", stdout);
        freopen("CONOUT$", "wb", stderr);
    }
#endif

    Common::Log::Initialize();
    Common::Log::SetColorConsoleBackendEnabled(true);
    Common::Log::Start();
    Common::DetachedTasks detached_tasks;

    int option_index = 0;
#ifdef _WIN32
    int argc_w;
    auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);

    if (argv_w == nullptr) {
        LOG_CRITICAL(Frontend, "Failed to get command line arguments");
        return -1;
    }
#endif
    std::string filepath;
    std::optional<std::string> config_path;
    std::string program_args;
    std::optional<int> selected_user;

    bool use_multiplayer = false;
    bool fullscreen = false;
    std::string nickname{};
    std::string password{};
    std::string address{};
    u16 port = Network::DefaultRoomPort;

    static struct option long_options[] = {
        // clang-format off
        {"config", required_argument, 0, 'c'},
        {"fullscreen", no_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {"game", required_argument, 0, 'g'},
        {"multiplayer", required_argument, 0, 'm'},
        {"program", optional_argument, 0, 'p'},
        {"user", required_argument, 0, 'u'},
        {"version", no_argument, 0, 'v'},
        {0, 0, 0, 0},
        // clang-format on
    };

    while (optind < argc) {
        int arg = getopt_long(argc, argv, "g:fhvp::c:u:", long_options, &option_index);
        if (arg != -1) {
            switch (static_cast<char>(arg)) {
            case 'c':
                config_path = optarg;
                break;
            case 'f':
                fullscreen = true;
                LOG_INFO(Frontend, "Starting in fullscreen mode...");
                break;
            case 'h':
                PrintHelp(argv[0]);
                return 0;
            case 'g': {
                const std::string str_arg(optarg);
                filepath = str_arg;
                break;
            }
            case 'm': {
                use_multiplayer = true;
                const std::string str_arg(optarg);
                // regex to check if the format is nickname:password@ip:port
                // with optional :password
                const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
                if (!std::regex_match(str_arg, re)) {
                    std::cout << "Wrong format for option --multiplayer\n";
                    PrintHelp(argv[0]);
                    return 0;
                }

                std::smatch match;
                std::regex_search(str_arg, match, re);
                ASSERT(match.size() == 5);
                nickname = match[1];
                password = match[2];
                address = match[3];
                if (!match[4].str().empty()) {
                    port = static_cast<u16>(std::strtoul(match[4].str().c_str(), nullptr, 0));
                }
                std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
                if (!std::regex_match(nickname, nickname_re)) {
                    std::cout
                        << "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n";
                    return 0;
                }
                if (address.empty()) {
                    std::cout << "Address to room must not be empty.\n";
                    return 0;
                }
                break;
            }
            case 'p':
                program_args = argv[optind];
                ++optind;
                break;
            case 'u':
                selected_user = atoi(optarg);
                break;
            case 'v':
                PrintVersion();
                return 0;
            }
        } else {
#ifdef _WIN32
            filepath = Common::UTF16ToUTF8(argv_w[optind]);
#else
            filepath = argv[optind];
#endif
            optind++;
        }
    }

    SdlConfig config{config_path};

    // apply the log_filter setting
    // the logger was initialized before and doesn't pick up the filter on its own
    Common::Log::Filter filter;
    filter.ParseFilterString(Settings::values.log_filter.GetValue());
    Common::Log::SetGlobalFilter(filter);

    if (!program_args.empty()) {
        Settings::values.program_args = program_args;
    }

    if (selected_user.has_value()) {
        Settings::values.current_user = std::clamp(*selected_user, 0, 7);
    }

#ifdef _WIN32
    LocalFree(argv_w);
#endif

    MicroProfileOnThreadCreate("EmuThread");
    SCOPE_EXIT {
        MicroProfileShutdown();
    };

    Common::ConfigureNvidiaEnvironmentFlags();

    if (filepath.empty()) {
        LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
        return -1;
    }

    Core::System system{};
    system.Initialize();

    InputCommon::InputSubsystem input_subsystem{};

    // Apply the command line arguments
    system.ApplySettings();

    std::unique_ptr<EmuWindow_SDL2> emu_window;
    switch (Settings::values.renderer_backend.GetValue()) {
    case Settings::RendererBackend::OpenGL:
        emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
        break;
    case Settings::RendererBackend::Vulkan:
        emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
        break;
    case Settings::RendererBackend::Null:
        emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen);
        break;
    }

#ifdef _WIN32
    Common::Windows::SetCurrentTimerResolutionToMaximum();
    system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
#endif

    system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
    system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
    system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
    system.GetUserChannel().clear();

    Service::AM::FrontendAppletParameters load_parameters{
        .applet_id = Service::AM::AppletId::Application,
    };
    const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};

    switch (load_result) {
    case Core::SystemResultStatus::ErrorGetLoader:
        LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
        return -1;
    case Core::SystemResultStatus::ErrorLoader:
        LOG_CRITICAL(Frontend, "Failed to load ROM!");
        return -1;
    case Core::SystemResultStatus::ErrorNotInitialized:
        LOG_CRITICAL(Frontend, "CPUCore not initialized");
        return -1;
    case Core::SystemResultStatus::ErrorVideoCore:
        LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
        return -1;
    case Core::SystemResultStatus::Success:
        break; // Expected case
    default:
        if (static_cast<u32>(load_result) >
            static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
            const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
            const u16 error_id = static_cast<u16>(load_result) - loader_id;
            LOG_CRITICAL(Frontend,
                         "While attempting to load the ROM requested, an error occurred. Please "
                         "refer to the yuzu wiki for more information or the yuzu discord for "
                         "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
                         loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
        }
        break;
    }

    system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");

    if (use_multiplayer) {
        if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) {
            member->BindOnChatMessageReceived(OnMessageReceived);
            member->BindOnStatusMessageReceived(OnStatusMessageReceived);
            member->BindOnStateChanged(OnStateChanged);
            member->BindOnError(OnNetworkError);
            LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
                      nickname);
            member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
        } else {
            LOG_ERROR(Network, "Could not access RoomMember");
            return 0;
        }
    }

    // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
    system.GPU().Start();
    system.GetCpuManager().OnGpuReady();

    if (Settings::values.use_disk_shader_cache.GetValue()) {
        system.Renderer().ReadRasterizer()->LoadDiskResources(
            system.GetApplicationProcessProgramID(), std::stop_token{},
            [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
    }

    system.RegisterExitCallback([&] {
        // Just exit right away.
        exit(0);
    });

#ifdef __unix__
    Common::Linux::StartGamemode();
#endif

    void(system.Run());
    if (system.DebuggerEnabled()) {
        system.InitializeDebugger();
    }
    while (emu_window->IsOpen()) {
        emu_window->WaitEvent();
    }
    system.DetachDebugger();
    void(system.Pause());
    system.ShutdownMainProcess();

#ifdef __unix__
    Common::Linux::StopGamemode();
#endif

    detached_tasks.WaitForAllTasks();
    return 0;
}


code:
// SPDX-FileCopyrightText: 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
YUZU_ICON               ICON                    "../../dist/yuzu.ico"

/////////////////////////////////////////////////////////////////////////////
//
// RT_MANIFEST
//

0                       RT_MANIFEST             "../../dist/yuzu.manifest"


code:
#!/bin/bash -ex

# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: MIT

git submodule sync
git submodule foreach --recursive git reset --hard
git submodule update --init --recursive


yuzu

dependencies: boost-algorithm boost-asio boost-bind boost-config boost-container boost-context boost-crc boost-functional boost-heap boost-icl boost-intrusive boost-mpl boost-process boost-range boost-spirit boost-test boost-timer boost-variant fmt lz4 nlohmann-json zlib zstd

description: Compile tests

description: Enable web services (telemetry, etc.)

openssl

platform: windows

description: Enable Android dependencies

oboe

platform: android


code:
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
 "winapi",
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi",
]

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "clap"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
dependencies = [
 "ansi_term",
 "atty",
 "bitflags",
 "strsim",
 "textwrap",
 "unicode-width",
 "vec_map",
]

[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
 "unicode-segmentation",
]

[[package]]
name = "hermit-abi"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
dependencies = [
 "libc",
]

[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"

[[package]]
name = "linkfinder"
version = "0.1.0"
dependencies = [
 "serde",
 "serde_json",
 "structopt",
]

[[package]]
name = "proc-macro-error"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
dependencies = [
 "proc-macro-error-attr",
 "proc-macro2",
 "quote",
 "syn",
 "version_check",
]

[[package]]
name = "proc-macro-error-attr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "syn-mid",
 "version_check",
]

[[package]]
name = "proc-macro2"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "ryu"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"

[[package]]
name = "serde"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"

[[package]]
name = "structopt"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef"
dependencies = [
 "clap",
 "lazy_static",
 "structopt-derive",
]

[[package]]
name = "structopt-derive"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a"
dependencies = [
 "heck",
 "proc-macro-error",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "syn"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-xid",
]

[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
 "unicode-width",
]

[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"

[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"

[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"

[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"

[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"

[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"


code:
[package]
name = "linkfinder"
version = "0.1.0"
authors = ["Orin Serimon <orin220444@protonmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
structopt = "*"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = { version = "1.0.48", default-features = false, features = ["alloc"] }


code:
use structopt::StructOpt;
extern crate serde;
extern crate serde_json;

/// searches links in telegram message
#[derive(StructOpt)]
struct Cli{
/// The path to the file with telegram entities
#[structopt(parse(from_os_str))]
path: std::path::PathBuf,
}
fn main() {
let args = Cli::from_args();
///get all the data from the file to a variable
let content = std::fs::read_to_string(&args.path)
.expect("could not read file");
println!("{}", content);
/// create a struct to clone data from the object in the array
use serde_json::Number;
struct Entity {
offset: Number,
length: Number,
type_is: String,
url:String,
}
/// create a vec of structs of entities
use std::fmt::Debug;
#[derive(Debug)]
let entities: Vec<Entity> =
serde_json::from_str(&content)?;//.expect("JSON not well-formatted");
println!("entities: {:#?}",entities);
for i in entities {
println!("{:#?}", [i]);
}

}


code:
*.kate-swp

# Object files
*.o
*.ko
*.obj
*.elf

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dirstamp
*.dSYM/

/autom4te.cache
/aclocal.m4
/m4
/compile
/configure
/depcomp
/install-sh
/missing
/stamp-h1
/configure.scan
Makefile.in
Makefile

.deps

/config.*
/settings.ini
/src/glmv

# ignore the external libs for now, i'll figure something out later for it
/src/c3dlas
#/src/text #merged into this project for now
/src/fxml

# these are not build files
!/assets/**/*.obj

# saved sdf font data
*.sdf
sdf.png

path: .gitignore


code:
SUBDIRS = src

ACLOCAL_AMFLAGS = -I m4


code:
{
	textures: {
		diffuse: {internalType: "GL_RGB", format: "GL_RGB", size: "GL_UNSIGNED_BYTE"},
		normal: {internalType: "GL_RGB", format: "GL_RGB", size: "GL_UNSIGNED_BYTE"},
		selection: {internalType: "GL_RGBA8", format: "GL_RGBA", size: "GL_UNSIGNED_BYTE"},
		lighting: {internalType: "GL_RGB8", format: "GL_RGB", size: "GL_UNSIGNED_BYTE"},
		depth: {internalType: "GL_DEPTH_COMPONENT32", format: "GL_DEPTH_COMPONENT", size: "GL_FLOAT"},
	},

}


code:
{
kb: {
	keyRotateSensitivity: 0.500,
	keyScrollSensitivity: 0.00500,
	keyZoomSensitivity  : 0.00500,

	mouseRotateSensitivity: 0.500,
	mouseScrollSensitivity: 0.500,
	mouseZoomSensitivity: 0.500,
}
}


code:
#!/bin/sh

mkdir -p m4

aclocal -I m4 \
&& autoheader \
&& automake --add-missing --copy \
&& autoconf \
&& ./configure CFLAGS='-O0' $*


code:
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT(eacsmb, 1.0, no@spam.com)
AM_INIT_AUTOMAKE([-Werror foreign subdir-objects])
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.
# FIXME: Replace `main' with a function in `-lGL':
AC_CHECK_LIB([GL], [main])
# FIXME: Replace `main' with a function in `-lGLEW':
AC_CHECK_LIB([GLEW], [main])
# FIXME: Replace `main' with a function in `-lGLU':
AC_CHECK_LIB([GLU], [main])
# FIXME: Replace `main' with a function in `-lX11':
AC_CHECK_LIB([X11], [main])
# FIXME: Replace `main' with a function in `-lfreetype':
AC_CHECK_LIB([freetype], [main])
# FIXME: Replace `main' with a function in `-lm':
AC_CHECK_LIB([m], [main])

# Checks for header files.
AC_PATH_X
AC_CHECK_HEADERS([stdlib.h string.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile
                 src/Makefile])
AC_OUTPUT


code:
bin_PROGRAMS = glmv

glmv_SOURCES = main.c \
	hash.c \
	collada.c \
	fxml/fxml.c \
	ds.c \
	MurmurHash3.c \
	dumpImage.c \
	utilities.c \
	settings.c \
	objloader.c \
	plyloader.c \
	staticMesh.c \
	bbox.c \
	renderable.c \
	uniformBuffer.c \
	timing.c \
	fbo.c \
	window.c \
	gui.c \
	game.c \
	shader.c \
	texture.c \
	json_gl.c \
	c_json/json.c \
	c3dlas/c3dlas.c \
	c3dlas/meshgen.c \
	text/text.c \
	text/fcfg.c

glmv_LDADD =  \
	-lm \
	-lX11 -lfreetype -lfontconfig -lpng \
	-lGL -lGLU -lGLEW

AM_CFLAGS = "-I/usr/include/freetype2" -v -std=gnu11 -g -DLINUX -Wno-unused-result -fdiagnostics-color
AM_CXXFLAGS = $(AM_CFLAGS)


code:
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.

// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.

#include "MurmurHash3.h"

#define	FORCE_INLINE inline __attribute__((always_inline))

inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
  return (x << r) | (x >> (32 - r));
}
uint32_t rotl32 ( uint32_t x, int8_t r );

inline uint64_t rotl64 ( uint64_t x, int8_t r )
{
  return (x << r) | (x >> (64 - r));
}
uint64_t rotl64 ( uint64_t x, int8_t r );

#define	ROTL32(x,y)	rotl32(x,y)
#define ROTL64(x,y)	rotl64(x,y)

#define BIG_CONSTANT(x) (x##LLU)

//-----------------------------------------------------------------------------
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here

FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
{
  return p[i];
}

FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
{
  return p[i];
}

//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche

FORCE_INLINE uint32_t fmix32 ( uint32_t h )
{
  h ^= h >> 16;
  h *= 0x85ebca6b;
  h ^= h >> 13;
  h *= 0xc2b2ae35;
  h ^= h >> 16;

  return h;
}

//----------

FORCE_INLINE uint64_t fmix64 ( uint64_t k )
{
  k ^= k >> 33;
  k *= BIG_CONSTANT(0xff51afd7ed558ccd);
  k ^= k >> 33;
  k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
  k ^= k >> 33;

  return k;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x86_32 ( const void * key, int len,
                          uint32_t seed, void * out )
{
  const uint8_t * data = (const uint8_t*)key;
  const int nblocks = len / 4;

  uint32_t h1 = seed;

  const uint32_t c1 = 0xcc9e2d51;
  const uint32_t c2 = 0x1b873593;

  int i;

  //----------
  // body

  const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);

  for(i = -nblocks; i; i++)
  {
    uint32_t k1 = getblock32(blocks,i);

    k1 *= c1;
    k1 = ROTL32(k1,15);
    k1 *= c2;

    h1 ^= k1;
    h1 = ROTL32(h1,13);
    h1 = h1*5+0xe6546b64;
  }

  //----------
  // tail

  const uint8_t * tail = (const uint8_t*)(data + nblocks*4);

  uint32_t k1 = 0;

  switch(len & 3)
  {
  case 3: k1 ^= tail[2] << 16;
  case 2: k1 ^= tail[1] << 8;
  case 1: k1 ^= tail[0];
          k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
  };

  //----------
  // finalization

  h1 ^= len;

  h1 = fmix32(h1);

  *(uint32_t*)out = h1;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x86_128 ( const void * key, const int len,
                           uint32_t seed, void * out )
{
  const uint8_t * data = (const uint8_t*)key;
  const int nblocks = len / 16;

  uint32_t h1 = seed;
  uint32_t h2 = seed;
  uint32_t h3 = seed;
  uint32_t h4 = seed;

  const uint32_t c1 = 0x239b961b;
  const uint32_t c2 = 0xab0e9789;
  const uint32_t c3 = 0x38b34ae5;
  const uint32_t c4 = 0xa1e38b93;

  int i;

  //----------
  // body

  const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);

  for(i = -nblocks; i; i++)
  {
    uint32_t k1 = getblock32(blocks,i*4+0);
    uint32_t k2 = getblock32(blocks,i*4+1);
    uint32_t k3 = getblock32(blocks,i*4+2);
    uint32_t k4 = getblock32(blocks,i*4+3);

    k1 *= c1; k1  = ROTL32(k1,15); k1 *= c2; h1 ^= k1;

    h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;

    k2 *= c2; k2  = ROTL32(k2,16); k2 *= c3; h2 ^= k2;

    h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;

    k3 *= c3; k3  = ROTL32(k3,17); k3 *= c4; h3 ^= k3;

    h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;

    k4 *= c4; k4  = ROTL32(k4,18); k4 *= c1; h4 ^= k4;

    h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
  }

  //----------
  // tail

  const uint8_t * tail = (const uint8_t*)(data + nblocks*16);

  uint32_t k1 = 0;
  uint32_t k2 = 0;
  uint32_t k3 = 0;
  uint32_t k4 = 0;

  switch(len & 15)
  {
  case 15: k4 ^= tail[14] << 16;
  case 14: k4 ^= tail[13] << 8;
  case 13: k4 ^= tail[12] << 0;
           k4 *= c4; k4  = ROTL32(k4,18); k4 *= c1; h4 ^= k4;

  case 12: k3 ^= tail[11] << 24;
  case 11: k3 ^= tail[10] << 16;
  case 10: k3 ^= tail[ 9] << 8;
  case  9: k3 ^= tail[ 8] << 0;
           k3 *= c3; k3  = ROTL32(k3,17); k3 *= c4; h3 ^= k3;

  case  8: k2 ^= tail[ 7] << 24;
  case  7: k2 ^= tail[ 6] << 16;
  case  6: k2 ^= tail[ 5] << 8;
  case  5: k2 ^= tail[ 4] << 0;
           k2 *= c2; k2  = ROTL32(k2,16); k2 *= c3; h2 ^= k2;

  case  4: k1 ^= tail[ 3] << 24;
  case  3: k1 ^= tail[ 2] << 16;
  case  2: k1 ^= tail[ 1] << 8;
  case  1: k1 ^= tail[ 0] << 0;
           k1 *= c1; k1  = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
  };

  //----------
  // finalization

  h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;

  h1 += h2; h1 += h3; h1 += h4;
  h2 += h1; h3 += h1; h4 += h1;

  h1 = fmix32(h1);
  h2 = fmix32(h2);
  h3 = fmix32(h3);
  h4 = fmix32(h4);

  h1 += h2; h1 += h3; h1 += h4;
  h2 += h1; h3 += h1; h4 += h1;

  ((uint32_t*)out)[0] = h1;
  ((uint32_t*)out)[1] = h2;
  ((uint32_t*)out)[2] = h3;
  ((uint32_t*)out)[3] = h4;
}

//-----------------------------------------------------------------------------

void MurmurHash3_x64_128 ( const void * key, const int len,
                           const uint32_t seed, void * out )
{
  const uint8_t * data = (const uint8_t*)key;
  const int nblocks = len / 16;

  uint64_t h1 = seed;
  uint64_t h2 = seed;

  const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
  const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);

  int i;
  //----------
  // body

  const uint64_t * blocks = (const uint64_t *)(data);

  for(i = 0; i < nblocks; i++)
  {
    uint64_t k1 = getblock64(blocks,i*2+0);
    uint64_t k2 = getblock64(blocks,i*2+1);

    k1 *= c1; k1  = ROTL64(k1,31); k1 *= c2; h1 ^= k1;

    h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;

    k2 *= c2; k2  = ROTL64(k2,33); k2 *= c1; h2 ^= k2;

    h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
  }

  //----------
  // tail

  const uint8_t * tail = (const uint8_t*)(data + nblocks*16);

  uint64_t k1 = 0;
  uint64_t k2 = 0;

  switch(len & 15)
  {
  case 15: k2 ^= ((uint64_t)tail[14]) << 48;
  case 14: k2 ^= ((uint64_t)tail[13]) << 40;
  case 13: k2 ^= ((uint64_t)tail[12]) << 32;
  case 12: k2 ^= ((uint64_t)tail[11]) << 24;
  case 11: k2 ^= ((uint64_t)tail[10]) << 16;
  case 10: k2 ^= ((uint64_t)tail[ 9]) << 8;
  case  9: k2 ^= ((uint64_t)tail[ 8]) << 0;
           k2 *= c2; k2  = ROTL64(k2,33); k2 *= c1; h2 ^= k2;

  case  8: k1 ^= ((uint64_t)tail[ 7]) << 56;
  case  7: k1 ^= ((uint64_t)tail[ 6]) << 48;
  case  6: k1 ^= ((uint64_t)tail[ 5]) << 40;
  case  5: k1 ^= ((uint64_t)tail[ 4]) << 32;
  case  4: k1 ^= ((uint64_t)tail[ 3]) << 24;
  case  3: k1 ^= ((uint64_t)tail[ 2]) << 16;
  case  2: k1 ^= ((uint64_t)tail[ 1]) << 8;
  case  1: k1 ^= ((uint64_t)tail[ 0]) << 0;
           k1 *= c1; k1  = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
  };

  //----------
  // finalization

  h1 ^= len; h2 ^= len;

  h1 += h2;
  h2 += h1;

  h1 = fmix64(h1);
  h2 = fmix64(h2);

  h1 += h2;
  h2 += h1;

  ((uint64_t*)out)[0] = h1;
  ((uint64_t*)out)[1] = h2;
}

//-----------------------------------------------------------------------------


code:
#include <stdio.h>

#include "common_math.h"
#include "common_gl.h"

#include "bbox.h"

typedef struct {
	Vector pos;
	union { uint8_t color_u[4]; uint32_t color; };
} BBVertex;

// recalculates all internal values then updates the renderable
void BoundingBoxVisual_update(BoundingBoxVisual* bbv, Vector* _min, Vector* _max) {

	Vector min, max;

	vMin(_min, _max, &min);
	vMax(_min, _max, &max);

	bbv->min = min;
	bbv->max = max;

	(Vector){0, 0, 0} = (Vector){
		((max.x - min.x) / 2) + min.x,
		((max.y - min.y) / 2) + min.y,
		((max.z - min.z) / 2) + min.z
	};
	bbv->dims = (Vector){
		(max.x - min.x),
		(max.y - min.y),
		(max.z - min.z)
	};

	//                          points in line
	//                          |   .edges on cube
	BBVertex* verts = calloc(1, 2 * 12 * sizeof(*verts));

	// the -z side
	// -x edge
	verts[0].pos = (Vector){min.x, min.y, min.z};
	verts[0].color = 0xffff0000;
	verts[1].pos = (Vector){min.x, max.y, min.z};
	verts[1].color = 0xff00ff00;

	// +x edge
	verts[2].pos = (Vector){max.x, min.y, min.z};
	verts[2].color = 0xffff0000;
	verts[3].pos = (Vector){max.x, max.y, min.z};
	verts[3].color = 0xffffff00;

	// -y edge
	verts[4].pos = (Vector){min.x, min.y, min.z};
	verts[4].color = 0xff000000;
	verts[5].pos = (Vector){max.x, min.y, min.z};
	verts[5].color = 0xffff0000;

	// +y edge
	verts[6].pos = (Vector){min.x, max.y, min.z};
	verts[6].color = 0xff00ff00;
	verts[7].pos = (Vector){max.x, max.y, min.z};
	verts[7].color = 0xffffff00;

	// the +z side
	// -x edge
	verts[8].pos = (Vector){min.x, min.y, max.z};
	verts[8].color = 0xff0000ff;
	verts[9].pos = (Vector){min.x, max.y, max.z};
	verts[9].color = 0xff00ffff;

	// +x edge
	verts[10].pos = (Vector){max.x, min.y, max.z};
	verts[10].color = 0xffff00ff;
	verts[11].pos = (Vector){max.x, max.y, max.z};
	verts[11].color = 0xffffffff;

	// -y edge
	verts[12].pos = (Vector){min.x, min.y, max.z};
	verts[12].color = 0xffff0000;
	verts[13].pos = (Vector){max.x, min.y, max.z};
	verts[13].color = 0xffff00ff;

	// +y edge
	verts[14].pos = (Vector){min.x, max.y, max.z};
	verts[14].color = 0xffffff00;
	verts[15].pos = (Vector){max.x, max.y, max.z};
	verts[15].color = 0xffffffff;

	// connectors between +z and -z
	// -y-x edge
	verts[16].pos = (Vector){min.x, min.y, min.z};
	verts[16].color = 0xff000000;
	verts[17].pos = (Vector){min.x, min.y, max.z};
	verts[17].color = 0xff0000ff;

	// -y+x edge
	verts[18].pos = (Vector){max.x, min.y, min.z};
	verts[18].color = 0xffff0000;
	verts[19].pos = (Vector){max.x, min.y, max.z};
	verts[19].color = 0xffff00ff;

	// +y+x edge
	verts[20].pos = (Vector){max.x, max.y, min.z};
	verts[20].color = 0xffffff00;
	verts[21].pos = (Vector){max.x, max.y, max.z};
	verts[21].color = 0xffffffff;

	// +y-x edge
	verts[22].pos = (Vector){min.x, max.y, min.z};
	verts[22].color = 0xff00ff00;
	verts[23].pos = (Vector){min.x, max.y, max.z};
	verts[23].color = 0xff00ffff;

	printf("min [%.7f, %.7f, %.7f]\n", min.x, min.y, min.z);
	printf("max [%.7f, %.7f, %.7f]\n", max.x, max.y, max.z);

	for(int i = 0; i < 24; i++) {
		printf(" - [%.7f, %.7f, %.7f]\n", verts[i].pos.x, verts[i].pos.y, verts[i].pos.z);

	}

	VAOConfig opts[] = {
		// per vertex
		{3, GL_FLOAT}, // position
		{4, GL_UNSIGNED_BYTE, GL_TRUE}, // color

		{0, 0}
	};

	if(bbv->rend) Renderable_Destroy(bbv->rend);

	bbv->rend = renderable_Create(
		GL_LINES,
		"bbox",
		opts,
		verts,
		2 * 12);

	bbv->rend->scale = 1;

	// central axes

	//                           points in line
	//                           |   .edges in the axis set
	BBVertex* averts = calloc(1, 2 * 3 * sizeof(*averts));

	float lineLen = .1 * fmin(bbv->dims.x, fmin(bbv->dims.y, bbv->dims.z));

	//printf("center: [%.2f, %.2f, %.2f]\n", (Vector){0, 0, 0}.x, (Vector){0, 0, 0}.y, (Vector){0, 0, 0}.z);
	// x
	averts[0].pos = bbv->center;
	averts[0].color = 0xffff0000;
	averts[1].pos = (Vector){bbv->center.x + lineLen, bbv->center.y, bbv->center.z};
	averts[1].color = 0xffff0000;

	// y
	averts[2].pos = bbv->center;
	averts[2].color = 0xff00ff00;
	averts[3].pos = (Vector){bbv->center.x, bbv->center.y + lineLen, bbv->center.z};
	averts[3].color = 0xff00ff00;

	// z
	averts[4].pos = bbv->center;
	averts[4].color = 0xff0000ff;
	averts[5].pos = (Vector){bbv->center.x, bbv->center.y, bbv->center.z + lineLen};
	averts[5].color = 0xff0000ff;

	if(bbv->rendAxes) Renderable_Destroy(bbv->rendAxes);

	bbv->rendAxes = renderable_Create(
		GL_LINES,
		"bbox",
		opts,
		averts,
		2 * 3);

	bbv->rendAxes->scale = 1;
}

void AxesVisual_init(AxesVisual* av, Vector* loc) {

	av->location = *loc;
	av->Xdir = (Vector){1, 0, 0};
	av->Ydir = (Vector){0, 1, 0};

	//                           points in line
	//                           |   .edges in the axis set
	BBVertex* averts = calloc(1, 2 * 3 * sizeof(*averts));

	float lineLen = 1;

	// x
	averts[0].pos = (Vector){0, 0, 0};
	averts[0].color = 0xffff0000;
	averts[1].pos = (Vector){lineLen, 0, 0};
	averts[1].color = 0xffff0000;

	// y
	averts[2].pos = (Vector){0, 0, 0};
	averts[2].color = 0xff00ff00;
	averts[3].pos = (Vector){0, lineLen, 0};
	averts[3].color = 0xff00ff00;

	// z
	averts[4].pos = (Vector){0, 0, 0};
	averts[4].color = 0xff0000ff;
	averts[5].pos = (Vector){0, 0, lineLen};
	averts[5].color = 0xff0000ff;

	VAOConfig opts[] = {
		// per vertex
		{3, GL_FLOAT}, // position
		{4, GL_UNSIGNED_BYTE, GL_TRUE}, // color

		{0, 0}
	};

	if(av->rend) Renderable_Destroy(av->rend);

	av->rend = renderable_Create(
		GL_LINES,
		"bbox",
		opts,
		averts,
		2 * 3);

	av->rend->scale = 1;
}

void AxesVisual_render(AxesVisual* av, Matrix* view, Matrix* proj) {
	//MatrixStack ms;

	//msAlloc(4, &ms);
	//msCopy(view, &ms);

	Matrix m;

	m = *view;
	mTransv(&av->location, &m);

	renderable_Draw(av->rend, &m, proj);

	//msFree(&ms);

}

/*

Renderable* CreateRotationCircles(Vector* rot) {
	Renderable* r;
	int i;
	int segments;

	BBVertex* verts = calloc(1, 2 * 12 * sizeof(*verts));

	// the -z side
	// -x edge
	verts[0].pos = (Vector){min.x, min.y, min.z};
	verts[0].color = 0xff0000ff;
	verts[1].pos = (Vector){min.x, max.y, min.z};
	verts[1].color = 0x00ff00ff;

	r = renderable_Create(
		GL_LINES,
		"bbox",
		opts,
		verts,
		);

}

*/


code:
#ifndef __model_viewer__bbox_h__
#define __model_viewer__bbox_h__

#include "ds.h"
#include "renderable.h"

typedef struct BoundingBoxVisual {

	Vector min, max;

	Vector center;
	Vector dims;

	Renderable* rend;
	Renderable* rendAxes;
	// todo: dimensions text

	char showAxes;
	char showDimensions;
	char showLocation;

} BoundingBoxVisual;

typedef struct AxesVisual {
	Vector location;
	Vector Xdir;
	Vector Ydir;

	Renderable* rend;

} AxesVisual;

#endif // __model_viewer__bbox_h__


code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "collada.h"

#include "fxml/fxml.h"

float* parseFloatArray(FXMLTag* tag, int* cnt) {

	char* e, *s, *raw;
	size_t count, i;
	float* data, *d;

	count = fxmlGetAttrInt(tag, "count");
	if(!count) {
		fprintf(stderr, "Collada: empty float array.\n");
		*cnt = 0;
		return NULL;
	}

	data = d = calloc(1, count * sizeof(*data));
	//CHECK_OOM(data);

	raw = s = fxmlGetTextContents(tag, NULL);

	i = 0;
	while(*s) {
		*d = strtod(s, &e);
		if(s == e) {
			if(count != i) {
				fprintf(stderr, "Collada: float_array count attribute disagrees with actual data count.\n");
			}
			break;
		}

		s = e;
		d++;
		i++;
	}

	free(raw);

	*cnt = i;
	return data;
}

void parseTriangles(FXMLTag* tag) {
	FXMLTag* x_p, *x_in;
	char* e, *s, *raw;
	size_t count, i;
	float* data, *d;

	count = fxmlGetAttrInt(tag, "count");
	if(!count) {
		fprintf(stderr, "Collada: empty float array.\n");
		return NULL;
	}

	// find the storage configuration
	x_in = fxmlTagFindFirstChild(tag, "input");
	for(int i = 0; x_in; i++) {

// 		char* p = fxmlGetAttr(x_p, "param");
// 		src->access[i] = p[0];
// 		free(p);

		x_in = fxmlTagFindNextSibling(x_p, "input", 1);
	}

	// grab the polygon data
	data = d = calloc(1, count * sizeof(*data));

	x_p = fxmlTagFindFirstChild(tag, "p");
	raw = s = fxmlGetTextContents(x_p, NULL);

	i = 0;
	while(*s) {
		*d = strtod(s, &e);
		if(s == e) {
			if(count != i) {
				fprintf(stderr, "Collada: float_array count attribute disagrees with actual data count.\n");
			}
			break;
		}

		s = e;
		d++;
		i++;
	}

	free(raw);

}

/*
	<source id="color_source" name="Colors">
		<float_array id="values" count="3">
			0.8 0.8 0.8
		</float_array>
		<technique_common>
			<accessor source="#values" count="1" stride="3">
				<param name="R" type="float"/>
				<param name="G" type="float"/>
				<param name="B" type="float"/>
			</accessor>
		</technique_common>
	</source>
*/
static void parseSource(FXMLTag* srctag) {
	FXMLTag* datatag, *tc, *acc;

	char* datanames[] = {
		"bool_array",
		"float_array",
		"IDREF_array",
		"int_array",
		"Name_array",
		"SIDREF_array",
		"token_array",
		NULL
	};

	//fetches the next sibling with any of the specified names, or null if it doesn't exist.
	datatag = fxmlTagFindFirstChildArray(srctag, datanames);
	if(!datatag) {
		fprintf(stderr, "Collada: no valid data tag found in source tag.\n");
		return;
	}

	// count, name? id?

	// technique_common
	tc = fxmlTagFindFirstChild(srctag, "technique_common");
	if(!tc) {
		fprintf(stderr, "Collada: no technique_common tag found in source tag.\n");
		return;
	}

	// accessor
	acc = fxmlTagFindFirstChild(srctag, "accessor");
	if(!acc) {
		fprintf(stderr, "Collada: no accessor tag found in technique_common tag.\n");
		return;
	}

	/*
	while() {
		// <param> tags
	}
	*/

}

static ColladaMesh* parseMesh(FXMLTag* meshtag) {

	ColladaMesh* cm;

	cm = calloc(1, sizeof(*cm));

		// multiple <source
		// one <vertices

}

static void parseGeomSourceTag(ColladaMesh* cm, FXMLTag* x_src) {
	ColladaGeomSource* src;
	FXMLTag* x_fa, *x_tc, *x_acc, *x_p;

	src = calloc(1, sizeof(*src));

	src->id = fxmlGetAttr(x_src, "id");

	// float data
	x_fa = fxmlTagFindFirstChild(x_src, "float_array");
	if(!x_fa) {
		printf("float aray not found in mesh source\n");
		return;
	}

	parseFloatArray(x_fa, &src->count);

	x_tc = fxmlTagFindFirstChild(x_src, "technique_common");
	if(!x_tc) {
		fprintf(stderr, "Collada: no technique_common tag found in source tag.\n");
		return;
	}
	x_acc = fxmlTagFindFirstChild(x_tc, "accessor");
	if(!x_acc) {
		fprintf(stderr, "Collada: no accessor tag found in technique_common tag.\n");
		return;
	}

	src->stride = fxmlGetAttrInt(x_acc, "stride");

	// find the storage order
	x_p = fxmlTagGetFirstChild(x_acc);
	for(int i = 0; x_p && i < 4; i++) {

		char* p = fxmlGetAttr(x_p, "param");
		src->access[i] = p[0];
		free(p);

		x_p = fxmlTagNextSibling(x_p, 1);
	}

	HT_set(&cm->sources, src->id, src);
}

static void parseGeomTag(ColladaFile* cf, FXMLTag* geom) {
	printf("parsing geometry\n");

	ColladaGeometry* g;
	g = calloc(1, sizeof(*g));

	g->name = fxmlGetAttr(geom, "name");
	g->id = fxmlGetAttr(geom, "id");

	FXMLTag* x_mesh = fxmlTagFindFirstChild(geom, "mesh");
	if(!x_mesh) {
		printf("No mesh found in geometry\n");
		return;
	}

	if(x_mesh) {
		ColladaMesh* cm;
		cm = calloc(1, sizeof(*cm));
		HT_init(&cm->sources, 2);

		char has_vertices = 0;

		cm->id = fxmlGetAttr(x_mesh, "id");

		FXMLTag* tag = fxmlTagGetFirstChild(x_mesh);

		while(tag) {
			if(0 == strncmp("source", tag->name, tag->name_len)) {
				parseGeomSourceTag(cm, tag);
			}
			else if(0 == strncmp("vertices", tag->name, tag->name_len)) {
				printf("NIH geom/mesh/vertices\n");

				has_vertices = 1;

			}
			else if(0 == strncmp("triangles", tag->name, tag->name_len)) {
				printf("NIH geom/mesh/triangles\n");
			}

			tag = fxmlTagNextSibling(tag, 1);
		}

		if(!has_vertices) {
			printf("mesh lacks vertices\n");
		}

		g->mesh = cm;
	}

	HT_set(&cf->geometries, g->id, g);
}

void colladaLoadFile(char* path) {
	FXMLFile* xml;
	FXMLTag* tag, *tag2, *tmp_tag;
	char* str;
	ColladaFile* cf;

	cf = calloc(1, sizeof(*cf));
	HT_init(&cf->geometries, 2);

	xml = fxmlLoadFile(path);

	tag = fxmlTagGetFirstChild(xml->root);

	while(tag) {
		if(0 == strncmp("asset", tag->name, tag->name_len)) {
			printf("found asset tag\n");
			tmp_tag = fxmlTagFindFirstChild(tag, "up_axis");
			if(tmp_tag) {
				str = fxmlGetTextContents(tmp_tag, NULL);
				if(0 == strcasecmp(str, "X_UP")) {
					cf->upAxis = 'x';
				}
				else if(0 == strcasecmp(str, "Y_UP")) {
					cf->upAxis = 'y';
				}
				else if(0 == strcasecmp(str, "Z_UP")) {
					cf->upAxis = 'z';
				}
				else {
					printf("COLLADA: unknown up axis: '%s'\n", str);
				}

				free(str);
			}
		}
		else if(0 == strncmp("library_geometries", tag->name, tag->name_len)) {
			printf("found geom library tag\n");

			tmp_tag = fxmlTagFindFirstChild(tag, "geometry");
			while(tmp_tag) {
				parseGeomTag(cf, tmp_tag);
				tmp_tag = fxmlTagFindNextSibling(tmp_tag, "geometry", 1);
				printf("v> %p\n", tmp_tag);
			}
			printf("finished geom\n");
		}
		else if(0 == strncmp("library_visual_scenes", tag->name, tag->name_len)) {
			printf("found vis. scene tag\n");
			// TODO: finish
		}
		printf("tag: %.*s\n", tag->name_len, tag->name);
		tag = fxmlTagNextSibling(tag, 1);
		printf("w> %p\n", tag);
	}

	/*
	// pull out each mesh
	while(1) {
		FXMLTag* geom;
		ColladaGeometry* cg;

		geom = fxmlFindFirstChild(libgeom, "geometry");
		cg = fxmlGetAttr(geom, "name");

		mesh = fxmlFindFirstChild(geom, "mesh");
		if(!mesh) {
			// this geomotry tag is an unupported type

			// free the shit

			continue;
		}

		colladaParseMesh(mesh);

	}
	*/
}


code:
#ifndef COLLADA_H_INCLUDED
#define COLLADA_H_INCLUDED

#include "ds.h"
#include "hash.h"

/*

asset/up_axis/{text}
library_geometries/geometry[id,name]
	/mesh
		/source[id]  (positions)
			/float_array[id,count](text)
			/technique_common/accessor[count, stride]
				/param[name=X,Y,Z, type=float]
		/source ... (normals)
		/source ... (uvs)
		/vertices[id]/input[semantic="position", source="</source[id]>"]
		/polylist[count]
			/input[semantic=VERTEX,NORMAL,TEXCOORD, source=#id, offset=<int>]
			/vcount(text)
			/p(text)
library_animations/animation[id]
	/source[id]
	...
library_visual_scenes/visual_scene/node[id, name]/matrix[sid=transform]
	... to find mesh relative positioning
*/

enum {
	COLLADA_NONE,
	COLLADA_FLOATS,
	COLLADA_INTS
};

typedef struct {

	char* id;
	char* name;

	int type;
	union {
		float* floats;
		int64_t* ints;
	};
	size_t count;

	int format;

} ColladaSource;

typedef struct {

	char* id;
	char* name;

	char* vertexSourceID;
	char* normalSourceID;
	char* texSourceID;

	HashTable sources;

} ColladaMesh;

typedef struct {
	char* id;

	int type; // 0 = f, 1 = d, 2 = int
	union {
		float* f;
		double* d;
		int64_t* i64;
	} data;

	int count;
	int stride;

	char access[4];

} ColladaGeomSource;

typedef struct {
	char* name;
	char* id;

	ColladaMesh* mesh;

} ColladaGeometry;

typedef struct {
// 	HashTable meshes;
	char upAxis;

	// for loading
	HashTable geometries;

} ColladaFile;

void colladaLoadFile(char* path);

#endif // COLLADA_H_INCLUDED


code:
#ifndef __common_gl_h__
#define __common_gl_h__

#include <GL/glew.h>
#include <GL/glx.h>
#include <GL/glu.h>

#include "common_math.h"

#include "utilities.h"
#include "shader.h"
#include "texture.h"
#include "objloader.h"
#include "renderable.h"

#endif // __common_gl_h__


code:
#include <math.h>
#include <stdint.h>
#include <limits.h>

#include "c3dlas/c3dlas.h"
#include "c3dlas/meshgen.h"


code:
#include "ds.h"

void vec_resize(void** data, size_t* size, size_t elem_size) {
	void* tmp;

	if(*size < 8) *size = 8;
	else *size *= 2;

	tmp = realloc(*data, *size * elem_size);
	if(!tmp) {
		fprintf(stderr, "Out of memory in vector resize");
		return;
	}

	*data = tmp;
}

ptrdiff_t vec_find(void* data, size_t len, size_t stride, void* search) {
	size_t i;
	for(i = 0; i < len; i++) {
		if(memcmp(data + (i * stride), search, stride)) {
			return i;
		}
	}

	return -1;
}


code:
#ifndef __DS_H__
#define __DS_H__

#include <stdint.h>
#include <malloc.h>

// declare a vector
#define VEC(t) \
struct { \
	size_t len, alloc; \
	t* data; \
}

// initialisze a vector
#define VEC_INIT(x) \
do { \
	(x)->data = NULL; \
	(x)->len = 0; \
	(x)->alloc = 0; \
} while(0)

// helpers
#define VEC_LEN(x) ((x)->len)
#define VEC_ALLOC(x) ((x)->alloc)
#define VEC_DATA(x) ((x)->data)
#define VEC_ITEM(x, i) (VEC_DATA(x)[(i)])

#define VEC_TAIL(x) (VEC_DATA(x)[VEC_LEN(x)-1])
#define VEC_HEAD(x) (VEC_DATA(x)[0])

#define VEC_FIND(x, ptr_o) vec_find(VEC_DATA(x), VEC_LEN(x), sizeof(*VEC_DATA(x)), ptr_o)

//

#define VEC_GROW(x) vec_resize((void**)&VEC_DATA(x), &VEC_ALLOC(x), sizeof(*VEC_DATA(x)))

// check if a size increase is needed to insert one more item
#define VEC_CHECK(x) \
do { \
	if(VEC_LEN(x) >= VEC_ALLOC(x)) { \
		VEC_GROW(x); \
	} \
} while(0)

// operations

// increase size and assign the new entry
#define VEC_PUSH(x, e) \
do { \
	VEC_CHECK(x); \
	VEC_DATA(x)[VEC_LEN(x)] = (e); \
	VEC_LEN(x)++; \
} while(0)

// increase size but don't assign
#define VEC_INC(x) \
do { \
	VEC_CHECK(x); \
	VEC_LEN(x)++; \
} while(0)

#define VEC_PEEK(x) VEC_DATA(x)[VEC_LEN(x) - 1]

#define VEC_POP(x, e) \
do { \
	VEC_CHECK(x); \
	(e) = VEC_PEEK(x); \
	VEC_LEN(x)--; \
} while(0)

#define VEC_FREE(x) \
do { \
	if(VEC_DATA(x)) free(VEC_DATA(x)); \
	VEC_DATA(x) = NULL; \
	VEC_LEN(x) = 0; \
	VEC_ALLOC(x) = 0; \
} while(0)

#define VEC_COPY(copy, orig) \
do { \
	void* tmp; \
	tmp = realloc(VEC_DATA(copy), VEC_ALLOC(orig) * sizeof(*VEC_DATA(orig)) ); \
	if(!tmp) { \
		fprintf(stderr, "Out of memory in vector copy"); \
		return; \
	} \
	\
	VEC_DATA(copy) = tmp; \
	VEC_LEN(copy) = VEC_LEN(orig); \
	VEC_ALLOC(copy) = VEC_ALLOC(orig); \
	\
	memcpy(VEC_DATA(copy), VEC_DATA(orig),  VEC_LEN(orig) * sizeof(*VEC_DATA(orig))); \
} while(0)

#define VEC_REVERSE(x) \
do { \
	size_t i, j; \
	void* tmp = alloca(sizeof(*VEC_DATA(x))); \
	for(i = 0, j = VEC_LEN(x); i < j; i++, j--) { \
		memcpy(tmp, VEC_DATA(x)[i]); \
		memcpy(VEC_DATA(x)[i], VEC_DATA(x)[j]); \
		memcpy(VEC_DATA(x)[j], tmp); \
	} \
} while(0)

#define VEC_SPLICE(x, y, where) \
do { \
	if(VEC_ALLOC(x) < VEC_LEN(x) + VEC_LEN(y)) { \
		vec_resize_to((void**)&VEC_DATA(x), &VEC_ALLOC(x), sizeof(*VEC_DATA(x)), VEC_LEN(x) + VEC_LEN(y)); \
	} \
	\
	memcpy( /* move the rest of x forward */ \
		VEC_DATA(x) + where + VEC_LEN(y), \
		VEC_DATA(x) + where,  \
		(VEC_LEN(x) - where) * sizeof(*VEC_DATA(x)) \
	); \
	memcpy( /* copy y into the space created */ \
		VEC_DATA(x) + where, \
		VEC_DATA(y),  \
		VEC_LEN(y) * sizeof(*VEC_DATA(y)) \
	); \
} while(0)

void vec_resize(void** data, size_t* size, size_t elem_size);
ptrdiff_t vec_find(void* data, size_t len, size_t stride, void* search);

#endif // __DS_H__


code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#include "dumpImage.h"

#include <png.h>
#include <setjmp.h>

// data is densely packed
int writePNG(char* path, unsigned int channels, char* data, unsigned int w, unsigned int h) {

	FILE* f;
	png_byte sig[8];
	png_bytep* rowPtrs;
	int i;

	png_structp pngPtr;
	png_infop infoPtr;

	int colorTypes[4] = {
		PNG_COLOR_TYPE_GRAY,
		PNG_COLOR_TYPE_GRAY_ALPHA,
		PNG_COLOR_TYPE_RGB,
		PNG_COLOR_TYPE_RGB_ALPHA
	};

	int ret = 2;

	printf("png write | w: %d, h: %d \n", w, h);

	if(channels > 4 || channels < 1) {
		return 3;
	}

	// file stuff
	f = fopen(path, "wb");
	if(!f) {
		fprintf(stderr, "Could not open \"%s\" (writePNG).\n", path);
		return 1;
	}

	/*
	if(png_sig_cmp(sig, 0, 8)) {
		fprintf(stderr, "\"%s\" is not a valid PNG file.\n", path);
		fclose(f);
		return NULL;
	}
	*/
	// init stuff
	pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!pngPtr) {
		goto CLEANUP1;
	}
	//png_destroy_write_struct (&pngPtr, (png_infopp)NULL);
	infoPtr = png_create_info_struct(pngPtr);
	if (!infoPtr) {
		goto CLEANUP2;
	}
	//if(infoPtr != NULL) png_free_data(pngPtr, infoPtr, PNG_FREE_ALL, -1);
	// header stuff
	if (setjmp(png_jmpbuf(pngPtr))) {
		goto CLEANUP3;
	}
	png_init_io(pngPtr, f);

	if (setjmp(png_jmpbuf(pngPtr))) {
		goto CLEANUP3;
	}
	png_set_IHDR(pngPtr, infoPtr, w, h,
		8, colorTypes[channels - 1], PNG_INTERLACE_NONE,
		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

	png_write_info(pngPtr, infoPtr);

	rowPtrs = malloc(h * sizeof(png_bytep));
	for(i = 0; i < h; i++) {
		rowPtrs[i] = data + (i * w);
	}

	// write data
	if (setjmp(png_jmpbuf(pngPtr))) {
		goto CLEANUP4;
	}
	png_write_image(pngPtr, rowPtrs);

	if (setjmp(png_jmpbuf(pngPtr))) {
		goto CLEANUP4;
	}
	png_write_end(pngPtr, NULL);

	// success

	ret = 0;

CLEANUP4:
	free(rowPtrs);

CLEANUP3:
	if(infoPtr != NULL) png_free_data(pngPtr, infoPtr, PNG_FREE_ALL, -1);

CLEANUP2:
	png_destroy_write_struct (&pngPtr, (png_infopp)NULL);

CLEANUP1:
	fclose(f);

	return ret;
}


code:
#ifndef __EACSMB_dumpImage_h__
#define __EACSMB_dumpImage_h__

int writePNG(char* path, unsigned int channels, char* data, unsigned int w, unsigned int h);

#endif // __EACSMB_dumpImage_h__


code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>

#include <X11/X.h>
#include <X11/Xlib.h>

#include <GL/glew.h>
#include <GL/glx.h>
#include <GL/glu.h>

#include "c3dlas/c3dlas.h"
#include "c3dlas/meshgen.h"

#include "utilities.h"
#include "config.h"
#include "fbo.h"

//
GLuint* initFBOTextures(int w, int h, FBOTexConfig* cfg) {
	int len, i;
	GLuint* texids;

	// calc length of the config array
	for(len = 0;
		cfg[len].internalType != 0
		&& cfg[len].format != 0
		&& cfg[len].size != 0; len++);

	texids = calloc(1, len + 1);

	printf("len %d\n" ,len);
	printf("w %d\n" ,w);
	printf("h %d\n" ,h);

	glGenTextures(len, texids);

	// allocate storage for each texture
	for(i = 0; i < len; i++) {

		glBindTexture(GL_TEXTURE_2D, texids[i]);

		printf("texid %d\n", texids[i]);

		glexit(" -- tex buffer creation");

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		printf("it %d, f %d, s %d \n", cfg[i].internalType, cfg[i].format, cfg[i].size);
		// just depth textures for this one
		if(cfg[i].format == GL_DEPTH_COMPONENT) { printf("depth \n");
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
		}
		glexit("pre tex buffer creation");

		glTexImage2D(GL_TEXTURE_2D, 0, cfg[i].internalType, w, h, 0, cfg[i].format, cfg[i].size, NULL);
		glexit("tex buffer creation");
	}

	glBindTexture(GL_TEXTURE_2D, 0);
	glexit("end of fb tex creation");

	return texids;
}

// don't do dumb things like pass in null pointers or empty lists. you get what you deserve if you do.
void destroyFBOTextures(GLuint* texids) {
	int len = 0;

	while(texids[len] != 0) len++;

	glDeleteTextures(len, texids);
	glexit("delete fbo textures");
}

Framebuffer* allocFBO() {
	return calloc(1, sizeof(Framebuffer));
}

void initFBO(Framebuffer* fb, FBOConfig* cfg) {
	int i, dblen = 0;
	GLenum status;
	GLenum DrawBuffers[32]; // nonsense beyond this point...

	glGenFramebuffers(1, &fb->fb);
	glBindFramebuffer(GL_FRAMEBUFFER, fb->fb);
	glexit("fbo creation");

	// The depth buffer
	for(i = 0; cfg[i].texture != 0; i++) {
		GLenum att = cfg[i].attachment;

		glFramebufferTexture2D(GL_FRAMEBUFFER, att, GL_TEXTURE_2D, cfg[i].texture, 0);

		if(att != GL_DEPTH_ATTACHMENT && att != GL_STENCIL_ATTACHMENT) {
			DrawBuffers[dblen++] = att;
		}
	}
	glexit("fbo texture attach");

	glDrawBuffers(dblen, DrawBuffers);
	glexit("fbo drawbuffers");

	status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if(status != GL_FRAMEBUFFER_COMPLETE) {
		printf("fbo status invalid\n");
		exit(1);
	}
	printf("FBO created.\n");

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glexit("unbind fb");
}

void destroyFBO(Framebuffer* fb) {

	glDeleteFramebuffers(1, &fb->fb);
	glexit("delete fb");

	fb->fb = 0;
}


code:
#ifndef __EACSMB_FBO_H__
#define __EACSMB_FBO_H__

typedef struct FBOConfig {
	GLenum attachment;
	GLuint texture;

} FBOConfig;

typedef struct FBOTexConfig {
	GLenum internalType;
	GLenum format;
	GLenum size;
} FBOTexConfig;

typedef struct Framebuffer {
	GLuint fb;

} Framebuffer;

GLuint* initFBOTextures(int w, int h, FBOTexConfig* cfg);
void initFBO(Framebuffer* fb, FBOConfig* cfg);
void destroyFBO(Framebuffer* fb);

#endif // __EACSMB_FBO_H__


code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include <unistd.h>

#include <X11/X.h>
#include <X11/Xlib.h>

#include <GL/glew.h>
#include <GL/glx.h>
#include <GL/glu.h>

#include "c3dlas/c3dlas.h"
#include "c3dlas/meshgen.h"
#include "c_json/json.h"
#include "json_gl.h"

#include "utilities.h"
#include "config.h"
#include "objloader.h"
#include "shader.h"
#include "texture.h"
#include "window.h"
#include "gui.h"
#include "staticMesh.h"
#include "renderable.h"
#include "game.h"
#include "plyloader.h"

#include "collada.h"
// #include "scene.h"

// #include "axes.h"

GLuint proj_ul, view_ul, model_ul;

Matrix mProj, mView, mModel;

float zoom;

float font_smooth_lower = 0.25;
float font_smooth_upper = 0.91;
float font_smooth_limit = 0.5;

// temp shit
GUIText* gt;

GLuint fsQuadVAO, fsQuadVBO;
ShaderProgram* shadingProg;

void initFSQuad() {
	float vertices[] = {
		-1.0, -1.0, 0.0,
		-1.0, 1.0, 0.0,
		1.0, -1.0, 0.0,
		1.0, 1.0, 0.0
	};

	glGenVertexArrays(1, &fsQuadVAO);
	glBindVertexArray(fsQuadVAO);

	glGenBuffers(1, &fsQuadVBO);
	glBindBuffer(GL_ARRAY_BUFFER, fsQuadVBO);

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0);

	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

}

void drawFSQuad() {

	glBindVertexArray(fsQuadVAO);
	glBindBuffer(GL_ARRAY_BUFFER, fsQuadVBO);
	glexit("quad vbo");

	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	glexit("quad draw");

}

#define getPrintGLEnum(e, m) _getPrintGLEnumMin(e, #e, m)

int _getPrintGLEnumMin(GLenum e, char* name, char* message) {
	GLint i;

	glGetIntegerv(e, &i);
	printf("%s: %d\n", name, i);

	return i;
}

static void unpack_fbo(json_value_t* p, char* key, FBOTexConfig* cfg) {
	char* a, *b, *c;
	json_value_t* o, *v1, *v2, *v3;

	json_obj_get_key(p, key, &o);

	json_obj_get_key(o, "internalType", &v1); a = v1->v.str;
	json_as_GLenum(v1, &cfg->internalType);

	json_obj_get_key(o, "format", &v2); b = v2->v.str;
	json_as_GLenum(v2, &cfg->format);

	json_obj_get_key(o, "size", &v3); c = v3->v.str;
	json_as_GLenum(v3, &cfg->size);

	printf("fbo cfg from json: %s: %x, %s: %x, %s: %x\n", a, cfg->internalType, b, cfg->format, c, cfg->size);
}

void setupFBOs(GameState* gs, int resized) {
	int ww = gs->screen.wh.x;
	int wh = gs->screen.wh.y;

	printf("creating fbos\n");

	if(gs->fboTextures) {
		destroyFBOTextures(gs->fboTextures);
		free(gs->fboTextures);
		printf("destryed old fbos\n");
	}

	json_file_t* jsf = json_load_path("assets/config/fbo.json");

	json_value_t* tex;
	json_obj_get_key(jsf->root, "textures", &tex);

	FBOTexConfig texcfg2[6];
	unpack_fbo(tex, "diffuse", &texcfg2[0]);
	unpack_fbo(tex, "normal", &texcfg2[1]);
	unpack_fbo(tex, "selection", &texcfg2[2]);
	unpack_fbo(tex, "lighting", &texcfg2[3]);
	unpack_fbo(tex, "depth", &texcfg2[4]);
	texcfg2[5].internalType = 0;
	texcfg2[5].format = 0;
	texcfg2[5].size = 0;

	/*
	FBOTexConfig texcfg[6];

	json_obj_unpack_struct(tex,
		JSON_UNPACK(&texcfg[0], internalType, JSON_TYPE_STRING),
		JSON_UNPACK(&texcfg[0], internalType, JSON_TYPE_STRING),
		JSON_UNPACK(&texcfg[0], internalType, JSON_TYPE_STRING),
		JSON_UNPACK(&texcfg[0], internalType, JSON_TYPE_STRING)

	);
	*/

	// backing textures
	FBOTexConfig texcfg[] = {
		{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
		{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
		{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
		{GL_RGB16F, GL_RGB, GL_HALF_FLOAT},
		{GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_FLOAT},
		{0,0,0}
	};

	printf("\nfbo cfg from code: %x, %x, %x\n", GL_RGB, GL_RGB, GL_UNSIGNED_BYTE);
	printf("fbo cfg from code: %x, %x, %x\n", GL_RGB, GL_RGB, GL_UNSIGNED_BYTE);
	printf("fbo cfg from code: %x, %x, %x\n", GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
	printf("fbo cfg from code: %x, %x, %x\n", GL_RGB16F, GL_RGB, GL_HALF_FLOAT);
	printf("fbo cfg from code: %x, %x, %x\n", GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_FLOAT);

	GLuint* texids = initFBOTextures(ww, wh, texcfg2);

	gs->diffuseTexBuffer = texids[0];
	gs->normalTexBuffer = texids[1];
	gs->selectionTexBuffer = texids[2];
	gs->lightingTexBuffer = texids[3];
	gs->depthTexBuffer = texids[4];

	printf("New Main Depth: %d \n", texids[3]);

	// main gbuffer setup
	if(gs->gbuf.fb) { // evil abstraction breaking. meh.
		destroyFBO(&gs->gbuf);
	}

	FBOConfig gbufConf[] = {
		{GL_COLOR_ATTACHMENT0, gs->diffuseTexBuffer },
		{GL_COLOR_ATTACHMENT1, gs->normalTexBuffer },
//		{GL_COLOR_ATTACHMENT2, gs->lightingTexBuffer },
		{GL_DEPTH_ATTACHMENT, gs->depthTexBuffer },
		{0,0}
	};

	initFBO(&gs->gbuf, gbufConf);

	// decal pass framebufer
	FBOConfig decalConf[] = {
		{GL_COLOR_ATTACHMENT0, gs->diffuseTexBuffer },
		{GL_COLOR_ATTACHMENT1, gs->normalTexBuffer },
		{GL_COLOR_ATTACHMENT2, gs->lightingTexBuffer },
		{GL_DEPTH_ATTACHMENT,  gs->depthTexBuffer },
		{0,0}
	};
	// depth buffer is also bound as a texture but disabled for writing

	initFBO(&gs->decalbuf, decalConf);

	// lighting pass framebufer
	FBOConfig lightingConf[] = {
		{GL_COLOR_ATTACHMENT0, gs->lightingTexBuffer },
		{GL_DEPTH_ATTACHMENT,  gs->depthTexBuffer },
		{0,0}
	};
	// depth buffer is also bound as a texture but disabled for writing

	initFBO(&gs->lightingbuf, lightingConf);

	// selection pass framebufer
	FBOConfig selectionConf[] = {
// 		{GL_COLOR_ATTACHMENT0, gs->diffuseTexBuffer },
// 		{GL_COLOR_ATTACHMENT1, gs->normalTexBuffer },
		{GL_COLOR_ATTACHMENT0, gs->selectionTexBuffer },
		{GL_DEPTH_ATTACHMENT,  gs->depthTexBuffer },
		{0,0}
	};
	// depth buffer is also bound as a texture but disabled for writing

	initFBO(&gs->selectionbuf, selectionConf);

	// pbo's for selection buffer
	gs->readPBO = -1;
	gs->activePBO = 0;

	if(gs->selectionPBOs[0]) {
		glDeleteBuffers(2, gs->selectionPBOs);
		glexit("");
	}

	glGenBuffers(2, gs->selectionPBOs);
	glexit("");

	glBindBuffer(GL_PIXEL_PACK_BUFFER, gs->selectionPBOs[0]);
	glexit("");
	glBufferData(GL_PIXEL_PACK_BUFFER, ww * wh * 4, NULL, GL_DYNAMIC_READ);
	glexit("");
	glBindBuffer(GL_PIXEL_PACK_BUFFER, gs->selectionPBOs[1]);
	glexit("");
	glBufferData(GL_PIXEL_PACK_BUFFER, ww * wh * 4, NULL, GL_DYNAMIC_READ);
	glexit("");
	glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

	if(gs->selectionData) free(gs->selectionData);
	printf("seldata size %d\n", ww * wh * 4);
	gs->selectionData = malloc(ww * wh * 4);

	printf("done creating fbos\n");
}

void initGame(XStuff* xs, GameState* gs) {
	int ww, wh;

	json_gl_init_lookup();

	glerr("left over error on game init");

	// HACK
	colladaLoadFile("assets/models/streetlight.dae");
	printf("normal debug early exit\n");
	exit(1);

	gs->hasMoved = 1;
	gs->lastSelectionFrame = 0;
	gs->frameCount = 0;

	gs->activeTool = 0;

	gs->debugMode = 0;

	gs->nearClipPlane = 0.1;
	gs->farClipPlane = 100;

	ww = xs->winAttr.width;
	wh = xs->winAttr.height;

	gs->screen.wh.x = (float)ww;
	gs->screen.wh.y = (float)wh;

	gs->screen.aspect = gs->screen.wh.x / gs->screen.wh.y;
	gs->screen.resized = 0;

	printf("w: %d, h: %d\n", ww, wh);

	glEnable(GL_DEPTH_TEST);
	//glEnable(GL_BLEND);
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	initUniformBuffers();

	uniformBuffer_init(&gs->perViewUB, sizeof(PerViewUniforms));
	uniformBuffer_init(&gs->perFrameUB, sizeof(PerFrameUniforms));

	setupFBOs(gs, 0);

	glBindTexture(GL_TEXTURE_2D, 0);

	// set up the Geometry Buffer

	shadingProg = loadCombinedProgram("shading");
	glexit("");
	printf("loaded shading prog\n");
	glUseProgram(shadingProg->id);
	glUniform1i(glGetUniformLocation(shadingProg->id, "sDiffuse"), 0);
	glUniform1i(glGetUniformLocation(shadingProg->id, "sNormals"), 1);
	glUniform1i(glGetUniformLocation(shadingProg->id, "sDepth"), 2);
 	glUniform1i(glGetUniformLocation(shadingProg->id, "sSelection"), 3);
 	glUniform1i(glGetUniformLocation(shadingProg->id, "sLighting"), 4);

	glexit("");
	printf("set buffs\n");
	initFSQuad();

	// check some prerequisites
// 	GLint MaxDrawBuffers = 0;
// 	glGetIntegerv(GL_MAX_DRAW_BUFFERS, &MaxDrawBuffers);
// 	printf("MAX_DRAW_BUFFERS: %d\n", MaxDrawBuffers);
//  	if(MaxDrawBuffers < 4) {
// 		fprintf(stderr, "FATAL: GL_MAX_DRAW_BUFFERS is too low: %d. Minimum required value is 4.\n", MaxDrawBuffers);
// 		exit(3);
// 	};

//	getPrintGLEnum(GL_MAX_COLOR_ATTACHMENTS, "meh");
	getPrintGLEnum(GL_MAX_DRAW_BUFFERS, "meh");
//	getPrintGLEnum(GL_MAX_FRAMEBUFFER_WIDTH, "meh");
//	getPrintGLEnum(GL_MAX_FRAMEBUFFER_HEIGHT, "meh");
//	getPrintGLEnum(GL_MAX_FRAMEBUFFER_LAYERS, "meh");
//	getPrintGLEnum(GL_MAX_FRAMEBUFFER_SAMPLES, "meh");
	getPrintGLEnum(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, "meh");
	//getPrintGLEnum(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, "meh");
	//getPrintGLEnum(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, "meh");
	getPrintGLEnum(GL_MAX_TEXTURE_IMAGE_UNITS, "meh");
	getPrintGLEnum(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, "meh");
	getPrintGLEnum(GL_MAX_ARRAY_TEXTURE_LAYERS, "meh");
	getPrintGLEnum(GL_MAX_SAMPLES, "meh");
	getPrintGLEnum(GL_MAX_VERTEX_ATTRIBS, "meh");
	getPrintGLEnum(GL_MIN_PROGRAM_TEXEL_OFFSET, "meh");
	getPrintGLEnum(GL_MAX_PROGRAM_TEXEL_OFFSET, "meh");
	getPrintGLEnum(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, "meh");
	getPrintGLEnum(GL_MAX_UNIFORM_BLOCK_SIZE, "meh");
	glexit("");

	query_queue_init(&gs->queries.draw);

	// set up matrix stacks
	MatrixStack* view, *proj;

	view = &gs->view;
	proj = &gs->proj;

	msAlloc(2, view);
	msAlloc(2, proj);

	msIdent(view);
	msIdent(proj);

	// perspective matrix, pretty standard
// 	msPerspective(60, 1.0, 01000.0f, 100000.0f, proj);
// 		msOrtho(0, 1, 0, 1, .01, 100000, proj);

	gs->zoom = -10.0;
	gs->direction = 0.0f;
	gs->lookCenter.x = 0;
	gs->lookCenter.y = 0;

	//initStaticMeshes();

	// initialize all those magic globals
// 	initMap(&gs->map);
// 	scene_init(&gs->scene);

// 	initUI(gs);

		// maye intel needs this
	printf("gbuf init id: %d\n", gs->gbuf.fb);
	glexit("");
	//glBindFramebuffer(GL_FRAMEBUFFER, gs->gbuf.fb);
	glexit("fbo preinit");

	// text rendering stuff

	gui_Init();

	gt = guiTextNew("gui!", &(Vector){1.0,1.0,0.0}, 6.0f, "Arial");

	//axes_Init();

	PLYContents* pc = PLYContents_loadPath("/home/izzy/3dimages/things/artifact/model_dense_mesh_refine_texture.ply");
	gs->activeMesh = staticMesh_FromPLY(pc);
	printf("PLY loaded\n");

	printf("vertices %d\n", VEC_LEN(&pc->vertices));
	printf("faces %d\n", VEC_LEN(&pc->faces));

	gs->renderable = renderable_FromPLY(pc);
	printf("PLY renderable created\n");
	//int axisindex = axes_Add(10, NULL);

	glLineWidth(2);

//	testmesh = staticMesh_LoadOBJ("assets/models/gazebo.obj");
	staticMesh_RegenMeta(gs->activeMesh);

	char tmpbuf[200];

	snprintf(tmpbuf, 200, "vertices: %d", VEC_LEN(&gs->activeMesh->vertices));
	guiTextNew(tmpbuf, &(Vector){10.0,1.0,0.0}, 6.0f, "Arial");

	snprintf(tmpbuf, 200, "faces: %d", VEC_LEN(&gs->activeMesh->vertexIndices) / 3);
	guiTextNew(tmpbuf, &(Vector){10.0,2.0,0.0}, 6.0f, "Arial");

}

void preFrame(GameState* gs) {

	// update timers
	char frameCounterBuf[128];
	unsigned int fpsColors[] = {0xeeeeeeff, 6, 0xeeee22ff, INT_MAX};

	static int frameCounter = 0;
	static double last_frame = 0;
	static double lastPoint = 0;

	double now;

	gs->frameTime = now = getCurrentTime();

	if (last_frame == 0)
		last_frame = now;

	gs->frameSpan = (double)(now - last_frame);
	last_frame = now;

	frameCounter = (frameCounter + 1) % 60;

	// update per-frame timer values

	PerFrameUniforms* pfu = uniformBuffer_begin(&gs->perFrameUB);

	double seconds = (float)(long)gs->frameTime;

	pfu->wholeSeconds = seconds;
	pfu->fracSeconds = gs->frameTime - seconds;

	uniformBuffer_bindRange(&gs->perFrameUB);

	static double sdtime;

	if(lastPoint == 0.0f) lastPoint = gs->frameTime;
	if(1 /*frameCounter == 0*/) {
		float fps = 60.0f / (gs->frameTime - lastPoint);

		uint64_t qdtime;

		if(!query_queue_try_result(&gs->queries.draw, &qdtime)) {
			sdtime = ((double)qdtime) / 1000000.0;
		}

		glexit("");
		snprintf(frameCounterBuf, 128, "dtime:  %.2fms", sdtime);

		guiTextSetValue(gt, frameCounterBuf);
		lastPoint = now;
	}

	// check the pbos
	GLenum val;
	if(gs->selectionFence) {
		val = glClientWaitSync(gs->selectionFence, 0, 0);

		if(val == GL_CONDITION_SATISFIED || val == GL_ALREADY_SIGNALED) {
			printf("signaled %d\n", gs->frameCount - gs->selectionFrame);
			glDeleteSync(gs->selectionFence);
			gs->selectionFence = 0;

			glBindBuffer(GL_PIXEL_PACK_BUFFER, gs->selectionPBOs[gs->activePBO]);
			void* p = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 600*600*4, GL_MAP_READ_BIT);
			glexit("buffer map");
			if(p == NULL) {
				exit(1);
			}

			int sz = gs->screen.wh.x * gs->screen.wh.y * 4;
			memcpy(gs->selectionData, p, sz);

			glUnmapBuffer(GL_PIXEL_PACK_BUFFER);

			gs->readPBO = gs->activePBO;
			gs->activePBO = (gs->activePBO + 1) % 2;
		}
	}
}

void postFrame(GameState* gs) {

	double now;

	now = getCurrentTime();

	gs->perfTimes.draw = now - gs->frameTime;

	uniformBuffer_finish(&gs->perFrameUB);
}

void handleInput(GameState* gs, InputState* is) {

	double te = gs->frameSpan;

	float moveSpeed = gs->settings.keyScroll * te; // should load from config
	float rotateSpeed = gs->settings.keyRotate * te; // 20.8 degrees
	float keyZoom = gs->settings.keyZoom * te;
	float mouseZoom = gs->settings.mouseZoom * te;

	if(is->keyState[54] & IS_KEYDOWN) {
		exit(0);
	}

	// [ and ] to scale model
	if(is->keyState[34] & IS_KEYDOWN) {
		StaticMesh_setScale(gs->activeMesh, gs->activeMesh->scale * 1.1);
	}
	if(is->keyState[35] & IS_KEYDOWN) {
		exit(0);
	}

	if(is->clickButton == 1) {
		/*
		flattenArea(gs->map.tb,
			gs->cursorPos.x - 5,
			gs->cursorPos.y - 5,
			gs->cursorPos.x + 5,
			gs->cursorPos.y + 5
		);

		setZone(&gs->map,
			gs->cursorPos.x - 5,
			gs->cursorPos.y - 5,
			gs->cursorPos.x + 5,
			gs->cursorPos.y + 5,
			gs->activeTool + 1
		);

		checkMapDirty(&gs->map);
		*/
	}

	if(is->buttonDown == 1) {
// 		gs->mouseDownPos.x = gs->cursorPos.x;
// 		gs->mouseDownPos.y = gs->cursorPos.y;
// 		printf("start dragging at (%d,%d)\n", (int)gs->cursorPos.x, (int)gs->cursorPos.y);
	}
	if(is->buttonUp == 1) {
		//vCopy(&gs->cursorPos, &gs->mouseDownPos);
// 		printf("stopped dragging at (%d,%d)\n", (int)gs->cursorPos.x, (int)gs->cursorPos.y);

	}

	if(is->clickButton == 2) {
		gs->activeTool = (gs->activeTool + 1) % 3;
	}

	// look direction
	if(is->keyState[38] & IS_KEYDOWN) {
		gs->direction += 0.1;
		gs->hasMoved = 1;
	}
	if(is->keyState[39] & IS_KEYDOWN) {
		gs->direction -= 0.1;
		gs->hasMoved = 1;
	}

	// 79/83
	// 80/84
	// 81/85
#define SANITY(var, inc) { var = MIN(1.0, MAX(0.0, var + inc)); printf(#var " = %.4f\n", var); }
	if(is->keyState[79] & IS_KEYDOWN) SANITY(font_smooth_lower, 0.001);
	if(is->keyState[83] & IS_KEYDOWN) SANITY(font_smooth_lower, -0.001);

	if(is->keyState[80] & IS_KEYDOWN) SANITY(font_smooth_upper, 0.001);
	if(is->keyState[84] & IS_KEYDOWN) SANITY(font_smooth_upper, -0.001);

	if(is->keyState[81] & IS_KEYDOWN) SANITY(font_smooth_limit, 0.001);
	if(is->keyState[85] & IS_KEYDOWN) SANITY(font_smooth_limit, -0.001);

	// keep rotation in [0,F_2PI)
	gs->direction = fmodf(F_2PI + gs->direction, F_2PI);

	// zoom
	if(is->keyState[52] & IS_KEYDOWN) {
		gs->zoom += .1;
 		gs->zoom = fmin(gs->zoom, -.01);
		gs->hasMoved = 1;
		//printf("52 zoom: %f\n", gs->zoom);
	}
	if(is->keyState[53] & IS_KEYDOWN) {
		gs->zoom -= .1;
		gs->hasMoved = 1;
		//printf("53 zoom: %f\n", gs->zoom);
	}
	if(is->clickButton == 4) {
		gs->zoom += mouseZoom;
 		gs->zoom = fmin(gs->zoom, -.01);
		gs->hasMoved = 1;
	}
	if(is->clickButton == 5) {
		gs->zoom -= mouseZoom;
		gs->hasMoved = 1;
	}

	// movement
	Vector move = {
		.x = 0.1 * sin(F_PI - gs->direction),
		.y = 0.1 * cos(F_PI - gs->direction),
		.z = 0.0f
	};

	if(is->keyState[111] & IS_KEYDOWN) {
		vAdd(&gs->lookCenter, &move, &gs->lookCenter);
		gs->hasMoved = 1;
		//printf("m\n");
	}
	if(is->keyState[116] & IS_KEYDOWN) {
		vSub(&gs->lookCenter, &move, &gs->lookCenter);
		gs->hasMoved = 1;
		//printf("n\n");
	}

	// flip x and y to get ccw normal, using move.z as the temp
	move.z = move.x;
	move.x = -move.y;
	move.y = move.z;
	move.z = 0.0f;

	if(is->keyState[113] & IS_KEYDOWN) {
		vSub(&gs->lookCenter, &move, &gs->lookCenter);
		gs->hasMoved = 1;
		//printf("113\n");
	}
	if(is->keyState[114] & IS_KEYDOWN) {
		vAdd(&gs->lookCenter, &move, &gs->lookCenter);
		gs->hasMoved = 1;
		//printf("114\n");
	}

	// these don't stimulate a selection pass since they are debug tools
	if(is->keyState[110] & IS_KEYDOWN) {
		gs->nearClipPlane += 50 * te;
		printf("near: %f, far: %f\n", gs->nearClipPlane, gs->farClipPlane);
	}
	if(is->keyState[115] & IS_KEYDOWN) {
		gs->nearClipPlane -= 50 * te;
		printf("near: %f, far: %f\n", gs->nearClipPlane, gs->farClipPlane);
	}
	if(is->keyState[112] & IS_KEYDOWN) {
		gs->farClipPlane += 250 * te;
		printf("near: %f, far: %f\n", gs->nearClipPlane, gs->farClipPlane);
	}
	if(is->keyState[117] & IS_KEYDOWN) {
		gs->farClipPlane -= 250 * te;
		printf("near: %f, far: %f\n", gs->nearClipPlane, gs->farClipPlane);
	}

	static lastChange = 0;
	if(is->keyState[119] & IS_KEYDOWN) {
		if(gs->frameTime > lastChange + 1) {
			gs->debugMode = (gs->debugMode + 1) % 5;
			lastChange = gs->frameTime;
		}
	}

}

void setGameSettings(GameSettings* g, UserConfig* u) {

	const float rotateFactor = 0.7260f;
	const float scrollFactor = 300.0f;
	const float zoomFactor = 600.0f;

	g->keyRotate = rotateFactor * fclampNorm(u->keyRotateSensitivity);
	g->keyScroll = scrollFactor * fclampNorm(u->keyScrollSensitivity);
	g->keyZoom = zoomFactor * fclampNorm(u->keyZoomSensitivity);

	g->mouseRotate = rotateFactor * fclampNorm(u->mouseRotateSensitivity);
	g->mouseScroll = scrollFactor * fclampNorm(u->mouseScrollSensitivity);
	g->mouseZoom = 4 * zoomFactor * fclampNorm(u->mouseZoomSensitivity);

	printf("keyRotate %.3f\n", g->keyRotate);
	printf("keyScroll %.3f\n", g->keyScroll);
	printf("keyZoom %.3f\n", g->keyZoom);
	printf("mouseRotate %.3f\n", g->mouseRotate);
	printf("mouseScroll %.3f\n", g->mouseScroll);
	printf("mouseZoom %.3f\n", g->mouseZoom);

}

void setUpView(GameState* gs) {

}

// actually the selection pass
void depthPrepass(XStuff* xs, GameState* gs, InputState* is) {

	// draw UI
	//renderUIPicking(xs, gs);

	//updateView(xs, gs, is);

	// draw terrain
// 	drawTerrainBlockDepth(&gs->map, msGetTop(&gs->model), msGetTop(&gs->view), msGetTop(&gs->proj));
//	drawTerrainDepth(&gs->scene.map, &gs->perViewUB, &gs->screen.wh);

	//msPop(&gs->view);
	//msPop(&gs->proj);

}

void updateView(XStuff* xs, GameState* gs, InputState* is) {

	msPush(&gs->proj);
	msPerspective(60, gs->screen.aspect, gs->nearClipPlane, gs->farClipPlane, &gs->proj);

	msPush(&gs->view);

	// order matters! don't mess with this.
	msTrans3f(0, -1, gs->zoom, &gs->view);
	msRot3f(1, 0, 0, F_PI / 6, &gs->view);
	msRot3f(0,1,0, gs->direction, &gs->view);
	msTrans3f(-gs->lookCenter.x, 0, -gs->lookCenter.y, &gs->view);

	// y-up to z-up rotation
	msRot3f(1, 0, 0, F_PI_2, &gs->view);
	// SFM models come upside down //msScale3f(1, 1, -1, &gs->view);

	// calculate cursor position
	Vector cursorp;
	Vector eyeCoord;
	Vector worldCoord;
	Matrix p, invp, invv;

	// device space (-1:1)
	Vector devCoord;
	devCoord.x = 0.50;
	devCoord.y = 0.50;
	devCoord.z = -1.0;

	// eye space
	mInverse(msGetTop(&gs->proj), &invp);
	vMatrixMul(&devCoord, &invp, &eyeCoord);
	vNorm(&eyeCoord, &eyeCoord);

	// world space
	mInverse(msGetTop(&gs->view), &invv);
	vMatrixMul(&eyeCoord, &invv, &worldCoord);
	vNorm(&worldCoord, &worldCoord);

	// TODO: only update if somethign changes
	PerViewUniforms* pvu = uniformBuffer_begin(&gs->perViewUB);

	memcpy(&pvu->view , msGetTop(&gs->view), sizeof(Matrix));
	memcpy(&pvu->proj , msGetTop(&gs->proj), sizeof(Matrix));

	uniformBuffer_bindRange(&gs->perViewUB);
}

void cleanUpView(XStuff* xs, GameState* gs, InputState* is) {
	msPop(&gs->view);
	msPop(&gs->proj);

	uniformBuffer_finish(&gs->perFrameUB);
	uniformBuffer_finish(&gs->perViewUB);
}

void renderFrame(XStuff* xs, GameState* gs, InputState* is) {

	//mModel = IDENT_MATRIX;

	Vector2 c2;

	c2.x = 300; //cursorp.x;
	c2.y = 300; //cursorp.z;
	if(gs->renderable) {
		//printf("yes renderable\n");
		//gs->renderable->scale = 1; // this segfaults, even with the guard
	}
	else {
		printf("no renderable \n");
	}
	//renderable_Draw(gs->renderable, msGetTop(&gs->view), msGetTop(&gs->proj));
 	drawStaticMesh(gs->activeMesh, msGetTop(&gs->view), msGetTop(&gs->proj));

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	gui_RenderAll(gs);

	glDisable(GL_BLEND);
}

void shadingPass(GameState* gs) {

	Matrix world;

	world = IDENT_MATRIX;

	glUseProgram(shadingProg->id);
	glexit("shading prog");

	glActiveTexture(GL_TEXTURE0 + 0);
	glBindTexture(GL_TEXTURE_2D, gs->diffuseTexBuffer);

	glActiveTexture(GL_TEXTURE0 + 1);
	glBindTexture(GL_TEXTURE_2D, gs->normalTexBuffer);

	glActiveTexture(GL_TEXTURE0 + 2);
	glBindTexture(GL_TEXTURE_2D, gs->depthTexBuffer);

	glActiveTexture(GL_TEXTURE0 + 3);
	glBindTexture(GL_TEXTURE_2D, gs->selectionTexBuffer);

	glUniform1i(glGetUniformLocation(shadingProg->id, "debugMode"), gs->debugMode);
	glUniform2f(glGetUniformLocation(shadingProg->id, "clipPlanes"), gs->nearClipPlane, gs->farClipPlane);

	glexit("shading samplers");

// 	glUniformMatrix4fv(glGetUniformLocation(shadingProg->id, "world"), 1, GL_FALSE, world.m);
// 	glexit("shading world");
// msGetTop(&gs->view),
	glUniformMatrix4fv(glGetUniformLocation(shadingProg->id, "mViewProj"), 1, GL_FALSE, msGetTop(&gs->proj)->m);
	glUniformMatrix4fv(glGetUniformLocation(shadingProg->id, "mWorldView"), 1, GL_FALSE, msGetTop(&gs->view)->m);
	glexit("shading world");

	//glUniform3fv(glGetUniformLocation(shadingProg->id, "sunNormal"), 1, (float*)&gs->sunNormal);

	if(gs->screen.resized) {
		glUniform2fv(glGetUniformLocation(shadingProg->id, "resolution"), 1, (float*)&gs->screen.wh);
	}

	drawFSQuad();
	glexit("post quad draw");
}

Vector2i viewWH = {
	.x = 0,
	.y = 0
};
void checkResize(XStuff* xs, GameState* gs) {
	if(viewWH.x != xs->winAttr.width || viewWH.y != xs->winAttr.height) {

		// TODO: destroy all the textures too

		printf("screen 0 resized\n");

		viewWH.x = xs->winAttr.width;
		viewWH.y = xs->winAttr.height;

		gs->screen.wh.x = (float)xs->winAttr.width;
		gs->screen.wh.y = (float)xs->winAttr.height;

		gs->screen.aspect = gs->screen.wh.x / gs->screen.wh.y;

		gs->screen.resized = 1;

		setupFBOs(gs, 1);

		printf("diffuse2: %d\n",gs->diffuseTexBuffer);
		printf("resize finished\n");
	}
}

#define PF_START(x) gs->perfTimes.x = getCurrentTime()
#define PF_STOP(x) gs->perfTimes.x = timeSince(gs->perfTimes.x)

void gameLoop(XStuff* xs, GameState* gs, InputState* is) {
	gs->frameCount++;

	checkResize(xs,gs);

		PF_START(preframe);
	preFrame(gs);
		PF_STOP(preframe);

	handleInput(gs, is);

	//setUpView(gs);
	updateView(xs, gs, is);

	// disable selection pass because intel gpus suck
	if(0 && gs->hasMoved && gs->lastSelectionFrame < gs->frameCount - 8) {
		printf("doing selection pass %d\n", gs->frameCount);
		gs->hasMoved = 0;
		gs->lastSelectionFrame = gs->frameCount;

		// really just the selection pass
			PF_START(selection);
		glDepthFunc(GL_LESS);
		//glDisable(GL_DEPTH_TEST);

		glBindFramebuffer(GL_FRAMEBUFFER, gs->selectionbuf.fb);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		depthPrepass(xs, gs, is);

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindFramebuffer(GL_READ_FRAMEBUFFER, gs->selectionbuf.fb);

		//glEnable(GL_DEPTH_TEST);
		glReadBuffer(GL_COLOR_ATTACHMENT0);

		printf("is buffer %d\n", gs->selectionPBOs[gs->activePBO]);
		glBindBuffer(GL_PIXEL_PACK_BUFFER, gs->selectionPBOs[gs->activePBO]);

		glexit("selection buff");
		//printf("cursor pixels: %f, %f\n", is->cursorPosPixels.x, is->cursorPosPixels.y);

		glReadPixels(
			0, //is->cursorPosPixels.x,
			0, //is->cursorPosPixels.y,
			gs->screen.wh.x,
			gs->screen.wh.y,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
			0);

		glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

		glexit("read selection");
		glexit("");

		if(gs->selectionFence) glDeleteSync(gs->selectionFence);
		gs->selectionFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

		gs->selectionFrame = gs->frameCount;

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
			PF_STOP(selection);

	}

// 	checkCursor(gs, is);
	// update world state
// 	glBeginQuery(GL_TIME_ELAPSED, gs->queries.dtime[gs->queries.dtimenum]);
	query_queue_start(&gs->queries.draw);

	glBindFramebuffer(GL_FRAMEBUFFER, gs->gbuf.fb);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		PF_START(draw);
		//static int fnum = 0;
		//printf("frame %d\n", fnum++);

		glexit("");

	// clear color buffer for actual rendering
	//glClear(GL_COLOR_BUFFER_BIT);
	glerr("pre shader create 1d");
	glDepthFunc(GL_LEQUAL);
	glerr("pre shader create e");

	renderFrame(xs, gs, is);

		PF_STOP(draw);
		PF_START(decal);

	// decals
	glBindFramebuffer(GL_FRAMEBUFFER, gs->decalbuf.fb);

	glDepthMask(GL_FALSE); // disable depth writes for decals

// 	renderDecals(xs, gs, is);

// 	renderParticles(xs, gs, is); // particles are alpha blended and need to be on top of decals

	glDepthMask(GL_TRUE);

		PF_STOP(decal);

	cleanUpView(xs, gs, is);

	// draw to the screen
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	shadingPass(gs);

// 	renderUI(xs, gs);

	gs->screen.resized = 0;

	postFrame(gs);
	//glEndQuery(GL_TIME_ELAPSED);

	query_queue_stop(&gs->queries.draw);

	glXSwapBuffers(xs->display, xs->clientWin);

}