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