diff --git a/.gitignore b/.gitignore index 1351ae0..540eca0 100644 --- a/.gitignore +++ b/.gitignore @@ -34,9 +34,16 @@ CMakeScripts Makefile cmake_install.cmake install_manifest.txt + +# VS Projects +*.sln +*.vcxproj +*.vcxproj.filters + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. + # User-specific files *.suo *.user @@ -47,6 +54,7 @@ install_manifest.txt *.userprefs # Build results +Win32/ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -275,4 +283,9 @@ Makefile* *.qmlproject.user *.qmlproject.user.* +#CMake Automoc +*_automoc.cpp + PkmGCSaveEditor_*.qm + +installdir/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b51b21..f5d85a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(PkmGCTools) set(CMAKE_INSTALL_PREFIX installdir) -option(BUILD_PKMGCSAVEEDITOR ON) +option(BUILD_PKMGCSAVEEDITOR "Build PkmGCSaveEditor" ON) add_subdirectory(LibPkmGC) add_definitions(${LIBPKMGC_DEFINITIONS}) diff --git a/LibPkmGC/CMakeLists.txt b/LibPkmGC/CMakeLists.txt index 9b25d98..1cf1917 100644 --- a/LibPkmGC/CMakeLists.txt +++ b/LibPkmGC/CMakeLists.txt @@ -16,7 +16,7 @@ set(LIBPKMGC_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include include_directories(${LIBPKMGC_INCLUDE_DIRS}) -option(LIBPKMGC_DYN_LIB ON) +option(LIBPKMGC_DYN_LIB "Build LibPkmGC as a shared library" ON) message("LIBPKMGC_DYN_LIB:\t${LIBPKMGC_DYN_LIB}") diff --git a/LibPkmGC/include/LibPkmGC/Base/Pokemon.h b/LibPkmGC/include/LibPkmGC/Base/Pokemon.h index e352d5a..3a8fb4b 100644 --- a/LibPkmGC/include/LibPkmGC/Base/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/Base/Pokemon.h @@ -51,7 +51,7 @@ public: PokemonSpeciesIndex getWurmpleEvolution(void) const; char getUnownForm(void) const; - bool isSpecialAbilityDefined(void) const; + bool isSecondAbilityDefined(void) const; virtual PokemonAbilityIndex getAbility(void) const = 0; virtual bool isEmptyOrInvalid(void) const; @@ -78,7 +78,7 @@ public: // u8 encounterType; VersionInfo version; - bool specialRibbons[12]; //special ribbons #7 only exists in XD/Col + bool specialRibbons[12]; //u32 statusFlags; diff --git a/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h index c4b921b..f7d7115 100644 --- a/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h @@ -43,9 +43,7 @@ pkm 0xbd : u8 specialRibbons[12] 0xc9 : u8 unused ? 0xca : u8 pkrsStatus - 0xcb : u8 ? - 0xcc : u8 abilityUsed - 0xcd : u16 ? ? + 0xcb : u8 flags[3] : egg, special ability, invalid pkm 0xcf : u8 marks // shadow pkm data ? 0xd0--0xd7 : ? ? @@ -68,15 +66,13 @@ public: Pokemon* clone(void) const; Pokemon* create(void) const; + bool isEmptyOrInvalid(void) const; + void save(void); void swap(Pokemon& other); Pokemon& operator=(Pokemon const& other); - bool hasSpecialAbility(void) const; - void setSpecialAbilityStatus(bool status); - bool specialAbilityStatus; - Pokemon(XD::Pokemon const& other); Pokemon& operator=(GC::Pokemon const& other); void swap(GC::Pokemon & other); diff --git a/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h b/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h index 5ed6160..128bfb3 100644 --- a/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h +++ b/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h @@ -27,6 +27,8 @@ #define LD_ARRAY(type,ar,sz,off) SM_H_TMP_NS()::toArrayOfIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz)) #define LD_ARRAY_E(type,ar,sz,off,etype) SM_H_TMP_NS()::toArrayOfEnumIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz)) #define LD_ARRAY_B(type,ar,sz,off) SM_H_TMP_NS()::toArrayOfBoolIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz)) +#define LD_BIT_ARRAY2(type,ar,sz,off,st) type ar##_tmp; LD_FIELD(type, ar##_tmp, off); for(int i__ = 0; i__ < sz; ++i__) ar[i__] = (ar##_tmp & (1U << (8*sizeof(type) - 1 - st - i__))) != 0; +#define LD_BIT_ARRAY(type, ar, sz, off) LD_BIT_ARRAY2(type,ar,sz,off,0) #define SV_FIELD(type,fld,off) SM_H_TMP_NS()::fromInteger(BUFFER_NAME+off, fld) #define SV_FIELD_E(type,fld,off,etype) SM_H_TMP_NS()::fromEnumInteger(BUFFER_NAME+off, fld) @@ -34,6 +36,10 @@ #define SV_ARRAY(type,ar,sz,off) SM_H_TMP_NS()::fromArrayOfIntegers(BUFFER_NAME+off, ar, ar+sz) #define SV_ARRAY_E(type,ar,sz,off,etype) SM_H_TMP_NS()::fromArrayOfEnumIntegers(BUFFER_NAME+off, ar, ar+sz) #define SV_ARRAY_B(type,ar,sz,off) SM_H_TMP_NS()::toArrayOfBoolIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_BIT_ARRAY2(type, ar, sz, off, st) type ar##_tmp; LD_FIELD(type, ar##_tmp, off); ar##_tmp &= ~(((1U << sz) - 1) << (8*sizeof(type) - 1 - st - sz));\ +for(int i__ = 0; i__ < sz; ++i__) ar##_tmp |= ((ar[i__]) ? 1U : 0U) << (8*sizeof(type) - 1 - st - i__); SV_FIELD(type, ar##_tmp, off); +#define SV_BIT_ARRAY(type, ar, sz, off) SV_BIT_ARRAY2(type,ar,sz,off,0) + #define LD_SUBSTRUCTURE(type, fld, off) fld = new type(data + off) #define LD_SUBSTRUCTURE_ARRAY(type, ar, sz, off) for(size_t i__ = 0; i__ < sz; ++i__) LD_SUBSTRUCTURE(type, ar[i__], off+type::size*i__); @@ -42,7 +48,6 @@ #define SV_SUBSTRUCTURE(type, fld, off) fld->save(); std::copy(fld->data, fld->data + type::size, data + off); #define SV_SUBSTRUCTURE2(type, fld, off) fld->save(); std::copy(fld->data, fld->data + fld->getSize(), data + off); #define SV_SUBSTRUCTURE_ARRAY(type, ar, sz, off) for(size_t i__ = 0; i__ < sz; ++i__) { SV_SUBSTRUCTURE(type, ar[i__], off+type::size*i__); } -//#define SV_SUBSTRUCTURE_ARRAY2(type, ar, sz, off) for(size_t i__ = 0; i__ < sz; ++i__) { SV_SUBSTRUCTURE2(type, ar[i__], off+ar[i__]->getSize()*i__); } diff --git a/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h index 0582f8c..c5cffe9 100644 --- a/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h @@ -3,6 +3,11 @@ #include #include + +#define LIBPKMGC_GC_EGG_FLAG 0 +#define LIBPKMGC_GC_SECOND_ABILITY_FLAG 1 +#define LIBPKMGC_GC_INVALID_POKEMON_FLAG 2 + namespace LibPkmGC { namespace GC { class LIBPKMGC_DECL Pokemon : @@ -15,10 +20,12 @@ public: virtual void swap(Pokemon& other); virtual Pokemon& operator=(Pokemon const& other); - virtual bool hasSpecialAbility(void) const = 0; - virtual void setSpecialAbilityStatus(bool status) = 0; + /*bool hasSecondAbility(void) const; + void setSpecialAbilityStatus(bool status);*/ virtual PokemonAbilityIndex getAbility(void) const; + u16 shadowPkmID; + bool pkmFlags[3]; virtual void swap(Base::Pokemon& other); virtual Pokemon& operator=(Base::Pokemon const& other); diff --git a/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h index e9da663..a192a2a 100644 --- a/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h @@ -28,8 +28,9 @@ namespace XD { bit 7: egg flag bit 6: special (second) ability flag. Pokémon XD's catchable Pkms have a 50% chance to have their special ability bit 5: invalidity flag. MUST **NOT** BE SET for the Pokémon to be considered as valid ("not empty") - bit 4: "not traded" flag ?? - bit 2: "caught" flag ?? + bit 4: "not tradable in game" flag + bit 3: ? + bit 2: "caught" flag 0x1e 0x1f : ?? 0x20: u32 experience 0x24: u16 SID @@ -73,15 +74,11 @@ public: void swap(Pokemon& other); Pokemon& operator=(Pokemon const& other); - bool hasSpecialAbility(void) const; - void setSpecialAbilityStatus(bool status); - - u8 pkmFlags; - - bool isEmptyOrInvalid(void) const; Pokemon(Colosseum::Pokemon const& other); Pokemon& operator=(GC::Pokemon const& other); void swap(GC::Pokemon & other); + + bool XDPkmFlags[3]; private: void loadFields(void); diff --git a/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp index ffd9e00..061b224 100644 --- a/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp @@ -136,7 +136,7 @@ bool Pokemon::isEmptyOrInvalid(void) const { return (species > 0x19e) || (species == 0) || !getSpeciesData(species).isValid || version.isIncomplete(); } -bool Pokemon::isSpecialAbilityDefined(void) const{ +bool Pokemon::isSecondAbilityDefined(void) const{ return (getSpeciesData(species).possibleAbilities[1] != NoAbility); } void Pokemon::swap(Pokemon& other) { diff --git a/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp index f9df3dd..7eaa064 100644 --- a/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp @@ -12,14 +12,17 @@ Pokemon::Pokemon(void) : GC::Pokemon(0x138) { initWithEmptyData(); } Pokemon::~Pokemon(void) { } -Pokemon::Pokemon(Pokemon const& other) : GC::Pokemon(other), specialAbilityStatus(other.specialAbilityStatus){} +Pokemon::Pokemon(Pokemon const& other) : GC::Pokemon(other){} Pokemon* Pokemon::clone(void) const { return new Pokemon(*this); } Pokemon* Pokemon::create(void) const { return new Pokemon; } void Pokemon::swap(Pokemon& other) { GC::Pokemon::swap(other); - SW(specialAbilityStatus); +} + +bool Pokemon::isEmptyOrInvalid(void) const { + return GC::Pokemon::isEmptyOrInvalid() && species == Bonsly; } void Pokemon::loadFields(void) { @@ -70,7 +73,7 @@ void Pokemon::loadFields(void) { LD_ARRAY_B(u8, specialRibbons, 12, 0xbd); LD_FIELD(u8, pkrsStatus, 0xca); - LD_FIELD_B(u8, specialAbilityStatus, 0xcc); + LD_ARRAY_B(u8, pkmFlags, 3, 0xcb); LD_FIELD(u8, marksTmp, 0xcf); @@ -80,9 +83,13 @@ void Pokemon::loadFields(void) { markings.load(marksTmp); for (size_t i = 0; i < 4; ++i) moves[i].load(data + 0x78 + 4 * i); + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; + } void Pokemon::save(void) { + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; + SV_FIELD_E(u16, species, 0x00, PokemonSpeciesIndex); SV_FIELD(u32, PID, 0x04); version.save(data + 0x08); @@ -119,7 +126,7 @@ void Pokemon::save(void) { SV_ARRAY_B(u8, specialRibbons, 12, 0xbd); SV_FIELD(u8, pkrsStatus, 0xca); - SV_FIELD_B(u8, specialAbilityStatus, 0xcc); + LD_ARRAY_B(u8, pkmFlags, 3, 0xcb); SV_FIELD(u8, markings.save(), 0xcf); @@ -134,19 +141,12 @@ void Pokemon::save(void) { Pokemon& Pokemon::operator=(Pokemon const& other) { if (this != &other) { GC::Pokemon::operator=(other); - CP(specialAbilityStatus); } return *this; } LIBPKMGC_GC_GEN_COL_VTF(Pokemon) -bool Pokemon::hasSpecialAbility(void) const { - return isSpecialAbilityDefined() && specialAbilityStatus; -} -void Pokemon::setSpecialAbilityStatus(bool status) { - specialAbilityStatus = isSpecialAbilityDefined() && status; -} } } \ No newline at end of file diff --git a/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp index 796d8db..5d0a3e4 100644 --- a/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp @@ -13,23 +13,39 @@ Pokemon::~Pokemon(void) { } -Pokemon::Pokemon(Pokemon const& other) : Base::Pokemon(other), shadowPkmID(other.shadowPkmID) {} -void Pokemon::swap(Pokemon& other) { Base::Pokemon::swap(other); SW(shadowPkmID); } +Pokemon::Pokemon(Pokemon const& other) : Base::Pokemon(other), shadowPkmID(other.shadowPkmID) { + CP_ARRAY(pkmFlags, 3); +} +void Pokemon::swap(Pokemon& other) { + Base::Pokemon::swap(other); + SW(shadowPkmID); + SW_ARRAY(pkmFlags, 3); +} PokemonAbilityIndex Pokemon::getAbility(void) const { const PokemonSpeciesData dt = getSpeciesData(species); - return (hasSpecialAbility()) ? dt.possibleAbilities[1] : dt.possibleAbilities[0]; + return (pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]) ? dt.possibleAbilities[1] : dt.possibleAbilities[0]; } Pokemon& Pokemon::operator=(Pokemon const& other) { if (this != &other) { Base::Pokemon::operator=(other); CP(shadowPkmID); + CP_ARRAY(pkmFlags, 3); } return *this; } +/* +bool Pokemon::hasSecondAbility(void) const { + return isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; +} + +void Pokemon::setSpecialAbilityStatus(bool status) { + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = status; +}*/ + void Pokemon::swap(Base::Pokemon & other) { return swap((Pokemon&)other); } diff --git a/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp index ce3235b..ec77761 100644 --- a/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp @@ -1,8 +1,8 @@ #include -#define EGG_FLAG 0x80 -#define SPECIAL_ABILITY_FLAG 0x40 -#define INVALID_PKM_FLAG 0x20 +#define LIBPKMGC_XD_NOT_TRADABLE_IN_GAME_FLAG 0 +#define LIBPKMGC_XD_UNKNOWN_FLAG 1 +#define LIBPKMGC_XD_CAUGHT_FLAG 2 namespace LibPkmGC { namespace XD { @@ -16,26 +16,27 @@ Pokemon::Pokemon(void) : GC::Pokemon(0xc4) { initWithEmptyData(); } Pokemon::~Pokemon(void) { } -Pokemon::Pokemon(Pokemon const& other) : GC::Pokemon(other), pkmFlags(other.pkmFlags) {} +Pokemon::Pokemon(Pokemon const& other) : GC::Pokemon(other) { + CP_ARRAY(XDPkmFlags, 3); +} + Pokemon* Pokemon::clone(void) const { return new Pokemon(*this); } Pokemon* Pokemon::create(void) const { return new Pokemon; } void Pokemon::swap(Pokemon& other) { GC::Pokemon::swap(other); - SW(pkmFlags); } Pokemon& Pokemon::operator=(Pokemon const& other) { if (this != &other) { GC::Pokemon::operator=(other); - CP(pkmFlags); + CP_ARRAY(XDPkmFlags, 3); } return *this; } void Pokemon::loadFields(void) { u8 marksTmp = 0; - u16 specialRibbonsTmp = 0; LD_FIELD_E(u16, species, 0x00, PokemonSpeciesIndex); LD_FIELD_E(u16, heldItem, 0x02, ItemIndex); LD_FIELD(u16, partyData.currentHP, 0x04); @@ -60,7 +61,8 @@ void Pokemon::loadFields(void) { LD_FIELD_E(u8, partyData.status, 0x16, PokemonStatus); partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; - LD_FIELD(u8, pkmFlags, 0x1d); + LD_BIT_ARRAY(u8, pkmFlags, 3, 0x1d); + LD_BIT_ARRAY2(u8, XDPkmFlags, 3, 0x1d, 3); LD_FIELD(u32, experience, 0x20); LD_FIELD(u16, SID, 0x24); LD_FIELD(u16, TID, 0x26); @@ -69,9 +71,7 @@ void Pokemon::loadFields(void) { OTName = new GC::PokemonString(data + 0x38, 10); name = new GC::PokemonString(data + 0x4e, 10); - - - LD_FIELD(u16, specialRibbonsTmp, 0x7c); + LD_BIT_ARRAY(u16, specialRibbons, 12, 0x7c); LD_ARRAY(u16, partyData.stats, 6, 0x90); u16 EVs_tmp[6]; // EVs are internally stored as u16 @@ -92,21 +92,11 @@ void Pokemon::loadFields(void) { for (size_t i = 0; i < 4; ++i) moves[i].load(data + 0x80 + 4 * i); - specialRibbonsTmp >>= 4; - for (size_t i = 0; i < 12; ++i) { - specialRibbons[11 - i] = (specialRibbonsTmp & 1) != 0; - specialRibbonsTmp >>= 1; - } + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; } void Pokemon::save(void) { - u16 specialRibbonsTmp = 0; - - for (size_t i = 0; i < 12; ++i) { - specialRibbonsTmp <<= 1; - specialRibbonsTmp |= (specialRibbons[i]) ? 1 : 0; - } - specialRibbonsTmp <<= 4; + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; SV_FIELD_E(u16, species, 0x00, PokemonSpeciesIndex); SV_FIELD_E(u16, heldItem, 0x02, ItemIndex); @@ -125,7 +115,9 @@ void Pokemon::save(void) { partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; SV_FIELD_E(u8, partyData.status, 0x16, PokemonStatus); - SV_FIELD(u8, pkmFlags, 0x1d); + SV_BIT_ARRAY(u8, pkmFlags, 3, 0x1d); + SV_BIT_ARRAY2(u8, XDPkmFlags, 3, 0x1d, 3); + SV_FIELD(u32, experience, 0x20); SV_FIELD(u16, SID, 0x24); SV_FIELD(u16, TID, 0x26); @@ -135,7 +127,7 @@ void Pokemon::save(void) { name->save(data + 0x4e, 10); name->save(data + 0x64, 10); - SV_FIELD(u16, specialRibbonsTmp, 0x7c); + SV_BIT_ARRAY(u16, specialRibbons, 12, 0x7c); SV_ARRAY(u16, partyData.stats, 6, 0x90); u16 EVs_tmp[6]; for (size_t i = 0; i < 6; ++i) EVs_tmp[i] = (u16)EVs[i]; @@ -156,19 +148,6 @@ void Pokemon::save(void) { } -bool Pokemon::isEmptyOrInvalid(void) const { - return GC::Pokemon::isEmptyOrInvalid() || (pkmFlags & INVALID_PKM_FLAG) != 0; -} -bool Pokemon::hasSpecialAbility(void) const{ - return isSpecialAbilityDefined() && ((pkmFlags & SPECIAL_ABILITY_FLAG) != 0); -} - -void Pokemon::setSpecialAbilityStatus(bool status) { - status = isSpecialAbilityDefined() && status; - pkmFlags &= ~SPECIAL_ABILITY_FLAG; // reset bit 5 - pkmFlags |= (status) ? SPECIAL_ABILITY_FLAG : 0; -} - LIBPKMGC_GC_GEN_XD_VTF(Pokemon) } } \ No newline at end of file diff --git a/PkmGCSaveEditor/CMakeLists.txt b/PkmGCSaveEditor/CMakeLists.txt index 7abf880..d6e0e64 100644 --- a/PkmGCSaveEditor/CMakeLists.txt +++ b/PkmGCSaveEditor/CMakeLists.txt @@ -6,8 +6,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) -find_package(Qt5Widgets) -find_package(Qt5LinguistTools OPTIONAL) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5LinguistTools) file(GLOB_RECURSE source_files src/*) @@ -28,14 +28,14 @@ if(Qt5LinguistTools_FOUND) list(APPEND QT5_TRANSLATION_QM ${QT5_QM_DIR}/qt_${language}.qm) elseif(EXISTS translations/qt/qt_${language}.qm) list(APPEND QT5_TRANSLATION_QM translations/qt/qt_${language}.qm) - endif() + endif(EXISTS ${QT5_QM_DIR}/qt_${language}.qm) if(EXISTS ${QT5_QM_DIR}/qtbase_${language}.qm) list(APPEND QT5_TRANSLATION_QM ${QT5_QM_DIR}/qtbase_${language}.qm) # Required for Qt > 5.3 elseif(EXISTS translations/qt/qtbase_${language}.qm) list(APPEND QT5_TRANSLATION_QM translations/qt/qtbase_${language}.qm) - endif() - endforeach() + endif(EXISTS ${QT5_QM_DIR}/qtbase_${language}.qm) + endforeach(language) list(REMOVE_DUPLICATES QT5_TRANSLATION_QM) set(QT5_TRANSLATION_QM ${QT5_TRANSLATION_QM} CACHE INTERNAL "Qt5 *.qm") @@ -48,7 +48,9 @@ if(Qt5LinguistTools_FOUND) install(FILES ${TRANSLATION_QM} DESTINATION Debug/bin/languages CONFIGURATIONS Debug) install(FILES ${TRANSLATION_QM} DESTINATION Release/bin/languages CONFIGURATIONS Release RelWithDebInfo MinSizeRel) -endif() +else(Qt5LinguistTools_FOUND) + message("Qt5LinguistTools was NOT found. The translation files will not be able to be updated.") +endif(Qt5LinguistTools_FOUND) # DLL hell diff --git a/PkmGCSaveEditor/src/Core/TrainerInfoLayout.cpp b/PkmGCSaveEditor/src/Core/TrainerInfoLayout.cpp index f5c46e5..8a7f18f 100644 --- a/PkmGCSaveEditor/src/Core/TrainerInfoLayout.cpp +++ b/PkmGCSaveEditor/src/Core/TrainerInfoLayout.cpp @@ -23,6 +23,9 @@ QFormLayout(){ this->addRow(tr("Name"), nameFld); this->addRow(tr("TID/SID"), TIDSIDLayout); set(inName, inSID, inTID, inGender); + connect(TIDFld, SIGNAL(valueChanged(int)), this, SLOT(TIDorSIDChangedEmitter())); + connect(SIDFld, SIGNAL(valueChanged(int)), this, SLOT(TIDorSIDChangedEmitter())); + emit TIDorSIDChanged(); } void TrainerInfoLayout::trainerName(LibPkmGC::Base::PokemonString * outName) { @@ -65,6 +68,6 @@ void TrainerInfoLayout::set(LibPkmGC::Base::PokemonString * inName, LibPkmGC::u1 setTrainerGender(inGender); } -void TrainerInfoLayout::TIDorSIDChangedEmmiter(void) { +void TrainerInfoLayout::TIDorSIDChangedEmitter(void) { emit TIDorSIDChanged(); } \ No newline at end of file diff --git a/PkmGCSaveEditor/src/Core/TrainerInfoLayout.h b/PkmGCSaveEditor/src/Core/TrainerInfoLayout.h index 9568f47..c3a3054 100644 --- a/PkmGCSaveEditor/src/Core/TrainerInfoLayout.h +++ b/PkmGCSaveEditor/src/Core/TrainerInfoLayout.h @@ -31,7 +31,7 @@ signals: void TIDorSIDChanged(void); public slots: - void TIDorSIDChangedEmmiter(void); + void TIDorSIDChangedEmitter(void); private: QHBoxLayout* genderLayout; QButtonGroup* genderButtonGroup; diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp index c35e60b..196b475 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp +++ b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp @@ -74,16 +74,16 @@ void PokemonDisplayWidget::updatePkmNameAndSummary(void) { nameFld->setText(pkm->name->toUTF8()); s = "("; - if (!getSpeciesData(pkm->species).isValid) { + if (!getSpeciesData(pkm->species).isValid || (pkm->species == Bonsly && !LIBPKMGC_IS_XD(Pokemon,pkm))) { tt = tr("Invalid species"); } if (pkm->version.isIncomplete()) { if (!tt.isEmpty()) tt += "\n"; tt = tr("Invalid version info"); } - if (LIBPKMGC_IS_XD(Pokemon, pkm) && (static_cast(pkm)->pkmFlags & 0x20) != 0) { + if (pkm->pkmFlags[LIBPKMGC_GC_INVALID_POKEMON_FLAG]) { if (!tt.isEmpty()) tt += "\n"; - tt += tr("Bit 5 set on byte at offset 0x1d"); + tt += tr("\"Invalid Pok\xc3\xa9mon\" flag set"); } summary->setToolTip(tt); diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp b/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp index 78e890e..11dba3c 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp +++ b/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp @@ -4,9 +4,6 @@ using namespace LibPkmGC; using namespace Localization; using namespace Base; -static QString s1[] = QT_TRANSLATE_NOOP3("GCUIs::PokemonUI", "None", "Status"); - - namespace GCUIs { PokemonMoveLayout::PokemonMoveLayout(PokemonMove const& inMove) : QHBoxLayout(){ @@ -138,6 +135,11 @@ void PokemonUI::initWidget(void){ happinessFld = new UnsignedSpinbox<8>; pkrsStatusFld = new UnsignedSpinbox<8>; statusFld = new QComboBox; + flagsLayout = new QGridLayout; + flagsButtonGroup = new QButtonGroup; + markingsLayout = new QHBoxLayout; + markingsButtonGroup = new QButtonGroup; + statusFld->addItems(statusNames()); for (size_t i = 0; i < 387; ++i) @@ -159,16 +161,33 @@ void PokemonUI::initWidget(void){ levelAndSyncLayout->setAlignment(Qt::AlignRight); experienceLevelAndSyncLayout->addLayout(levelAndSyncLayout); - markingsLayout = new QHBoxLayout; - markingsButtonGroup = new QButtonGroup; + eggFlagCheckBox = new QCheckBox(tr("Egg")); secondAbilityFlagCheckBox = new QCheckBox(tr("Second ability")); + invalidPokemonCheckBox = new QCheckBox(tr("Invalid Pok\xc3\xa9mon")); + notTradableInGameFlagCheckBox = new QCheckBox(tr("Not tradable in-game")); unknownFlagCheckBox = new QCheckBox(tr("Unknown")); + caughtFlagCheckBox = new QCheckBox(tr("Caught")); + + QCheckBox* g1[] = { eggFlagCheckBox, secondAbilityFlagCheckBox, invalidPokemonCheckBox, + notTradableInGameFlagCheckBox, unknownFlagCheckBox, caughtFlagCheckBox }; + + flagsButtonGroup->setExclusive(false); + for (int i = 0; i < 6; ++i) { + flagsButtonGroup->addButton(g1[i], i); + flagsLayout->addWidget(g1[i], i / 3, i % 3); + connect(g1[i], SIGNAL(stateChanged(int)), this, SLOT(flagsStateChangeHandler())); + } + + circleMarkingCheckBox = new QCheckBox("\xe2\x97\x8f"); squareMarkingCheckBox = new QCheckBox("\xe2\x96\xa0"); triangleMarkingCheckBox = new QCheckBox("\xe2\x96\xb2"); heartMarkingCheckBox = new QCheckBox("\xe2\x99\xa5"); + QCheckBox* g2[] = { circleMarkingCheckBox, squareMarkingCheckBox, triangleMarkingCheckBox, heartMarkingCheckBox }; + markingsButtonGroup->setExclusive(false); - markingsButtonGroup->addButton(circleMarkingCheckBox); markingsButtonGroup->addButton(squareMarkingCheckBox); - markingsButtonGroup->addButton(triangleMarkingCheckBox); markingsButtonGroup->addButton(heartMarkingCheckBox); - markingsLayout->addWidget(circleMarkingCheckBox); markingsLayout->addWidget(squareMarkingCheckBox); - markingsLayout->addWidget(triangleMarkingCheckBox); markingsLayout->addWidget(heartMarkingCheckBox); + + for (int i = 0; i < 4; ++i) { + markingsButtonGroup->addButton(g2[i], i); + markingsLayout->addWidget(g2[i]); + } generalTabLayout->addRow(tr("Species"), speciesFld); generalTabLayout->addRow(tr("Name or nickname"), nameLayout); @@ -180,6 +199,7 @@ void PokemonUI::initWidget(void){ generalTabLayout->addRow(tr("Happiness"), happinessFld); generalTabLayout->addRow(tr("Pok\xc3\xa9rus"), pkrsStatusFld); generalTabLayout->addRow(tr("Status"), statusFld); + generalTabLayout->addRow(tr("Flags"), flagsLayout); generalTabLayout->addRow(tr("Markings"), markingsLayout); @@ -245,7 +265,6 @@ void PokemonUI::initWidget(void){ mainStatsFormLayout->addRow(statNames[i], mainStatsFlds[i]); connect(mainStatsFlds[i], SIGNAL(IVOrEVChanged()), this, SLOT(updateMainStats())); } - autoUpdateMainStatsCheckBox = new QCheckBox(tr("Update stats automatically")); autoUpdateMainStatsCheckBox->setChecked(true); @@ -341,6 +360,7 @@ void PokemonUI::initWidget(void){ connect(speciesFld, SIGNAL(currentIndexChanged(int)), this, SLOT(speciesChangeHandler())); connect(resetNameButton, SIGNAL(clicked()), this, SLOT(resetName())); connect(PIDFld, SIGNAL(valueChanged(int)), this, SLOT(PIDChangeHandler())); + connect(abilityFld, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFlags())); connect(experienceFld, SIGNAL(valueChanged(int)), this, SLOT(updateLevelFromExperience())); connect(levelFld, SIGNAL(valueChanged(int)), this, SLOT(updateExperienceFromLevel())); connect(OTField, SIGNAL(TIDorSIDChanged()), this, SLOT(updatePkmAttributes())); @@ -374,6 +394,7 @@ void PokemonUI::parseData(void){ nameFld->setText(pkm->name->toUTF8()); speciesFld->setCurrentIndex((int)Localization::pkmSpeciesIndexToNameIndex(pkm->species)); + if (pkm->species == Bonsly && !isXD) speciesFld->setCurrentIndex(0); PIDFld->setUnsignedValue(pkm->PID); @@ -389,6 +410,16 @@ void PokemonUI::parseData(void){ pkrsStatusFld->setUnsignedValue(pkm->pkrsStatus); statusFld->setCurrentIndex((pkm->partyData.status == NoStatus) ? 0 : (int)pkm->partyData.status - 2); + QCheckBox* g1[] = { eggFlagCheckBox, secondAbilityFlagCheckBox, invalidPokemonCheckBox, + notTradableInGameFlagCheckBox, unknownFlagCheckBox, caughtFlagCheckBox }; + + for (size_t i = 0; i < 3; ++i) { + g1[i]->setChecked(pkm->pkmFlags[i]); + g1[3 + i]->setVisible(isXD); + if (isXD) + g1[3 + i]->setChecked(static_cast(pkm)->XDPkmFlags[i]); + } + circleMarkingCheckBox->setChecked(pkm->markings.circle); squareMarkingCheckBox->setChecked(pkm->markings.square); triangleMarkingCheckBox->setChecked(pkm->markings.triangle); @@ -402,7 +433,7 @@ void PokemonUI::parseData(void){ versionFld->setInfo(pkm->version); connect(versionFld, SIGNAL(versionChanged()), this, SLOT(versionChangeHandler())); - + currentHPFld->setUnsignedValue(pkm->partyData.currentHP); for (size_t i = 0; i < 6; ++i) { PokemonStatLayout* l = mainStatsFlds[i]; l->IVFld->setUnsignedValue(pkm->IVs[i]); @@ -422,7 +453,7 @@ void PokemonUI::parseData(void){ autoUpdateMainStatsCheckBox->setChecked(true); versionChangeHandler(); - abilityFld->setCurrentIndex(pkm->hasSpecialAbility()); + abilityFld->setCurrentIndex((pkm->pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]) ? 1 : 0); } @@ -431,7 +462,7 @@ void PokemonUI::saveChanges(void){ pkm->name->fromUTF8(nameFld->text().toUtf8().data()); pkm->PID = PIDFld->unsignedValue(); - pkm->setSpecialAbilityStatus(abilityFld->currentIndex() != 0); + pkm->pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = (abilityFld->currentIndex() != 0); pkm->experience = experienceFld->unsignedValue(); pkm->partyData.level = (u8)levelFld->unsignedValue(); @@ -440,6 +471,15 @@ void PokemonUI::saveChanges(void){ pkm->pkrsStatus = (u8)pkrsStatusFld->unsignedValue(); pkm->partyData.status = (statusFld->currentIndex() == 0) ? NoStatus : (PokemonStatus)(2 + statusFld->currentIndex()); + QCheckBox* g1[] = { eggFlagCheckBox, secondAbilityFlagCheckBox, invalidPokemonCheckBox, + notTradableInGameFlagCheckBox, unknownFlagCheckBox, caughtFlagCheckBox }; + + for (size_t i = 0; i < 3; ++i) { + pkm->pkmFlags[i] = g1[i]->isChecked(); + if (isXD) + static_cast(pkm)->XDPkmFlags[i] = g1[3 + i]->isChecked(); + } + pkm->markings.circle = circleMarkingCheckBox->isChecked(); pkm->markings.square = squareMarkingCheckBox->isChecked(); pkm->markings.triangle = triangleMarkingCheckBox->isChecked(); @@ -455,6 +495,7 @@ void PokemonUI::saveChanges(void){ pkm->version = versionFld->info(); + pkm->partyData.currentHP = currentHPFld->unsignedValue(); for (size_t i = 0; i < 6; ++i) { PokemonStatLayout* l = mainStatsFlds[i]; pkm->IVs[i] = (u8)l->IVFld->unsignedValue(); @@ -484,9 +525,9 @@ QString PokemonUI::getShortPkmAttributeText(LibPkmGC::PokemonSpeciesIndex specie else if (Base::Pokemon::getGender(species, PID) == Female) ret += "\xe2\x99\x80"; else { - if (!ret.isEmpty()) ret += ", "; + if (!ret.isEmpty()) ret += ","; - ret += ""; + ret += " "; ret += tr("Genderless"); ret += ""; } @@ -521,7 +562,7 @@ QString PokemonUI::getShortPkmAttributeText(void){ if (versionFld->info().isIncomplete()) tt = tr("Invalid version info"); - if (isXD && (static_cast(pkm)->pkmFlags & 0x20) != 0) { + if (isXD && invalidPokemonCheckBox->isChecked()) { if (!tt.isEmpty()) tt += "\n"; tt += tr("Bit 5 set on byte at offset 0x1d"); } @@ -545,7 +586,7 @@ QString PokemonUI::getLongPkmAttributeText(void){ if (versionFld->info().isIncomplete()) tt = tr("Invalid version info"); - if (isXD && (static_cast(pkm)->pkmFlags & 0x20) != 0) { + if (isXD && invalidPokemonCheckBox->isChecked()) { if (!tt.isEmpty()) tt += "\n"; tt += tr("Bit 5 set on byte at offset 0x1d"); } @@ -571,9 +612,10 @@ void PokemonUI::updateMainStats(void) { PokemonNatureIndex natureIndex = (PokemonNatureIndex)((u32)PIDFld->value() % 25); u16 stat = Base::Pokemon::calculateStat(i, id, natureIndex, (u8) levelFld->value(), (u8) l->IVFld->value(), (u8) l->EVFld->value()); - l->statFld->setValue((int)stat); + l->statFld->setUnsignedValue(stat); } - currentHPFld->setValue(mainStatsFlds[0]->statFld->value()); + //currentHPFld->setUnsignedMaximum(mainStatsFlds[0]->statFld->value()); + currentHPFld->setUnsignedValue(mainStatsFlds[0]->statFld->value()); } } @@ -585,18 +627,25 @@ void PokemonUI::updateAbilityList(void) { LanguageIndex lg = generateDumpedNamesLanguage(); PokemonSpeciesIndex id = Localization::nameIndexToPkmSpeciesIndex((size_t)speciesFld->currentIndex()); const PokemonAbilityIndex* ab = getSpeciesData(id).possibleAbilities; + bool sec = abilityFld->currentIndex() == 1; + + disconnect(abilityFld, SIGNAL(currentIndexChanged(int))); + disconnect(secondAbilityFlagCheckBox, SIGNAL(stateChanged(int))); abilityFld->clear(); abilityFld->addItem(Localization::getPokemonAbilityName(lg, ab[0])); if (ab[1] != NoAbility) { abilityFld->addItem(Localization::getPokemonAbilityName(lg, ab[1])); abilityFld->setDisabled(false); - return; + abilityFld->setCurrentIndex((sec) ? 1 : 0); } else { abilityFld->setDisabled(true); - return; } + + updateFlags(); + connect(abilityFld, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFlags())); + connect(secondAbilityFlagCheckBox, SIGNAL(stateChanged(int)), this, SLOT(flagsStateChangeHandler())); } @@ -636,6 +685,16 @@ void PokemonUI::PIDChangeHandler(void) { updateMainStats(); } +void PokemonUI::updateFlags(void) { + secondAbilityFlagCheckBox->setDisabled(abilityFld->count() == 1); + secondAbilityFlagCheckBox->setChecked(abilityFld->currentIndex() == 1); +} + +void PokemonUI::flagsStateChangeHandler(void) { + abilityFld->setCurrentIndex((secondAbilityFlagCheckBox->isChecked() && abilityFld->count() == 2) ? 1 : 0); + updatePkmAttributes(); +} + void PokemonUI::autoUpdateStatsStateChangeHanler(void) { bool checked = autoUpdateMainStatsCheckBox->isChecked(); if (checked) updateMainStats(); diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonUI.h b/PkmGCSaveEditor/src/GCUIs/PokemonUI.h index 03b7219..597135a 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonUI.h +++ b/PkmGCSaveEditor/src/GCUIs/PokemonUI.h @@ -109,6 +109,11 @@ private: QComboBox* statusFld; + QGridLayout *flagsLayout; + QButtonGroup *flagsButtonGroup; + QCheckBox *eggFlagCheckBox, *secondAbilityFlagCheckBox, *invalidPokemonCheckBox; + QCheckBox *notTradableInGameFlagCheckBox, *unknownFlagCheckBox, *caughtFlagCheckBox; + QHBoxLayout* markingsLayout; QButtonGroup* markingsButtonGroup; QCheckBox *circleMarkingCheckBox, *squareMarkingCheckBox, *triangleMarkingCheckBox, *heartMarkingCheckBox; @@ -174,6 +179,10 @@ public slots: void updateLevelFromExperience(void); void speciesChangeHandler(void); void PIDChangeHandler(void); + + void updateFlags(void); + void flagsStateChangeHandler(void); + void autoUpdateStatsStateChangeHanler(void); void versionChangeHandler(void); diff --git a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp index f6dcf4f..909111c 100644 --- a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp +++ b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp @@ -24,11 +24,10 @@ void StrategyMemoEntryWidget::updatePIDText(void) { } } -void StrategyMemoEntryWidget::generateShinyPID(void) { +void StrategyMemoEntryWidget::generateShinyIDs(void) { u32 PID = firstPIDFld->unsignedValue(); - PID &= 0xffff0000; - PID |= (PID >> 16) ^ firstSIDFld->unsignedValue() ^ firstTIDFld->unsignedValue(); - firstPIDFld->setUnsignedValue(PID); + firstTIDFld->setUnsignedValue(PID >> 16); + firstSIDFld->setUnsignedValue(PID & 0xffff); } void StrategyMemoEntryWidget::initWidget(void) { @@ -44,7 +43,7 @@ void StrategyMemoEntryWidget::initWidget(void) { PIDLayout = new QVBoxLayout; firstPIDFld = new UnsignedSpinbox<32>; PIDText = new QLabel; - generateShinyPIDButton = new QPushButton(tr("Generate shiny PID")); + generateShinyIDsButton = new QPushButton(tr("Generate shiny IDs")); for (size_t i = 0; i <= 386; ++i) speciesSelector->addItem(getPokemonSpeciesNameByPkdxIndex(lg, (u16)i)); @@ -58,7 +57,7 @@ void StrategyMemoEntryWidget::initWidget(void) { mainLayout2->addRow(tr("First PID"), PIDLayout); mainLayout2->addRow(tr("Partial information"), partialInfoCheckBox); mainLayout->addLayout(mainLayout2); - mainLayout->addWidget(generateShinyPIDButton); + mainLayout->addWidget(generateShinyIDsButton); mainLayout2->setHorizontalSpacing(20); this->setLayout(mainLayout); @@ -66,7 +65,9 @@ void StrategyMemoEntryWidget::initWidget(void) { connect(speciesSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(speciesChangeHandler(int))); connect(firstPIDFld, SIGNAL(valueChanged(int)), this, SLOT(updatePIDText())); - connect(generateShinyPIDButton, SIGNAL(clicked()), this, SLOT(generateShinyPID())); + connect(firstTIDFld, SIGNAL(valueChanged(int)), this, SLOT(updatePIDText())); + connect(firstSIDFld, SIGNAL(valueChanged(int)), this, SLOT(updatePIDText())); + connect(generateShinyIDsButton, SIGNAL(clicked()), this, SLOT(generateShinyIDs())); } StrategyMemoEntryWidget::~StrategyMemoEntryWidget(void) { @@ -76,7 +77,7 @@ void StrategyMemoEntryWidget::parseData(void) { if (entry == NULL) return; isXD = LIBPKMGC_IS_XD(StrategyMemoEntry, entry); - generateShinyPIDButton->setVisible(!isXD); + generateShinyIDsButton->setVisible(!isXD); partialInfoCheckBox->setChecked(entry->isInfoPartial()); partialInfoCheckBox->setDisabled(isXD); @@ -98,7 +99,7 @@ void StrategyMemoEntryWidget::saveChanges(void) { void StrategyMemoEntryWidget::speciesChangeHandler(int nameIndex) { - generateShinyPIDButton->setDisabled(nameIndex == 0); + generateShinyIDsButton->setDisabled(nameIndex == 0); updatePIDText(); emit speciesChanged(entryIndex, nameIndex); } diff --git a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h index fbdbbc0..d5a54eb 100644 --- a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h +++ b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h @@ -32,7 +32,7 @@ signals: public slots: void speciesChangeHandler(int nameIndex); void updatePIDText(void); - void generateShinyPID(void); + void generateShinyIDs(void); protected: void initWidget(void); @@ -48,7 +48,7 @@ private: QVBoxLayout* PIDLayout; UnsignedSpinbox<32> *firstPIDFld; QLabel* PIDText; - QPushButton* generateShinyPIDButton; + QPushButton* generateShinyIDsButton; }; diff --git a/PkmGCSaveEditor/translations/qt/qt_fr.qm b/PkmGCSaveEditor/translations/qt/qt_fr.qm new file mode 100644 index 0000000..a3e8eaf Binary files /dev/null and b/PkmGCSaveEditor/translations/qt/qt_fr.qm differ diff --git a/PkmGCSaveEditor/translations/qt/qtbase_fr.qm b/PkmGCSaveEditor/translations/qt/qtbase_fr.qm new file mode 100644 index 0000000..8353f0a Binary files /dev/null and b/PkmGCSaveEditor/translations/qt/qtbase_fr.qm differ