00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <cstring>
00026 #include <fstream>
00027
00028 #include <QtCore/qendian.h>
00029
00030 #include "model/sramfile.hh"
00031
00032 using namespace soesrame;
00033
00034 const std::pair<int, int> SRAMFile::SRAM_ALCHEMY_OFFSETS[] =
00035 {
00036 std::pair<int, int>(0x1F7, 0x01),
00037 std::pair<int, int>(0x1F7, 0x02),
00038 std::pair<int, int>(0x1F7, 0x04),
00039 std::pair<int, int>(0x1F7, 0x08),
00040 std::pair<int, int>(0x1F7, 0x10),
00041 std::pair<int, int>(0x1F7, 0x20),
00042 std::pair<int, int>(0x1F7, 0x40),
00043 std::pair<int, int>(0x1F7, 0x80),
00044
00045 std::pair<int, int>(0x1F8, 0x01),
00046 std::pair<int, int>(0x1F8, 0x02),
00047 std::pair<int, int>(0x1F8, 0x04),
00048 std::pair<int, int>(0x1F8, 0x08),
00049 std::pair<int, int>(0x1F8, 0x10),
00050 std::pair<int, int>(0x1F8, 0x20),
00051 std::pair<int, int>(0x1F8, 0x40),
00052 std::pair<int, int>(0x1F8, 0x80),
00053
00054 std::pair<int, int>(0x1F9, 0x01),
00055 std::pair<int, int>(0x1F9, 0x02),
00056 std::pair<int, int>(0x1F9, 0x04),
00057 std::pair<int, int>(0x1F9, 0x08),
00058 std::pair<int, int>(0x1F9, 0x10),
00059 std::pair<int, int>(0x1F9, 0x20),
00060 std::pair<int, int>(0x1F9, 0x40),
00061 std::pair<int, int>(0x1F9, 0x80),
00062
00063 std::pair<int, int>(0x1FA, 0x01),
00064 std::pair<int, int>(0x1FA, 0x02),
00065 std::pair<int, int>(0x1FA, 0x04),
00066 std::pair<int, int>(0x1FA, 0x08),
00067 std::pair<int, int>(0x1FA, 0x10),
00068 std::pair<int, int>(0x1FA, 0x20),
00069 std::pair<int, int>(0x1FA, 0x40),
00070 std::pair<int, int>(0x1FA, 0x80),
00071
00072 std::pair<int, int>(0x1FB, 0x01),
00073 std::pair<int, int>(0x1FB, 0x02),
00074 std::pair<int, int>(0x1FB, 0x04)
00075 };
00076
00077 const std::pair<int, int> SRAMFile::SRAM_CHARM_OFFSETS[] =
00078 {
00079 std::pair<int, int>(0x200, 0x20),
00080 std::pair<int, int>(0x200, 0x40),
00081 std::pair<int, int>(0x200, 0x80),
00082
00083 std::pair<int, int>(0x201, 0x01),
00084 std::pair<int, int>(0x201, 0x02),
00085 std::pair<int, int>(0x201, 0x04),
00086 std::pair<int, int>(0x201, 0x08),
00087 std::pair<int, int>(0x201, 0x10),
00088 std::pair<int, int>(0x201, 0x20),
00089 std::pair<int, int>(0x201, 0x40),
00090 std::pair<int, int>(0x201, 0x80),
00091
00092 std::pair<int, int>(0x202, 0x01),
00093 std::pair<int, int>(0x202, 0x02),
00094 std::pair<int, int>(0x202, 0x04)
00095 };
00096
00097 const std::pair<int, int> SRAMFile::SRAM_WEAPON_OFFSETS[] =
00098 {
00099 std::pair<int, int>(0x279, 0x02),
00100 std::pair<int, int>(0x279, 0x04),
00101 std::pair<int, int>(0x279, 0x08),
00102 std::pair<int, int>(0x279, 0x10),
00103
00104 std::pair<int, int>(0x279, 0x20),
00105 std::pair<int, int>(0x279, 0x40),
00106 std::pair<int, int>(0x279, 0x80),
00107 std::pair<int, int>(0x27A, 0x01),
00108
00109 std::pair<int, int>(0x27A, 0x02),
00110 std::pair<int, int>(0x27A, 0x04),
00111 std::pair<int, int>(0x27A, 0x08),
00112 std::pair<int, int>(0x27A, 0x10),
00113
00114 std::pair<int, int>(0x27A, 0x20)
00115 };
00116
00117 SRAMFile::SRAMFile(const QString &filename) throw(InvalidSRAMFileException)
00118 : modified(false) {
00119 std::ifstream file(filename.toAscii().data(),
00120 std::ios_base::in | std::ios_base::binary);
00121
00122 if (!file) {
00123 throw InvalidSRAMFileException(ISFE_FILENOTFOUND);
00124 }
00125
00126 file.seekg(0, std::ios_base::end);
00127
00128 if (file.tellg() != static_cast<std::streampos>(SRAM_FILE_SIZE)) {
00129 throw InvalidSRAMFileException(ISFE_INVALIDSIZE);
00130 }
00131
00132 file.seekg(0, std::ios_base::beg);
00133 file.read(reinterpret_cast<char *>(sram), SRAM_FILE_SIZE);
00134 file.close();
00135
00136 bool valid = false;
00137
00138 for (int game = 3; game >= 0; --game) {
00139 if (checksum(game) == getChecksum(game)) {
00140 valid = true;
00141 this->valid[game] = true;
00142 setGame(game);
00143 }
00144 }
00145
00146 if (!valid) {
00147 throw InvalidSRAMFileException(ISFE_NOVALIDGAMES);
00148 }
00149 }
00150
00151 quint16 SRAMFile::checksum(int game) const {
00152 quint32 checksum = 0x43F;
00153
00154 for (int i = 2; i < SRAM_GAME_SIZE - 1; ++i) {
00155 unsigned char temp = checksum;
00156 temp += sram[SRAM_GAME_OFFSET + i + game * SRAM_GAME_SIZE];
00157
00158 checksum &= 0xFF00;
00159 checksum |= temp;
00160 checksum <<= 1;
00161
00162 if (checksum > 0xFFFF) {
00163 checksum -= 0xFFFF;
00164 }
00165 }
00166
00167 return static_cast<quint16>(checksum);
00168 }
00169
00170 bool SRAMFile::save(const QString &filename) {
00171 for (int game = 0; game < 4; ++game) {
00172 if (isValid(game)) {
00173 setChecksum(game, checksum(game));
00174 }
00175 }
00176
00177 std::ofstream file(filename.toAscii().data(),
00178 std::ios_base::out | std::ios_base::binary);
00179
00180 if (!file) {
00181 return false;
00182 }
00183
00184 file.write(reinterpret_cast<char *>(sram), SRAM_FILE_SIZE);
00185
00186 if (file.tellp() != static_cast<std::streampos>(SRAM_FILE_SIZE)) {
00187 return false;
00188 }
00189
00190 file.close();
00191 modified = false;
00192
00193 return true;
00194 }
00195
00196 bool SRAMFile::hasAlchemy(enum sf_alchemy alchemy) const {
00197 Q_ASSERT(isValid(getGame()));
00198
00199 return (offset[SRAM_ALCHEMY_OFFSETS[alchemy].first] &
00200 SRAM_ALCHEMY_OFFSETS[alchemy].second);
00201 }
00202
00203 void SRAMFile::setAlchemy(enum sf_alchemy alchemy, bool have) {
00204 Q_ASSERT(isValid(getGame()));
00205
00206 unsigned char *data = offset + SRAM_ALCHEMY_OFFSETS[alchemy].first;
00207
00208 if (have) {
00209 *data |= SRAM_ALCHEMY_OFFSETS[alchemy].second;
00210 } else {
00211 *data &= ~SRAM_ALCHEMY_OFFSETS[alchemy].second;
00212 }
00213
00214 modified = true;
00215 }
00216
00217 std::pair<int, int> SRAMFile::getAlchemyLevel(enum sf_alchemy alchemy) const {
00218 Q_ASSERT(isValid(getGame()));
00219
00220 return std::pair<int, int>
00221 (offset[SRAM_ALCHEMYMAJORLEVELS_OFFSET + (alchemy * 2)],
00222 offset[SRAM_ALCHEMYMINORLEVELS_OFFSET + (alchemy * 2)]);
00223 }
00224
00225 void SRAMFile::setAlchemyLevel(enum sf_alchemy alchemy,
00226 std::pair<int, int> level) {
00227 Q_ASSERT(isValid(getGame()));
00228 Q_ASSERT((level.first >= 0) && (level.first < 10));
00229 Q_ASSERT((level.second >= 0) && (level.second < 100));
00230
00231 offset[SRAM_ALCHEMYMAJORLEVELS_OFFSET + (alchemy * 2)] = level.first;
00232 offset[SRAM_ALCHEMYMINORLEVELS_OFFSET + (alchemy * 2)] = level.second;
00233
00234 modified = true;
00235 }
00236
00237 std::pair<int, int> SRAMFile::getAttackLevel() const {
00238 Q_ASSERT(isValid(getGame()));
00239
00240 return std::pair<int, int>
00241 (offset[SRAM_DOG_ATTACKLEVEL_OFFSET + 1],
00242 offset[SRAM_DOG_ATTACKLEVEL_OFFSET]);
00243 }
00244
00245 void SRAMFile::setAttackLevel(std::pair<int, int> level) {
00246 Q_ASSERT(isValid(getGame()));
00247 Q_ASSERT((level.first >= 1) && (level.first < 4));
00248 Q_ASSERT((level.second >= 0) && (level.second < 256));
00249
00250 offset[SRAM_DOG_ATTACKLEVEL_OFFSET + 1] = level.first;
00251 offset[SRAM_DOG_ATTACKLEVEL_OFFSET] = level.second;
00252
00253 modified = true;
00254 }
00255
00256 bool SRAMFile::hasCharm(enum sf_charm charm) const {
00257 Q_ASSERT(isValid(getGame()));
00258
00259 return (offset[SRAM_CHARM_OFFSETS[charm].first] &
00260 SRAM_CHARM_OFFSETS[charm].second);
00261 }
00262
00263 void SRAMFile::setCharm(enum sf_charm charm, bool have) {
00264 Q_ASSERT(isValid(getGame()));
00265
00266 unsigned char *data = offset + SRAM_CHARM_OFFSETS[charm].first;
00267
00268 if (have) {
00269 *data |= SRAM_CHARM_OFFSETS[charm].second;
00270 } else {
00271 *data &= ~SRAM_CHARM_OFFSETS[charm].second;
00272 }
00273
00274 modified = true;
00275 }
00276
00277 quint16 SRAMFile::getChecksum(int game) const {
00278 const quint16 *data =
00279 reinterpret_cast<const quint16 *>(sram + SRAM_GAME_OFFSET +
00280 game * SRAM_GAME_SIZE +
00281 SRAM_CHECKSUM_OFFSET);
00282
00283 return qFromLittleEndian(*data);
00284 }
00285
00286 void SRAMFile::setChecksum(int game, quint16 checksum) {
00287 quint16 *data = reinterpret_cast<quint16 *>(sram + SRAM_GAME_OFFSET +
00288 game * SRAM_GAME_SIZE +
00289 SRAM_CHECKSUM_OFFSET);
00290
00291 *data = qToLittleEndian(checksum);
00292 }
00293
00294 quint16 SRAMFile::getCurrentHP(enum sf_hero hero) const {
00295 Q_ASSERT(isValid(getGame()));
00296
00297 const quint16 *data =
00298 reinterpret_cast<const quint16 *>(offset +
00299 ((hero == SF_BOY) ?
00300 SRAM_BOY_CURRENTHP_OFFSET :
00301 SRAM_DOG_CURRENTHP_OFFSET));
00302
00303 return qFromLittleEndian(*data);
00304 }
00305
00306 void SRAMFile::setCurrentHP(enum sf_hero hero, quint16 hp) {
00307 Q_ASSERT(isValid(getGame()));
00308 Q_ASSERT((hp >= 0) && (hp < 1000));
00309
00310 quint16 *data = reinterpret_cast<quint16 *>(offset +
00311 ((hero == SF_BOY) ?
00312 SRAM_BOY_CURRENTHP_OFFSET :
00313 SRAM_DOG_CURRENTHP_OFFSET));
00314
00315 *data = qToLittleEndian(hp);
00316 modified = true;
00317 }
00318
00319 quint32 SRAMFile::getExperience(enum sf_hero hero) const {
00320 Q_ASSERT(isValid(getGame()));
00321
00322 const quint32 *data =
00323 reinterpret_cast<const quint32 *>(offset +
00324 ((hero == SF_BOY) ?
00325 SRAM_BOY_EXPERIENCE_OFFSET :
00326 SRAM_DOG_EXPERIENCE_OFFSET));
00327
00328 return (qFromLittleEndian(*data) & 0xFFFFFF);
00329 }
00330
00331 void SRAMFile::setExperience(enum sf_hero hero, quint32 experience) {
00332 Q_ASSERT(isValid(getGame()));
00333 Q_ASSERT((experience >= 0) && (experience < 16777216));
00334
00335 unsigned char *data = offset + ((hero == SF_BOY) ?
00336 SRAM_BOY_EXPERIENCE_OFFSET :
00337 SRAM_DOG_EXPERIENCE_OFFSET);
00338
00339 data[0] = experience;
00340 data[1] = experience >> 8;
00341 data[2] = experience >> 16;
00342
00343 modified = true;
00344 }
00345
00346 void SRAMFile::setGame(int game) {
00347 Q_ASSERT((game >= 0) && (game < 4));
00348
00349 this->game = game;
00350 offset = sram + SRAM_GAME_OFFSET + (game * SRAM_GAME_SIZE);
00351 }
00352
00353 int SRAMFile::getIngredient(enum sf_ingredient ingredient) const {
00354 Q_ASSERT(isValid(getGame()));
00355
00356 return offset[SRAM_INGREDIENTS_OFFSET + ingredient];
00357 }
00358
00359 void SRAMFile::setIngredient(enum sf_ingredient ingredient, int count) {
00360 Q_ASSERT(isValid(getGame()));
00361 Q_ASSERT((count >= 0) && (count < 100));
00362
00363 offset[SRAM_INGREDIENTS_OFFSET + ingredient] = count;
00364 modified = true;
00365 }
00366
00367 int SRAMFile::getItem(enum sf_item item) const {
00368 Q_ASSERT(isValid(getGame()));
00369
00370 return offset[SRAM_ITEMS_OFFSET + item];
00371 }
00372
00373 void SRAMFile::setItem(enum sf_item item, int count) {
00374 Q_ASSERT(isValid(getGame()));
00375 Q_ASSERT((count >= 0) && (count < 100));
00376
00377 offset[SRAM_ITEMS_OFFSET + item] = count;
00378 modified = true;
00379 }
00380
00381 int SRAMFile::getLevel(enum sf_hero hero) const {
00382 Q_ASSERT(isValid(getGame()));
00383
00384 return offset[((hero == SF_BOY) ?
00385 SRAM_BOY_LEVEL_OFFSET :
00386 SRAM_DOG_LEVEL_OFFSET)];
00387 }
00388
00389 void SRAMFile::setLevel(enum sf_hero hero, int level) {
00390 Q_ASSERT(isValid(getGame()));
00391 Q_ASSERT((level >= 1) && (level < 100));
00392
00393 offset[((hero == SF_BOY) ?
00394 SRAM_BOY_LEVEL_OFFSET :
00395 SRAM_DOG_LEVEL_OFFSET)] = level;
00396 modified = true;
00397 }
00398
00399 quint16 SRAMFile::getMaxHP(enum sf_hero hero) const {
00400 Q_ASSERT(isValid(getGame()));
00401
00402 const quint16 *data =
00403 reinterpret_cast<const quint16 *>(offset +
00404 ((hero == SF_BOY) ?
00405 SRAM_BOY_MAXHP_OFFSET :
00406 SRAM_DOG_MAXHP_OFFSET));
00407
00408 return qFromLittleEndian(*data);
00409 }
00410
00411 void SRAMFile::setMaxHP(enum sf_hero hero, quint16 hp) {
00412 Q_ASSERT(isValid(getGame()));
00413 Q_ASSERT((hp >= 0) && (hp < 1000));
00414
00415 quint16 *data = reinterpret_cast<quint16 *>(offset +
00416 ((hero == SF_BOY) ?
00417 SRAM_BOY_MAXHP_OFFSET :
00418 SRAM_DOG_MAXHP_OFFSET));
00419
00420 *data = qToLittleEndian(hp);
00421 modified = true;
00422 }
00423
00424 quint32 SRAMFile::getMoney(enum sf_money money) const {
00425 Q_ASSERT(isValid(getGame()));
00426
00427 const quint32 *data =
00428 reinterpret_cast<const quint32 *>(offset +
00429 SRAM_MONEY_OFFSET + (money * 3));
00430
00431 return (qFromLittleEndian(*data) & 0xFFFFFF);
00432 }
00433
00434 void SRAMFile::setMoney(enum sf_money money, quint32 count) {
00435 Q_ASSERT(isValid(getGame()));
00436 Q_ASSERT((count >= 0) && (count < 16777216));
00437
00438 unsigned char *data = offset + SRAM_MONEY_OFFSET + (money * 3);
00439
00440 data[0] = count;
00441 data[1] = count >> 8;
00442 data[2] = count >> 16;
00443
00444 modified = true;
00445 }
00446
00447 QString SRAMFile::getName(enum sf_hero hero) const {
00448 Q_ASSERT(isValid(getGame()));
00449
00450 const char *data =
00451 reinterpret_cast<const char *>(offset +
00452 ((hero == SF_BOY) ?
00453 SRAM_BOY_NAME_OFFSET :
00454 SRAM_DOG_NAME_OFFSET));
00455
00456 return QString::fromAscii(data);
00457 }
00458
00459 void SRAMFile::setName(enum sf_hero hero, const QString &name) {
00460 Q_ASSERT(isValid(getGame()));
00461 Q_ASSERT(name.length() <= 15);
00462
00463 char *data = reinterpret_cast<char *>(offset + ((hero == SF_BOY) ?
00464 SRAM_BOY_NAME_OFFSET :
00465 SRAM_DOG_NAME_OFFSET));
00466
00467 std::strcpy(data, name.left(15).toAscii().data());
00468
00469 modified = true;
00470 }
00471
00472 quint16 SRAMFile::getTradeGood(enum sf_tradegood tradegood) const {
00473 Q_ASSERT(isValid(getGame()));
00474
00475 const quint16 *data =
00476 reinterpret_cast<const quint16 *>(offset + SRAM_TRADEGOODS_OFFSET);
00477
00478 return qFromLittleEndian(data[tradegood]);
00479 }
00480
00481 void SRAMFile::setTradeGood(enum sf_tradegood tradegood, quint16 count) {
00482 Q_ASSERT(isValid(getGame()));
00483 Q_ASSERT((count >= 0) && (count < 100));
00484
00485 quint16 *data =
00486 reinterpret_cast<quint16 *>(offset + SRAM_TRADEGOODS_OFFSET);
00487
00488 data[tradegood] = qToLittleEndian(count);
00489 modified = true;
00490 }
00491
00492 bool SRAMFile::hasWeapon(enum sf_weapon weapon) const {
00493 Q_ASSERT(isValid(getGame()));
00494
00495 return (offset[SRAM_WEAPON_OFFSETS[weapon].first] &
00496 SRAM_WEAPON_OFFSETS[weapon].second);
00497 }
00498
00499 void SRAMFile::setWeapon(enum sf_weapon weapon, bool have) {
00500 Q_ASSERT(isValid(getGame()));
00501
00502 unsigned char *data = offset + SRAM_WEAPON_OFFSETS[weapon].first;
00503
00504 if (have) {
00505 *data |= SRAM_WEAPON_OFFSETS[weapon].second;
00506 } else {
00507 *data &= ~SRAM_WEAPON_OFFSETS[weapon].second;
00508 }
00509
00510 modified = true;
00511 }
00512
00513 std::pair<int, int> SRAMFile::getWeaponLevel(enum sf_weapon weapon) const {
00514 Q_ASSERT(isValid(getGame()));
00515 Q_ASSERT(weapon != SF_BAZOOKA);
00516
00517 const unsigned char *data =
00518 offset + SRAM_WEAPONLEVELS_OFFSET + (weapon * 2);
00519
00520 return std::pair<int, int>(data[1], data[0]);
00521 }
00522
00523 void SRAMFile::setWeaponLevel(enum sf_weapon weapon,
00524 std::pair<int, int> level) {
00525 Q_ASSERT(isValid(getGame()));
00526 Q_ASSERT(weapon != SF_BAZOOKA);
00527 Q_ASSERT((level.first >= 1) && (level.first < 4));
00528 Q_ASSERT((level.second >= 0) && (level.second < 256));
00529
00530 unsigned char *data = offset + SRAM_WEAPONLEVELS_OFFSET + (weapon * 2);
00531
00532 data[1] = level.first;
00533 data[0] = level.second;
00534
00535 modified = true;
00536 }
00537