build system updates and other fixes

add preset config
import qt
separate core and frontend
This commit is contained in:
zhupengfei
2019-08-28 11:43:44 +08:00
parent d612b9cf37
commit fd5106759a
15 changed files with 723 additions and 46 deletions
+61
View File
@@ -1,8 +1,20 @@
# CMake 3.8 required for 17 to be a valid value for CXX_STANDARD
cmake_minimum_required(VERSION 3.8)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
include(DownloadExternals)
include(CMakeDependentOption)
if (POLICY CMP0076)
# TODO: set this to NEW
cmake_policy(SET CMP0076 OLD)
endif()
project(threeSD)
option(WARNINGS_AS_ERRORS "Treat warnings as errors" ON)
CMAKE_DEPENDENT_OPTION(USE_BUNDLED_QT "Download bundled Qt binaries" ON "MSVC" OFF)
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ON "MINGW" OFF)
# Sanity check : Check that all submodules are present
# =======================================================================
function(check_submodules_present)
@@ -26,6 +38,55 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set up output paths for executable binaries
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# System imported libraries
# ======================
# TODO: Is this necessary?
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if (USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.10.0-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_VER)
download_bundled_external("qt/" ${QT_VER} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
else()
# Passing an empty HINTS seems to cause default system paths to get ignored in CMake 2.8 so
# make sure to not pass anything if we don't have one.
set(QT_PREFIX_HINT)
endif()
find_package(Qt5 REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT})
# Platform-specific library requirements
# ======================================
# TODO: Check the necessity of these
if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
elseif (WIN32)
# WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
set(PLATFORM_LIBRARIES winmm ws2_32)
if (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()
# Include source code
# ===================
add_subdirectory(externals)
+32
View File
@@ -0,0 +1,32 @@
function(copy_Qt5_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
# set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(PLATFORMS ${DLL_DEST}platforms/)
set(STYLES ${DLL_DEST}styles/)
# set(IMAGEFORMATS ${DLL_DEST}imageformats/)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
icuuc*.dll
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(citra-qt ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
# windows_copy_files(${target_dir} ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
# qgif$<$<CONFIG:Debug>:d>.dll
# qicns$<$<CONFIG:Debug>:d>.dll
# qico$<$<CONFIG:Debug>:d>.dll
# qjpeg$<$<CONFIG:Debug>:d>.dll
# qsvg$<$<CONFIG:Debug>:d>.dll
# qtga$<$<CONFIG:Debug>:d>.dll
# qtiff$<$<CONFIG:Debug>:d>.dll
# qwbmp$<$<CONFIG:Debug>:d>.dll
# qwebp$<$<CONFIG:Debug>:d>.dll
# )
endfunction(copy_citra_Qt5_deps)
+18
View File
@@ -0,0 +1,18 @@
# This function downloads a binary library package from our external repo.
# Params:
# remote_path: path to the file to download, relative to the remote repository root
# prefix_var: name of a variable which will be set with the path to the extracted contents
function(download_bundled_external remote_path lib_name prefix_var)
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
if (NOT EXISTS "${prefix}")
message(STATUS "Downloading binaries for ${lib_name}...")
file(DOWNLOAD
https://github.com/citra-emu/ext-windows-bin/raw/master/${remote_path}${lib_name}.7z
"${CMAKE_BINARY_DIR}/externals/${lib_name}.7z" SHOW_PROGRESS)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${CMAKE_BINARY_DIR}/externals/${lib_name}.7z"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
message(STATUS "Using bundled binaries at ${prefix}")
set(${prefix_var} "${prefix}" PARENT_SCOPE)
endfunction()
+28
View File
@@ -0,0 +1,28 @@
# Copyright 2016 Citra Emulator Project
# Licensed under GPLv2 or any later version
# Refer to the license.txt file included.
# This file provides the function windows_copy_files.
# This is only valid on Windows.
# Include guard
if(__windows_copy_files)
return()
endif()
set(__windows_copy_files YES)
# Any number of files to copy from SOURCE_DIR to DEST_DIR can be specified after DEST_DIR.
# This copying happens post-build.
function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
# windows commandline expects the / to be \ so switch them
string(REPLACE "/" "\\\\" SOURCE_DIR ${SOURCE_DIR})
string(REPLACE "/" "\\\\" DEST_DIR ${DEST_DIR})
# /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output
# cmake adds an extra check for command success which doesn't work too well with robocopy
# so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND if not exist ${DEST_DIR} mkdir ${DEST_DIR} 2> nul
COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
)
endfunction()
+34
View File
@@ -0,0 +1,34 @@
threeSD
========
threeSD is a tool to help prepare your system for the Nintendo 3DS emulator [Citra](https://citra-emu.org).
## Instructions
First of all, of course, you should download a [release](https://github.com/zhaowenlan1779/threeSD/releases) of threeSD and extract the archive somewhere.
### What you'll need
* Nintendo 3DS with access to CFW and [GodMode9](https://github.com/d0k3/GodMode9)
* If your 3DS is not yet hacked, you can do so by following the instructions [here](https://3ds.hacks.guide).
* You can install GodMode9 by downloading it and copying the `firm` file to `luma/payloads` on your 3DS. You can rename it to begin with `[BUTTON]_` (e.g. `X_GodMode9.firm`) to set a convenicence button to hold during boot to enter GodMode9.
* PC compatible with Citra
* You will need a graphics card compatible with OpenGL 3.3 and install the latest graphics drivers from your vendor's website.
* Operating system requirements: **64-bit** Windows (7+), Linux (flatpak) or macOS (10.13+). Note that Citra on macOS 10.13 is currently broken. It is recommended to update to 10.14.
* SD / microSD card reader
### On Your 3DS
You will need to run a GodMode9 script. If you are unsure about the script's safety (which is good!), check the source code yourself [here](https://github.com/zhaowenlan1779/threeSD/blob/master/dist/threeSDumper.gm9).
1. Copy the gm9 script (`threeSDumper.gm9`) in `dist` to the `gm9/scripts` folder on your SD card.
1. Power up your 3DS to launch GodMode9 (you will need to hold a button corresponding to your `firm` file's name, or hold `START` to enter the chainloader). Press the `Home` button to bring up GodMode9's `HOME Menu`. Use the d-pad and the `A` button to select `Scripts...`.
1. Use the d-pad and the `A` button to select `threeSDumper`. You will be prompted with a question "Execute threeSD Dumper?". Press `A` to confirm.
1. After a moment or two you will see the message "Successfully dumped necessary files for threeSD." Your 3DS SD card is now prepared for use with threeSD and Citra. Press `A` to exit the script.
1. Power off your 3DS with `R+START`. Remove the SD card from your 3DS and insert it into your PC (with a card reader).
### On your PC
Make sure the SD card is properly recognized and shows up as a disk.
1.
+89 -3
View File
@@ -1,9 +1,95 @@
# Enable modules to include each other's files
include_directories(.)
add_executable(threeSD)
# CMake seems to only define _DEBUG on Windows
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
# Set compilation flags
if (MSVC)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
# Silence "deprecation" warnings
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
# Avoid windows.h junk
add_definitions(-DNOMINMAX)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
/W3
/MP
/Zi
/Zo
/permissive-
/EHsc
/std:c++latest
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
)
# /GS- - No stack buffer overflow checks
add_compile_options("$<$<CONFIG:Release>:/GS->")
if (WARNINGS_AS_ERRORS)
add_compile_options(/WX)
endif()
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
else()
add_compile_options(
-Wall
-Wno-attributes
)
if (WARNINGS_AS_ERRORS)
add_compile_options(-Werror -Wfatal-errors)
endif()
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options("-stdlib=libc++")
endif()
# Set file offset size to 64 bits.
#
# On modern Unixes, this is typically already the case. The lone exception is
# glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
if (MINGW)
add_definitions(-DMINGW_HAS_SECURE_API)
if (COMPILE_WITH_DWARF)
add_compile_options("-gdwarf")
endif()
if (MINGW_STATIC_BUILD)
add_definitions(-DQT_STATICPLUGIN)
add_compile_options("-static")
endif()
endif()
endif()
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(frontend)
target_link_libraries(threeSD PRIVATE cryptopp fmt)
+15 -13
View File
@@ -1,14 +1,16 @@
target_sources(threeSD PRIVATE
common/assert.h
common/bit_field.h
common/common_funcs.h
common/common_paths.h
common/common_types.h
common/file_util.cpp
common/file_util.h
common/logging/log.h
common/misc.cpp
common/string_util.cpp
common/string_util.h
common/swap.h
add_library(common STATIC
assert.h
bit_field.h
common_funcs.h
common_paths.h
common_types.h
file_util.cpp
file_util.h
logging/log.h
misc.cpp
string_util.cpp
string_util.h
swap.h
)
target_link_libraries(common PUBLIC fmt)
+15 -13
View File
@@ -1,14 +1,16 @@
target_sources(threeSD PRIVATE
core/data_container.cpp
core/data_container.h
core/decryptor.cpp
core/decryptor.h
core/importer.cpp
core/importer.h
core/inner_fat.cpp
core/inner_fat.h
core/key/arithmetic128.cpp
core/key/arithmetic128.h
core/key/key.cpp
core/key/key.h
add_library(core STATIC
data_container.cpp
data_container.h
decryptor.cpp
decryptor.h
importer.cpp
importer.h
inner_fat.cpp
inner_fat.h
key/arithmetic128.cpp
key/arithmetic128.h
key/key.cpp
key/key.h
)
target_link_libraries(core PRIVATE common cryptopp)
+74 -1
View File
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <regex>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
@@ -17,7 +18,7 @@ SDMCImporter::SDMCImporter(const Config& config_) : config(config_) {
SDMCImporter::~SDMCImporter() = default;
bool SDMCImporter::Init() {
ASSERT_MSG(config.is_good && !config.sdmc_path.empty() && !config.user_path.empty() &&
ASSERT_MSG(!config.sdmc_path.empty() && !config.user_path.empty() &&
!config.bootrom_path.empty() && !config.movable_sed_path.empty(),
"Config is not good");
@@ -245,3 +246,75 @@ void SDMCImporter::ListSysdata(std::vector<ContentSpecifier>& out) const {
#undef CHECK_CONTENT
}
std::vector<Config> LoadPresetConfig(std::string mount_point) {
if (mount_point.back() != '/' && mount_point.back() != '\\') {
mount_point += '/';
}
// Not a Nintendo 3DS sd card at all
if (!FileUtil::Exists(mount_point + "Nintendo 3DS/")) {
return {};
}
Config config_template{};
config_template.user_path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir);
// Load dumped data paths if using our dumper
if (FileUtil::Exists(mount_point + "threeSD/")) {
#define LOAD_DATA(var, path) \
if (FileUtil::Exists(mount_point + "threeSD/" + path)) { \
config_template.var = mount_point + "threeSD/" + path; \
}
LOAD_DATA(movable_sed_path, MOVABLE_SED);
LOAD_DATA(bootrom_path, BOOTROM9);
LOAD_DATA(safe_mode_firm_path, "firm/");
LOAD_DATA(seed_db_path, SEED_DB);
LOAD_DATA(secret_sector_path, SECRET_SECTOR);
#undef LOAD_DATA
}
// Regex for 3DS ID0 and ID1
const std::regex id_regex{"[0-9a-f]{32}"};
// Load SDMC dir
std::vector<Config> out;
const auto ProcessDirectory = [&id_regex, &config_template, &out](const std::string& path) {
return FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&id_regex, &config_template, &out](u64* /*num_entries_out*/,
const std::string& directory,
const std::string& virtual_name) {
if (!FileUtil::IsDirectory(directory + virtual_name)) {
return true;
}
if (!std::regex_match(virtual_name, id_regex)) {
return true;
}
Config config = config_template;
config.sdmc_path = directory + virtual_name + "/";
out.push_back(config);
return true;
});
};
FileUtil::ForeachDirectoryEntry(
nullptr, mount_point + "Nintendo 3DS/",
[&id_regex, &ProcessDirectory](u64* /*num_entries_out*/, const std::string& directory,
const std::string& virtual_name) {
if (!FileUtil::IsDirectory(directory + virtual_name)) {
return true;
}
if (!std::regex_match(virtual_name, id_regex)) {
return true;
}
return ProcessDirectory(directory + virtual_name);
});
return out;
}
+6 -3
View File
@@ -51,9 +51,6 @@ struct Config {
std::string safe_mode_firm_path; ///< Path to safe mode firm (A folder) (Sysdata 1)
std::string seed_db_path; ///< Path to seeddb.bin (Sysdata 2)
std::string secret_sector_path; ///< Path to secret sector (New3DS only) (Sysdata 3)
// Whether this config has all necessary information
bool is_good;
};
class SDMCImporter {
@@ -96,3 +93,9 @@ private:
Config config;
std::unique_ptr<SDMCDecryptor> decryptor;
};
/**
* Look for and load preset config for a SD card mounted at mount_point.
* @return a list of preset config available. can be empty
*/
std::vector<Config> LoadPresetConfig(std::string mount_point);
+2 -1
View File
@@ -31,7 +31,8 @@ struct KeyDesc {
bool same_as_before;
};
AESKey HexToKey(const std::string& hex) {
// TODO: Use this to support manual input of keys
[[maybe_unused]] AESKey HexToKey(const std::string& hex) {
if (hex.size() < 32) {
throw std::invalid_argument("hex string is too short");
}
+53 -2
View File
@@ -1,3 +1,54 @@
target_sources(threeSD PRIVATE
frontend/main.cpp
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if (POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
add_executable(threeSD
main.cpp
main.h
main.ui
)
target_link_libraries(threeSD PRIVATE common core)
target_link_libraries(threeSD PRIVATE Qt5::Widgets)
target_link_libraries(threeSD PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (APPLE)
# TODO: support macOS
# set(MACOSX_ICON "../../dist/citra.icns")
# set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
# target_sources(threeSD PRIVATE ${MACOSX_ICON})
# set_target_properties(threeSD PROPERTIES MACOSX_BUNDLE TRUE)
# set_target_properties(threeSD PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
elseif(WIN32)
# compile as a win32 gui application instead of a console application
target_link_libraries(threeSD PRIVATE Qt5::WinMain)
if(MSVC)
set_target_properties(threeSD PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
elseif(MINGW)
set_target_properties(threeSD PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
endif()
endif()
target_compile_definitions(threeSD PRIVATE
# Use QStringBuilder for string concatenation to reduce
# the overall number of temporary strings created.
-DQT_USE_QSTRINGBUILDER
# Disable implicit type narrowing in signal/slot connect() calls.
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
# Disable unsafe overloads of QProcess' start() function.
-DQT_NO_PROCESS_COMBINED_ARGUMENT_START
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
-DQT_NO_URL_CAST_FROM_STRING
)
if (MSVC)
include(CopyQt5Deps)
copy_Qt5_deps(threeSD)
endif()
+34 -10
View File
@@ -1,14 +1,38 @@
// Dummy
// Copyright 2014 Citra Emulator Project / 2019 threeSD Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <iostream>
#include "common/logging/log.h"
#include <QApplication>
#include "frontend/main.h"
#include "ui_main.h"
int main() {
#ifdef __APPLE__
#include <string>
#include <unistd.h>
#include "common/common_paths.h"
#include "common/file_util.h"
#endif
LOG_ERROR(Frontend, "test");
_sleep(1000);
LOG_WARNING(Frontend, "test2");
system("pause");
MainDialog::MainDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique<Ui::MainDialog>()) {
ui->setupUi(this);
}
return 0;
}
MainDialog::~MainDialog() = default;
int main(int argc, char* argv[]) {
// Init settings params
QCoreApplication::setOrganizationName("zhaowenlan1779");
QCoreApplication::setApplicationName("threeSD");
#ifdef __APPLE__
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
chdir(bin_path.c_str());
#endif
QApplication app(argc, argv);
MainDialog main_dialog;
main_dialog.show();
return app.exec();
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2019 threeSD Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QDialog>
namespace Ui {
class MainDialog;
}
class MainDialog : public QDialog {
Q_OBJECT;
public:
explicit MainDialog(QWidget* parent = nullptr);
~MainDialog() override;
private:
std::unique_ptr<Ui::MainDialog> ui;
};
+239
View File
@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainDialog</class>
<widget class="QDialog" name="MainDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string>threeSD</string>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>Auto-detected Configuration:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="configSelect">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QPushButton" name="advancedButton">
<property name="text">
<string>Advanced...</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox">
<property name="title">
<string>Custom</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>SDMC:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="sdmcPath"/>
</item>
<item>
<widget class="QPushButton" name="sdmcPathExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>User Directory:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="userPath"/>
</item>
<item>
<widget class="QPushButton" name="userPathExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>movable.sed:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="movableSedPath"/>
</item>
<item>
<widget class="QPushButton" name="movableSedExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>boot9.bin:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="bootrom9Path"/>
</item>
<item>
<widget class="QPushButton" name="bootrom9Explore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>Safe mode firm Path:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="safeModeFirmPath"/>
</item>
<item>
<widget class="QPushButton" name="safeModeFirmExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>seeddb.bin:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="seeddbPath"/>
</item>
<item>
<widget class="QPushButton" name="seeddbExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>sector0x96.bin:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="secretSectorPath"/>
</item>
<item>
<widget class="QPushButton" name="secretSectorExplore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>