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, enum sf_region region)
00118 throw(InvalidSRAMFileException) : region(region), 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 std::memset(this->valid, 0, 4 * sizeof(bool));
00138
00139 for (int game = 3; game >= 0; --game) {
00140 if (checksum(game) == getChecksum(game)) {
00141 valid = true;
00142 this->valid[game] = true;
00143 setGame(game);
00144 }
00145 }
00146
00147 if (!valid) {
00148 throw InvalidSRAMFileException(ISFE_NOVALIDGAMES);
00149 }
00150 }
00151
00152 quint16 SRAMFile::checksum(int game) const {
00153 quint32 checksum = ((region == REGION_UNITEDSTATES) ?
00154 SRAM_CHECKSUM_START_US :
00155 SRAM_CHECKSUM_START_EUROPE);
00156 unsigned char temp = checksum +
00157 sram[SRAM_GAME_OFFSET + 2 + game * SRAM_GAME_SIZE];
00158
00159 for (int i = 3; i < SRAM_GAME_SIZE; ++i) {
00160 checksum &= 0xFF00;
00161 checksum |= temp;
00162 checksum <<= 1;
00163
00164 if (checksum > 0xFFFF) {
00165 checksum -= 0xFFFF;
00166 }
00167
00168 temp = checksum + sram[SRAM_GAME_OFFSET + i + game * SRAM_GAME_SIZE];
00169 }
00170
00171 return static_cast<quint16>(checksum);
00172 }
00173
00174 bool SRAMFile::save(const QString &filename) {
00175 for (int game = 0; game < 4; ++game) {
00176 if (isValid(game)) {
00177 setChecksum(game, checksum(game));
00178 }
00179 }
00180
00181 std::ofstream file(filename.toAscii().data(),
00182 std::ios_base::out | std::ios_base::binary);
00183
00184 if (!file) {
00185 return false;
00186 }
00187
00188 file.write(reinterpret_cast<char *>(sram), SRAM_FILE_SIZE);
00189
00190 if (file.tellp() != static_cast<std::streampos>(SRAM_FILE_SIZE)) {
00191 return false;
00192 }
00193
00194 file.close();
00195 modified = false;
00196
00197 return true;
00198 }
00199
00200 bool SRAMFile::hasAlchemy(enum sf_alchemy alchemy) const {
00201 Q_ASSERT(isValid(getGame()));
00202
00203 return (offset[SRAM_ALCHEMY_OFFSETS[alchemy].first] &
00204 SRAM_ALCHEMY_OFFSETS[alchemy].second);
00205 }
00206
00207 void SRAMFile::setAlchemy(enum sf_alchemy alchemy, bool have) {
00208 Q_ASSERT(isValid(getGame()));
00209
00210 unsigned char *data = offset + SRAM_ALCHEMY_OFFSETS[alchemy].first;
00211
00212 if (have) {
00213 *data |= SRAM_ALCHEMY_OFFSETS[alchemy].second;
00214 } else {
00215 *data &= ~SRAM_ALCHEMY_OFFSETS[alchemy].second;
00216 }
00217
00218 modified = true;
00219 }
00220
00221 std::pair<int, int> SRAMFile::getAlchemyLevel(enum sf_alchemy alchemy) const {
00222 Q_ASSERT(isValid(getGame()));
00223
00224 return std::pair<int, int>
00225 (offset[SRAM_ALCHEMYMAJORLEVELS_OFFSET + (alchemy * 2)],
00226 offset[SRAM_ALCHEMYMINORLEVELS_OFFSET + (alchemy * 2)]);
00227 }
00228
00229 void SRAMFile::setAlchemyLevel(enum sf_alchemy alchemy,
00230 std::pair<int, int> level) {
00231 Q_ASSERT(isValid(getGame()));
00232 Q_ASSERT((level.first >= 0) && (level.first < 10));
00233 Q_ASSERT((level.second >= 0) && (level.second < 100));
00234
00235 offset[SRAM_ALCHEMYMAJORLEVELS_OFFSET + (alchemy * 2)] = level.first;
00236 offset[SRAM_ALCHEMYMINORLEVELS_OFFSET + (alchemy * 2)] = level.second;
00237
00238 modified = true;
00239 }
00240
00241 std::pair<int, int> SRAMFile::getAttackLevel() const {
00242 Q_ASSERT(isValid(getGame()));
00243
00244 return std::pair<int, int>
00245 (offset[SRAM_DOG_ATTACKLEVEL_OFFSET + 1],
00246 offset[SRAM_DOG_ATTACKLEVEL_OFFSET]);
00247 }
00248
00249 void SRAMFile::setAttackLevel(std::pair<int, int> level) {
00250 Q_ASSERT(isValid(getGame()));
00251 Q_ASSERT((level.first >= 1) && (level.first < 4));
00252 Q_ASSERT((level.second >= 0) && (level.second < 256));
00253
00254 offset[SRAM_DOG_ATTACKLEVEL_OFFSET + 1] = level.first;
00255 offset[SRAM_DOG_ATTACKLEVEL_OFFSET] = level.second;
00256
00257 modified = true;
00258 }
00259
00260 bool SRAMFile::hasCharm(enum sf_charm charm) const {
00261 Q_ASSERT(isValid(getGame()));
00262
00263 return (offset[SRAM_CHARM_OFFSETS[charm].first] &
00264 SRAM_CHARM_OFFSETS[charm].second);
00265 }
00266
00267 void SRAMFile::setCharm(enum sf_charm charm, bool have) {
00268 Q_ASSERT(isValid(getGame()));
00269
00270 unsigned char *data = offset + SRAM_CHARM_OFFSETS[charm].first;
00271
00272 if (have) {
00273 *data |= SRAM_CHARM_OFFSETS[charm].second;
00274 } else {
00275 *data &= ~SRAM_CHARM_OFFSETS[charm].second;
00276 }
00277
00278 modified = true;
00279 }
00280
00281 quint16 SRAMFile::getChecksum(int game) const {
00282 const quint16 *data =
00283 reinterpret_cast<const quint16 *>(sram + SRAM_GAME_OFFSET +
00284 game * SRAM_GAME_SIZE +
00285 SRAM_CHECKSUM_OFFSET);
00286
00287 return qFromLittleEndian(*data);
00288 }
00289
00290 void SRAMFile::setChecksum(int game, quint16 checksum) {
00291 quint16 *data = reinterpret_cast<quint16 *>(sram + SRAM_GAME_OFFSET +
00292 game * SRAM_GAME_SIZE +
00293 SRAM_CHECKSUM_OFFSET);
00294
00295 *data = qToLittleEndian(checksum);
00296 }
00297
00298 quint16 SRAMFile::getCurrentHP(enum sf_hero hero) const {
00299 Q_ASSERT(isValid(getGame()));
00300
00301 const quint16 *data =
00302 reinterpret_cast<const quint16 *>(offset +
00303 ((hero == SF_BOY) ?
00304 SRAM_BOY_CURRENTHP_OFFSET :
00305 SRAM_DOG_CURRENTHP_OFFSET));
00306
00307 return qFromLittleEndian(*data);
00308 }
00309
00310 void SRAMFile::setCurrentHP(enum sf_hero hero, quint16 hp) {
00311 Q_ASSERT(isValid(getGame()));
00312 Q_ASSERT(hp < 1000);
00313
00314 quint16 *data = reinterpret_cast<quint16 *>(offset +
00315 ((hero == SF_BOY) ?
00316 SRAM_BOY_CURRENTHP_OFFSET :
00317 SRAM_DOG_CURRENTHP_OFFSET));
00318
00319 *data = qToLittleEndian(hp);
00320 modified = true;
00321 }
00322
00323 quint32 SRAMFile::getExperience(enum sf_hero hero) const {
00324 Q_ASSERT(isValid(getGame()));
00325
00326 const quint32 *data =
00327 reinterpret_cast<const quint32 *>(offset +
00328 ((hero == SF_BOY) ?
00329 SRAM_BOY_EXPERIENCE_OFFSET :
00330 SRAM_DOG_EXPERIENCE_OFFSET));
00331
00332 return (qFromLittleEndian(*data) & 0xFFFFFF);
00333 }
00334
00335 void SRAMFile::setExperience(enum sf_hero hero, quint32 experience) {
00336 Q_ASSERT(isValid(getGame()));
00337 Q_ASSERT((experience >= 0) && (experience < 16777216));
00338
00339 unsigned char *data = offset + ((hero == SF_BOY) ?
00340 SRAM_BOY_EXPERIENCE_OFFSET :
00341 SRAM_DOG_EXPERIENCE_OFFSET);
00342
00343 data[0] = experience;
00344 data[1] = experience >> 8;
00345 data[2] = experience >> 16;
00346
00347 modified = true;
00348 }
00349
00350 void SRAMFile::setGame(int game) {
00351 Q_ASSERT((game >= 0) && (game < 4));
00352
00353 this->game = game;
00354 offset = sram + SRAM_GAME_OFFSET + (game * SRAM_GAME_SIZE);
00355 }
00356
00357 int SRAMFile::getIngredient(enum sf_ingredient ingredient) const {
00358 Q_ASSERT(isValid(getGame()));
00359
00360 return offset[SRAM_INGREDIENTS_OFFSET + ingredient];
00361 }
00362
00363 void SRAMFile::setIngredient(enum sf_ingredient ingredient, int count) {
00364 Q_ASSERT(isValid(getGame()));
00365 Q_ASSERT((count >= 0) && (count < 100));
00366
00367 offset[SRAM_INGREDIENTS_OFFSET + ingredient] = count;
00368 modified = true;
00369 }
00370
00371 int SRAMFile::getItem(enum sf_item item) const {
00372 Q_ASSERT(isValid(getGame()));
00373
00374 return offset[SRAM_ITEMS_OFFSET + item];
00375 }
00376
00377 void SRAMFile::setItem(enum sf_item item, int count) {
00378 Q_ASSERT(isValid(getGame()));
00379 Q_ASSERT((count >= 0) && (count < 100));
00380
00381 offset[SRAM_ITEMS_OFFSET + item] = count;
00382 modified = true;
00383 }
00384
00385 int SRAMFile::getLevel(enum sf_hero hero) const {
00386 Q_ASSERT(isValid(getGame()));
00387
00388 return offset[((hero == SF_BOY) ?
00389 SRAM_BOY_LEVEL_OFFSET :
00390 SRAM_DOG_LEVEL_OFFSET)];
00391 }
00392
00393 void SRAMFile::setLevel(enum sf_hero hero, int level) {
00394 Q_ASSERT(isValid(getGame()));
00395 Q_ASSERT((level >= 1) && (level < 100));
00396
00397 offset[((hero == SF_BOY) ?
00398 SRAM_BOY_LEVEL_OFFSET :
00399 SRAM_DOG_LEVEL_OFFSET)] = level;
00400 modified = true;
00401 }
00402
00403 quint16 SRAMFile::getMaxHP(enum sf_hero hero) const {
00404 Q_ASSERT(isValid(getGame()));
00405
00406 const quint16 *data =
00407 reinterpret_cast<const quint16 *>(offset +
00408 ((hero == SF_BOY) ?
00409 SRAM_BOY_MAXHP_OFFSET :
00410 SRAM_DOG_MAXHP_OFFSET));
00411
00412 return qFromLittleEndian(*data);
00413 }
00414
00415 void SRAMFile::setMaxHP(enum sf_hero hero, quint16 hp) {
00416 Q_ASSERT(isValid(getGame()));
00417 Q_ASSERT(hp < 1000);
00418
00419 quint16 *data = reinterpret_cast<quint16 *>(offset +
00420 ((hero == SF_BOY) ?
00421 SRAM_BOY_MAXHP_OFFSET :
00422 SRAM_DOG_MAXHP_OFFSET));
00423
00424 *data = qToLittleEndian(hp);
00425 modified = true;
00426 }
00427
00428 quint32 SRAMFile::getMoney(enum sf_money money) const {
00429 Q_ASSERT(isValid(getGame()));
00430
00431 const quint32 *data =
00432 reinterpret_cast<const quint32 *>(offset +
00433 SRAM_MONEY_OFFSET + (money * 3));
00434
00435 return (qFromLittleEndian(*data) & 0xFFFFFF);
00436 }
00437
00438 void SRAMFile::setMoney(enum sf_money money, quint32 count) {
00439 Q_ASSERT(isValid(getGame()));
00440 Q_ASSERT((count >= 0) && (count < 16777216));
00441
00442 unsigned char *data = offset + SRAM_MONEY_OFFSET + (money * 3);
00443
00444 data[0] = count;
00445 data[1] = count >> 8;
00446 data[2] = count >> 16;
00447
00448 modified = true;
00449 }
00450
00451 QString SRAMFile::getName(enum sf_hero hero) const {
00452 Q_ASSERT(isValid(getGame()));
00453
00454 const char *data =
00455 reinterpret_cast<const char *>(offset +
00456 ((hero == SF_BOY) ?
00457 SRAM_BOY_NAME_OFFSET :
00458 SRAM_DOG_NAME_OFFSET));
00459
00460 QString name = QString::fromAscii(data);
00461
00462 if (region == REGION_GERMANY) {
00463 name.replace(QChar(0xCB), QChar(0xC4));
00464 name.replace(QChar(0xDB), QChar(0xD6));
00465 name.replace(QChar(0xDF), QChar(0xDC));
00466 name.replace(QChar(0xE3), QChar(0xE4));
00467 name.replace(QChar(0xEF), QChar(0xF6));
00468 name.replace(QChar(0xF3), QChar(0xFC));
00469 name.replace(QChar(0xC6), QChar(0xDF));
00470 } else if (region == REGION_SPAIN) {
00471 name.replace(QChar(0xD7), QChar(0xF1));
00472 }
00473
00474 return name;
00475 }
00476
00477 void SRAMFile::setName(enum sf_hero hero, const QString &name) {
00478 Q_ASSERT(isValid(getGame()));
00479 Q_ASSERT(name.length() <= 15);
00480
00481 char *data = reinterpret_cast<char *>(offset + ((hero == SF_BOY) ?
00482 SRAM_BOY_NAME_OFFSET :
00483 SRAM_DOG_NAME_OFFSET));
00484
00485 QString temp = name;
00486
00487 if (region == REGION_GERMANY) {
00488 temp.replace(QChar(0xDF), QChar(0xC6));
00489 temp.replace(QChar(0xFC), QChar(0xF3));
00490 temp.replace(QChar(0xF6), QChar(0xEF));
00491 temp.replace(QChar(0xE4), QChar(0xE3));
00492 temp.replace(QChar(0xDC), QChar(0xDF));
00493 temp.replace(QChar(0xD6), QChar(0xDB));
00494 temp.replace(QChar(0xC4), QChar(0xCB));
00495 } else if (region == REGION_SPAIN) {
00496 temp.replace(QChar(0xF1), QChar(0xD7));
00497 }
00498
00499 std::strcpy(data, temp.left(15).toAscii().data());
00500
00501 modified = true;
00502 }
00503
00504 quint16 SRAMFile::getTradeGood(enum sf_tradegood tradegood) const {
00505 Q_ASSERT(isValid(getGame()));
00506
00507 const quint16 *data =
00508 reinterpret_cast<const quint16 *>(offset + SRAM_TRADEGOODS_OFFSET);
00509
00510 return qFromLittleEndian(data[tradegood]);
00511 }
00512
00513 void SRAMFile::setTradeGood(enum sf_tradegood tradegood, quint16 count) {
00514 Q_ASSERT(isValid(getGame()));
00515 Q_ASSERT(count < 100);
00516
00517 quint16 *data =
00518 reinterpret_cast<quint16 *>(offset + SRAM_TRADEGOODS_OFFSET);
00519
00520 data[tradegood] = qToLittleEndian(count);
00521 modified = true;
00522 }
00523
00524 bool SRAMFile::hasWeapon(enum sf_weapon weapon) const {
00525 Q_ASSERT(isValid(getGame()));
00526
00527 return (offset[SRAM_WEAPON_OFFSETS[weapon].first] &
00528 SRAM_WEAPON_OFFSETS[weapon].second);
00529 }
00530
00531 void SRAMFile::setWeapon(enum sf_weapon weapon, bool have) {
00532 Q_ASSERT(isValid(getGame()));
00533
00534 unsigned char *data = offset + SRAM_WEAPON_OFFSETS[weapon].first;
00535
00536 if (have) {
00537 *data |= SRAM_WEAPON_OFFSETS[weapon].second;
00538 } else {
00539 *data &= ~SRAM_WEAPON_OFFSETS[weapon].second;
00540 }
00541
00542 modified = true;
00543 }
00544
00545 std::pair<int, int> SRAMFile::getWeaponLevel(enum sf_weapon weapon) const {
00546 Q_ASSERT(isValid(getGame()));
00547 Q_ASSERT(weapon != SF_BAZOOKA);
00548
00549 const unsigned char *data =
00550 offset + SRAM_WEAPONLEVELS_OFFSET + (weapon * 2);
00551
00552 return std::pair<int, int>(data[1], data[0]);
00553 }
00554
00555 void SRAMFile::setWeaponLevel(enum sf_weapon weapon,
00556 std::pair<int, int> level) {
00557 Q_ASSERT(isValid(getGame()));
00558 Q_ASSERT(weapon != SF_BAZOOKA);
00559 Q_ASSERT((level.first >= 1) && (level.first < 4));
00560 Q_ASSERT((level.second >= 0) && (level.second < 256));
00561
00562 unsigned char *data = offset + SRAM_WEAPONLEVELS_OFFSET + (weapon * 2);
00563
00564 data[1] = level.first;
00565 data[0] = level.second;
00566
00567 modified = true;
00568 }
00569