diff --git a/LibPkmGC/include/LibPkmGC/Base/Pokemon.h b/LibPkmGC/include/LibPkmGC/Base/Pokemon.h index a969934..b0fa465 100644 --- a/LibPkmGC/include/LibPkmGC/Base/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/Base/Pokemon.h @@ -27,7 +27,8 @@ namespace LibPkmGC { LIBPKMGC_FWD_DECL_GC_CLS(Pokemon) - +namespace GC { class Pokemon; } +namespace GBA { class Pokemon; } namespace Base { @@ -75,7 +76,7 @@ public: char getUnownForm(void) const; bool isSecondAbilityDefined(void) const; - virtual PokemonAbilityIndex getAbility(void) const = 0; + PokemonAbilityIndex getAbility(void) const; virtual bool isEmptyOrInvalid(void) const; PokemonSpeciesIndex species; //u16 @@ -98,10 +99,12 @@ public: u16 TID; u32 PID; -// u8 encounterType; + u8 encounterType; VersionInfo version; + bool obedient; bool specialRibbons[12]; + u8 unimplementedRibbons; //u32 statusFlags; @@ -112,20 +115,36 @@ public: u8 contestStats[5]; ContestAchievementLevel contestAchievements[5]; - bool usesPartyData; // we will avoid using ptrs here + u16 unk1; + u16 unk2; + + virtual bool isEgg(void) const = 0; + virtual bool hasSecondAbility(void) const = 0; + virtual void setEggFlag(bool flag) = 0; + virtual void setSecondAbilityFlag(bool flag) = 0; + struct PokemonComputedPartyData { + s8 pkrsDaysRemaining; u16 currentHP; u8 level; PokemonStatus status; + s8 turnsOfBadPoison, turnsOfSleepRemaining; u16 stats[6]; }; PokemonComputedPartyData partyData; + + void normalizePkrs(void); + void normalizeStatus(void); + void resetPartyData(void); protected: Pokemon(Pokemon const& other); virtual void deleteFields(void); +private: + void copyNonVirtual(Pokemon const& other); + }; } diff --git a/LibPkmGC/include/LibPkmGC/Base/PokemonString.h b/LibPkmGC/include/LibPkmGC/Base/PokemonString.h index 043f4a5..73c9041 100644 --- a/LibPkmGC/include/LibPkmGC/Base/PokemonString.h +++ b/LibPkmGC/include/LibPkmGC/Base/PokemonString.h @@ -22,7 +22,40 @@ #include #include +#if defined(LIBPKMGC_SOURCE) && !defined(RET_ILSEQ) +/* Return code if invalid. (xxx_mbtowc) */ +# define RET_ILSEQ -1 +/* Return code if no bytes were read. (xxx_mbtowc) */ +//# define RET_TOOFEW -2 +# define RET_TOOFEW(c) -2 + +/* Return code if invalid. (xxx_wctomb) */ +# define RET_ILUNI -1 +/* Return code if output buffer is too small. (xxx_wctomb, xxx_reset) */ +# define RET_TOOSMALL -2 + +#endif + + namespace LibPkmGC { + +namespace Detail { + +typedef void* conv_t; +typedef u32 ucs4_t; +int ucs2be_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n); +int ucs2be_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n); +int utf8_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n); +int utf8_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n); + +int pkmgba_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char* s, int n, bool jap = false); +int pkmgba_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n, bool jap = false); + +} + + +namespace GC { class PokemonString; } namespace GBA { class PokemonString; } + namespace Base { // Commands (scroll, color etc...) are NOT supported @@ -45,8 +78,12 @@ public: virtual void load(u8* data, size_t nb) = 0; // nb: number of characters, not counting NULL virtual void save(u8* data, size_t nb) const = 0; + virtual bool isGBA(void) const = 0; + bool usesJapaneseCharset(void) const; + void setCharset(bool jap); protected: + bool _japanese; PokemonString(PokemonString const& other); @@ -55,6 +92,13 @@ protected: mutable size_t strSz, strCapacity; void resizeStr(void) const; + + u8* _data; + + size_t dataSz; + size_t dataCapacity; + + void resizeData(void); }; } diff --git a/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h index 40ef702..c1cfd65 100644 --- a/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/Colosseum/Common/Pokemon.h @@ -25,7 +25,6 @@ namespace LibPkmGC { namespace Colosseum { /* -pkm 0x00: u16 index 0x02 : u16 ? ? ? ? (0 on shadow pkm) 0x04 : u32 PID @@ -44,10 +43,11 @@ pkm 0x5c : u32 experience 0x60 : u8 currentLevel 0x61 -- 0x64 : ? ? - 0x65 : u16 status (u8 on XD) - 0x65 -- 0x67 : ? ? - 0x68 : u32 ? ? - 0x6c--0x78 ? ? + 0x65 -- 0x77: status info + 0x65 : u16 status + 0x69 : s8 turnsOfSleepRemaining + 0x6b : s8 turnsOfBadPoison + 0x74 : u32 = pokemonStatusToBitField(status, 0, turnsOfSleepRemaining) 0x78 : moves info 0x88 : u16 itemHeld 0x8a : u16 currentHP @@ -59,21 +59,27 @@ pkm 0xb7 : u8 contestsRibbons[5] 0xbc : u8 contestLuster 0xbd : u8 specialRibbons[12] - 0xc9 : u8 unused ? + 0xc9 : u8 unimplentedRibbons (max. 15) 0xca : u8 pkrsStatus 0xcb : u8 flags[3] : egg, special ability, invalid pkm + 0xce : u8 GCUnk 0xcf : u8 marks // shadow pkm data ? - 0xd0--0xd7 : ? ? + 0xd0: s8 pkrsRemainingDays + 0xd1--0xd7 : ? ? + 0xd2: u16 unk2 + 0xd4: u16 unk1 0xd8 : u16 shadowPkmID 0xda--0xdb : padding ? 0xdc : s32 purificationCounter 0xe0 : u32 expStored 0xe4 : u16 ? ? 0xe6 : u16 ? ? + 0xf8: u8 obedient 0xfb : u8 encounterType 0xfc--0x138 : ? ? ? ? ? ? ? ? ? ? */ + class LIBPKMGC_DECL Pokemon : public GC::Pokemon { public: static const size_t size = 0x138; @@ -84,14 +90,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); Pokemon(XD::Pokemon const& other); + Pokemon(GBA::Pokemon const& other); Pokemon& operator=(GC::Pokemon const& other); void swap(GC::Pokemon & other); protected: diff --git a/LibPkmGC/include/LibPkmGC/Core/Config.h b/LibPkmGC/include/LibPkmGC/Core/Config.h index 25780a8..237e057 100644 --- a/LibPkmGC/include/LibPkmGC/Core/Config.h +++ b/LibPkmGC/include/LibPkmGC/Core/Config.h @@ -19,17 +19,16 @@ #ifndef _LIBPKMGC_CONFIG_H #define _LIBPKMGC_CONFIG_H -// _LICENSE_ - #include #include #include -#define LIBPKMGC_VERSION 1000000 + +#define LIBPKMGC_VERSION 1001000 #define LIBPKMGC_VERSION_MAJOR ((LIBPKMGC_VERSION / 1000000) % 1000) #define LIBPKMGC_VERSION_MINOR ((LIBPKMGC_VERSION / 1000) % 1000) #define LIBPKMGC_VERSION_BUILD (LIBPKMGC_VERSION % 1000) -#if defined(LIBPKMGC_DYN_LIB) && !defined(LIBPKMGC_STATIC_LIB) +#if defined(LIBPKMGC_DYN_LIB) # ifdef LIBPKMGC_SOURCE # define LIBPKMGC_DECL BOOST_SYMBOL_EXPORT # else diff --git a/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h b/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h index 1229625..1158cef 100644 --- a/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h +++ b/LibPkmGC/include/LibPkmGC/Core/Detail/StructMacros.h @@ -37,29 +37,52 @@ #ifndef BUFFER_NAME #define BUFFER_NAME data #endif - +/* #ifndef TARGET_ENDIANNESS #define TARGET_ENDINANNESS BE #endif +*/ -#define SM_H_TMP_NS_EVAL() TARGET_ENDINANNESS -#define SM_H_TMP_NS() LibPkmGC::IntegerManip::SM_H_TMP_NS_EVAL() +using namespace LibPkmGC::IntegerManip; +#if !defined(TARGET_ENDIANNESS_LE) +using namespace BE; +#else +using namespace LE; +#endif -#define LD_FIELD(type,fld,off) fld = SM_H_TMP_NS()::toInteger(BUFFER_NAME+off) -#define LD_FIELD_E(type,fld,off,etype) fld = SM_H_TMP_NS()::toEnumInteger(BUFFER_NAME+off) -#define LD_FIELD_B(type,fld,off) fld = SM_H_TMP_NS()::toBoolInteger(BUFFER_NAME+off) -#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_FIELD(type,fld,off) fld = toInteger(BUFFER_NAME+off) +#define LD_FIELD_MAX(type,fld,off,mx) fld = toInteger(BUFFER_NAME+off); fld = (fld > mx) ? mx : fld +#define LD_FIELD_CONV(type,fld,off,type2) type fld##_tmp; LD_FIELD_MAX(type,fld##_tmp,off,(type2)-1); fld = (type2) fld##_tmp; +#define LD_FIELD_E(type,fld,off,etype) fld = toEnumInteger(BUFFER_NAME+off) +#define LD_FIELD_E_MAX(type,fld,off,etype,mx) fld = toEnumInteger(BUFFER_NAME+off); fld = ((u32)fld > (u32)mx) ? (etype)0 : fld +#define LD_FIELD_B(type,fld,off) fld = toBoolInteger(BUFFER_NAME+off) +#define LD_ARRAY(type,ar,sz,off) toArrayOfIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz)) +#define LD_ARRAY_MAX(type,ar,sz,off,mx) toArrayOfIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz));\ +for(size_t i__ = 0; i__ < sz; ++i__) ar[i__] = (ar[i__] > mx) ? mx : ar[i__]; +#define LD_ARRAY_CONV(type,ar,sz,off,type2) type ar##_tmp[sz]; toArrayOfIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz));\ +for(size_t i__ = 0; i__ < sz; ++i__) ar[i__] = (ar##_tmp[i__] > mx) ? mx : ar##_tmp[i__]; +#define LD_ARRAY_E(type,ar,sz,off,etype) toArrayOfEnumIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz)) +#define LD_ARRAY_E_MAX(type,ar,sz,off,etype,mx) toArrayOfEnumIntegers(ar, BUFFER_NAME+off, BUFFER_NAME+off+(sizeof(type)*sz));\ +for(size_t i__ = 0; i__ < sz; ++i__) ar[i__] = ((u32)ar[i__] > (u32)mx) ? (etype)0 : ar[i__]; +#define LD_ARRAY_B(type,ar,sz,off) 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) -#define SV_FIELD_B(type,fld,off) SM_H_TMP_NS()::fromBoolInteger(BUFFER_NAME+off, fld) -#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_FIELD(type,fld,off) fromInteger(BUFFER_NAME+off, fld) +#define SV_FIELD_MAX(type,fld,off,mx) fld = (fld > mx) ? mx : fld; fromInteger(BUFFER_NAME+off, fld) +#define SV_FIELD_CONV(type,fld,off,type2) fld = (fld > (type2)-1) ? (type2)-1 : fld; fromInteger(BUFFER_NAME+off, (type)fld) +#define SV_FIELD_E(type,fld,off,etype) fromEnumInteger(BUFFER_NAME+off, fld) +#define SV_FIELD_E_MAX(type,fld,off,etype,mx) fld = ((u32)fld > (u32)mx) ? (etype)0 : fld; fromEnumInteger(BUFFER_NAME+off, fld) +#define SV_FIELD_B(type,fld,off) fromBoolInteger(BUFFER_NAME+off, fld) +#define SV_ARRAY(type,ar,sz,off) fromArrayOfIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_ARRAY_MAX(type,ar,sz,off,mx) for(size_t i__ = 0; i__ < sz; ++i__) ar[i__] = (ar[i__] > mx) ? mx : ar[i__];\ +fromArrayOfIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_ARRAY_CONV(type,ar,sz,off,type2) type ar##_tmp[sz]; for(size_t i__=0;i__ mx) ? mx : (type) ar[i__];\ +fromArrayOfIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_ARRAY_E(type,ar,sz,off,etype) fromArrayOfEnumIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_ARRAY_E_MAX(type,ar,sz,off,etype,mx) for(size_t i__ = 0; i__ < sz; ++i__) ar[i__] = ((u32)ar[i__] > (u32)mx) ? (etype)0 : ar[i__];\ +fromArrayOfEnumIntegers(BUFFER_NAME+off, ar, ar+sz) +#define SV_ARRAY_B(type,ar,sz,off) 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) @@ -99,18 +122,20 @@ XD::cls::cls(Colosseum::cls const& other) : GC::cls(XD::cls::size){\ #define LIBPKMGC_GEN_CONVERTER_CTOR(cls) LIBPKMGC_GEN_CONVERTER_CTOR2(cls, 0) -#define LIBPKMGC_GEN_UNIMPLEMENTED_CONVERTER_CTOR(cls)\ -Colosseum::cls::cls(XD::cls const& other) : GC::cls(Colosseum::cls::size, 0){}\ -XD::cls::cls(Colosseum::cls const& other) : GC::cls(XD::cls::size, 0){} - -#define LIBPKMGC_GEN_UNIMPLEMENTED_SAVE_EDITING_CONVERTER_CTOR(cls)\ -Colosseum::SaveEditing::cls::cls(XD::SaveEditing::cls const& other) : GC::SaveEditing::cls(Colosseum::SaveEditing::cls::size, 0){}\ -XD::SaveEditing::cls::cls(Colosseum::SaveEditing::cls const& other) : GC::SaveEditing::cls(XD::SaveEditing::cls::size, 0){} +#define LIBPKMGC_GEN_CONVERTER_CTOR_W_GBA(cls) LIBPKMGC_GEN_CONVERTER_CTOR(cls)\ +XD::cls::cls(GBA::cls const& other) : GC::cls(XD::cls::size){\ + initWithEmptyData(0);\ + GC::cls::operator=(other);\ +}\ +Colosseum::cls::cls(GBA::cls const& other) : GC::cls(XD::cls::size){\ + initWithEmptyData(0);\ + GC::cls::operator=(other);\ +} #define LIBPKMGC_GC_GEN_XD_VTF2(cls, flgs) \ cls& cls::operator=(GC::cls const& other){\ if(LIBPKMGC_IS_XD(cls,&other)) return (cls&) operator=((cls const&)other);\ -else { deleteFields(); initWithEmptyData(flgs); return (cls&) GC::cls::operator=(other); }\ +else { if (this == &other) return *this; deleteFields(); initWithEmptyData(flgs); return (cls&) GC::cls::operator=(other); }\ }\ void cls::swap(GC::cls & other){\ if(LIBPKMGC_IS_XD(cls,&other)) swap((cls&)other);\ @@ -120,7 +145,7 @@ else {cls obj((Colosseum::cls&)other); swap(obj); other.swap(obj);}\ #define LIBPKMGC_GC_GEN_COL_VTF2(cls, flgs) \ cls& cls::operator=(GC::cls const& other){\ if(LIBPKMGC_IS_COLOSSEUM(cls,&other)) return operator=((cls const&)other);\ -else { deleteFields(); initWithEmptyData(flgs); return (cls&) GC::cls::operator=(other); }\ +else { if (this == &other) return *this; deleteFields(); initWithEmptyData(flgs); return (cls&) GC::cls::operator=(other); }\ }\ void cls::swap(GC::cls & other){\ if(LIBPKMGC_IS_COLOSSEUM(cls,&other)) swap((cls&)other);\ @@ -169,7 +194,7 @@ template bool test_isXD_or_true(T* obj, typename boost::enable_iffixedSize == LibPkmGC::XD::cls::size && LibPkmGC::Detail::test_isXD_or_true(obj)) #define LIBPKMGC_IS_COLOSSEUM(cls, obj) ((obj)->fixedSize == LibPkmGC::Colosseum::cls::size && LibPkmGC::Detail::test_isXD_or_true(obj)) - +#define LIBPKMGC_IS_GBA(cls, obj) ((obj)->fixedSize == LibPkmGC::GBA::cls::size) #endif diff --git a/LibPkmGC/include/LibPkmGC/Core/IntegerManip.h b/LibPkmGC/include/LibPkmGC/Core/IntegerManip.h index 38e683c..527ead3 100644 --- a/LibPkmGC/include/LibPkmGC/Core/IntegerManip.h +++ b/LibPkmGC/include/LibPkmGC/Core/IntegerManip.h @@ -47,7 +47,7 @@ I toInteger(IteratorType const& _bufferIterator, size_t _integerSize = sizeof(I) size_t shift = 0; IteratorType it(_bufferIterator); for (; shift <= 8 * _integerSize - 8; ++it) { - ret |= (I(*it) << shift); + ret |= (I((u8)*it) << shift); shift += 8; } return ret; @@ -80,7 +80,7 @@ I toInteger(IteratorType const& _bufferIterator, size_t _integerSize = sizeof(I) IteratorType it(_bufferIterator); size_t shift = 8 * _integerSize - 8; for (;; ++it) { - ret |= (I(*it) << shift); + ret |= (I((u8)*it) << shift); if (shift == 0) break; else shift -= 8; } diff --git a/LibPkmGC/include/LibPkmGC/Core/PokemonInfo.h b/LibPkmGC/include/LibPkmGC/Core/PokemonInfo.h index 9f03de7..e0d6388 100644 --- a/LibPkmGC/include/LibPkmGC/Core/PokemonInfo.h +++ b/LibPkmGC/include/LibPkmGC/Core/PokemonInfo.h @@ -31,8 +31,9 @@ struct LIBPKMGC_DECL VersionInfo { LanguageIndex language; void load(u8* data); - + void load(u8 lg, u8 gm); // GBA void save(u8* data); + void save(u8& lg, u8& gm); // GBA bool isIncomplete(void) const; @@ -964,6 +965,9 @@ enum PokemonStatus { NoStatus = 0, Poisoned = 3, BadlyPoisoned = 4, Paralyzed = 5, Burnt = 6, Frozen = 7, Asleep = 8 }; +LIBPKMGC_DECL u16 pokemonStatusToBitField(PokemonStatus status, s8 turnsOfBadPoison = 0, s8 turnsOfSleepRemaining = 0); +LIBPKMGC_DECL PokemonStatus pokemonStatusFromBitField(u16 status, s8* turnsOfBadPoison = NULL, s8* turnsOfSleepRemaining = NULL); + struct LIBPKMGC_DECL PokemonSpeciesData { bool isValid; PokemonSpeciesExpGrowthType expGrowthType; @@ -983,7 +987,7 @@ LIBPKMGC_DECL u16 getPokedexIndexOf(PokemonSpeciesIndex speciesIndex); LIBPKMGC_DECL PokemonSpeciesIndex getSpeciesIndexOf(u16 pokedexIndex); struct LIBPKMGC_DECL PokemonMove { - PokemonMoveIndex moveIndex; + PokemonMoveIndex move; u8 currentPPs; u8 nbPPUpsUsed; diff --git a/LibPkmGC/include/LibPkmGC/GBA/Pokemon.h b/LibPkmGC/include/LibPkmGC/GBA/Pokemon.h new file mode 100644 index 0000000..b25dd3b --- /dev/null +++ b/LibPkmGC/include/LibPkmGC/GBA/Pokemon.h @@ -0,0 +1,75 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#ifndef _LIBPKMGC_GBA_POKEMON_H +#define _LIBPKMGC_GBA_POKEMON_H +#include +#include + + +namespace LibPkmGC { +namespace GBA { +class LIBPKMGC_DECL Pokemon : + public Base::Pokemon { +public: + static const size_t size = 100; + Pokemon(const u8* inData, u32 flags = 0); + static Pokemon* load80(const u8* inData, u32 flags = 0); + Pokemon(void); + Pokemon(Pokemon const& other); + ~Pokemon(void); + Pokemon* clone(void) const; + Pokemon* create(void) const; + + bool checkChecksum(bool fix = false); + bool isEmptyOrInvalid(void) const; + void save(void); + void saveEncrypted(u8* outData); + + void swap(Pokemon& other); + Pokemon& operator=(Pokemon const& other); + bool hasSecondAbility(void) const; + void setSecondAbilityFlag(bool status); + bool isEgg(void) const; + void setEggFlag(bool status); + + void swap(Base::Pokemon& other); + Pokemon& operator=(Base::Pokemon const& other); + Pokemon(GC::Pokemon const& other); + + void reload(const u8* data = NULL, u32 inFlags = 0); + + u16 checksum; + u8 GCFlags; + +protected: + void load(u32 flags = 0); + void loadData(u32 flags = 0); + void loadFields(void); + +private: + u32 _flags; + bool _egg, _secondAbility; + + void decryptOrEncrypt(u8* outData); +}; + +} +} + +#endif \ No newline at end of file diff --git a/LibPkmGC/include/LibPkmGC/GBA/PokemonString.h b/LibPkmGC/include/LibPkmGC/GBA/PokemonString.h new file mode 100644 index 0000000..fb78fba --- /dev/null +++ b/LibPkmGC/include/LibPkmGC/GBA/PokemonString.h @@ -0,0 +1,55 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#ifndef _LIBPKMGC_GBA_POKEMON_STRING_H +#define _LIBPKMGC_GBA_POKEMON_STRING_H + +#include + +namespace LibPkmGC { +namespace GBA { + +class LIBPKMGC_DECL PokemonString : public Base::PokemonString { +public: + PokemonString(const char* str = NULL, bool jap = false); + PokemonString(u8* data, size_t nb, bool jap = false); + PokemonString(PokemonString const& other); + ~PokemonString(void); + + bool isGBA(void) const; + PokemonString* clone(void) const; + PokemonString* create(void) const; + + PokemonString& operator=(PokemonString const& other); + PokemonString& operator=(Base::PokemonString const& other); + + void fromUTF8(const char* str = NULL); + const char* toUTF8(void) const; + + size_t size(void) const; + void load(u8* data, size_t nb); + void save(u8* data, size_t nb) const; + + PokemonString(GC::PokemonString const& other); + +}; + +} +} + +#endif diff --git a/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h index bb805aa..006ac40 100644 --- a/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/GC/Common/Pokemon.h @@ -38,15 +38,18 @@ public: virtual void swap(Pokemon& other); virtual Pokemon& operator=(Pokemon const& other); - /*bool hasSecondAbility(void) const; - void setSpecialAbilityStatus(bool status);*/ - virtual PokemonAbilityIndex getAbility(void) const; + bool hasSecondAbility(void) const; + void setSecondAbilityFlag(bool status); + bool isEgg(void) const; + void setEggFlag(bool status); + u8 GCUnk; u16 shadowPkmID; bool pkmFlags[3]; - virtual void swap(Base::Pokemon& other); - virtual Pokemon& operator=(Base::Pokemon const& other); + void swap(Base::Pokemon& other); + Pokemon& operator=(Base::Pokemon const& other); + protected: Pokemon(Pokemon const& other); }; diff --git a/LibPkmGC/include/LibPkmGC/GC/Common/PokemonString.h b/LibPkmGC/include/LibPkmGC/GC/Common/PokemonString.h index 6bd760f..d65721c 100644 --- a/LibPkmGC/include/LibPkmGC/GC/Common/PokemonString.h +++ b/LibPkmGC/include/LibPkmGC/GC/Common/PokemonString.h @@ -44,12 +44,10 @@ public: void load(u8* data, size_t nb); void save(u8* data, size_t nb) const; -private: - u8* _data; - size_t dataSz; - size_t dataCapacity; + bool isGBA(void) const; + + PokemonString(GBA::PokemonString const& other); - void resizeData(void); }; } diff --git a/LibPkmGC/include/LibPkmGC/GC/Common/StrategyMemoData.h b/LibPkmGC/include/LibPkmGC/GC/Common/StrategyMemoData.h index c1321b9..bdce150 100644 --- a/LibPkmGC/include/LibPkmGC/GC/Common/StrategyMemoData.h +++ b/LibPkmGC/include/LibPkmGC/GC/Common/StrategyMemoData.h @@ -51,8 +51,8 @@ public: virtual void save(void); - virtual bool registerSpecies(PokemonSpeciesIndex index, u32 PID, u16 SID = 0, u16 TID = 0); - virtual bool registerSpecies(Pokemon* pkm); + virtual size_t registerSpecies(PokemonSpeciesIndex index, u32 PID, u16 SID = 0, u16 TID = 0); + virtual size_t registerSpecies(Pokemon* pkm); void deleteEntry(size_t index); size_t recount(void) const; diff --git a/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h b/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h index a96f450..472024d 100644 --- a/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h +++ b/LibPkmGC/include/LibPkmGC/XD/Common/Pokemon.h @@ -25,12 +25,15 @@ namespace LibPkmGC { namespace XD { /* - 0x00: u16 species - 0x02: u16 itemHeld +XD::Pokemon + 0x00: u16 species (cf PokemonInfo.h) + 0x03: u8 itemHeld (ItemInfo.h) 0x04: u16 currentHP 0x06: u16 happiness 0x08: u16 locationCaught // 0x09 -- 0xd :: ?? + 0x0a: u16 unk1 + 0x0c: u16 unk2 0x0e: u8 levelMet 0x0f: u8 ballCaughtWith 0x10: u8 OTGender (00 male 01 female 02 genderless=none) @@ -38,11 +41,12 @@ namespace XD { 0x12: u8 Contest Luster 0x13: u8 pkrsStatus 0x14: u8 marks (bitfield) - 0x15: 0xff ? + 0x15: s8 pkrsRemainingDays 0x16: u16 status (3 psn, 4 psn (toxic ?), 5 par, 6 brn, 7 frzn, 8 slp) - 0x17: ? - 0x18 -- 0x1b : ? (0x50) - 0x1d: u8 pkmFlags + 0x17: s8 turnsOfBadPoison (max 15.) + 0x18: s8 turnsOfSleepRemaining (max. 8) + 0x19 -- 0x1b : ? (0x50) + 0x1d: u8 pkmFlags | XDPkmFlags 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") @@ -54,15 +58,16 @@ namespace XD { 0x24: u16 SID 0x26: u16 TID 0x28: u32 PID - 0x2c: u32 12_status_bits appended at the most significant positions ... - 0x32 : ?? (0 on shadow pkm) + 0x2c: u32 = pokemonStatusToBitField(status, 0, turnsOfSleepRemaining) + 0x30: u8 obedient + 0x31, 0x32: ?? 0x33: u8 encounterType 0x34 -- 0x37 : Version info (actual region, original region, original language) 0x38: GC::PokemonString OTName (10+1 chars = 22 bytes) 0x4e: GC::PokemonString name (10+1 chars) 0x64: pkm name backup 0x7a -- 0x7b: ?? - 0x7c: u16 specialRibbons + 0x7c: u16 specialRibbons | unimplementedRibbons 0x7e -- 0x7f: ?? 0x80: moves[4]{u16 moveID, u8 basePP (?), u8 nbPPUps} 0x90: u16 stats[6] @@ -74,8 +79,7 @@ namespace XD { 0xb8 : u16 ?? 0xba: shadow pkm id 0xbc -- 0xbf : ??? ???? - 0xc0 -- 0xc1 : unused ? - 0xc2: party identify (lead = 00, 01 otherwise) + 0xc0 -- 0xc4 : unused ? */ class LIBPKMGC_DECL Pokemon : public GC::Pokemon { @@ -87,18 +91,19 @@ public: ~Pokemon(void); Pokemon* clone(void) const; Pokemon* create(void) const; - + bool isEmptyOrInvalid(void) const; void save(void); void swap(Pokemon& other); Pokemon& operator=(Pokemon const& other); Pokemon(Colosseum::Pokemon const& other); + Pokemon(GBA::Pokemon const& other); Pokemon& operator=(GC::Pokemon const& other); void swap(GC::Pokemon & other); bool XDPkmFlags[3]; -private: +protected: void loadFields(void); }; diff --git a/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp index 5019c80..c3b7968 100644 --- a/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/Base/Pokemon.cpp @@ -17,6 +17,7 @@ */ #include +#include namespace LibPkmGC { @@ -69,11 +70,11 @@ u32 Pokemon::fixExperienceProportionally(PokemonSpeciesIndex oldSpecies, u32 old } -Pokemon::Pokemon(size_t inSize, const u8* inData) : DataStruct(inSize, inData) { +Pokemon::Pokemon(size_t inSize, const u8* inData) : DataStruct(inSize, inData) { } Pokemon::~Pokemon(void) { - + Pokemon::deleteFields(); } @@ -154,25 +155,52 @@ char Pokemon::getUnownForm(void) const { return getUnownForm(PID); } +void Pokemon::normalizePkrs(void) { + u8 st = pkrsStatus & 0xf, dr = pkrsStatus >> 4; + if (st == 0) { + dr = 0; + partyData.pkrsDaysRemaining = -1; + } + else { + u8 mx = 1 + (st & 0x3); + dr = (dr > mx) ? mx : dr; + partyData.pkrsDaysRemaining = (partyData.pkrsDaysRemaining > mx) ? mx : partyData.pkrsDaysRemaining; + } + + pkrsStatus = (dr << 4) | st; +} + +void Pokemon::normalizeStatus(void) { + partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; + if (partyData.status != Asleep) partyData.turnsOfSleepRemaining = 0; + if (partyData.status != BadlyPoisoned) partyData.turnsOfBadPoison = 0; + partyData.turnsOfSleepRemaining = ((u8)partyData.turnsOfSleepRemaining > 7) ? 7 : partyData.turnsOfSleepRemaining; + partyData.turnsOfBadPoison = ((u8)partyData.turnsOfBadPoison > 15) ? 15 : partyData.turnsOfBadPoison; +} + void Pokemon::resetPartyData(void) { updateStats(); updateLevelFromExp(); partyData.status = NoStatus; partyData.currentHP = partyData.stats[0]; + partyData.pkrsDaysRemaining = ((pkrsStatus & 0xf0) != 0) ? pkrsStatus & 0xf : -1; } -Pokemon::Pokemon(Pokemon const & other) : Base::DataStruct(other.fixedSize, NULL, true){ - data = new u8[other.fixedSize]; - Pokemon::operator=(other); +Pokemon::Pokemon(Pokemon const & other) : Base::DataStruct(other), OTName(NULL), name(NULL){ + copyNonVirtual(other); } void Pokemon::deleteFields(void){ + delete OTName; + delete name; +} + + +PokemonAbilityIndex Pokemon::getAbility(void) const { + const PokemonSpeciesData dt = getThisSpeciesData(); + return (hasSecondAbility()) ? dt.possibleAbilities[1] : dt.possibleAbilities[0]; } -/* -void Pokemon::calculateSpindaSpotsPosition(std::pair[4]) const{ - u32 PID_2 = PID; -}*/ bool Pokemon::isEmptyOrInvalid(void) const { - return (species > 0x19e) || (species == 0) || !getSpeciesData(species).isValid || version.isIncomplete(); + return (species >= Bonsly) || (species == NoSpecies) || !getSpeciesData(species).isValid || version.isIncomplete(); } bool Pokemon::isSecondAbilityDefined(void) const{ @@ -188,8 +216,20 @@ void Pokemon::swap(Pokemon& other) { SW(ballCaughtWith); SW(levelMet); SW(OTGender); - SW(OTName); - SW(name); + if (OTName->isGBA() == other.OTName->isGBA()) + SW(OTName); + else { + std::string s1(OTName->toUTF8()); + OTName->fromUTF8(other.OTName->toUTF8()); + other.OTName->fromUTF8(s1.c_str()); + } + if (name->isGBA() == other.name->isGBA()) + SW(name); + else { + std::string s1(name->toUTF8()); + name->fromUTF8(other.name->toUTF8()); + other.name->fromUTF8(s1.c_str()); + } SW(contestLuster); SW(pkrsStatus); SW(markings); @@ -199,13 +239,14 @@ void Pokemon::swap(Pokemon& other) { SW(TID); SW(PID); -// SW(encounterType); + if(fixedSize == other.fixedSize) SW(encounterType); SW(version); - SW(usesPartyData); SW(partyData); - SW_ARRAY(specialRibbons, 12) + SW_ARRAY(specialRibbons, 12); + SW(unimplementedRibbons); + SW_ARRAY(moves, 4); SW_ARRAY(EVs, 6); SW_ARRAY(IVs, 6); @@ -213,43 +254,63 @@ void Pokemon::swap(Pokemon& other) { SW_ARRAY(contestStats, 5); SW_ARRAY(contestAchievements, 5); + SW(obedient); + SW(unk1); + SW(unk2); + + bool e1 = isEgg(), e2 = other.isEgg(); + setEggFlag(e2); other.setEggFlag(e1); + + bool a1 = hasSecondAbility(), a2 = other.hasSecondAbility(); + setSecondAbilityFlag(a2); setSecondAbilityFlag(a1); +} + +void Pokemon::copyNonVirtual(Pokemon const& other) { + //Pokemon::deleteFields(); nope, don't do that + CP(species); + CP(heldItem); + + CP(happiness); + CP(locationCaught); + CP(ballCaughtWith); + CP(levelMet); + CP(OTGender); + CP(contestLuster); + CP(pkrsStatus); + CP(markings); + + CP(experience); + CP(SID); + CP(TID); + CP(PID); + + if (fixedSize == other.fixedSize) CP(encounterType); + CP(version); + + CP_ARRAY(specialRibbons, 12); + CP(unimplementedRibbons); + + CP_ARRAY(moves, 4); + CP_ARRAY(EVs, 6); + CP_ARRAY(IVs, 6); + + CP(partyData); + CP_ARRAY(contestStats, 5); + CP_ARRAY(contestAchievements, 5); + + CP(obedient); + CP(unk1); + CP(unk2); } Pokemon & Pokemon::operator=(Pokemon const & other){ if (this != &other) { Base::DataStruct::operator=(other); - CP(species); - CP(heldItem); - - CP(happiness); - CP(locationCaught); - CP(ballCaughtWith); - CP(levelMet); - CP(OTGender); - CL(OTName) - CL(name); - CP(contestLuster); - CP(pkrsStatus); - CP(markings); - - CP(experience); - CP(SID); - CP(TID); - CP(PID); - - // CP(encounterType); - CP(version); - - CP_ARRAY(specialRibbons, 12); - CP(usesPartyData); - CP(partyData); - - CP_ARRAY(moves, 4); - CP_ARRAY(EVs, 6); - CP_ARRAY(IVs, 6); - - CP_ARRAY(contestStats, 5); - CP_ARRAY(contestAchievements, 5); + copyNonVirtual(other); + *OTName = *(other.OTName); + *name = *(other.name); + setEggFlag(other.isEgg()); + setSecondAbilityFlag(other.hasSecondAbility()); } return *this; } diff --git a/LibPkmGC/src/LibPkmGC/Base/PokemonBox.cpp b/LibPkmGC/src/LibPkmGC/Base/PokemonBox.cpp index a1cf97e..95c6625 100644 --- a/LibPkmGC/src/LibPkmGC/Base/PokemonBox.cpp +++ b/LibPkmGC/src/LibPkmGC/Base/PokemonBox.cpp @@ -26,6 +26,7 @@ PokemonBox::PokemonBox(size_t inSize, const u8* inData) : DataStruct(inSize, inD } void PokemonBox::deleteFields(void) { + delete name; for (size_t i = 0; i < 30; ++i) delete pkm[i]; } diff --git a/LibPkmGC/src/LibPkmGC/Base/PokemonString.cpp b/LibPkmGC/src/LibPkmGC/Base/PokemonString.cpp index 7110ae7..3b7f08c 100644 --- a/LibPkmGC/src/LibPkmGC/Base/PokemonString.cpp +++ b/LibPkmGC/src/LibPkmGC/Base/PokemonString.cpp @@ -16,25 +16,53 @@ * along with LibPkmGC. If not, see . */ +// This file uses code from libiconv : + +/* +* Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc. +* This file is part of the GNU LIBICONV Library. +* +* The GNU LIBICONV Library is free software; you can redistribute it +* and/or modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* The GNU LIBICONV Library is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with the GNU LIBICONV Library; see the file COPYING.LIB. +* If not, write to the Free Software Foundation, Inc., 51 Franklin Street, +* Fifth Floor, Boston, MA 02110-1301, USA. +*/ + #include namespace LibPkmGC { namespace Base { -PokemonString::PokemonString(void) : hasChanged(true), _str(NULL), strSz(0), strCapacity(0) { +PokemonString::PokemonString(void) : hasChanged(true), _str(NULL), strSz(0), strCapacity(0), _data(NULL), dataSz(0), dataCapacity(0), _japanese(false) { } PokemonString::~PokemonString(void) { + delete[] _str; + delete[] _data; } PokemonString & PokemonString::operator=(PokemonString const & other) { if (this != &other) { - hasChanged = other.hasChanged; + assert(isGBA() == other.isGBA()); + hasChanged = true; strSz = other.strSz; + _japanese = other._japanese; + dataSz = other.dataSz; + dataCapacity = other.dataCapacity; resizeStr(); - std::copy(other._str, other._str + strSz, _str); - + resizeData(); + std::copy(other._data, other._data + dataSz, _data); } return *this; } @@ -44,9 +72,20 @@ PokemonString::operator const char*(void) const { return toUTF8(); } -PokemonString::PokemonString(PokemonString const & other) : hasChanged(other.hasChanged), strSz(other.strSz), strCapacity(0), _str(NULL) { +bool PokemonString::usesJapaneseCharset(void) const { + return _japanese; +} + +void PokemonString::setCharset(bool jap) { + if (jap != _japanese) hasChanged = true; + _japanese = jap; +} + +PokemonString::PokemonString(PokemonString const & other) : hasChanged(true), strSz(other.strSz), strCapacity(0), _str(NULL), +dataSz(other.dataSz), dataCapacity(0), _data(NULL), _japanese(other._japanese) { resizeStr(); - std::copy(other._str, other._str + strSz, _str); + resizeData(); + std::copy(other._data, other._data + dataSz, _data); } void PokemonString::resizeStr(void) const { @@ -57,5 +96,132 @@ void PokemonString::resizeStr(void) const { } } +void PokemonString::resizeData(void) { + if (dataCapacity < dataSz) { + dataCapacity = dataSz; + delete[] _data; + _data = new u8[dataSz]; + } +} + +} + +namespace Detail { + +/* +* UTF-8 +*/ + +/* Specification: RFC 3629 */ + +int +utf8_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n) +{ + unsigned char c = s[0]; + + if (c < 0x80) { + *pwc = c; + return 1; + } + else if (c < 0xc2) { + return RET_ILSEQ; + } + else if (c < 0xe0) { + if (n < 2) + return RET_TOOFEW(0); + if (!((s[1] ^ 0x80) < 0x40)) + return RET_ILSEQ; + *pwc = ((ucs4_t)(c & 0x1f) << 6) + | (ucs4_t)(s[1] ^ 0x80); + return 2; + } + else if (c < 0xf0) { + if (n < 3) + return RET_TOOFEW(0); + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (c >= 0xe1 || s[1] >= 0xa0))) + return RET_ILSEQ; + *pwc = ((ucs4_t)(c & 0x0f) << 12) + | ((ucs4_t)(s[1] ^ 0x80) << 6) + | (ucs4_t)(s[2] ^ 0x80); + return 3; + } + else if (c < 0xf8 && sizeof(ucs4_t) * 8 >= 32) { + if (n < 4) + return RET_TOOFEW(0); + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 + && (c >= 0xf1 || s[1] >= 0x90))) + return RET_ILSEQ; + *pwc = ((ucs4_t)(c & 0x07) << 18) + | ((ucs4_t)(s[1] ^ 0x80) << 12) + | ((ucs4_t)(s[2] ^ 0x80) << 6) + | (ucs4_t)(s[3] ^ 0x80); + return 4; + } + else if (c < 0xfc && sizeof(ucs4_t) * 8 >= 32) { + if (n < 5) + return RET_TOOFEW(0); + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 + && (c >= 0xf9 || s[1] >= 0x88))) + return RET_ILSEQ; + *pwc = ((ucs4_t)(c & 0x03) << 24) + | ((ucs4_t)(s[1] ^ 0x80) << 18) + | ((ucs4_t)(s[2] ^ 0x80) << 12) + | ((ucs4_t)(s[3] ^ 0x80) << 6) + | (ucs4_t)(s[4] ^ 0x80); + return 5; + } + else if (c < 0xfe && sizeof(ucs4_t) * 8 >= 32) { + if (n < 6) + return RET_TOOFEW(0); + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 + && (s[5] ^ 0x80) < 0x40 + && (c >= 0xfd || s[1] >= 0x84))) + return RET_ILSEQ; + *pwc = ((ucs4_t)(c & 0x01) << 30) + | ((ucs4_t)(s[1] ^ 0x80) << 24) + | ((ucs4_t)(s[2] ^ 0x80) << 18) + | ((ucs4_t)(s[3] ^ 0x80) << 12) + | ((ucs4_t)(s[4] ^ 0x80) << 6) + | (ucs4_t)(s[5] ^ 0x80); + return 6; + } + else + return RET_ILSEQ; +} + +int +utf8_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n) /* n == 0 is acceptable */ { + int count; + if (wc < 0x80) + count = 1; + else if (wc < 0x800) + count = 2; + else if (wc < 0x10000) + count = 3; + else if (wc < 0x200000) + count = 4; + else if (wc < 0x4000000) + count = 5; + else if (wc <= 0x7fffffff) + count = 6; + else + return RET_ILUNI; + if (n < count) + return RET_TOOSMALL; + switch (count) { /* note: code falls through cases! */ + case 6: r[5] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x4000000; + case 5: r[4] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x200000; + case 4: r[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000; + case 3: r[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800; + case 2: r[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0; + case 1: r[0] = wc; + } + return count; +} + } } diff --git a/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp index 92a28ea..f0730ac 100644 --- a/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/Colosseum/Common/Pokemon.cpp @@ -32,17 +32,17 @@ Pokemon::~Pokemon(void) { 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; } +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); } -bool Pokemon::isEmptyOrInvalid(void) const { - return GC::Pokemon::isEmptyOrInvalid() && species == Bonsly; -} - void Pokemon::loadFields(void) { u8 marksTmp = 0; @@ -50,27 +50,31 @@ void Pokemon::loadFields(void) { LD_FIELD(u32, PID, 0x04); version.load(data + 0x08); - u16 locationCaught_tmp; - LD_FIELD(u16, locationCaught_tmp, 0x0c); - locationCaught = (u8)((locationCaught_tmp > 255) ? 255 : locationCaught_tmp); + LD_FIELD_CONV(u16, locationCaught, 0x0c, u8); - LD_FIELD(u8, levelMet, 0x0e); - LD_FIELD_E(u8, ballCaughtWith, 0x0f, ItemIndex); - LD_FIELD_E(u8, OTGender, 0x10, Gender); + LD_FIELD_MAX(u8, levelMet, 0x0e, 100); + LD_FIELD_E_MAX(u8, ballCaughtWith, 0x0f, ItemIndex, PremierBall); + if (ballCaughtWith == NoItem) ballCaughtWith = PokeBall; + LD_FIELD_E_MAX(u8, OTGender, 0x10, Gender, Female); LD_FIELD(u16, SID, 0x14); LD_FIELD(u16, TID, 0x16); OTName = new GC::PokemonString(data + 0x18, 10); name = new GC::PokemonString(data + 0x2e, 10); - LD_FIELD(u32, experience, 0x5c); + LD_FIELD_MAX(u32, experience, 0x5c, getSpeciesExpTable(species)[100]); LD_FIELD(u8, partyData.level, 0x60); if (partyData.level > 100) partyData.level = 100; + LD_FIELD_E(u16, partyData.status, 0x65, PokemonStatus); - partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; + LD_FIELD(s8, partyData.turnsOfSleepRemaining, 0x69); + LD_FIELD(s8, partyData.turnsOfBadPoison, 0x6b); + u32 st = pokemonStatusToBitField(partyData.status, 0, partyData.turnsOfSleepRemaining); + SV_FIELD(u32, st, 0x74); + LD_FIELD_E(u16, heldItem, 0x88, ItemIndex); - - LD_FIELD(u16, partyData.currentHP, 0x8a); + LD_ARRAY(u16, partyData.stats, 6, 0x8c); + LD_FIELD_MAX(u16, partyData.currentHP, 0x8a, partyData.stats[0]); u16 EVs_tmp[6]; // EVs are internally stored as u16 LD_ARRAY(u16, EVs_tmp, 6, 0x98); @@ -79,56 +83,64 @@ void Pokemon::loadFields(void) { LD_ARRAY(u8, IVs, 6, 0xa4); for (size_t i = 0; i < 6; ++i) IVs[i] = (IVs[i] > 31) ? 31 : IVs[i]; - u16 happiness_tmp; - LD_FIELD(u16, happiness_tmp, 0xb0); - happiness = (happiness_tmp > 255) ? 255 : happiness_tmp; - + LD_FIELD_CONV(u16, happiness, 0xb0, u8); LD_ARRAY(u8, contestStats, 5, 0xb2); - LD_ARRAY_E(u8, contestAchievements, 5, 0xb7, ContestAchievementLevel); + LD_ARRAY_E_MAX(u8, contestAchievements, 5, 0xb7, ContestAchievementLevel, MasterContestWon); LD_FIELD(u8, contestLuster, 0xbc); LD_ARRAY_B(u8, specialRibbons, 12, 0xbd); + LD_FIELD_MAX(u8, unimplementedRibbons, 0xc9, 15); LD_FIELD(u8, pkrsStatus, 0xca); LD_ARRAY_B(u8, pkmFlags, 3, 0xcb); - + LD_FIELD(u8, GCUnk, 0xce); LD_FIELD(u8, marksTmp, 0xcf); - + LD_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 0xd0, 4); + LD_FIELD(u16, unk2, 0xd2); + LD_FIELD(u16, unk1, 0xd4); LD_FIELD(u16, shadowPkmID, 0xd8); - + SV_FIELD_B(u8, obedient, 0xf8); + SV_FIELD(u8, encounterType, 0xfb); 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]; + normalizePkrs(); + normalizeStatus(); } void Pokemon::save(void) { + normalizePkrs(); + normalizeStatus(); + 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); - SV_FIELD(u16, (u16) locationCaught, 0x0c); - SV_FIELD(u8, levelMet, 0x0e); - SV_FIELD_E(u8, ballCaughtWith, 0x0f, ItemIndex); - SV_FIELD_E(u8, OTGender, 0x10, Gender); + SV_FIELD_CONV(u16, locationCaught, 0x0c, u8); + SV_FIELD_MAX(u8, levelMet, 0x0e, 100); + if (ballCaughtWith == NoItem || ballCaughtWith > PremierBall) ballCaughtWith = PokeBall; + SV_FIELD_E_MAX(u8, ballCaughtWith, 0x0f, ItemIndex, PremierBall); + SV_FIELD_E_MAX(u8, OTGender, 0x10, Gender, Female); SV_FIELD(u16, SID, 0x14); SV_FIELD(u16, TID, 0x16); OTName->save(data + 0x18, 10); name->save(data + 0x2e, 10); name->save(data + 0x44, 10); - SV_FIELD(u32, experience, 0x5c); + SV_FIELD_MAX(u32, experience, 0x5c, getSpeciesExpTable(species)[100]); if (partyData.level > 100) partyData.level = 100; SV_FIELD(u8, partyData.level, 0x60); - partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; SV_FIELD_E(u16, partyData.status, 0x65, PokemonStatus); + LD_FIELD(s8, partyData.turnsOfSleepRemaining, 0x69); + LD_FIELD(s8, partyData.turnsOfBadPoison, 0x6b); SV_FIELD_E(u16, heldItem, 0x88, ItemIndex); - SV_FIELD(u16, partyData.currentHP, 0x8a); + SV_FIELD_MAX(u16, partyData.currentHP, 0x8a, partyData.stats[0]); SV_ARRAY(u16, partyData.stats, 6, 0x8c); u16 EVs_tmp[6]; for (size_t i = 0; i < 6; ++i) EVs_tmp[i] = (u16)EVs[i]; @@ -142,14 +154,20 @@ void Pokemon::save(void) { SV_FIELD(u8, contestLuster, 0xbc); SV_ARRAY_B(u8, specialRibbons, 12, 0xbd); + SV_FIELD_MAX(u8, unimplementedRibbons, 0xc9, 15); + SV_FIELD(u8, pkrsStatus, 0xca); LD_ARRAY_B(u8, pkmFlags, 3, 0xcb); - + SV_FIELD_MAX(u8, GCUnk, 0xce, 31); SV_FIELD(u8, markings.save(), 0xcf); - + SV_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 0xd0, 4); + SV_FIELD(u16, unk2, 0xd2); + SV_FIELD(u16, unk1, 0xd4); SV_FIELD(u16, shadowPkmID, 0xd8); + SV_FIELD_B(u8, obedient, 0xf8); + SV_FIELD(u8, encounterType, 0xfb); for (size_t i = 0; i < 4; ++i) moves[i].save(data + 0x78 + 4 * i); diff --git a/LibPkmGC/src/LibPkmGC/Core/PokemonInfo.cpp b/LibPkmGC/src/LibPkmGC/Core/PokemonInfo.cpp index 71c8e10..f536056 100644 --- a/LibPkmGC/src/LibPkmGC/Core/PokemonInfo.cpp +++ b/LibPkmGC/src/LibPkmGC/Core/PokemonInfo.cpp @@ -4,17 +4,45 @@ namespace LibPkmGC{ void VersionInfo::load(u8 * data) { - LD_FIELD_E(u8, game, 0, GameIndex); - LD_FIELD_E(u8, currentRegion, 1, RegionIndex); - LD_FIELD_E(u8, originalRegion, 2, RegionIndex); - LD_FIELD_E(u8, language, 3, LanguageIndex); + LD_FIELD_E_MAX(u8, game, 0, GameIndex, Colosseum_XD); + LD_FIELD_E_MAX(u8, currentRegion, 1, RegionIndex, PAL); + LD_FIELD_E_MAX(u8, originalRegion, 2, RegionIndex, PAL); + LD_FIELD_E_MAX(u8, language, 3, LanguageIndex, Spanish); +} + +void VersionInfo::load(u8 lg, u8 gm) { + const LanguageIndex L[] = { NoLanguage, Japanese, English, French, Italian, German, NoLanguage, Spanish }; + GameIndex G[16] = { NoGame, Sapphire, Ruby, Emerald, FireRed, LeafGreen }; G[15] = Colosseum_XD; + + game = G[(gm > 7) ? 0 : gm]; + language = L[(lg > 15) ? 0 : lg]; + + switch (language){ + case Japanese: originalRegion = NTSC_J; break; + case English: originalRegion = NTSC_U; break; + case NoLanguage: originalRegion = NoRegion; break; + default: originalRegion = PAL; break; + } + currentRegion = originalRegion; + +} + +void VersionInfo::save(u8& lg, u8& gm) { + const u8 L[] = { 0, 1, 2, 5, 3, 4, 7 }; + u8 G[12] = { 0, 4, 5, 1, 2, 3 }; G[11] = 15; + + game = (game > Colosseum_XD) ? NoGame : game; + language = (language > Spanish) ? NoLanguage : language; + + gm = G[game]; + lg = L[language]; } void VersionInfo::save(u8 * data) { - SV_FIELD_E(u8, game, 0, GameIndex); - SV_FIELD_E(u8, currentRegion, 1, RegionIndex); - SV_FIELD_E(u8, originalRegion, 2, RegionIndex); - SV_FIELD_E(u8, language, 3, LanguageIndex); + SV_FIELD_E_MAX(u8, game, 0, GameIndex, Colosseum_XD); + SV_FIELD_E_MAX(u8, currentRegion, 1, RegionIndex, PAL); + SV_FIELD_E_MAX(u8, originalRegion, 2, RegionIndex, PAL); + SV_FIELD_E_MAX(u8, language, 3, LanguageIndex, Spanish); } bool VersionInfo::isIncomplete(void) const { @@ -22,19 +50,19 @@ bool VersionInfo::isIncomplete(void) const { } void PokemonMove::load(u8 * data) { - LD_FIELD_E(u16, moveIndex, 0, PokemonMoveIndex); + LD_FIELD_E_MAX(u16, move, 0, PokemonMoveIndex, PsychoBoost); LD_FIELD(u8, currentPPs, 2); - LD_FIELD(u8, nbPPUpsUsed, 3); + LD_FIELD_MAX(u8, nbPPUpsUsed, 3, 3); } void PokemonMove::save(u8 * data) { - SV_FIELD_E(u16, moveIndex, 0, PokemonMoveIndex); + SV_FIELD_E_MAX(u16, move, 0, PokemonMoveIndex, PsychoBoost); SV_FIELD(u8, currentPPs, 2); - SV_FIELD(u8, nbPPUpsUsed, 3); + SV_FIELD_MAX(u8, nbPPUpsUsed, 3, 3); } u8 PokemonMove::calculateMaxPP(void) const { - u32 baseMaxPP = (moveIndex > PsychoBoost) ? 0 : (u32)getBaseMoveMaxPPs(moveIndex); + u32 baseMaxPP = (move > PsychoBoost) ? 0 : (u32)getBaseMoveMaxPPs(move); return (u8)(baseMaxPP*(100 + (u32)nbPPUpsUsed * 20) / 100); } @@ -51,6 +79,40 @@ u8 PokemonMarkings::save(void) const { return m; } +u16 pokemonStatusToBitField(PokemonStatus status, s8 turnsOfBadPoison, s8 turnsOfSleepRemaining) { + static const u8 reverseStatuses[] = { 3, 7, 6, 4, 5 }; + + u16 st; + status = (status != NoStatus && status < Poisoned && status > Asleep) ? NoStatus : status; + if (status == Asleep) st = turnsOfSleepRemaining; + else if (status != NoStatus) st = reverseStatuses[status - 3]; + + return st; +} + +PokemonStatus pokemonStatusFromBitField(u16 status, s8 * turnsOfBadPoison, s8 * turnsOfSleepRemaining) +{ + static const PokemonStatus statuses[] = { Poisoned, Burnt, Frozen, Paralyzed, BadlyPoisoned }; + + PokemonStatus ret; + s8 tobp = 0, tosr = 0; + tosr = (s8)(status & 7); + tobp = (s8)((status >> 8) & 0xf); + if (tobp != 0) ret = Asleep; + + status >>= 3; + for (size_t i = 0; i < 5 && ret == NoStatus; ++i) { + if ((status & 1) != 0) status = statuses[i]; + status >>= 1; + } + + if (ret != BadlyPoisoned) tobp = 0; + + if (turnsOfBadPoison != NULL) *turnsOfBadPoison = tobp; + if (turnsOfSleepRemaining != NULL) *turnsOfSleepRemaining = tosr; + return ret; +} + u16 getPokedexIndexOf(PokemonSpeciesIndex speciesIndex) { static const u16 remaining[] = { 290, 291, 292, 276, 277, 285, 286, 327, 278, 279, 283, 284, 320, 321, 300, 301, 352, 343, 344, 299, 324, @@ -5608,8 +5670,9 @@ PokemonSpeciesData getSpeciesData(PokemonSpeciesIndex index) { return speciesData[(size_t)index]; } + const u32* getSpeciesExpTable(PokemonSpeciesIndex index) { - if (index > Munchlax) return NULL; + if (index > Munchlax) return getSpeciesExpTable(NoSpecies); return expTables[(size_t)getSpeciesData(index).expGrowthType]; } diff --git a/LibPkmGC/src/LibPkmGC/Detail/CircularDependencies.cpp b/LibPkmGC/src/LibPkmGC/Detail/CircularDependencies.cpp new file mode 100644 index 0000000..046e3f3 --- /dev/null +++ b/LibPkmGC/src/LibPkmGC/Detail/CircularDependencies.cpp @@ -0,0 +1,111 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#include +#include +#include + +namespace LibPkmGC { + +LIBPKMGC_GEN_CONVERTER_CTOR(DaycareData) +LIBPKMGC_GEN_CONVERTER_CTOR_W_GBA(Pokemon) +LIBPKMGC_GEN_CONVERTER_CTOR(StrategyMemoData) +LIBPKMGC_GEN_CONVERTER_CTOR(PokemonBox) + +namespace GBA { + +PokemonString::PokemonString(GC::PokemonString const& other) : Base::PokemonString() { + fromUTF8(other.toUTF8()); +} + +void Pokemon::swap(Base::Pokemon & other) { + if (LIBPKMGC_IS_GBA(Pokemon, &other)) swap((Pokemon&)other); + else { + GC::Pokemon& gco = static_cast(other); + //bool inval = (GCFlags & 1) != 0; by default always true until the Pkm is traded to Colo/XD + bool inval = !checkChecksum(false); + u8 GCUnk = GCFlags >> 3; + Base::Pokemon::swap(other); + std::swap(GCUnk, gco.GCUnk); + std::swap(inval, gco.pkmFlags[LIBPKMGC_GC_INVALID_POKEMON_FLAG]); + bool tradedFromColoXD = true; + bool flgs[] = { _egg, inval, tradedFromColoXD }; + if (GCUnk > 31) GCUnk = 31; + GCFlags = GCUnk << 3; + for (size_t i = 0; i < 3; ++i) + GCFlags |= flgs[i] << (2 - i); + } +} + +Pokemon& Pokemon::operator=(Base::Pokemon const& other) { + if (LIBPKMGC_IS_GBA(Pokemon, &other)) return operator=((Pokemon const&)other); + else { + if (this == &other) return *this; + deleteFields(); + _flags = 1; + initWithEmptyData(1); + Base::Pokemon::operator=(other); + + GC::Pokemon const& gco = static_cast(other); + bool tradedFromColoXD = true; + bool flgs[] = { _egg, gco.pkmFlags[LIBPKMGC_GC_INVALID_POKEMON_FLAG], tradedFromColoXD }; + + GCFlags = gco.GCUnk << 3; + for (size_t i = 0; i < 3; ++i) + GCFlags |= flgs[i] << (2 - i); + return *this; + } +} + +Pokemon::Pokemon(GC::Pokemon const& other) : Base::Pokemon(100) { + _flags = 1; + initWithEmptyData(1); + *this = other; +} +} + +namespace GC { + +PokemonString::PokemonString(GBA::PokemonString const& other) : Base::PokemonString(){ + fromUTF8(other.toUTF8()); +} + +Pokemon& Pokemon::operator=(Base::Pokemon const& other) { + if (LIBPKMGC_IS_GBA(Pokemon, &other)) { + if (this == &other) return *this; + deleteFields(); + initWithEmptyData(); + Base::Pokemon::operator=(other); + GBA::Pokemon const& gbao = static_cast(other); + GCUnk = gbao.GCFlags >> 3; + pkmFlags[LIBPKMGC_GC_INVALID_POKEMON_FLAG] = !(((GBA::Pokemon&)(other)).checkChecksum(false)); + return *this; + } + else return operator=((Pokemon const&)other); +} + +void Pokemon::swap(Base::Pokemon& other) { + if (LIBPKMGC_IS_GBA(Pokemon, &other)) + static_cast(other).swap(*this); + else + swap((Pokemon&)other); +} + + +} +} \ No newline at end of file diff --git a/LibPkmGC/src/LibPkmGC/GBA/Detail/GBACharTables.cpp b/LibPkmGC/src/LibPkmGC/GBA/Detail/GBACharTables.cpp new file mode 100644 index 0000000..c9e57ba --- /dev/null +++ b/LibPkmGC/src/LibPkmGC/GBA/Detail/GBACharTables.cpp @@ -0,0 +1,492 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#include +#include +#include + +namespace LibPkmGC { + +namespace Detail { + +typedef boost::bimap< boost::bimaps::unordered_set_of, boost::bimaps::unordered_set_of > chartable_t; +typedef chartable_t::value_type charset_t; + +inline chartable_t initEurCharTable(void) { + // Some characters have no Unicode equivalents + chartable_t tbl; + tbl.insert(charset_t(0x00, 0x0020)); + tbl.insert(charset_t(0x01, 0x00C0)); + tbl.insert(charset_t(0x02, 0x00C1)); + tbl.insert(charset_t(0x03, 0x00C2)); + tbl.insert(charset_t(0x04, 0x00C7)); + tbl.insert(charset_t(0x05, 0x00C8)); + tbl.insert(charset_t(0x06, 0x00C9)); + tbl.insert(charset_t(0x07, 0x00CA)); + tbl.insert(charset_t(0x08, 0x00CB)); + tbl.insert(charset_t(0x09, 0x00CC)); + tbl.insert(charset_t(0x0B, 0x00CE)); + tbl.insert(charset_t(0x0C, 0x00CF)); + tbl.insert(charset_t(0x0D, 0x00D2)); + tbl.insert(charset_t(0x0E, 0x00D3)); + tbl.insert(charset_t(0x0F, 0x00D4)); + tbl.insert(charset_t(0x10, 0x0152)); + tbl.insert(charset_t(0x11, 0x00D9)); + tbl.insert(charset_t(0x12, 0x00DA)); + tbl.insert(charset_t(0x13, 0x00DB)); + tbl.insert(charset_t(0x14, 0x00D1)); + tbl.insert(charset_t(0x15, 0x00DF)); + tbl.insert(charset_t(0x16, 0x00E0)); + tbl.insert(charset_t(0x17, 0x00E1)); + tbl.insert(charset_t(0x19, 0x00E7)); + tbl.insert(charset_t(0x1A, 0x00E8)); + tbl.insert(charset_t(0x1B, 0x00E9)); + tbl.insert(charset_t(0x1C, 0x00EA)); + tbl.insert(charset_t(0x1D, 0x00EB)); + tbl.insert(charset_t(0x1E, 0x00EC)); + tbl.insert(charset_t(0x20, 0x00EE)); + tbl.insert(charset_t(0x21, 0x00EF)); + tbl.insert(charset_t(0x22, 0x00F2)); + tbl.insert(charset_t(0x23, 0x00F3)); + tbl.insert(charset_t(0x24, 0x00F4)); + tbl.insert(charset_t(0x25, 0x0153)); + tbl.insert(charset_t(0x26, 0x00F9)); + tbl.insert(charset_t(0x27, 0x00FA)); + tbl.insert(charset_t(0x28, 0x00FB)); + tbl.insert(charset_t(0x29, 0x00F1)); + tbl.insert(charset_t(0x2A, 0x00BA)); + tbl.insert(charset_t(0x2B, 0x01AA)); + tbl.insert(charset_t(0x2D, 0x0026)); + tbl.insert(charset_t(0x2E, 0x002B)); + tbl.insert(charset_t(0x35, 0x003D)); + tbl.insert(charset_t(0x36, 0x003B)); + tbl.insert(charset_t(0x50, 0x25AF)); + tbl.insert(charset_t(0x51, 0x00BF)); + tbl.insert(charset_t(0x52, 0x00A1)); + tbl.insert(charset_t(0x5A, 0x00CD)); + tbl.insert(charset_t(0x5B, 0x0025)); + tbl.insert(charset_t(0x5C, 0x0028)); + tbl.insert(charset_t(0x5D, 0x0029)); + tbl.insert(charset_t(0x68, 0x00E2)); + tbl.insert(charset_t(0x6F, 0x00ED)); + tbl.insert(charset_t(0x79, 0x2191)); + tbl.insert(charset_t(0x7A, 0x2193)); + tbl.insert(charset_t(0x7B, 0x2190)); + tbl.insert(charset_t(0x7C, 0x2192)); + tbl.insert(charset_t(0x85, 0x003C)); + tbl.insert(charset_t(0x86, 0x003E)); + tbl.insert(charset_t(0xA1, 0x0030)); + tbl.insert(charset_t(0xA2, 0x0031)); + tbl.insert(charset_t(0xA3, 0x0032)); + tbl.insert(charset_t(0xA4, 0x0033)); + tbl.insert(charset_t(0xA5, 0x0034)); + tbl.insert(charset_t(0xA6, 0x0035)); + tbl.insert(charset_t(0xA7, 0x0036)); + tbl.insert(charset_t(0xA8, 0x0037)); + tbl.insert(charset_t(0xA9, 0x0038)); + tbl.insert(charset_t(0xAA, 0x0039)); + tbl.insert(charset_t(0xAB, 0x0021)); + tbl.insert(charset_t(0xAC, 0x003F)); + tbl.insert(charset_t(0xAD, 0x002E)); + tbl.insert(charset_t(0xAE, 0x002D)); + tbl.insert(charset_t(0xAF, 0x00B7)); + tbl.insert(charset_t(0xB0, 0x2026)); + tbl.insert(charset_t(0xB1, 0x00AB)); + tbl.insert(charset_t(0xB2, 0x00BB)); + tbl.insert(charset_t(0xB3, 0x2018)); + tbl.insert(charset_t(0xB4, 0x2019)); + tbl.insert(charset_t(0xB5, 0x2642)); + tbl.insert(charset_t(0xB6, 0x2640)); + tbl.insert(charset_t(0xB7, 0x0024)); + tbl.insert(charset_t(0xB8, 0x002C)); + tbl.insert(charset_t(0xB9, 0x00D7)); + tbl.insert(charset_t(0xBA, 0x002F)); + tbl.insert(charset_t(0xBB, 0x0041)); + tbl.insert(charset_t(0xBC, 0x0042)); + tbl.insert(charset_t(0xBD, 0x0043)); + tbl.insert(charset_t(0xBE, 0x0044)); + tbl.insert(charset_t(0xBF, 0x0045)); + tbl.insert(charset_t(0xC0, 0x0046)); + tbl.insert(charset_t(0xC1, 0x0047)); + tbl.insert(charset_t(0xC2, 0x0048)); + tbl.insert(charset_t(0xC3, 0x0049)); + tbl.insert(charset_t(0xC4, 0x004A)); + tbl.insert(charset_t(0xC5, 0x004B)); + tbl.insert(charset_t(0xC6, 0x004C)); + tbl.insert(charset_t(0xC7, 0x004D)); + tbl.insert(charset_t(0xC8, 0x004E)); + tbl.insert(charset_t(0xC9, 0x004F)); + tbl.insert(charset_t(0xCA, 0x0050)); + tbl.insert(charset_t(0xCB, 0x0051)); + tbl.insert(charset_t(0xCC, 0x0052)); + tbl.insert(charset_t(0xCD, 0x0053)); + tbl.insert(charset_t(0xCE, 0x0054)); + tbl.insert(charset_t(0xCF, 0x0055)); + tbl.insert(charset_t(0xD0, 0x0056)); + tbl.insert(charset_t(0xD1, 0x0057)); + tbl.insert(charset_t(0xD2, 0x0058)); + tbl.insert(charset_t(0xD3, 0x0059)); + tbl.insert(charset_t(0xD4, 0x005A)); + tbl.insert(charset_t(0xD5, 0x0061)); + tbl.insert(charset_t(0xD6, 0x0062)); + tbl.insert(charset_t(0xD7, 0x0063)); + tbl.insert(charset_t(0xD8, 0x0064)); + tbl.insert(charset_t(0xD9, 0x0065)); + tbl.insert(charset_t(0xDA, 0x0066)); + tbl.insert(charset_t(0xDB, 0x0067)); + tbl.insert(charset_t(0xDC, 0x0068)); + tbl.insert(charset_t(0xDD, 0x0069)); + tbl.insert(charset_t(0xDE, 0x006A)); + tbl.insert(charset_t(0xDF, 0x006B)); + tbl.insert(charset_t(0xE0, 0x006C)); + tbl.insert(charset_t(0xE1, 0x006D)); + tbl.insert(charset_t(0xE2, 0x006E)); + tbl.insert(charset_t(0xE3, 0x006F)); + tbl.insert(charset_t(0xE4, 0x0070)); + tbl.insert(charset_t(0xE5, 0x0071)); + tbl.insert(charset_t(0xE6, 0x0072)); + tbl.insert(charset_t(0xE7, 0x0073)); + tbl.insert(charset_t(0xE8, 0x0074)); + tbl.insert(charset_t(0xE9, 0x0075)); + tbl.insert(charset_t(0xEA, 0x0076)); + tbl.insert(charset_t(0xEB, 0x0077)); + tbl.insert(charset_t(0xEC, 0x0078)); + tbl.insert(charset_t(0xED, 0x0079)); + tbl.insert(charset_t(0xEE, 0x007A)); + tbl.insert(charset_t(0xEF, 0x2023)); + tbl.insert(charset_t(0xF0, 0x003A)); + tbl.insert(charset_t(0xF1, 0x00C4)); + tbl.insert(charset_t(0xF2, 0x00D6)); + tbl.insert(charset_t(0xF3, 0x00DC)); + tbl.insert(charset_t(0xF4, 0x00E4)); + tbl.insert(charset_t(0xF5, 0x00F6)); + tbl.insert(charset_t(0xF6, 0x00FC)); + + //F7 ... FF : commands + + return tbl; +} + +inline chartable_t initJapCharTable(void) { + chartable_t tbl; + + tbl.insert(charset_t(0x00, 0x3000)); + //---------------------------------------------------------- + // Medium-sized hirgananas + //---------------------------------------------------------- + tbl.insert(charset_t(0x01, 0x3042)); + tbl.insert(charset_t(0x02, 0x3044)); + tbl.insert(charset_t(0x03, 0x3046)); + tbl.insert(charset_t(0x04, 0x3048)); + tbl.insert(charset_t(0x05, 0x304A)); + tbl.insert(charset_t(0x06, 0x304B)); + tbl.insert(charset_t(0x07, 0x304D)); + tbl.insert(charset_t(0x08, 0x304F)); + tbl.insert(charset_t(0x09, 0x3051)); + tbl.insert(charset_t(0x0A, 0x3053)); + tbl.insert(charset_t(0x0B, 0x3055)); + tbl.insert(charset_t(0x0C, 0x3057)); + tbl.insert(charset_t(0x0D, 0x3059)); + tbl.insert(charset_t(0x0E, 0x305B)); + tbl.insert(charset_t(0x0F, 0x305D)); + tbl.insert(charset_t(0x10, 0x305F)); + tbl.insert(charset_t(0x11, 0x3061)); + tbl.insert(charset_t(0x12, 0x3064)); + tbl.insert(charset_t(0x13, 0x3066)); + tbl.insert(charset_t(0x14, 0x3068)); + tbl.insert(charset_t(0x15, 0x306A)); + tbl.insert(charset_t(0x16, 0x306B)); + tbl.insert(charset_t(0x17, 0x306C)); + tbl.insert(charset_t(0x18, 0x306D)); + tbl.insert(charset_t(0x19, 0x306E)); + tbl.insert(charset_t(0x1A, 0x307F)); + tbl.insert(charset_t(0x1B, 0x3072)); + tbl.insert(charset_t(0x1C, 0x3075)); + tbl.insert(charset_t(0x1D, 0x3078)); + tbl.insert(charset_t(0x1E, 0x307B)); + tbl.insert(charset_t(0x1F, 0x307E)); + tbl.insert(charset_t(0x20, 0x307F)); + tbl.insert(charset_t(0x21, 0x3080)); + tbl.insert(charset_t(0x22, 0x3081)); + tbl.insert(charset_t(0x23, 0x3082)); + tbl.insert(charset_t(0x24, 0x3084)); + tbl.insert(charset_t(0x25, 0x3086)); + tbl.insert(charset_t(0x26, 0x3088)); + tbl.insert(charset_t(0x27, 0x3089)); + tbl.insert(charset_t(0x28, 0x308A)); + tbl.insert(charset_t(0x29, 0x308B)); + tbl.insert(charset_t(0x2A, 0x308C)); + tbl.insert(charset_t(0x2B, 0x308D)); + tbl.insert(charset_t(0x2C, 0x308F)); + tbl.insert(charset_t(0x2D, 0x3092)); + tbl.insert(charset_t(0x2E, 0x3093)); + //-------------------------------------------------------- + // Small-sized hiraganas + //-------------------------------------------------------- + tbl.insert(charset_t(0x2F, 0x3041)); + tbl.insert(charset_t(0x30, 0x3043)); + tbl.insert(charset_t(0x31, 0x3045)); + tbl.insert(charset_t(0x32, 0x3047)); + tbl.insert(charset_t(0x33, 0x3049)); + tbl.insert(charset_t(0x34, 0x3083)); + tbl.insert(charset_t(0x35, 0x3085)); + tbl.insert(charset_t(0x36, 0x3087)); + //------------------------------------------------------------ + // Medium-sized hiraganas with dakuten + //------------------------------------------------------------ + tbl.insert(charset_t(0x37, 0x304C)); + tbl.insert(charset_t(0x38, 0x304E)); + tbl.insert(charset_t(0x39, 0x3050)); + tbl.insert(charset_t(0x3A, 0x3052)); + tbl.insert(charset_t(0x3B, 0x3054)); + tbl.insert(charset_t(0x3C, 0x3056)); + tbl.insert(charset_t(0x3D, 0x3058)); + tbl.insert(charset_t(0x3E, 0x305A)); + tbl.insert(charset_t(0x3F, 0x305C)); + tbl.insert(charset_t(0x40, 0x305E)); + tbl.insert(charset_t(0x41, 0x3060)); + tbl.insert(charset_t(0x42, 0x3062)); + tbl.insert(charset_t(0x43, 0x3065)); + tbl.insert(charset_t(0x44, 0x3067)); + tbl.insert(charset_t(0x45, 0x3069)); + tbl.insert(charset_t(0x46, 0x3070)); + tbl.insert(charset_t(0x47, 0x3073)); + tbl.insert(charset_t(0x48, 0x3076)); + tbl.insert(charset_t(0x49, 0x3079)); + tbl.insert(charset_t(0x4A, 0x307C)); + //---------------------------------------------------------- + // Medium-sized hiraganas with handakuten + //---------------------------------------------------------- + tbl.insert(charset_t(0x4B, 0x3071)); + tbl.insert(charset_t(0x4C, 0x3074)); + tbl.insert(charset_t(0x4D, 0x3077)); + tbl.insert(charset_t(0x4E, 0x307A)); + tbl.insert(charset_t(0x4F, 0x307D)); + //-------------------------------------------------------- + // Small-sized hiragana + //-------------------------------------------------------- + tbl.insert(charset_t(0x50, 0x3063)); + //---------------------------------------------------------- + // Medium-sized katakanas + //---------------------------------------------------------- + tbl.insert(charset_t(0x51, 0x30A2)); + tbl.insert(charset_t(0x52, 0x30A4)); + tbl.insert(charset_t(0x53, 0x30A6)); + tbl.insert(charset_t(0x54, 0x30A8)); + tbl.insert(charset_t(0x55, 0x30AA)); + tbl.insert(charset_t(0x56, 0x30AB)); + tbl.insert(charset_t(0x57, 0x30AD)); + tbl.insert(charset_t(0x58, 0x30AF)); + tbl.insert(charset_t(0x59, 0x30B1)); + tbl.insert(charset_t(0x5A, 0x30B3)); + tbl.insert(charset_t(0x5B, 0x30B5)); + tbl.insert(charset_t(0x5C, 0x30B7)); + tbl.insert(charset_t(0x5D, 0x30B9)); + tbl.insert(charset_t(0x5E, 0x30BB)); + tbl.insert(charset_t(0x5F, 0x30BD)); + tbl.insert(charset_t(0x60, 0x30BF)); + tbl.insert(charset_t(0x61, 0x30C1)); + tbl.insert(charset_t(0x62, 0x30C4)); + tbl.insert(charset_t(0x63, 0x30C6)); + tbl.insert(charset_t(0x64, 0x30C8)); + tbl.insert(charset_t(0x65, 0x30CA)); + tbl.insert(charset_t(0x66, 0x30CB)); + tbl.insert(charset_t(0x67, 0x30CC)); + tbl.insert(charset_t(0x68, 0x30CD)); + tbl.insert(charset_t(0x69, 0x30CE)); + tbl.insert(charset_t(0x6A, 0x30CF)); + tbl.insert(charset_t(0x6B, 0x30D2)); + tbl.insert(charset_t(0x6C, 0x30D5)); + tbl.insert(charset_t(0x6D, 0x30D8)); + tbl.insert(charset_t(0x6E, 0x30DB)); + tbl.insert(charset_t(0x6F, 0x30DE)); + tbl.insert(charset_t(0x70, 0x30DF)); + tbl.insert(charset_t(0x71, 0x30E0)); + tbl.insert(charset_t(0x72, 0x30E1)); + tbl.insert(charset_t(0x73, 0x30E2)); + tbl.insert(charset_t(0x74, 0x30E4)); + tbl.insert(charset_t(0x75, 0x30E6)); + tbl.insert(charset_t(0x76, 0x30E8)); + tbl.insert(charset_t(0x77, 0x30E9)); + tbl.insert(charset_t(0x78, 0x30EA)); + tbl.insert(charset_t(0x79, 0x30EB)); + tbl.insert(charset_t(0x7A, 0x30EC)); + tbl.insert(charset_t(0x7B, 0x30ED)); + tbl.insert(charset_t(0x7C, 0x30EF)); + tbl.insert(charset_t(0x7D, 0x30F2)); + tbl.insert(charset_t(0x7E, 0x30F3)); + //---------------------------------------------------------- + // Small-sized katakanas + //---------------------------------------------------------- + tbl.insert(charset_t(0x7F, 0x30A1)); + tbl.insert(charset_t(0x80, 0x30A3)); + tbl.insert(charset_t(0x81, 0x30A5)); + tbl.insert(charset_t(0x82, 0x30A7)); + tbl.insert(charset_t(0x83, 0x30A9)); + tbl.insert(charset_t(0x84, 0x30E3)); + tbl.insert(charset_t(0x85, 0x30E5)); + tbl.insert(charset_t(0x86, 0x30E7)); + //------------------------------------------------------------ + // Medium-sized katakanas with dakuten + //------------------------------------------------------------ + tbl.insert(charset_t(0x87, 0x30AC)); + tbl.insert(charset_t(0x88, 0x30AE)); + tbl.insert(charset_t(0x89, 0x30B0)); + tbl.insert(charset_t(0x8A, 0x30B2)); + tbl.insert(charset_t(0x8B, 0x30B4)); + tbl.insert(charset_t(0x8C, 0x30B6)); + tbl.insert(charset_t(0x8D, 0x30B8)); + tbl.insert(charset_t(0x8E, 0x30BA)); + tbl.insert(charset_t(0x8F, 0x30BC)); + tbl.insert(charset_t(0x90, 0x30BE)); + tbl.insert(charset_t(0x91, 0x30C0)); + tbl.insert(charset_t(0x92, 0x30C2)); + tbl.insert(charset_t(0x93, 0x30C5)); + tbl.insert(charset_t(0x94, 0x30C7)); + tbl.insert(charset_t(0x95, 0x30C9)); + tbl.insert(charset_t(0x96, 0x30D0)); + tbl.insert(charset_t(0x97, 0x30D3)); + tbl.insert(charset_t(0x98, 0x30D6)); + tbl.insert(charset_t(0x99, 0x30D9)); + tbl.insert(charset_t(0x9A, 0x30DC)); + //------------------------------------------------------------ + // Medium-sized katakanas with handakuten + //------------------------------------------------------------ + tbl.insert(charset_t(0x9B, 0x30D1)); + tbl.insert(charset_t(0x9C, 0x30D4)); + tbl.insert(charset_t(0x9D, 0x30D7)); + tbl.insert(charset_t(0x9E, 0x30DA)); + tbl.insert(charset_t(0x9F, 0x30DD)); + //------------------------------------------------------------ + // Small-sized katakana + //------------------------------------------------------------ + tbl.insert(charset_t(0xA0, 0x30C3)); + //-------------------------------------------------------------------------------------------------- + // Full-width Roman characters (or equivalent), ideographic characters, + // and other common symbols + //-------------------------------------------------------------------------------------------------- + tbl.insert(charset_t(0xA1, 0xFF10)); + tbl.insert(charset_t(0xA2, 0xFF11)); + tbl.insert(charset_t(0xA3, 0xFF12)); + tbl.insert(charset_t(0xA4, 0xFF13)); + tbl.insert(charset_t(0xA5, 0xFF14)); + tbl.insert(charset_t(0xA6, 0xFF15)); + tbl.insert(charset_t(0xA7, 0xFF16)); + tbl.insert(charset_t(0xA8, 0xFF17)); + tbl.insert(charset_t(0xA9, 0xFF18)); + tbl.insert(charset_t(0xAA, 0xFF19)); + tbl.insert(charset_t(0xAB, 0xFF01)); + tbl.insert(charset_t(0xAC, 0xFF1F)); + tbl.insert(charset_t(0xAD, 0x3002)); + tbl.insert(charset_t(0xAE, 0xFF0D)); + tbl.insert(charset_t(0xAF, 0x30FB)); + tbl.insert(charset_t(0xB0, 0x2025)); + tbl.insert(charset_t(0xB1, 0x300E)); + tbl.insert(charset_t(0xB2, 0x300F)); + tbl.insert(charset_t(0xB3, 0x300C)); + tbl.insert(charset_t(0xB4, 0x300D)); + tbl.insert(charset_t(0xB5, 0x2642)); + tbl.insert(charset_t(0xB6, 0x2640)); + tbl.insert(charset_t(0xB7, 0x5186)); + tbl.insert(charset_t(0xB8, 0xFF0E)); + tbl.insert(charset_t(0xB9, 0x00D7)); + tbl.insert(charset_t(0xBA, 0xFF0F)); + tbl.insert(charset_t(0xBB, 0xFF21)); + tbl.insert(charset_t(0xBC, 0xFF22)); + tbl.insert(charset_t(0xBD, 0xFF23)); + tbl.insert(charset_t(0xBE, 0xFF24)); + tbl.insert(charset_t(0xBF, 0xFF25)); + tbl.insert(charset_t(0xC0, 0xFF26)); + tbl.insert(charset_t(0xC1, 0xFF27)); + tbl.insert(charset_t(0xC2, 0xFF28)); + tbl.insert(charset_t(0xC3, 0xFF29)); + tbl.insert(charset_t(0xC4, 0xFF2A)); + tbl.insert(charset_t(0xC5, 0xFF2B)); + tbl.insert(charset_t(0xC6, 0xFF2C)); + tbl.insert(charset_t(0xC7, 0xFF2D)); + tbl.insert(charset_t(0xC8, 0xFF2E)); + tbl.insert(charset_t(0xC9, 0xFF2F)); + tbl.insert(charset_t(0xCA, 0xFF30)); + tbl.insert(charset_t(0xCB, 0xFF31)); + tbl.insert(charset_t(0xCC, 0xFF32)); + tbl.insert(charset_t(0xCD, 0xFF33)); + tbl.insert(charset_t(0xCE, 0xFF34)); + tbl.insert(charset_t(0xCF, 0xFF35)); + tbl.insert(charset_t(0xD0, 0xFF36)); + tbl.insert(charset_t(0xD1, 0xFF37)); + tbl.insert(charset_t(0xD2, 0xFF38)); + tbl.insert(charset_t(0xD3, 0xFF39)); + tbl.insert(charset_t(0xD4, 0xFF3A)); + tbl.insert(charset_t(0xD5, 0xFF41)); + tbl.insert(charset_t(0xD6, 0xFF42)); + tbl.insert(charset_t(0xD7, 0xFF43)); + tbl.insert(charset_t(0xD8, 0xFF44)); + tbl.insert(charset_t(0xD9, 0xFF45)); + tbl.insert(charset_t(0xDA, 0xFF46)); + tbl.insert(charset_t(0xDB, 0xFF47)); + tbl.insert(charset_t(0xDC, 0xFF48)); + tbl.insert(charset_t(0xDD, 0xFF49)); + tbl.insert(charset_t(0xDE, 0xFF4A)); + tbl.insert(charset_t(0xDF, 0xFF4B)); + tbl.insert(charset_t(0xE0, 0xFF4C)); + tbl.insert(charset_t(0xE1, 0xFF4D)); + tbl.insert(charset_t(0xE2, 0xFF4E)); + tbl.insert(charset_t(0xE3, 0xFF4F)); + tbl.insert(charset_t(0xE4, 0xFF50)); + tbl.insert(charset_t(0xE5, 0xFF51)); + tbl.insert(charset_t(0xE6, 0xFF52)); + tbl.insert(charset_t(0xE7, 0xFF53)); + tbl.insert(charset_t(0xE8, 0xFF54)); + tbl.insert(charset_t(0xE9, 0xFF55)); + tbl.insert(charset_t(0xEA, 0xFF56)); + tbl.insert(charset_t(0xEB, 0xFF57)); + tbl.insert(charset_t(0xEC, 0xFF58)); + tbl.insert(charset_t(0xED, 0xFF59)); + tbl.insert(charset_t(0xEE, 0xFF5A)); + //-------------------------------------------------------------------------------------------------- + //Symbols and accentued (fullwidth in-game) roman characters + //-------------------------------------------------------------------------------------------------- + tbl.insert(charset_t(0xEF, 0x25BA)); + tbl.insert(charset_t(0xF0, 0xFF1A)); + tbl.insert(charset_t(0xF1, 0x00C4)); + tbl.insert(charset_t(0xF2, 0x00D6)); + tbl.insert(charset_t(0xF3, 0x00DC)); + tbl.insert(charset_t(0xF4, 0x00E4)); + tbl.insert(charset_t(0xF5, 0x00F6)); + tbl.insert(charset_t(0xF6, 0x00FC)); + + return tbl; +} + +static const chartable_t eurCharTable = initEurCharTable(), japCharTable = initJapCharTable(); + +u32 GBAToUCS4(u8 val, bool jp, u32 deflt) { + if (jp && deflt == '?') deflt = 0xff1f; // (fullwidth) + chartable_t const& tbl = (jp) ? japCharTable : eurCharTable; + chartable_t::left_const_iterator it = tbl.left.find(val); + return (it == tbl.left.end()) ? deflt : it->get_right(); +} + +u8 UCS4ToGBA(u32 val, bool jp, u8 deflt) { + chartable_t const& tbl = (jp) ? japCharTable : eurCharTable; + chartable_t::right_const_iterator it = tbl.right.find(val); + return (it == tbl.right.end()) ? deflt : it->get_left(); +} + +} +} \ No newline at end of file diff --git a/LibPkmGC/src/LibPkmGC/GBA/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/GBA/Pokemon.cpp new file mode 100644 index 0000000..8978735 --- /dev/null +++ b/LibPkmGC/src/LibPkmGC/GBA/Pokemon.cpp @@ -0,0 +1,393 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#define TARGET_ENDIANNESS_LE +#include + +namespace LibPkmGC { +namespace GBA { + +Pokemon::Pokemon(const u8* inData, u32 flags) : Base::Pokemon(100, inData) { + _flags = flags; + load(flags); +} + +Pokemon* Pokemon::load80(const u8 * inData, u32 flags){ + u8 dta[100] = { 0 }; + std::copy(inData, inData + 80, dta); + Pokemon* ret = new Pokemon(dta, flags); + return ret; +} + +Pokemon::Pokemon(void) : Base::Pokemon(100) { + _flags = 1; // decrypted, unshuffled + initWithEmptyData(1); +} + +Pokemon::~Pokemon(void) { +} + +Pokemon::Pokemon(Pokemon const& other) : Base::Pokemon(other) { + CP(checksum); + CP(GCFlags); + CP(_flags); + CP(_egg); + CP(_secondAbility); + CL(OTName); + CL(name); + setEggFlag(other.isEgg()); + setSecondAbilityFlag(other.hasSecondAbility()); +} +void Pokemon::swap(Pokemon& other) { + Base::Pokemon::swap(other); + SW(checksum); + SW(GCFlags); + SW(_flags); + SW(_egg); + SW(_secondAbility); + +} + +Pokemon* Pokemon::clone(void) const { + return new Pokemon(*this); +} +Pokemon* Pokemon::create(void) const { + return new Pokemon; +} + +Pokemon& Pokemon::operator=(Pokemon const& other) { + Base::Pokemon::operator=(other); + if (this != &other) { + CP(checksum); + CP(GCFlags); + CP(_flags); + CP(_egg); + CP(_secondAbility); + } + return *this; +} + +bool Pokemon::hasSecondAbility(void) const { + return _secondAbility; +} + +void Pokemon::setSecondAbilityFlag(bool status) { + _secondAbility = status; +} + +bool Pokemon::isEgg(void) const { + return _egg; +} + +void Pokemon::setEggFlag(bool status) { + _egg = status; +} + + + +void Pokemon::reload(const u8 * data, u32 inFlags) { + if (data == NULL) return; +} + +static const size_t shuffleTable[24][4] = { + { 0,1,2,3 },{ 0,1,3,2 },{ 0,2,1,3 },{ 0,2,3,1 }, + { 0,3,1,2 },{ 0,3,2,1 },{ 1,0,2,3 },{ 1,0,3,2 }, + { 1,2,0,3 },{ 1,2,3,0 },{ 1,3,0,2 },{ 1,3,2,0 }, + { 2,0,1,3 },{ 2,0,3,1 },{ 2,1,0,3 },{ 2,1,3,0 }, + { 2,3,0,1 },{ 2,3,1,0 },{ 3,0,1,2 },{ 3,0,2,1 }, + { 3,1,0,2 },{ 3,1,2,0 },{ 3,2,0,1 },{ 3,2,1,0 } +}; + +inline void crypt_impl(u32 key, u8* data, u8* outData) { + u32 D; + + for (size_t i = 0; i < 48; i += 4) { + std::copy(data + 32 + i, data + 36 + i, (u8*)&D); + D ^= key; + std::copy((u8*)&D, 4 + (u8*)&D, outData + 32 + i); + } +} +void Pokemon::decryptOrEncrypt(u8 * outData) { + u32 key = PID ^ (((u32)SID << 16) | TID); + + u8 buf[48]; + + const size_t *P = shuffleTable[PID % 24]; + + if (outData == data) { // when unshuffling + for (size_t i = 0; i < 4; ++i) + std::copy(data + 32 + 12*i, data + 32 + 12*i + 12, buf + 12 * P[i]); + std::copy(buf, buf + 48, outData + 32); + + } + else { // shuffling + for (size_t i = 0; i < 4; ++i) + std::copy(data + 32 + 12 * P[i], data + 32 + 12 * P[i] + 12, buf + 12 * i); + std::copy(buf, buf + 48, outData + 32); + } + crypt_impl(key, outData, outData); + +} + +void Pokemon::load(u32 flags) { + if (flags == 0) { + u8 buf[48]; + std::copy(data + 32, data + 80, buf); + + load(1); // assume it is unencrypted by default + if (!checkChecksum(false)) { + deleteFields(); + load(2); + if (!checkChecksum(false)) { + std::copy(buf, buf + 48, data + 32); + load(1); + } + } + } + else { + loadData(flags); + loadFields(); + } +} + +void Pokemon::loadData(u32 flags) { + LD_FIELD(u32, PID, 0); + LD_FIELD(u16, TID, 4); + LD_FIELD(u16, SID, 6); + + if(flags == 2) + decryptOrEncrypt(data); + _flags = 1; +} + +static const size_t statsOrder[] = { 0, 1, 2, 5, 3, 4 }; + +void Pokemon::loadFields(void) { + u8 lg = data[18]; + markings.load(data[27]); + + LD_FIELD(u8, GCFlags, 19); + LD_FIELD(u16, checksum, 28); + LD_FIELD(u16, unk1, 30); + LD_FIELD_E(u16, species, 32, PokemonSpeciesIndex); + LD_FIELD_E(u16, heldItem, 34, ItemIndex); + LD_FIELD_MAX(u32, experience, 36, getSpeciesExpTable(species)[100]); + LD_FIELD(u8, happiness, 41); + LD_FIELD(u16, unk2, 42); + for (size_t i = 0; i < 6; ++i) EVs[i] = data[56 + statsOrder[i]]; + + LD_ARRAY(u8, contestStats, 5, 62); + LD_FIELD(u8, contestLuster, 67); + + LD_FIELD(u8, pkrsStatus, 68); + LD_FIELD(u8, locationCaught, 69); + + u16 origins; + LD_FIELD(u16, origins, 70); + + encounterType = (u8)(origins & 0x7f); + origins >>= 7; + u8 gm = (u8)(origins & 0xf); + origins >>= 4; + u16 pkb = origins & 0xf; + ballCaughtWith = (pkb > 12 || pkb == 0) ? PokeBall : (ItemIndex) pkb; + origins >>= 4; + OTGender = (Gender)(origins & 1); + + u32 iea; + LD_FIELD(u32, iea, 72); + for (size_t i = 0; i < 6; ++i) { + IVs[statsOrder[i]] = (u8)(iea & 0x1f); + iea >>= 5; + } + _egg = (iea & 1) != 0; + _secondAbility = (iea & 2) != 0; + + u32 ribbons; + LD_FIELD(u32, ribbons, 76); + + for (size_t i = 0; i < 5; ++i) { + contestAchievements[i] = (ContestAchievementLevel)(ribbons & 7); + if (contestAchievements[i] > MasterContestWon) contestAchievements[i] = NoContestWon; + ribbons >>= 3; + } + for (size_t i = 0; i < 12; ++i) { + specialRibbons[i] = (ribbons & 1) != 0; + ribbons >>= 1; + } + unimplementedRibbons = (u8)(ribbons & 15); + ribbons >>= 4; + obedient = ribbons != 0; + + u8 PPBonuses = data[40]; + PokemonMoveIndex mvs[4]; + LD_ARRAY_E_MAX(u16, mvs, 4, 44, PokemonMoveIndex, PsychoBoost); + for (size_t i = 0; i < 4; ++i) { + moves[i].move = mvs[i]; + moves[i].currentPPs = data[52 + i]; + moves[i].nbPPUpsUsed = (PPBonuses & 3); + PPBonuses >>= 2; + } + + u32 st; + LD_FIELD(u32, st, 80); + pokemonStatusFromBitField((u16)st, &(partyData.turnsOfBadPoison), &(partyData.turnsOfSleepRemaining)); + + LD_FIELD_MAX(u8, partyData.level, 84, 100); + LD_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 85, 4); + LD_FIELD(u16, partyData.currentHP, 86); + u16 sta[6]; + LD_ARRAY(u16, sta, 6, 88); + bool regen = true; + for (size_t i = 0; i < 6; ++i) { + partyData.stats[statsOrder[i]] = sta[i]; + if (sta[i] != 0) regen = false; + } + if (regen) resetPartyData(); + if (partyData.currentHP > partyData.stats[0]) partyData.currentHP = partyData.stats[0]; + + version.load(lg, gm); + + name = new PokemonString(data + 8, 10, version.language == Japanese); + OTName = new PokemonString(data + 20, 7, version.language == Japanese); + + normalizePkrs(); + normalizeStatus(); +} + + + + +bool Pokemon::checkChecksum(bool fix) { + u16 newChecksum = 0; + for (size_t i = 0; i < 48; i += 2) + newChecksum += toInteger(data + 32 + i); + bool ret = newChecksum == checksum; + if (fix) checksum = newChecksum; + return ret; +} + +bool Pokemon::isEmptyOrInvalid(void) const { + return Base::Pokemon::isEmptyOrInvalid() || !const_cast(this)->checkChecksum(false); +} + +void Pokemon::save(void) { + u8 lg, gm; + data[27] = markings.save(); + version.save(lg, gm); + + normalizePkrs(); + + name->save(data + 8, 10); + OTName->save(data + 20, 7); + + data[18] = lg; + + SV_FIELD(u32, PID, 0); + SV_FIELD(u16, TID, 4); + SV_FIELD(u16, SID, 6); + + SV_FIELD(u8, GCFlags, 19); + SV_FIELD(u16, unk1, 30); + SV_FIELD_E(u16, species, 32, PokemonSpeciesIndex); + SV_FIELD_E(u16, heldItem, 34, ItemIndex); + SV_FIELD_MAX(u32, experience, 36, getSpeciesExpTable(species)[100]); + SV_FIELD(u8, happiness, 41); + SV_FIELD(u16, unk2, 42); + for (size_t i = 0; i < 6; ++i) data[56 + statsOrder[i]] = EVs[i]; + + SV_ARRAY(u8, contestStats, 5, 62); + SV_FIELD(u8, contestLuster, 67); + + SV_FIELD(u8, pkrsStatus, 68); + SV_FIELD(u8, locationCaught, 69); + + u16 origins = 0; + if (encounterType > 0x7f) encounterType = 0x7f; + origins |= encounterType; + origins |= (u16) gm << 7; + if (ballCaughtWith == 0 || ballCaughtWith > PremierBall) ballCaughtWith = PokeBall; + origins |= (u16)ballCaughtWith << 11; + if ((u32)OTGender > (u32)Female) OTGender = Male; + origins |= (u16)OTGender << 15; + SV_FIELD(u16, origins, 70); + + u32 iea = 0; + for (size_t i = 0; i < 6; ++i) { + if (IVs[statsOrder[i]] > 31) IVs[statsOrder[i]] = 31; + iea |= IVs[statsOrder[i]] << (5 * i); + } + iea |= ((_egg) ? 1 : 0) << 30; + iea |= ((_secondAbility) ? 1 : 0) << 31; + SV_FIELD(u32, iea, 72); + + + u32 ribbons = 0; + + for (size_t i = 0; i < 5; ++i) { + if ((u32)contestAchievements[i] >(u32) MasterContestWon) contestAchievements[i] = NoContestWon; + ribbons |= (u32)contestAchievements[i] << (3 * i); + } + for (size_t i = 0; i < 12; ++i) + ribbons |= ((specialRibbons[i]) ? 1 : 0) << (15 + i); + unimplementedRibbons = (unimplementedRibbons > 15) ? 15 : unimplementedRibbons; + ribbons |= (u32)unimplementedRibbons << 27; + ribbons |= ((obedient) ? 1 : 0) << 31; + SV_FIELD(u32, ribbons, 76); + + u8 PPBonuses = 0; + for (size_t i = 0; i < 4; ++i) { + if ((u32)moves[i].move >(u32)PsychoBoost) moves[i].move = NoMove; + SV_FIELD(u16, (u16)moves[i].move, 44 + 2 * i); + SV_FIELD(u8, moves[i].currentPPs, 52 + i); + if (moves[i].nbPPUpsUsed > 3) moves[i].nbPPUpsUsed = 3; + PPBonuses |= moves[i].nbPPUpsUsed << (2 * i); + } + SV_FIELD(u8, PPBonuses, 40); + + normalizeStatus(); + u32 st = pokemonStatusToBitField(partyData.status, partyData.turnsOfBadPoison, partyData.turnsOfSleepRemaining); + SV_FIELD(u32, st, 80); + + SV_FIELD_MAX(u8, partyData.level, 84, 100); + SV_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 85, 4); + if (partyData.pkrsDaysRemaining < -1) partyData.pkrsDaysRemaining = -1; + SV_FIELD_MAX(u16, partyData.currentHP, 86, partyData.stats[0]); + u16 sta[6]; + for (size_t i = 0; i < 6; ++i) sta[i] = partyData.stats[statsOrder[i]]; + SV_ARRAY(u16, sta, 6, 88); + + + checkChecksum(true); + SV_FIELD(u32, checksum, 28); + +} + +void Pokemon::saveEncrypted(u8 * outData) { + save(); + std::copy(data, data + 32, outData); + std::copy(data + 80, data + 100, outData + 80); + if (outData == NULL || outData == data) return; + decryptOrEncrypt(outData); +} + + +} +} + +#undef TARGET_ENDIANNESS_LE diff --git a/LibPkmGC/src/LibPkmGC/GBA/PokemonString.cpp b/LibPkmGC/src/LibPkmGC/GBA/PokemonString.cpp new file mode 100644 index 0000000..5eeeccb --- /dev/null +++ b/LibPkmGC/src/LibPkmGC/GBA/PokemonString.cpp @@ -0,0 +1,182 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ + +#include +#include +#include +#include + +using namespace LibPkmGC::Detail; + +namespace LibPkmGC { +namespace GBA { + +PokemonString::PokemonString(const char* str, bool jap) : Base::PokemonString() { + _japanese = jap; + fromUTF8(str); +} + +PokemonString::PokemonString(u8 * data, size_t nb, bool jap) : Base::PokemonString() { + _japanese = jap; + load(data, nb); +} + +PokemonString::PokemonString(PokemonString const& other) : Base::PokemonString(other) { +} + +PokemonString::~PokemonString(void) { +} + +bool PokemonString::isGBA(void) const { + return true; +} + +PokemonString * PokemonString::clone(void) const { + return new PokemonString(*this); +} + +PokemonString * PokemonString::create(void) const { + return new PokemonString; +} + +PokemonString & PokemonString::operator=(PokemonString const & other) { + return (PokemonString&)Base::PokemonString::operator=(other); +} + +PokemonString& PokemonString::operator=(Base::PokemonString const& other) { + if (!other.isGBA()) { + _japanese = other.usesJapaneseCharset(); + + fromUTF8(other.toUTF8()); + return *this; + } + else return operator=((PokemonString const&)other); +} + + +} +} + +namespace LibPkmGC { +namespace Detail { + +int pkmgba_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char* s, int n, bool jap) { + if (n < 1) return RET_TOOFEW(0); + + if (*s == 0xff) *pwc = 0; + else if (*s >= 0xf7 && *s <= 0xfe) return RET_ILSEQ; // commands + else *pwc = GBAToUCS4(*s, jap); + + return 1; +} + +int pkmgba_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n, bool jap) { + if (n < 1) return RET_TOOSMALL; + + if (wc == 0) *r = 0xff; + else *r = UCS4ToGBA(wc, jap); + + return 1; +} + + + + +} +} + + +namespace LibPkmGC { +namespace GBA { + +void PokemonString::fromUTF8(const char * str) { + str = (str == NULL) ? "" : str; + strSz = std::strlen(str) + 1; + resizeStr(); + + dataSz = strSz; + resizeData(); + + dataSz = 0; + + int s = 0; + size_t offset = 0; + while (offset < strSz - 1) { + u32 c; + s = utf8_mbtowc(NULL, &c, (const unsigned char*)str + offset, strSz - 1); + if (s < 0) break; + offset += s; + if (c == 0) break; + s = pkmgba_wctomb(NULL, _data + dataSz, c, 2, _japanese); + if (s < 0) break; + dataSz += s; + } + _data[dataSz++] = 0xff; + hasChanged = true; +} + +const char * PokemonString::toUTF8(void) const { + if (!hasChanged) return _str; + + strSz = 3 * dataSz + 3; + resizeStr(); + + strSz = 0; + + int s = 0; + size_t offset = 0; + + while (offset < dataSz) { + u32 c; + s = pkmgba_mbtowc(NULL, &c, (const unsigned char*)_data + offset, 2, _japanese); + if (s < 0) break; + offset += s; + if (c == 0) break; + s = utf8_wctomb(NULL, (unsigned char*)_str + strSz, c, 6); + if (s < 0) break; + strSz += s; + } + _str[strSz++] = 0; + hasChanged = false; + return _str; +} + +size_t PokemonString::size(void) const { + size_t i = 0; + while ((i < dataSz) && (_data[i] != 0xff)) ++i; + return i; +} + +void PokemonString::load(u8 * data, size_t nb) { + dataSz = nb; + resizeData(); + std::copy(data, data + dataSz, _data); +} + +void PokemonString::save(u8 * data, size_t nb) const { + size_t sz = size(), m = ((nb <= sz) ? nb : sz); // how much characters should we write ? + std::copy(_data, _data + m, data); + if (sz < nb) { + data[sz] = 0xff; + if(sz+1 < nb) std::fill(data + sz + 1, data + nb, 0); + } +} + + +} +} \ No newline at end of file diff --git a/LibPkmGC/src/LibPkmGC/GC/Common/GroupBattleRule.cpp b/LibPkmGC/src/LibPkmGC/GC/Common/GroupBattleRule.cpp index 63b4d05..9551c36 100644 --- a/LibPkmGC/src/LibPkmGC/GC/Common/GroupBattleRule.cpp +++ b/LibPkmGC/src/LibPkmGC/GC/Common/GroupBattleRule.cpp @@ -29,11 +29,11 @@ GroupBattleRule::GroupBattleRule(GroupBattleRule const& other) : Base::DataStruc *this = other; } void GroupBattleRule::deleteFields(void) { - delete bannedItems; + delete[] bannedItems; } GroupBattleRule::~GroupBattleRule(void) { - delete bannedItems; + GroupBattleRule::deleteFields(); } void GroupBattleRule::swap(GroupBattleRule & other) { diff --git a/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp index f562c12..957e7c9 100644 --- a/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/GC/Common/Pokemon.cpp @@ -23,7 +23,6 @@ namespace LibPkmGC { namespace GC { Pokemon::Pokemon(size_t inSize, const u8* inData) : Base::Pokemon(inSize, inData) { - usesPartyData = true; } @@ -31,35 +30,45 @@ Pokemon::~Pokemon(void) { } -Pokemon::Pokemon(Pokemon const& other) : Base::Pokemon(other), shadowPkmID(other.shadowPkmID) { +Pokemon::Pokemon(Pokemon const& other) : Base::Pokemon(other), shadowPkmID(other.shadowPkmID), GCUnk(other.GCUnk) { + CL(OTName); + CL(name); CP_ARRAY(pkmFlags, 3); + setEggFlag(other.isEgg()); + setSecondAbilityFlag(other.hasSecondAbility()); } void Pokemon::swap(Pokemon& other) { - Base::Pokemon::swap(other); - SW(shadowPkmID); + Base::Pokemon::swap(other); + SW(GCUnk); + if (other.fixedSize == fixedSize) SW(shadowPkmID); SW_ARRAY(pkmFlags, 3); } - -PokemonAbilityIndex Pokemon::getAbility(void) const { - const PokemonSpeciesData dt = getThisSpeciesData(); - 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(GCUnk); + if(other.fixedSize == fixedSize) CP(shadowPkmID); + else shadowPkmID = 0; CP_ARRAY(pkmFlags, 3); } return *this; } -void Pokemon::swap(Base::Pokemon & other) { - return swap((Pokemon&)other); +bool Pokemon::hasSecondAbility(void) const { + return pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; } -Pokemon& Pokemon::operator=(Base::Pokemon const& other) { - return operator=((Pokemon const&)other); + +void Pokemon::setSecondAbilityFlag(bool status) { + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = status; +} + +bool Pokemon::isEgg(void) const { + return pkmFlags[LIBPKMGC_GC_EGG_FLAG]; +} + +void Pokemon::setEggFlag(bool status) { + pkmFlags[LIBPKMGC_GC_EGG_FLAG] = status; } } diff --git a/LibPkmGC/src/LibPkmGC/GC/Common/PokemonString.cpp b/LibPkmGC/src/LibPkmGC/GC/Common/PokemonString.cpp index 250760f..f5ef85d 100644 --- a/LibPkmGC/src/LibPkmGC/GC/Common/PokemonString.cpp +++ b/LibPkmGC/src/LibPkmGC/GC/Common/PokemonString.cpp @@ -1,3 +1,20 @@ +/* +* Copyright (C) TuxSH 2015 +* This file is part of LibPkmGC. +* +* LibPkmGC is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* LibPkmGC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with LibPkmGC. If not, see . +*/ // This file uses code from libiconv : @@ -27,24 +44,27 @@ #include +using namespace LibPkmGC::Detail; + namespace LibPkmGC { namespace GC { -PokemonString::PokemonString(const char* str) : Base::PokemonString(), _data(NULL), dataSz(0), dataCapacity(0) { +PokemonString::PokemonString(const char* str) : Base::PokemonString() { fromUTF8(str); } -PokemonString::PokemonString(u8 * data, size_t nb) : Base::PokemonString(), _data(NULL), dataSz(0), dataCapacity(0){ +PokemonString::PokemonString(u8 * data, size_t nb) : Base::PokemonString(){ load(data, nb); } -PokemonString::PokemonString(PokemonString const& other) : Base::PokemonString(other), dataSz(other.dataSz), dataCapacity(0), _data(NULL) { - resizeData(); - std::copy(other._data, other._data + dataSz, _data); +PokemonString::PokemonString(PokemonString const& other) : Base::PokemonString(other) { } PokemonString::~PokemonString(void) { - delete[] _data; +} + +bool PokemonString::isGBA(void) const { + return false; } PokemonString * PokemonString::clone(void) const { @@ -56,32 +76,23 @@ PokemonString * PokemonString::create(void) const { } PokemonString & PokemonString::operator=(PokemonString const & other) { - if (this != &other) { - Base::PokemonString::operator=(other); - delete[] _data; - std::copy(other._data, other._data + dataSz, _data); - } - return *this; + return (PokemonString&) Base::PokemonString::operator=(other); } PokemonString& PokemonString::operator=(Base::PokemonString const& other) { - return operator=((PokemonString const&)other); + if (other.isGBA()) { + _japanese = other.usesJapaneseCharset(); + fromUTF8(other.toUTF8()); + return *this; + } + else return operator=((PokemonString const&)other); } } } -namespace { -using namespace LibPkmGC; -typedef void* conv_t; -typedef u32 ucs4_t; - -// Custom : -#define RET_ILSEQ -1 -#define RET_ILUNI -2 -#define RET_TOOSMALL -3 -#define RET_TOOFEW(c) (-4*(c+1)) +namespace LibPkmGC { namespace Detail { // Adapted from libiconv (see license above) @@ -90,7 +101,7 @@ typedef u32 ucs4_t; * UCS-2BE = UCS-2 big endian */ -static int +int ucs2be_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n) { if (n >= 2) { @@ -108,7 +119,7 @@ ucs2be_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n) return RET_TOOFEW(0); } -static int +int ucs2be_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n) { if (wc < 0x10000 && !(wc >= 0xd800 && wc < 0xe000) && wc != 0xffff) { // [LibPkmGC] @@ -123,123 +134,10 @@ ucs2be_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n) return RET_ILUNI; } -/* -* UTF-8 -*/ - -/* Specification: RFC 3629 */ - -static int -utf8_mbtowc(conv_t conv, ucs4_t *pwc, const unsigned char *s, int n) -{ - unsigned char c = s[0]; - - if (c < 0x80) { - *pwc = c; - return 1; - } - else if (c < 0xc2) { - return RET_ILSEQ; - } - else if (c < 0xe0) { - if (n < 2) - return RET_TOOFEW(0); - if (!((s[1] ^ 0x80) < 0x40)) - return RET_ILSEQ; - *pwc = ((ucs4_t)(c & 0x1f) << 6) - | (ucs4_t)(s[1] ^ 0x80); - return 2; - } - else if (c < 0xf0) { - if (n < 3) - return RET_TOOFEW(0); - if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 - && (c >= 0xe1 || s[1] >= 0xa0))) - return RET_ILSEQ; - *pwc = ((ucs4_t)(c & 0x0f) << 12) - | ((ucs4_t)(s[1] ^ 0x80) << 6) - | (ucs4_t)(s[2] ^ 0x80); - return 3; - } - else if (c < 0xf8 && sizeof(ucs4_t) * 8 >= 32) { - if (n < 4) - return RET_TOOFEW(0); - if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 - && (s[3] ^ 0x80) < 0x40 - && (c >= 0xf1 || s[1] >= 0x90))) - return RET_ILSEQ; - *pwc = ((ucs4_t)(c & 0x07) << 18) - | ((ucs4_t)(s[1] ^ 0x80) << 12) - | ((ucs4_t)(s[2] ^ 0x80) << 6) - | (ucs4_t)(s[3] ^ 0x80); - return 4; - } - else if (c < 0xfc && sizeof(ucs4_t) * 8 >= 32) { - if (n < 5) - return RET_TOOFEW(0); - if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 - && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 - && (c >= 0xf9 || s[1] >= 0x88))) - return RET_ILSEQ; - *pwc = ((ucs4_t)(c & 0x03) << 24) - | ((ucs4_t)(s[1] ^ 0x80) << 18) - | ((ucs4_t)(s[2] ^ 0x80) << 12) - | ((ucs4_t)(s[3] ^ 0x80) << 6) - | (ucs4_t)(s[4] ^ 0x80); - return 5; - } - else if (c < 0xfe && sizeof(ucs4_t) * 8 >= 32) { - if (n < 6) - return RET_TOOFEW(0); - if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 - && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 - && (s[5] ^ 0x80) < 0x40 - && (c >= 0xfd || s[1] >= 0x84))) - return RET_ILSEQ; - *pwc = ((ucs4_t)(c & 0x01) << 30) - | ((ucs4_t)(s[1] ^ 0x80) << 24) - | ((ucs4_t)(s[2] ^ 0x80) << 18) - | ((ucs4_t)(s[3] ^ 0x80) << 12) - | ((ucs4_t)(s[4] ^ 0x80) << 6) - | (ucs4_t)(s[5] ^ 0x80); - return 6; - } - else - return RET_ILSEQ; -} - -static int -utf8_wctomb(conv_t conv, unsigned char *r, ucs4_t wc, int n) /* n == 0 is acceptable */ { - int count; - if (wc < 0x80) - count = 1; - else if (wc < 0x800) - count = 2; - else if (wc < 0x10000) - count = 3; - else if (wc < 0x200000) - count = 4; - else if (wc < 0x4000000) - count = 5; - else if (wc <= 0x7fffffff) - count = 6; - else - return RET_ILUNI; - if (n < count) - return RET_TOOSMALL; - switch (count) { /* note: code falls through cases! */ - case 6: r[5] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x4000000; - case 5: r[4] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x200000; - case 4: r[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000; - case 3: r[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800; - case 2: r[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0; - case 1: r[0] = wc; - } - return count; -} - } +} + namespace LibPkmGC { namespace GC { @@ -250,28 +148,32 @@ void PokemonString::fromUTF8(const char * str) { resizeStr(); dataSz = 2 * strSz; - resizeStr(); + resizeData(); dataSz = 0; int s = 0; size_t offset = 0; - while (offset < strSz) { + while (offset < strSz - 1) { u32 c; s = utf8_mbtowc(NULL, &c, (const unsigned char*)str + offset, 6); if (s < 0) break; offset += s; + if (c == 0) break; s = ucs2be_wctomb(NULL, _data + dataSz, c, 2); if (s < 0) break; dataSz += s; - if (c == 0) break; } + assert(dataSz % 2 == 0); + _data[dataSz++] = 0; + _data[dataSz++] = 0; hasChanged = true; } const char * PokemonString::toUTF8(void) const { if (!hasChanged) return _str; - + assert(dataSz % 2 == 0); + strSz = 3 * (dataSz / 2); resizeStr(); @@ -281,6 +183,8 @@ const char * PokemonString::toUTF8(void) const { size_t offset = 0; while (offset < dataSz) { + assert(strSz < 3 * (dataSz / 2)); + u32 c; s = ucs2be_mbtowc(NULL, &c, (const unsigned char*)_data + offset, 2); if (s < 0) break; @@ -296,10 +200,11 @@ const char * PokemonString::toUTF8(void) const { size_t PokemonString::size(void) const { size_t i = 0; - while ((2 * i < dataSz) && (_data[2 * i] != 0 || _data[2 * i + 1] != 0)) i += 2; + while ((2 * i < dataSz) && (_data[2 * i] != 0 || _data[2 * i + 1] != 0)) ++i; return i; } + void PokemonString::load(u8 * data, size_t nb) { dataSz = 2 * nb + 2; resizeData(); @@ -310,15 +215,8 @@ void PokemonString::save(u8 * data, size_t nb) const { size_t sz = size(), m = ((nb <= sz) ? nb : sz); // how much characters should we write ? std::copy(_data, _data + 2*m, data); - std::fill(data + 2 * m, data + 2 * (nb + 1), 0); -} - -void PokemonString::resizeData(void) { - if (dataCapacity < dataSz) { - dataCapacity = dataSz; - delete[] _data; - _data = new u8[dataSz]; - } + if(m <= nb) + std::fill(data + 2 * m, data + 2 * (nb + 1), 0); } diff --git a/LibPkmGC/src/LibPkmGC/GC/Common/StrategyMemoData.cpp b/LibPkmGC/src/LibPkmGC/GC/Common/StrategyMemoData.cpp index 163ad31..af23c25 100644 --- a/LibPkmGC/src/LibPkmGC/GC/Common/StrategyMemoData.cpp +++ b/LibPkmGC/src/LibPkmGC/GC/Common/StrategyMemoData.cpp @@ -60,13 +60,15 @@ void StrategyMemoData::save(void) { SV_FIELD(u16, nbEntries, 0); } -bool StrategyMemoData::registerSpecies(PokemonSpeciesIndex index, u32 PID, u16 SID, u16 TID) { +size_t StrategyMemoData::registerSpecies(PokemonSpeciesIndex index, u32 PID, u16 SID, u16 TID) { u16 i = 0; - while ((i < nbEntries) && (index != entries[i++]->species)); - if ((i == nbEntries) || (nbEntries == 0x1f4)) return false; + if (!getSpeciesData(index).isValid) return 501; + while (i < nbEntries && index != NoSpecies && index != entries[i++]->species); + if (i == nbEntries || nbEntries == 500) return 501; - StrategyMemoEntry* entry = entries[++nbEntries - 1]; + i = ++nbEntries - 1; + StrategyMemoEntry* entry = entries[i]; entry->setInfoCompleteness(true); entry->species = index; @@ -74,10 +76,10 @@ bool StrategyMemoData::registerSpecies(PokemonSpeciesIndex index, u32 PID, u16 S entry->firstTID = TID; entry->firstPID = PID; - return true; + return i; } -bool StrategyMemoData::registerSpecies(Pokemon * pkm) { +size_t StrategyMemoData::registerSpecies(Pokemon * pkm) { return registerSpecies(pkm->species, pkm->PID, pkm->SID, pkm->TID); } @@ -95,7 +97,7 @@ void StrategyMemoData::deleteEntry(size_t index) { size_t StrategyMemoData::recount(void) const { size_t i = 0; - while (i < 500 && getSpeciesData(entries[i++]->species).isValid); + while (i < 500 && getSpeciesData(entries[i]->species).isValid) ++i; return i; } diff --git a/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp b/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp index 9874f28..0567293 100644 --- a/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp +++ b/LibPkmGC/src/LibPkmGC/XD/Common/Pokemon.cpp @@ -43,6 +43,11 @@ Pokemon* Pokemon::create(void) const { return new Pokemon; } void Pokemon::swap(Pokemon& other) { GC::Pokemon::swap(other); + SW_ARRAY(XDPkmFlags, 3); +} + +bool Pokemon::isEmptyOrInvalid(void) const { + return (species >= Munchlax) || (species == NoSpecies) || !getSpeciesData(species).isValid || version.isIncomplete(); } Pokemon& Pokemon::operator=(Pokemon const& other) { @@ -56,20 +61,19 @@ Pokemon& Pokemon::operator=(Pokemon const& other) { void Pokemon::loadFields(void) { u8 marksTmp = 0; LD_FIELD_E(u16, species, 0x00, PokemonSpeciesIndex); - LD_FIELD_E(u16, heldItem, 0x02, ItemIndex); + LD_FIELD_E(u8, heldItem, 0x03, ItemIndex); LD_FIELD(u16, partyData.currentHP, 0x04); - u16 happiness_tmp; - LD_FIELD(u16, happiness_tmp, 0x06); - happiness = (happiness_tmp > 255) ? 255 : happiness_tmp; + LD_FIELD_CONV(u16, happiness, 0x06, u8); - u16 locationCaught_tmp; - LD_FIELD(u16, locationCaught_tmp, 0x08); - locationCaught = (u8)((locationCaught_tmp > 255) ? 255 : locationCaught_tmp); - - LD_FIELD(u8, levelMet, 0x0e); - LD_FIELD_E(u8, ballCaughtWith, 0x0f, ItemIndex); - LD_FIELD_E(u8, OTGender, 0x10, Gender); + LD_FIELD_CONV(u16, locationCaught, 0x08, u8); + + LD_FIELD(u16, unk1, 0xa); + LD_FIELD(u16, unk2, 0xc); + LD_FIELD_MAX(u8, levelMet, 0x0e, 100); + LD_FIELD_E_MAX(u8, ballCaughtWith, 0x0f, ItemIndex, PremierBall); + if (ballCaughtWith == NoItem) ballCaughtWith = PokeBall; + LD_FIELD_E_MAX(u8, OTGender, 0x10, Gender, Female); LD_FIELD(u8, partyData.level, 0x11); if (partyData.level > 100) partyData.level = 100; @@ -77,19 +81,25 @@ void Pokemon::loadFields(void) { LD_FIELD(u8, pkrsStatus, 0x13); LD_FIELD(u8, marksTmp, 0x14); + LD_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 0x15, 4); LD_FIELD_E(u8, partyData.status, 0x16, PokemonStatus); - partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; + LD_FIELD(s8, partyData.turnsOfBadPoison, 0x17); + LD_FIELD(s8, partyData.turnsOfSleepRemaining, 0x18); + LD_FIELD_MAX(u8, GCUnk, 0x1a, 31); LD_BIT_ARRAY(u8, pkmFlags, 3, 0x1d); LD_BIT_ARRAY2(u8, XDPkmFlags, 3, 0x1d, 3); - LD_FIELD(u32, experience, 0x20); + LD_FIELD_MAX(u32, experience, 0x20, getSpeciesExpTable(species)[100]); LD_FIELD(u16, SID, 0x24); LD_FIELD(u16, TID, 0x26); LD_FIELD(u32, PID, 0x28); + LD_FIELD_B(u8, obedient, 0x30); + LD_FIELD(u8, encounterType, 0x33); OTName = new GC::PokemonString(data + 0x38, 10); name = new GC::PokemonString(data + 0x4e, 10); LD_BIT_ARRAY(u16, specialRibbons, 12, 0x7c); + unimplementedRibbons = specialRibbons_tmp & 15; // specialRibbons_tmp is defined by LD_BIT_ARRAY LD_ARRAY(u16, partyData.stats, 6, 0x90); u16 EVs_tmp[6]; // EVs are internally stored as u16 @@ -99,8 +109,8 @@ void Pokemon::loadFields(void) { LD_ARRAY(u8, IVs, 6, 0xa8); for (size_t i = 0; i < 6; ++i) IVs[i] = (IVs[i] > 31) ? 31 : IVs[i]; - LD_ARRAY(u8, contestStats, 6, 0xae); - LD_ARRAY_E(u8, contestAchievements, 5, 0xb3, ContestAchievementLevel); + LD_ARRAY(u8, contestStats, 5, 0xae); + LD_ARRAY_E_MAX(u8, contestAchievements, 5, 0xb3, ContestAchievementLevel, MasterContestWon); LD_FIELD(u16, shadowPkmID, 0xba); @@ -110,20 +120,34 @@ void Pokemon::loadFields(void) { for (size_t i = 0; i < 4; ++i) moves[i].load(data + 0x80 + 4 * i); + if (partyData.currentHP > partyData.stats[0]) partyData.currentHP = partyData.stats[0]; pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; + normalizePkrs(); + normalizeStatus(); + } void Pokemon::save(void) { + normalizePkrs(); + normalizeStatus(); + pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = isSecondAbilityDefined() && pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]; + if (partyData.currentHP > partyData.stats[0]) partyData.currentHP = partyData.stats[0]; + SV_FIELD_E(u16, species, 0x00, PokemonSpeciesIndex); - SV_FIELD_E(u16, heldItem, 0x02, ItemIndex); + SV_FIELD_E(u8, heldItem, 0x03, ItemIndex); SV_FIELD(u16, partyData.currentHP, 0x04); - SV_FIELD(u16, (u16)happiness, 0x06); - SV_FIELD(u16, (u16)locationCaught, 0x08); - SV_FIELD(u8, levelMet, 0x0e); - SV_FIELD_E(u8, ballCaughtWith, 0x0f, ItemIndex); - SV_FIELD_E(u8, OTGender, 0x10, Gender); + SV_FIELD_CONV(u16, happiness, 0x06, u8); + SV_FIELD_CONV(u16, locationCaught, 0x08, u8); + + SV_FIELD(u16, unk1, 0xa); + SV_FIELD(u16, unk2, 0xc); + + SV_FIELD_MAX(u8, levelMet, 0x0e, 100); + if (ballCaughtWith == NoItem || ballCaughtWith > PremierBall) ballCaughtWith = PokeBall; + SV_FIELD_E_MAX(u8, ballCaughtWith, 0x0f, ItemIndex, PremierBall); + SV_FIELD_E_MAX(u8, OTGender, 0x10, Gender, Female); if (partyData.level > 100) partyData.level = 100; SV_FIELD(u8, partyData.level, 0x11); @@ -131,21 +155,29 @@ void Pokemon::save(void) { SV_FIELD(u8, pkrsStatus, 0x13); SV_FIELD(u8, markings.save(), 0x14); - partyData.status = (partyData.status != NoStatus && partyData.status < Poisoned && partyData.status > Asleep) ? NoStatus : partyData.status; + SV_FIELD_MAX(s8, partyData.pkrsDaysRemaining, 0x15, 4); SV_FIELD_E(u8, partyData.status, 0x16, PokemonStatus); + SV_FIELD(s8, partyData.turnsOfBadPoison, 0x17); + SV_FIELD(s8, partyData.turnsOfSleepRemaining, 0x18); + SV_FIELD_MAX(u8, GCUnk, 0x1a, 31); SV_BIT_ARRAY(u8, pkmFlags, 3, 0x1d); SV_BIT_ARRAY2(u8, XDPkmFlags, 3, 0x1d, 3); - SV_FIELD(u32, experience, 0x20); + SV_FIELD_MAX(u32, experience, 0x20, getSpeciesExpTable(species)[100]); SV_FIELD(u16, SID, 0x24); SV_FIELD(u16, TID, 0x26); SV_FIELD(u32, PID, 0x28); - + u32 st = pokemonStatusToBitField(partyData.status, 0, partyData.turnsOfSleepRemaining); + SV_FIELD(u32, st, 0x2c); + SV_FIELD_B(u8, obedient, 0x30); + SV_FIELD(u8, encounterType, 0x33); OTName->save(data + 0x38, 10); name->save(data + 0x4e, 10); name->save(data + 0x64, 10); SV_BIT_ARRAY(u16, specialRibbons, 12, 0x7c); + if (unimplementedRibbons > 15) unimplementedRibbons = 15; + data[0x7d] = (data[0x7d] & 0xf0) | unimplementedRibbons; 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]; @@ -154,8 +186,8 @@ void Pokemon::save(void) { SV_ARRAY(u8, IVs, 6, 0xa8); - SV_ARRAY(u8, contestStats, 6, 0xae); - SV_ARRAY_E(u8, contestAchievements, 5, 0xb3, ContestAchievementLevel); + SV_ARRAY(u8, contestStats, 5, 0xae); + SV_ARRAY_E_MAX(u8, contestAchievements, 5, 0xb3, ContestAchievementLevel, MasterContestWon); SV_FIELD(u16, shadowPkmID, 0xba); diff --git a/PkmGCSaveEditor/src/Core/Globals.h b/PkmGCSaveEditor/src/Core/Globals.h index b41c261..46a81dd 100644 --- a/PkmGCSaveEditor/src/Core/Globals.h +++ b/PkmGCSaveEditor/src/Core/Globals.h @@ -19,7 +19,7 @@ #ifndef _PKMGCSAVEEDITOR_GLOBALS_H #define _PKMGCSAVEEDITOR_GLOBALS_H -#define PKMGCSAVEEDITOR_VERSION 1000000 +#define PKMGCSAVEEDITOR_VERSION 1001000 #define PKMGCSAVEEDITOR_VERSION_MAJOR ((PKMGCSAVEEDITOR_VERSION / 1000000) % 1000) #define PKMGCSAVEEDITOR_VERSION_MINOR ((PKMGCSAVEEDITOR_VERSION / 1000) % 1000) #define PKMGCSAVEEDITOR_VERSION_BUILD (PKMGCSAVEEDITOR_VERSION % 1000) @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/PkmGCSaveEditor/src/GCUIs/GameConfigUI.cpp b/PkmGCSaveEditor/src/GCUIs/GameConfigUI.cpp index e88d051..b98fa10 100644 --- a/PkmGCSaveEditor/src/GCUIs/GameConfigUI.cpp +++ b/PkmGCSaveEditor/src/GCUIs/GameConfigUI.cpp @@ -64,7 +64,7 @@ void GameConfigUI::parseData(void) { SaveSlot* sl = saveSlot_; isXD = LIBPKMGC_IS_XD(SaveEditing::SaveSlot, sl); - versionFld->disconnect(SIGNAL(versionChanged())); + versionFld->disconnect(SIGNAL(versionChanged()), this); versionFld->setInfo(sl->version); connect(versionFld, SIGNAL(versionChanged()), this, SLOT(versionChangeHandler())); diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp index 7bac5d8..5da980a 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp +++ b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.cpp @@ -136,72 +136,45 @@ void PokemonDisplayWidget::deletePkm(void) { -QString PokemonDisplayWidget::selectFilters(void) { +QString PokemonDisplayWidget::selectFilters(bool op) { + const QString allsupportedfilesfilter = tr("All supported files (*.colopkm *.xdpkm *.pkm *.3gpkm)"); const QString colofilter = tr("Colosseum Pok\xc3\xa9mon files (*.colopkm)"); const QString xdfilter = tr("XD Pok\xc3\xa9mon files (*.xdpkm)"); + const QString gbafilter = tr("GBA Pok\xc3\xa9mon files (*.pkm *.3gpkm)"); + const QString gbaencfilter = tr("Encrypted GBA Pok\xc3\xa9mon files (*.pkm *.3gpkm)"); const QString allfilesfilter = tr("All files (*)"); - return ((pkm == NULL || LIBPKMGC_IS_COLOSSEUM(Pokemon, pkm)) ? - colofilter + ";;" + xdfilter + ";;" + allfilesfilter : - xdfilter + ";;" + colofilter + ";;" + allfilesfilter); + QString F[] = { colofilter, xdfilter, gbafilter }; + if (pkm != NULL) { + if (LIBPKMGC_IS_GBA(Pokemon, pkm)) std::swap(F[0], F[2]); + else if (LIBPKMGC_IS_XD(Pokemon, pkm)) std::swap(F[0], F[1]); + } + return (op) ? (allsupportedfilesfilter + ";;" + F[0] + ";;" + F[1] + ";;" + F[2]) : + (F[0] + ";;" + F[1] + ";;" + F[2] + ";;" + gbaencfilter); } - -void PokemonDisplayWidget::openImportPkmDialog(void) { - const QString errmsg = tr("Could not open file."); - const QString errmsg2 = tr("An error occured while reading the specified Pok\xc3\xa9mon file."); - QString fileName = QFileDialog::getOpenFileName(this, tr("Open Pok\xc3\xa9mon file"), lastPkmDirectory, selectFilters()); +void PokemonDisplayWidget::openImportPkmDialog(void) { + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Pok\xc3\xa9mon file"), lastPkmDirectory, selectFilters(true)); if (fileName.isEmpty()) return; QFileInfo fileInfo(fileName); size_t fileSize = (size_t) fileInfo.size(); - if (fileSize == 0x138) { // Colosseum Pokémon - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - QMessageBox::critical(this, tr("Error"), errmsg); - return; - } - - QByteArray ba = file.readAll(); - if (ba.size() != 0x138) { - QMessageBox::critical(this, tr("Error"), errmsg2); - return; - } - Colosseum::Pokemon importedPkm((u8*)ba.data()); - if (pkm == NULL) pkm = importedPkm.clone(); - else { *pkm = importedPkm; } - parseData(); - lastPkmDirectory = fileInfo.canonicalPath(); + switch (fileSize) { + case 0x138: readExpected(fileName); break; + case 0xc4: readExpected(fileName); break; + case 100: case 80: readExpected(fileName, fileSize); break; + default: QMessageBox::critical(this, tr("Error"), tr("Invalid file size.")); break; } - else if (fileSize == 0xc4) { - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - QMessageBox::critical(this, tr("Error"), errmsg); - return; - } - - QByteArray ba = file.readAll(); - if (ba.size() != 0xc4) { - QMessageBox::critical(this, tr("Error"), errmsg2); - return; - } - XD::Pokemon importedPkm((const u8*)ba.data()); - if (pkm == NULL) pkm = importedPkm.clone(); - else { *pkm = importedPkm; } - parseData(); - lastPkmDirectory = fileInfo.canonicalPath(); - } - else { - QMessageBox::critical(this, tr("Error"), tr("Invalid file size.")); - } } void PokemonDisplayWidget::openExportPkmDialog(void) { const QString errmsg = tr("Could not write to file."); const QString errmsg2 = tr("An error occured while writing to the specified Pok\xc3\xa9mon file."); + const QString gbaencfilter = tr("Encrypted GBA Pok\xc3\xa9mon files (*.pkm *.3gpkm)"); - QString fileName = QFileDialog::getSaveFileName(this, tr("Save Pok\xc3\xa9mon file"), lastPkmDirectory, selectFilters()); + QString selectedFilter; + QString fileName = QFileDialog::getSaveFileName(this, tr("Save Pok\xc3\xa9mon file"), lastPkmDirectory, selectFilters(false), &selectedFilter); if (fileName.isEmpty()) return; QFile file(fileName); @@ -213,7 +186,7 @@ void PokemonDisplayWidget::openExportPkmDialog(void) { QString selectedExt = fileName.section(".", -1); - GC::Pokemon* exportedPkm = pkm; + Base::Pokemon* exportedPkm = pkm; bool created = false; if ((selectedExt == "colopkm") && !LIBPKMGC_IS_COLOSSEUM(Pokemon, pkm)) { @@ -224,10 +197,22 @@ void PokemonDisplayWidget::openExportPkmDialog(void) { exportedPkm = new XD::Pokemon(*(Colosseum::Pokemon*)pkm); created = true; } + else if ((selectedExt == "3gpkm" || selectedExt == "pkm") && !LIBPKMGC_IS_GBA(Pokemon, pkm)) { + exportedPkm = new GBA::Pokemon(*pkm); + created = true; + } exportedPkm->save(); - if (file.write((const char*)exportedPkm->data, exportedPkm->fixedSize) != (qint64)exportedPkm->fixedSize) + char* dta = (char*)exportedPkm->data; + QByteArray dt(100, 0); + + if (selectedFilter == gbaencfilter) { + static_cast(exportedPkm)->saveEncrypted((u8*)dt.data()); + dta = dt.data(); + } + + if (file.write(dta, exportedPkm->fixedSize) != (qint64)exportedPkm->fixedSize) QMessageBox::critical(this, tr("Error"), errmsg2); else lastPkmDirectory = QFileInfo(fileName).canonicalPath(); diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.h b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.h index e704abc..457df1a 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.h +++ b/PkmGCSaveEditor/src/GCUIs/PokemonDisplayWidget.h @@ -43,7 +43,50 @@ private: QPushButton *importButton; QPushButton *exportButton; - QString selectFilters(void); + QString selectFilters(bool op = true); + + template + void readExpected(QString fileName, size_t sz = P::size) { + const QString errmsg = tr("Could not open file."); + const QString errmsg2 = tr("An error occured while reading the specified Pok\xc3\xa9mon file."); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, tr("Error"), errmsg); + return; + } + + QByteArray ba = file.readAll(); + if (ba.size() != sz) { + QMessageBox::critical(this, tr("Error"), errmsg2); + return; + } + + P* importedPkm = NULL; + if (sz != 80) + importedPkm = new P((const u8*)ba.data()); + else { + GBA::Pokemon* pk2 = GBA::Pokemon::load80((const u8*)ba.data()); + importedPkm = new P(*pk2); + delete pk2; + } + + + if (pkm == NULL) { + if (LIBPKMGC_IS_XD(Pokemon, importedPkm) || (currentSaveSlot != NULL && LIBPKMGC_IS_XD(SaveEditing::SaveSlot, currentSaveSlot))) + pkm = new XD::Pokemon(*importedPkm); + else + pkm = new Colosseum::Pokemon(*importedPkm); + } + else *pkm = *importedPkm; + + if (P::size == 100 && currentSaveSlot != NULL) + pkm->version.currentRegion = currentSaveSlot->version.currentRegion; + + parseData(); + lastPkmDirectory = QFileInfo(fileName).canonicalPath(); + delete importedPkm; + } + }; diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp b/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp index e595b27..fc49d3c 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp +++ b/PkmGCSaveEditor/src/GCUIs/PokemonUI.cpp @@ -16,12 +16,18 @@ PokemonMoveLayout::PokemonMoveLayout(PokemonMove const& inMove) : QHBoxLayout(){ currentPPsFld->setRange(0, 64); - this->addWidget(moveNameFld); - this->addWidget(currentPPsFld); - this->addWidget(maxPPsFld); - this->addWidget(nbPPUpsUsedFld, 0, Qt::AlignRight); - this->addWidget(nbPPUpsUsedText, 0, Qt::AlignRight); - + QHBoxLayout* w1 = new QHBoxLayout; + w1->addWidget(moveNameFld); + w1->addWidget(currentPPsFld); + w1->addWidget(maxPPsFld); + + QHBoxLayout* w2 = new QHBoxLayout; + w2->addWidget(nbPPUpsUsedFld); + w2->addWidget(nbPPUpsUsedText); + w2->setAlignment(Qt::AlignRight); + + this->addLayout(w1, 3); + this->addLayout(w2, 1); for (size_t i = 0; i <= (size_t)PsychoBoost; ++i) moveNameFld->addItem(getPokemonMoveName(lg, (PokemonMoveIndex)i)); @@ -38,12 +44,17 @@ PokemonMove PokemonMoveLayout::move(void) const { } void PokemonMoveLayout::setMove(PokemonMove const& inMove) { - blockSignals(true); + moveNameFld->disconnect(SIGNAL(currentIndexChanged(int)), this); + nbPPUpsUsedFld->disconnect(SIGNAL(valueChanged(int)), this); + currentPPsFld->setUnsignedRange(0, 64); currentPPsFld->setUnsignedValue(inMove.currentPPs); - moveNameFld->setCurrentIndex((inMove.moveIndex > PsychoBoost) ? 0 : (int) inMove.moveIndex); + moveNameFld->setCurrentIndex((inMove.move > PsychoBoost) ? 0 : (int) inMove.move); nbPPUpsUsedFld->setUnsignedValue(inMove.nbPPUpsUsed); - blockSignals(false); + + connect(moveNameFld, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFields())); + connect(nbPPUpsUsedFld, SIGNAL(valueChanged(int)), this, SLOT(updateFields())); + updateFields(); } @@ -59,9 +70,9 @@ PokemonStatLayout::PokemonStatLayout(u8 inIV, u8 inEV, u16 inStat) : QHBoxLayout EVFld = new UnsignedSpinbox<8>; statFld = new UnsignedSpinbox<16>; - IVFld->setValue((int)inIV); - EVFld->setValue((int)inEV); - statFld->setValue((int)inStat); + IVFld->setUnsignedValue(inIV); + EVFld->setUnsignedValue(inEV); + statFld->setUnsignedValue(inStat); this->addWidget(IVFld); this->addWidget(EVFld); this->addWidget(statFld); @@ -111,13 +122,19 @@ void PokemonUI::initWidget(void){ movesTab = new QWidget; ribbonsTab = new QWidget; - generalTabLayout = new QFormLayout; + generalTabLayout = new QVBoxLayout; metTabLayout = new QVBoxLayout; statsTabLayout = new QVBoxLayout; movesTabLayout = new QVBoxLayout; ribbonsTabLayout = new QVBoxLayout; //-----------------------------"General" tab:----------------------------------------------- + generalSubTabs = new QTabWidget; + generalCoreSubTab = new QWidget; + generalCoreSubTabLayout = new QFormLayout; + + generalStatusSubTab = new QWidget; + generalStatusSubTabLayout = new QFormLayout; speciesFld = new QComboBox; nameLayout = new QHBoxLayout; @@ -134,14 +151,14 @@ void PokemonUI::initWidget(void){ heldItemFld = new ItemComboBox; happinessFld = new UnsignedSpinbox<8>; - pkrsStatusFld = new UnsignedSpinbox<8>; - statusFld = new QComboBox; + pkrsStatusLayout = new QHBoxLayout; + pkrsDaysRemainingFld = new UnsignedSpinbox<3>; + pkrsStrainFld = new UnsignedSpinbox<4>; flagsLayout = new QGridLayout; flagsButtonGroup = new QButtonGroup; markingsLayout = new QHBoxLayout; markingsButtonGroup = new QButtonGroup; - statusFld->addItems(statusNames()); for (size_t i = 0; i < 387; ++i) speciesFld->addItem(Localization::getPokemonSpeciesNameByPkdxIndex(lg, i)); @@ -155,6 +172,8 @@ void PokemonUI::initWidget(void){ levelFld->setRange(1, 100); syncLevelAndExpFldsCheckBox->setChecked(true); + pkrsDaysRemainingFld->setUnsignedRange(0, 4); + experienceLevelAndSyncLayout->addWidget(experienceFld,2); levelAndSyncLayout->addWidget(levelFld,1); @@ -162,6 +181,9 @@ void PokemonUI::initWidget(void){ levelAndSyncLayout->setAlignment(Qt::AlignRight); experienceLevelAndSyncLayout->addLayout(levelAndSyncLayout); + pkrsStatusLayout->addWidget(pkrsDaysRemainingFld); + pkrsStatusLayout->addWidget(pkrsStrainFld); + 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")); @@ -190,20 +212,43 @@ void PokemonUI::initWidget(void){ markingsLayout->addWidget(g2[i]); } - generalTabLayout->addRow(tr("Species"), speciesFld); - generalTabLayout->addRow(tr("Name or nickname"), nameLayout); - generalTabLayout->addRow(tr("PID"), PIDFld); - generalTabLayout->addRow(tr("Attributes"), attributesFld); - generalTabLayout->addRow(tr("Ability"), abilityFld); - generalTabLayout->addRow(tr("Experience and level"), experienceLevelAndSyncLayout); - generalTabLayout->addRow(tr("Held item"), heldItemFld); - 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); + generalCoreSubTabLayout->addRow(tr("Species"), speciesFld); + generalCoreSubTabLayout->addRow(tr("Name or nickname"), nameLayout); + generalCoreSubTabLayout->addRow(tr("PID"), PIDFld); + generalCoreSubTabLayout->addRow(tr("Attributes"), attributesFld); + generalCoreSubTabLayout->addRow(tr("Ability"), abilityFld); + generalCoreSubTabLayout->addRow(tr("Experience and level"), experienceLevelAndSyncLayout); + generalCoreSubTabLayout->addRow(tr("Held item"), heldItemFld); + generalCoreSubTabLayout->addRow(tr("Happiness"), happinessFld); + generalCoreSubTabLayout->addRow(tr("Pok\xc3\xa9rus (days remaing and strain)"), pkrsStatusLayout); + generalCoreSubTabLayout->addRow(tr("Flags"), flagsLayout); + generalCoreSubTabLayout->addRow(tr("Markings"), markingsLayout); - + + statusFld = new QComboBox; + partyPrksDaysRemainingFld = new QSpinBox; + turnsOfBadPoisonFld = new QSpinBox; + turnsOfSleepRemainingFld = new QSpinBox; + + statusFld->addItems(statusNames()); + + partyPrksDaysRemainingFld->setRange(-1, 4); + turnsOfBadPoisonFld->setRange(0, 15); + turnsOfSleepRemainingFld->setRange(0, 7); + + generalStatusSubTabLayout->addRow(tr("Status"), statusFld); + generalStatusSubTabLayout->addRow(tr("Pok\xc3\xa9rus days remaining"), partyPrksDaysRemainingFld); + generalStatusSubTabLayout->addRow(tr("Turns of sleep remaining"), turnsOfSleepRemainingFld); + generalStatusSubTabLayout->addRow(tr("Turns of bad poison"), turnsOfBadPoisonFld); + + generalCoreSubTab->setLayout(generalCoreSubTabLayout); + generalStatusSubTab->setLayout(generalStatusSubTabLayout); + + generalSubTabs->addTab(generalCoreSubTab, tr("Core")); + generalSubTabs->addTab(generalStatusSubTab, tr("Status")); + + generalTabLayout->addWidget(generalSubTabs); + generalTab->setLayout(generalTabLayout); //---------------------------------------------------------------------------------------- @@ -217,16 +262,23 @@ void PokemonUI::initWidget(void){ OTField = new TrainerInfoLayout; versionFld = new VersionInfoLayout; - ballCaughtWithFld = new ItemComboBox(POKEBALLS_ALLOWED | EMPTY_ITEM_FORBIDDEN); locationCaughtFld = new UnsignedSpinbox<8>; + levelMetFld = new UnsignedSpinbox<7>; + obedientFld = new QCheckBox; + ballCaughtWithFld = new ItemComboBox(POKEBALLS_ALLOWED | EMPTY_ITEM_FORBIDDEN); metActionsLayout = new QHBoxLayout; copyInfoFromSaveButton = new QPushButton(tr("Copy info from save")); generateShinyIDsButton = new QPushButton(tr("Generate shiny IDs")); - coreCaptureInfoLayout->addRow(tr("Ball caught with"), ballCaughtWithFld); - coreCaptureInfoLayout->addRow(tr("Location caught"), locationCaughtFld); + levelMetFld->setUnsignedRange(1, 100); + coreCaptureInfoLayout->addRow(tr("Location caught"), locationCaughtFld); + coreCaptureInfoLayout->addRow(tr("Level met"), levelMetFld); + coreCaptureInfoLayout->addRow(tr("Fateful encounter (obedient)"), obedientFld); + coreCaptureInfoLayout->addRow(tr("Ball caught with"), ballCaughtWithFld); + + coreCaptureInfoLayout->labelForField(obedientFld)->setToolTip(tr("Mew and Deoxys need this field to be checked so they can obey")); coreCaptureInfoBox->setLayout(coreCaptureInfoLayout); OTBox->setLayout(OTField); versionBox->setLayout(versionFld); @@ -343,7 +395,6 @@ void PokemonUI::initWidget(void){ - generalTab->setLayout(generalTabLayout); metTab->setLayout(metTabLayout); statsTab->setLayout(statsTabLayout); movesTab->setLayout(movesTabLayout); @@ -364,6 +415,8 @@ void PokemonUI::initWidget(void){ 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(statusFld, SIGNAL(currentIndexChanged(int)), SLOT(statusChangeHandler())); + connect(pkrsStrainFld, SIGNAL(valueChanged(int)), this, SLOT(updatePkrsDaysRemaining())); connect(OTField, SIGNAL(TIDorSIDChanged()), this, SLOT(updatePkmAttributes())); connect(versionFld, SIGNAL(versionChanged()), this, SLOT(versionChangeHandler())); connect(copyInfoFromSaveButton, SIGNAL(clicked()), this, SLOT(copyInfoFromSave())); @@ -376,7 +429,7 @@ void PokemonUI::parseData(void){ if (pkm == NULL) return; copyInfoFromSaveButton->setDisabled(currentSaveSlot == NULL); - // Blocking signals have a strange yet enormous cost on the main window when loading the save + // Blocking signals have a strange yet enormous cost on the main window when loading the save file autoUpdateMainStatsCheckBox->setChecked(false); syncLevelAndExpFldsCheckBox->setChecked(false); @@ -408,8 +461,37 @@ void PokemonUI::parseData(void){ heldItemFld->setCurrentItemIndex(pkm->heldItem); happinessFld->setUnsignedValue(pkm->happiness); - pkrsStatusFld->setUnsignedValue(pkm->pkrsStatus); + + + statusFld->disconnect(SIGNAL(currentIndexChanged(int)), this); statusFld->setCurrentIndex((pkm->partyData.status == NoStatus) ? 0 : (int)pkm->partyData.status - 2); + turnsOfBadPoisonFld->setValue(pkm->partyData.turnsOfBadPoison); + turnsOfSleepRemainingFld->setValue(pkm->partyData.turnsOfSleepRemaining); + statusChangeHandler(); + connect(statusFld, SIGNAL(currentIndexChanged(int)), SLOT(statusChangeHandler())); + + + pkrsStrainFld->disconnect(SIGNAL(valueChanged(int)), this); + pkrsStrainFld->setUnsignedValue(pkm->pkrsStatus & 0xf); + pkrsDaysRemainingFld->setUnsignedValue(pkm->pkrsStatus >> 4); + partyPrksDaysRemainingFld->setValue(pkm->pkrsStatus >> 4); + updatePkrsDaysRemaining(); + + /*if ((pkm->pkrsStatus & 0xf) == 0) { + pkrsDaysRemainingFld->setValue(0); + pkrsDaysRemainingFld->setDisabled(true); + partyPrksDaysRemainingFld->setValue(-1); + partyPrksDaysRemainingFld->setDisabled(true); + } + else { + pkrsDaysRemainingFld->setUnsignedRange(0, (pkm->pkrsStatus & 0x3) + 1); + partyPrksDaysRemainingFld->setRange(0, (pkm->pkrsStatus & 0x3) + 1); + pkrsDaysRemainingFld->setUnsignedValue(pkm->pkrsStatus >> 4); + partyPrksDaysRemainingFld->setValue(pkm->pkrsStatus >> 4); + }*/ + connect(pkrsStrainFld, SIGNAL(valueChanged(int)), this, SLOT(updatePkrsDaysRemaining())); + + QCheckBox* g1[] = { eggFlagCheckBox, secondAbilityFlagCheckBox, invalidPokemonCheckBox, notTradableInGameFlagCheckBox, unknownFlagCheckBox, caughtFlagCheckBox }; @@ -425,12 +507,14 @@ void PokemonUI::parseData(void){ squareMarkingCheckBox->setChecked(pkm->markings.square); triangleMarkingCheckBox->setChecked(pkm->markings.triangle); heartMarkingCheckBox->setChecked(pkm->markings.heart); - - ballCaughtWithFld->setCurrentItemIndex(pkm->ballCaughtWith); + locationCaughtFld->setUnsignedValue(pkm->locationCaught); + levelMetFld->setUnsignedValue(pkm->levelMet); + ballCaughtWithFld->setCurrentItemIndex(pkm->ballCaughtWith); + obedientFld->setChecked(pkm->obedient); OTField->set(pkm->OTName, pkm->TID, pkm->SID, pkm->OTGender); - versionFld->disconnect(SIGNAL(versionChanged())); + versionFld->disconnect(SIGNAL(versionChanged()), this); versionFld->setInfo(pkm->version); connect(versionFld, SIGNAL(versionChanged()), this, SLOT(versionChangeHandler())); @@ -454,7 +538,7 @@ void PokemonUI::parseData(void){ autoUpdateMainStatsCheckBox->setChecked(true); versionChangeHandler(); - abilityFld->setCurrentIndex((pkm->pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG]) ? 1 : 0); + abilityFld->setCurrentIndex((pkm->hasSecondAbility()) ? 1 : 0); oldSpecies = pkm->species; } @@ -464,13 +548,14 @@ void PokemonUI::saveChanges(void){ pkm->name->fromUTF8(nameFld->text().toUtf8().data()); pkm->PID = PIDFld->unsignedValue(); - pkm->pkmFlags[LIBPKMGC_GC_SECOND_ABILITY_FLAG] = (abilityFld->currentIndex() != 0); + pkm->setSecondAbilityFlag(abilityFld->currentIndex() != 0); pkm->experience = experienceFld->unsignedValue(); pkm->partyData.level = (u8)levelFld->unsignedValue(); pkm->heldItem = heldItemFld->currentItemIndex(); pkm->happiness = (u8)happinessFld->unsignedValue(); - pkm->pkrsStatus = (u8)pkrsStatusFld->unsignedValue(); + pkm->pkrsStatus = (u8)((pkrsDaysRemainingFld->unsignedValue() << 4) | pkrsStrainFld->unsignedValue()); + pkm->partyData.pkrsDaysRemaining = (s8)(partyPrksDaysRemainingFld->value()); pkm->partyData.status = (statusFld->currentIndex() == 0) ? NoStatus : (PokemonStatus)(2 + statusFld->currentIndex()); QCheckBox* g1[] = { eggFlagCheckBox, secondAbilityFlagCheckBox, invalidPokemonCheckBox, @@ -487,8 +572,10 @@ void PokemonUI::saveChanges(void){ pkm->markings.triangle = triangleMarkingCheckBox->isChecked(); pkm->markings.heart = heartMarkingCheckBox->isChecked(); - pkm->ballCaughtWith = ballCaughtWithFld->currentItemIndex(); pkm->locationCaught = (u8)locationCaughtFld->unsignedValue(); + pkm->levelMet = (u8)levelMetFld->unsignedValue(); + pkm->ballCaughtWith = ballCaughtWithFld->currentItemIndex(); + pkm->obedient = obedientFld->isChecked(); OTField->trainerName(pkm->OTName); pkm->TID = OTField->TID(); @@ -631,8 +718,8 @@ void PokemonUI::updateAbilityList(void) { const PokemonAbilityIndex* ab = getSpeciesData(id).possibleAbilities; bool sec = abilityFld->currentIndex() == 1; - disconnect(abilityFld, SIGNAL(currentIndexChanged(int))); - disconnect(secondAbilityFlagCheckBox, SIGNAL(stateChanged(int))); + abilityFld->disconnect(SIGNAL(currentIndexChanged(int)), this); + secondAbilityFlagCheckBox->disconnect(SIGNAL(stateChanged(int)), this); abilityFld->clear(); abilityFld->addItem(Localization::getPokemonAbilityName(lg, ab[0])); @@ -652,7 +739,6 @@ void PokemonUI::updateAbilityList(void) { void PokemonUI::updateExperienceFromLevel(bool proportionally) { - proportionally = false; // not working a.t.m if (syncLevelAndExpFldsCheckBox->isChecked()) { u32 experience = 0; if (!proportionally) { @@ -663,7 +749,7 @@ void PokemonUI::updateExperienceFromLevel(bool proportionally) { else { experience = Base::Pokemon::fixExperienceProportionally(oldSpecies, experienceFld->unsignedValue(), nameIndexToPkmSpeciesIndex(speciesFld->currentIndex())); } - experienceFld->disconnect(SIGNAL(valueChanged(int))); + experienceFld->disconnect(SIGNAL(valueChanged(int)), this); experienceFld->setValue((int)experience); connect(experienceFld, SIGNAL(valueChanged(int)), this, SLOT(updateLevelFromExperience())); } @@ -673,7 +759,7 @@ void PokemonUI::updateLevelFromExperience(void) { if (syncLevelAndExpFldsCheckBox->isChecked()) { u8 lvl = Pokemon::calculateLevelFromExp(nameIndexToPkmSpeciesIndex((size_t)speciesFld->currentIndex()), (u32)experienceFld->value()); - levelFld->disconnect(SIGNAL(valueChanged(int))); + levelFld->disconnect(SIGNAL(valueChanged(int)), this); levelFld->setValue((int)lvl); connect(levelFld, SIGNAL(valueChanged(int)), this, SLOT(updateExperienceFromLevel())); } @@ -694,6 +780,42 @@ void PokemonUI::PIDChangeHandler(void) { updateMainStats(); } +void PokemonUI::updatePkrsDaysRemaining(void) { + if (pkrsStrainFld->unsignedValue() == 0) { + pkrsDaysRemainingFld->setValue(0); + pkrsDaysRemainingFld->setDisabled(true); + partyPrksDaysRemainingFld->setRange(-1, -1); + partyPrksDaysRemainingFld->setValue(-1); + partyPrksDaysRemainingFld->setDisabled(true); + } + else { + pkrsDaysRemainingFld->setDisabled(false); + partyPrksDaysRemainingFld->setDisabled(false); + pkrsDaysRemainingFld->setUnsignedRange(0, (pkrsStrainFld->unsignedValue() & 0x3) + 1); + partyPrksDaysRemainingFld->setRange(0, (pkrsStrainFld->unsignedValue() & 0x3) + 1); + } +} + +void PokemonUI::statusChangeHandler(void) { + PokemonStatus status = (statusFld->currentIndex() == 0) ? NoStatus : (PokemonStatus)(2 + statusFld->currentIndex()); + if (status != BadlyPoisoned) { + turnsOfBadPoisonFld->setValue(0); + turnsOfBadPoisonFld->setDisabled(true); + } + else { + turnsOfBadPoisonFld->setDisabled(false); + } + + if (status != Asleep) { + turnsOfSleepRemainingFld->setValue(0); + turnsOfSleepRemainingFld->setDisabled(true); + } + else { + turnsOfSleepRemainingFld->setDisabled(false); + } + +} + void PokemonUI::updateFlags(void) { secondAbilityFlagCheckBox->setDisabled(abilityFld->count() == 1); secondAbilityFlagCheckBox->setChecked(abilityFld->currentIndex() == 1); diff --git a/PkmGCSaveEditor/src/GCUIs/PokemonUI.h b/PkmGCSaveEditor/src/GCUIs/PokemonUI.h index cff17cc..b99e9ef 100644 --- a/PkmGCSaveEditor/src/GCUIs/PokemonUI.h +++ b/PkmGCSaveEditor/src/GCUIs/PokemonUI.h @@ -100,11 +100,17 @@ protected: private: QTabWidget* tabs; - QWidget *generalTab, *metTab; - QFormLayout *generalTabLayout; + QWidget *generalTab; + QWidget *metTab; QVBoxLayout *metTabLayout; // "General" tab: + QVBoxLayout *generalTabLayout; + QTabWidget *generalSubTabs; + + QWidget *generalCoreSubTab, *generalStatusSubTab; + QFormLayout *generalCoreSubTabLayout, *generalStatusSubTabLayout; + QComboBox *speciesFld; QHBoxLayout* nameLayout; QLineEdit* nameFld; @@ -123,9 +129,15 @@ private: ItemComboBox* heldItemFld; UnsignedSpinbox<8>* happinessFld; - UnsignedSpinbox<8>* pkrsStatusFld; + QHBoxLayout* pkrsStatusLayout; + UnsignedSpinbox<3>* pkrsDaysRemainingFld; + UnsignedSpinbox<4>* pkrsStrainFld; QComboBox* statusFld; + QSpinBox* turnsOfBadPoisonFld; + QSpinBox* turnsOfSleepRemainingFld; + QSpinBox* partyPrksDaysRemainingFld; + QGridLayout *flagsLayout; QButtonGroup *flagsButtonGroup; @@ -140,8 +152,10 @@ private: // "Met" tab: QGroupBox* coreCaptureInfoBox; QFormLayout* coreCaptureInfoLayout; - ItemComboBox* ballCaughtWithFld; UnsignedSpinbox<8>* locationCaughtFld; + ItemComboBox* ballCaughtWithFld; + UnsignedSpinbox<7>* levelMetFld; + QCheckBox* obedientFld; QGroupBox* OTBox; TrainerInfoLayout* OTField; @@ -172,7 +186,6 @@ private: // "Moves" tab: QWidget* movesTab; QVBoxLayout* movesTabLayout; - //QHBoxLayout* movesTitleLayout; PokemonMoveLayout* moveLayouts[4]; // "Ribbons" tab: @@ -197,6 +210,8 @@ public slots: void updateLevelFromExperience(void); void speciesChangeHandler(void); void PIDChangeHandler(void); + void updatePkrsDaysRemaining(void); + void statusChangeHandler(void); void updateFlags(void); void flagsStateChangeHandler(void); diff --git a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp index 8724e2b..a4bf492 100644 --- a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp +++ b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.cpp @@ -49,6 +49,10 @@ void StrategyMemoEntryWidget::generateShinyIDs(void) { firstSIDFld->setUnsignedValue(PID & 0xffff); } +void StrategyMemoEntryWidget::truncateMemoFromHere(void) { + speciesSelector->setCurrentIndex(0); +} + void StrategyMemoEntryWidget::initWidget(void) { LanguageIndex lg = generateDumpedNamesLanguage(); mainLayout = new QVBoxLayout; @@ -63,6 +67,7 @@ void StrategyMemoEntryWidget::initWidget(void) { firstPIDFld = new UnsignedSpinbox<32>; PIDText = new QLabel; generateShinyIDsButton = new QPushButton(tr("Generate shiny IDs")); + truncateMemoFromHereButton = new QPushButton(tr("Truncate memo from here")); for (size_t i = 0; i <= 386; ++i) speciesSelector->addItem(getPokemonSpeciesNameByPkdxIndex(lg, (u16)i)); @@ -77,6 +82,7 @@ void StrategyMemoEntryWidget::initWidget(void) { mainLayout2->addRow(tr("Partial information"), partialInfoCheckBox); mainLayout->addLayout(mainLayout2); mainLayout->addWidget(generateShinyIDsButton); + mainLayout->addWidget(truncateMemoFromHereButton); mainLayout2->setHorizontalSpacing(20); this->setLayout(mainLayout); @@ -87,6 +93,7 @@ void StrategyMemoEntryWidget::initWidget(void) { connect(firstTIDFld, SIGNAL(valueChanged(int)), this, SLOT(updatePIDText())); connect(firstSIDFld, SIGNAL(valueChanged(int)), this, SLOT(updatePIDText())); connect(generateShinyIDsButton, SIGNAL(clicked()), this, SLOT(generateShinyIDs())); + connect(truncateMemoFromHereButton, SIGNAL(clicked()), this, SLOT(truncateMemoFromHere())); } StrategyMemoEntryWidget::~StrategyMemoEntryWidget(void) { diff --git a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h index 059ae86..72157a0 100644 --- a/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h +++ b/PkmGCSaveEditor/src/GCUIs/StrategyMemoEntryWidget.h @@ -47,10 +47,11 @@ public: signals: void speciesChanged(int index, size_t nameIndex); - public slots: +public slots: void speciesChangeHandler(int nameIndex); void updatePIDText(void); void generateShinyIDs(void); + void truncateMemoFromHere(void); protected: void initWidget(void); @@ -67,6 +68,7 @@ private: UnsignedSpinbox<32> *firstPIDFld; QLabel* PIDText; QPushButton* generateShinyIDsButton; + QPushButton* truncateMemoFromHereButton; }; diff --git a/PkmGCSaveEditor/src/GCUIs/StrategyMemoUI.cpp b/PkmGCSaveEditor/src/GCUIs/StrategyMemoUI.cpp index 32b7fb4..b4d9cbb 100644 --- a/PkmGCSaveEditor/src/GCUIs/StrategyMemoUI.cpp +++ b/PkmGCSaveEditor/src/GCUIs/StrategyMemoUI.cpp @@ -39,6 +39,7 @@ void StrategyMemoUI::initWidget(void) { entrySelector = new QComboBox; nbEntriesFld = new UnsignedSpinbox<16>; + nbEntriesFld->setDisabled(true); currentEntry = new StrategyMemoEntryWidget; connect(currentEntry, SIGNAL(speciesChanged(int, size_t)), this, SLOT(updateEntryNameAndNbEntries(int, size_t))); diff --git a/PkmGCSaveEditor/translations/PkmGCSaveEditor_en.ts b/PkmGCSaveEditor/translations/PkmGCSaveEditor_en.ts index fa71d4d..cde6611 100644 --- a/PkmGCSaveEditor/translations/PkmGCSaveEditor_en.ts +++ b/PkmGCSaveEditor/translations/PkmGCSaveEditor_en.ts @@ -231,62 +231,76 @@ + All supported files (*.colopkm *.xdpkm *.pkm *.3gpkm) + All supported files (*.colopkm *.xdpkm *.pkm *.3gpkm) + + + Colosseum Pokémon files (*.colopkm) Colosseum Pokémon files (*.colopkm) - + XD Pokémon files (*.xdpkm) XD Pokémon files (*.xdpkm) - + + GBA Pokémon files (*.pkm *.3gpkm) + GBA Pokémon files (*.pkm *.3gpkm) + + + + + Encrypted GBA Pokémon files (*.pkm *.3gpkm) + Encrypted GBA Pokémon files (*.pkm *.3gpkm) + + + All files (*) All files (*) - + Could not open file. Could not open file. - + An error occured while reading the specified Pokémon file. An error occured while reading the specified Pokémon file. - + Open Pokémon file Open Pokémon file - + + - - - - - + + Error Error - + Invalid file size. Invalid file size. - + Could not write to file. Could not write to file. - + An error occured while writing to the specified Pokémon file. An error occured while writing to the specified Pokémon file. - + Save Pokémon file Save Pokémon file @@ -295,7 +309,7 @@ GCUIs::PokemonMoveLayout - + (max. %n) (max. %n) @@ -311,445 +325,481 @@ GCUIs::PokemonUI - + None Status None - + Poisoned Poisoned - + Badly poisoned Badly poisoned - + Paralyzed Paralyzed - + Burnt Burnt - + Frozen Frozen - + Asleep Asleep - + Coolness Coolness - + Beauty Beauty - + Cuteness Cuteness - + Cleverness Cleverness - + Toughness Toughness - + None Contest None - + Normal Contest Normal - + Super Contest Super - + Hyper Contest Hyper - + Master Contest Master - + HP HP - + Attack Attack - + Defense Defense - + S. Attack S. Attack - + S. Defense S. Defense - + Speed Speed - + Champion Champion - + Winning Winning - + Victory Victory - + Artist Artist - + Effort Effort - + Marine Marine - + Land Land - + Sky Sky - + Country Country - + National National - + Earth Earth - + World World - + Reset Reset - + Egg Egg - + Second ability Second ability - + Invalid Pokémon Invalid Pokémon - + Not tradable in-game Not tradable in-game - + Unknown Unknown - + Caught Caught - + Species Species - + Name or nickname Name or nickname - + PID PID - + Attributes Attributes - + Ability Ability - + Experience and level Experience and level - + Held item Held item - + Happiness Happiness - - Pokérus - Pokérus + + Mew and Deoxys need this field to be checked so they can obey + Mew and Deoxys need this field to be checked so they can obey - + + Status Status - + Flags Flags - + Markings Markings - + + Pokérus days remaining + Pokérus days remaining + + + + Turns of sleep remaining + Turns of sleep remaining + + + + Turns of bad poison + Turns of bad poison + + + + Core + Core + + + Core information Core information - + Original trainer Original trainer - + Game version Game version - + Copy info from save Copy info from save - + Generate shiny IDs Generate shiny IDs - + Ball caught with Ball caught with - + Location caught Location caught - + + Pokérus (days remaing and strain) + Pokérus (days remaing and strain) + + + + Level met + Level met + + + + Fateful encounter (obedient) + Fateful encounter (obedient) + + + IV IV - + EV EV - + Stat Stat - + Current HP Current HP - + Update stats automatically Update stats automatically - + Luster Luster - + Main stats Main stats - + Contest stats Contest stats - + Contest ribbons Contest ribbons - + Contest type Contest type - + Achievement Achievement - + Special ribbons Special ribbons - + General General - + Met/OT Met/OT - + Stats Stats - + Moves Moves - + Ribbons Ribbons - + Genderless Genderless - + Unown form: Unown form: - + will evolve into: will evolve into: - - + + Invalid version info Invalid version info - - + + "Invalid Pokémon" flag set "Invalid Pokémon" flag set - + Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(Generation_III)'>here</a>) Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(Generation_III)'>here</a>) - + Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(GCN)'>here</a>) Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(GCN)'>here</a>) - + Warning Warning - + The version info you specified is invalid. The game will therefore consider this Pokémon invalid. The version info you specified is invalid. The game will therefore consider this Pokémon invalid. @@ -762,32 +812,37 @@ (randomly generated) - + Generate shiny IDs Generate shiny IDs - + + Truncate memo from here + Truncate memo from here + + + Species Species - + First TID First TID - + First SID First SID - + First PID First PID - + Partial information Partial information @@ -795,18 +850,18 @@ GCUIs::StrategyMemoUI - - + + #%1: %2 #%1: %2 - + Number of entries Number of entries - + Entry Entry diff --git a/PkmGCSaveEditor/translations/PkmGCSaveEditor_fr.ts b/PkmGCSaveEditor/translations/PkmGCSaveEditor_fr.ts index 995ff24..844f71e 100644 --- a/PkmGCSaveEditor/translations/PkmGCSaveEditor_fr.ts +++ b/PkmGCSaveEditor/translations/PkmGCSaveEditor_fr.ts @@ -231,62 +231,76 @@ + All supported files (*.colopkm *.xdpkm *.pkm *.3gpkm) + Tous les fichiers pris en charge (*.colopkm *.xdpkm *.pkm *.3gpkm) + + + Colosseum Pokémon files (*.colopkm) Fichiers Pokémon de Colosseum (*.colopkm) - + XD Pokémon files (*.xdpkm) Fichier Pokémon d'XD (*.xdpkm) - + + GBA Pokémon files (*.pkm *.3gpkm) + Fichiers Pokémon GBA (*.pkm *.3gpkm) + + + + + Encrypted GBA Pokémon files (*.pkm *.3gpkm) + Fichiers Pokémon GBA cryptés (*.pkm *.3gpkm) + + + All files (*) Tous les fichiers (*) - + Could not open file. Impossible d'ouvrir le fichier. - + An error occured while reading the specified Pokémon file. Une erreur s'est produite durant la lecture du fichier Pokémon. - + Open Pokémon file Ouvrir fichier Pokémon - + + - - - - - + + Error Erreur - + Invalid file size. Taille de fichier invalide. - + Could not write to file. Impossible d'écrire dans le fichier. - + An error occured while writing to the specified Pokémon file. Une erreur s'est produite pendant l'écriture des données sur le fichier Pokémon. - + Save Pokémon file Sauvegarder fichier Pokémon @@ -295,7 +309,7 @@ GCUIs::PokemonMoveLayout - + (max. %n) (max. %n) @@ -311,445 +325,481 @@ GCUIs::PokemonUI - + None Status Aucun - + Poisoned Empoisonné - + Badly poisoned Gravement empoisonné - + Paralyzed Paralysé - + Burnt Brûlé - + Frozen Gelé - + Asleep Endormi - + Coolness Sang-froid - + Beauty Beauté - + Cuteness Grâce - + Cleverness Intelligence - + Toughness Robustesse - + None Contest Aucun - + Normal Contest Normal - + Super Contest Super - + Hyper Contest Hyper - + Master Contest Master - + HP PV - + Attack Attaque - + Defense Défense - + S. Attack Attaque Spé. - + S. Defense Défense spé. - + Speed Vitesse - + Champion Maître - + Winning Victoire 1 - + Victory Victoire 2 - + Artist Artiste - + Effort - + Marine Marin - + Land Terrestre - + Sky Céleste - + Country Régional - + National National - + Earth Terre - + World Monde - + Reset RaZ - + Egg Œuf - + Second ability Second talent - + Invalid Pokémon Pokémon invalide - + Not tradable in-game Non échangeable en interne - + Unknown Inconnu - + Caught Capturé - + Species Espèce - + Name or nickname Nom ou surnom - + PID PID - + Attributes Caractéristiques - + Ability Talent - + Experience and level Expérience et niveau - + Held item Objet tenu - + Happiness Bonheur - - Pokérus - Pokérus + + Mew and Deoxys need this field to be checked so they can obey + Cette case doit être cochée afin que Mew et Deoxys puissent obéir - + + Status Statut - + Flags Drapeaux - + Markings Marques - + + Pokérus days remaining + Jours restants de Pokérus + + + + Turns of sleep remaining + Tours de sommeil restants + + + + Turns of bad poison + Tours de poison grave + + + + Core + Essentiel + + + Core information Informations essentielles - + Original trainer Dresseur d'origine - + Game version Version du jeu - + Copy info from save Recopier les infos à partir de la sauvegarde - + Generate shiny IDs Générer des IDs rendant ce Pokémon shiny - + Ball caught with Pokéball utilisée - + Location caught Lieu de capture - + + Pokérus (days remaing and strain) + Pokérus (jours restants et forme) + + + + Level met + Rencontré au niveau + + + + Fateful encounter (obedient) + Rencontré par hasard (obéissant) + + + IV IV - + EV EV - + Stat Statistique - + Current HP PVs actuels - + Update stats automatically Mettre à jour les statistiques automatiquement - + Luster Lustre - + Main stats Statistiques principales - + Contest stats Statistiques de concours - + Contest ribbons Rubans de concours - + Contest type Type de concours - + Achievement Avancement - + Special ribbons Rubans spéciaux - + General Général - + Met/OT Infos de rencontre/D.O - + Stats Statistiques - + Moves Attaques - + Ribbons Rubans - + Genderless Asexué - + Unown form: Forme Zarbi : - + will evolve into: évoluera en : - - + + Invalid version info Infos de version invalides - - + + "Invalid Pokémon" flag set Drapeau "Pokémon invalide" activé - + Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(Generation_III)'>here</a>) Lieu de capture (cf. <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(Generation_III)'>Bulbapedia</a>) - + Location caught (see <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(GCN)'>here</a>) Lieu de capture (cf. <a href='http://bulbapedia.bulbagarden.net/wiki/List_of_locations_by_index_number_(GCN)'>Bulbapedia</a>) - + Warning Avertissement - + The version info you specified is invalid. The game will therefore consider this Pokémon invalid. Les informations sur la version du jeu que vous avez entrées sont invalides. Le jeu considérera de ce fait ce Pokémon invalide. @@ -762,32 +812,37 @@ (géneré aléatoirement) - + Generate shiny IDs Générer des IDs rendant ce Pokémon shiny - + + Truncate memo from here + Tronquer le Mémo à partir d'ici + + + Species Espèce - + First TID Premier TID - + First SID Premier SID - + First PID Premier PID - + Partial information Informations partielles @@ -795,18 +850,18 @@ GCUIs::StrategyMemoUI - - + + #%1: %2 #%1 : %2 - + Number of entries Nombre d'entrées - + Entry Entrée diff --git a/PkmGCSaveEditor/translations/qt/qt_fr.qm b/PkmGCSaveEditor/translations/qt/qt_fr.qm deleted file mode 100644 index a3e8eaf..0000000 Binary files a/PkmGCSaveEditor/translations/qt/qt_fr.qm and /dev/null differ diff --git a/PkmGCSaveEditor/translations/qt/qtbase_fr.qm b/PkmGCSaveEditor/translations/qt/qtbase_fr.qm deleted file mode 100644 index 8353f0a..0000000 Binary files a/PkmGCSaveEditor/translations/qt/qtbase_fr.qm and /dev/null differ