Skip to content

Add Windows installer #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/installer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Installer CI

on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:

jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}-${{ matrix.config.os-version }}

strategy:
fail-fast: false
matrix:
config:
- name: Windows MSVC Installer
os: windows
os-version: 2022
msvc: true
buildtype: release
library_type: static
shell: pwsh
defaults:
run:
shell: ${{ matrix.config.shell }}

steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"

- name: Setup MSVC (Windows)
if: matrix.config.os == 'windows' && matrix.config.msvc == true
uses: TheMrMilchmann/setup-msvc-dev@v3
with:
arch: x64
toolset: 14.39

- name: Setup meson
if: matrix.config.os != 'macos'
run: |
python -m pip install --upgrade pip
pip install meson

- name: Configure
run: meson setup build -Dbuildtype=${{ matrix.config.buildtype }} -Ddefault_library=${{ matrix.config.library_type }} -Dclang_libcpp=${{ ( ( matrix.config.os == 'ubuntu' && matrix.config.use-clang == true && matrix.config.use-clang_stdlib ) || matrix.config.os == 'macos' ) && 'enabled' || 'disabled' }} -Dbuild_installer=true

- name: Build
run: meson compile -C build

- name: Download EnVar plugin for NSIS
uses: carlosperate/download-file-action@v2
with:
file-url: https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip
file-name: envar_plugin.zip
location: ${{ github.workspace }}

- name: Extract EnVar plugin
run: 7z x "-oC:/Program Files (x86)/NSIS" "${{ github.workspace }}/envar_plugin.zip"

- name: Build installer (Windows)
run: |
meson compile -C build windows_installer

- name: Upload artifacts - Windows
uses: actions/upload-artifact@v4
if: matrix.config.os == 'windows'
with:
name: ${{ matrix.config.name }}
path: tools/installer/OOPetris Setup.exe
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ logs/
/toolchains/
.flatpak-builder/
flatpak-build/
tools/installer/*.exe
Binary file added assets/icon/icon.ico
Binary file not shown.
37 changes: 35 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ project(
version: '0.5.5',
)

oopetris_author = 'Coder2k'
oopetris_name = 'OOPetris'

subdir('tools/options')

subdir('tools/dependencies')
Expand Down Expand Up @@ -50,7 +53,11 @@ elif meson.is_cross_build() and host_machine.system() == '3ds'
subdir('platforms/3ds')
else

executable(
if host_machine.system() == 'windows'
subdir('platforms/windows')
endif

oopetris_exe = executable(
'oopetris',
main_files,
dependencies: [liboopetris_graphics_dep, graphic_application_deps],
Expand All @@ -59,9 +66,10 @@ else
'werror': true,
},
install: true,
win_subsystem: 'windows',
)

executable(
oopetris_recordings_utility_exe = executable(
'oopetris_recordings_utility',
recordings_main_files,
dependencies: liboopetris_recordings_dep,
Expand All @@ -70,8 +78,33 @@ else
'werror': true,
},
install: true,
win_subsystem: 'console',
)

if build_installer
if host_machine.system() == 'windows'

makensis = find_program('makensis')

nsis_script = meson.project_source_root() / 'tools' / 'installer' / 'setup.nsi'

run_target(
'windows_installer',
command: [
makensis,
'-DVERSION=' + meson.project_version(),
'-DNAME=' + oopetris_name,
'-DAUTHOR=' + oopetris_author,
'-DPROJECT_SOURCE_DIR=' + meson.project_source_root(),
'-DPROJECT_BUILD_DIR=' + meson.project_build_root(),
nsis_script,
],
depends: [oopetris_exe, oopetris_recordings_utility_exe],
)

endif
endif

endif

if get_option('tests')
Expand Down
28 changes: 28 additions & 0 deletions platforms/windows/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
windows = import('windows')

version_arr = meson.project_version().split('.')
major_version = version_arr[0].to_int()
minor_version = version_arr[1].to_int()
patch_version = version_arr[2].to_int()

rc_conf = configuration_data(
{
'OOPETRIS_VERSION': meson.project_version(),
'OOPETRIS_NAME': oopetris_name,
'OOPETRIS_AUTHOR': oopetris_author,
'OOPETRIS_MAJOR_VERSION': major_version,
'OOPETRIS_MINOR_VERSION': minor_version,
'OOPETRIS_PATCH_VERSION': patch_version,
'PROJECT_SOURCE_DIR': meson.project_source_root().replace('\\', '/'),
},
)

oopetris_win_rc = configure_file(
input: 'oopetris.rc.in',
output: 'oopetris.rc',
configuration: rc_conf,
)

oopetris_resource_windows = windows.compile_resources(oopetris_win_rc)

main_files += oopetris_resource_windows
26 changes: 26 additions & 0 deletions platforms/windows/oopetris.rc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

#include <winver.h>

VS_VERSION_INFO VERSIONINFO
FILEVERSION @OOPETRIS_MAJOR_VERSION@,@OOPETRIS_MINOR_VERSION@,@OOPETRIS_PATCH_VERSION@,0
PRODUCTVERSION @OOPETRIS_MAJOR_VERSION@,@OOPETRIS_MINOR_VERSION@,@OOPETRIS_PATCH_VERSION@,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "FileDescription", "@OOPETRIS_NAME@"
VALUE "FileVersion", "@OOPETRIS_VERSION@.0"
VALUE "ProductName", "@OOPETRIS_NAME@"
VALUE "ProductVersion", "@OOPETRIS_VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

#define AppIcon 101
AppIcon ICON "@PROJECT_SOURCE_DIR@/assets/icon/icon.ico"

3 changes: 2 additions & 1 deletion settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"rotate_left": "Left",
"rotate_right": "Right"
},
"volume": 0.2
"volume": 0.2,
"discord": false
}
39 changes: 23 additions & 16 deletions src/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,6 @@ void Application::render() const {

void Application::initialize() {

#if defined(_HAVE_DISCORD_SDK)
auto discord_instance = DiscordInstance::initialize();
if (not discord_instance.has_value()) {
spdlog::warn("Error initializing the discord instance, it might not be running: {}", discord_instance.error());
} else {
m_discord_instance = std::move(discord_instance.value());
m_discord_instance->after_setup();
}

#endif

try_load_settings();
load_resources();
push_scene(scenes::create_scene(*this, SceneId::MainMenu, ui::FullScreenLayout{ *m_window }));
Expand All @@ -268,6 +257,21 @@ void Application::initialize() {
ui::RelativeLayout{ window(), 0.0, 0.0, 0.1, 0.05 }, false
);
#endif

#if defined(_HAVE_DISCORD_SDK)
if (m_settings.discord) {
auto discord_instance = DiscordInstance::initialize();
if (not discord_instance.has_value()) {
spdlog::warn(
"Error initializing the discord instance, it might not be running: {}", discord_instance.error()
);
} else {
m_discord_instance = std::move(discord_instance.value());
m_discord_instance->after_setup();
}
}

#endif
}

void Application::try_load_settings() {
Expand All @@ -281,20 +285,23 @@ void Application::try_load_settings() {
spdlog::error("unable to load settings from \"{}\": {}", settings_filename, result.error());
spdlog::warn("applying default settings");
}

// apply settings
m_music_manager.set_volume(m_settings.volume);
}

void Application::load_resources() {
constexpr auto fonts_size = 128;
const std::vector<std::tuple<FontId, std::string>> fonts {
const std::vector<std::tuple<FontId, std::string>> fonts{
#if defined(__3DS__)
//TODO: debug why the other font crashed, not on loading, but on trying to render text!
{ FontId::Default, "LeroyLetteringLightBeta01.ttf" },
{ FontId::Default, "LeroyLetteringLightBeta01.ttf" },
#else
{ FontId::Default, "PressStart2P.ttf" },
#endif
{ FontId::Arial, "arial.ttf" }, { FontId::NotoColorEmoji, "NotoColorEmoji.ttf" }, {
FontId::Symbola, "Symbola.ttf"
}
{ FontId::Arial, "arial.ttf" },
{ FontId::NotoColorEmoji, "NotoColorEmoji.ttf" },
{ FontId::Symbola, "Symbola.ttf" }
};
for (const auto& [font_id, path] : fonts) {
const auto font_path = utils::get_assets_folder() / "fonts" / path;
Expand Down
8 changes: 4 additions & 4 deletions src/helper/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ namespace constants {
#define STRINGIFY(a) STRINGIFY_HELPER_(a) //NOLINT(cppcoreguidelines-macro-usage)
#define STRINGIFY_HELPER_(a) #a //NOLINT(cppcoreguidelines-macro-usage)

#if !defined(OOPETRIS_VERSION)
#define OOPETRIS_VERSION "Unknown"
#if !defined(OOPETRIS_NAME) || !defined(OOPETRIS_AUTHOR) || !defined(OOPETRIS_VERSION)
#error "not all needed OOPETRIS_* macros defined"
#endif


constexpr auto program_name = StaticString{ "oopetris" };
constexpr auto author = StaticString{ "coder2k" };
constexpr auto program_name = StaticString{ STRINGIFY(OOPETRIS_NAME) };
constexpr auto author = StaticString{ STRINGIFY(OOPETRIS_AUTHOR) };
constexpr auto version = StaticString{ STRINGIFY(OOPETRIS_VERSION) };
constexpr auto music_change_level = 30;
constexpr auto recordings_directory = "recordings";
Expand Down
41 changes: 28 additions & 13 deletions src/helper/graphic_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,51 +25,66 @@ std::vector<std::string> utils::supported_features() {
return features;
}


/**
* \brief this returns the directory of the app, it's read and write-able
*/
[[nodiscard]] std::filesystem::path utils::get_root_folder() {
#if defined(__ANDROID__) or defined(BUILD_INSTALLER)
#if defined(__ANDROID__)
// this call also creates the dir (at least tries to) it returns
char* pref_path = SDL_GetPrefPath(constants::author, constants::program_name);
if (!pref_path) {
throw std::runtime_error{ "Failed in getting the Pref Path: " + std::string{ SDL_GetError() } };
}
return std::filesystem::path{ std::string{ pref_path } };
#elif defined(__CONSOLE__)
// this is in the sdcard of the switch, since internal storage is read-only for applications!
// this is in the sdcard of the switch / 3ds , since internal storage is read-only for applications!
return std::filesystem::path{ "." };

#elif defined(FLATPAK_BUILD)
#elif defined(BUILD_INSTALLER)
#if defined(FLATPAK_BUILD)
// this is a read write location in the flatpak build, see https://docs.flatpak.org/en/latest/conventions.html
const char* data_home = std::getenv("XDG_DATA_HOME");
if (data_home == = nullptr) {
if (data_home == nullptr) {
throw std::runtime_error{ "Failed to get flatpak data directory (XDG_DATA_HOME)" };
}

return std::filesystem::path{ data_home };
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
char* pref_path = SDL_GetPrefPath(constants::author, constants::program_name);
if (!pref_path) {
throw std::runtime_error{ "Failed in getting the Pref Path: " + std::string{ SDL_GetError() } };
}
return std::filesystem::path{ pref_path };
#else
#error "unrecognized installer build"
#endif
#else
// this is only used in local build for debugging, when compiling in release mode the path is real path where the app can store many things without interfering with other things (eg. AppData\Roaming\... on Windows or .local/share/... on Linux )
return std::filesystem::path{ "." };
#endif
}

/**
* \brief this returns the directory of the assets, and is read-only
*/
[[nodiscard]] std::filesystem::path utils::get_assets_folder() {
#if defined(__ANDROID__)
return std::filesystem::path{ "" };
#elif defined(__CONSOLE__)
// this is in the internal storage of the nintendo switch, it ios mounted by libnx (runtime switch support library) and filled at compile time with assets (its called ROMFS there)
return std::filesystem::path{ "romfs:/assets" };
#elif defined(BUILD_INSTALLER)

// if you build in BUILD_INSTALLER mode, you have to assure that the data is there e.g. music + fonts!
#if defined(FLATPAK_BUILD)
const char* resource_path = "/app/share/oopetris/";
#else
char* resource_path = SDL_GetPrefPath(constants::author, constants::program_name);
return std::filesystem::path{ "/app/share/oopetris/assets/" };
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
char* resource_path = SDL_GetBasePath();
if (!resource_path) {
throw std::runtime_error{ "Failed in getting the Pref Path: " + std::string{ SDL_GetError() } };
throw std::runtime_error{ "Failed in getting the Base Path: " + std::string{ SDL_GetError() } };
}
// if you build in BUILD_INSTALLER mode, you have to assure that the data is there e.g. music + fonts!
return std::filesystem::path{ resource_path } / "assets";
#else
#error "unrecognized installer build"
#endif
return std::filesystem::path{ std::string{ resource_path } } / "assets";
#else
return std::filesystem::path{ "assets" };
#endif
Expand Down
Loading
Loading