sramfile.cc

Go to the documentation of this file.
00001 /*
00002  * lozsrame - Legend of Zelda SRAM Editor
00003  * Copyright (C) 2007-2008 emuWorks
00004  * http://games.technoplaza.net/
00005  *
00006  * This file is part of lozsrame.
00007  *
00008  * lozsrame is free software; you can redistribute it and/or modify it under the
00009  * terms of the GNU General Public License as published by the Free Software
00010  * Foundation; either version 2 of the License, or (at your option) any later
00011  * version.
00012  *
00013  * lozsrame is distributed in the hope that it will be useful, but WITHOUT ANY
00014  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00015  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License along with
00018  * lozsrame; if not, write to the Free Software Foundation, Inc.,
00019  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020  */
00021 
00022 // $Id: sramfile.cc,v 1.8 2008/01/30 22:57:04 technoplaza Exp $
00023 
00024 #include <cstring>
00025 #include <fstream>
00026 
00027 #include <QtCore/qendian.h>
00028 
00029 #include "model/sramfile.hh"
00030 
00031 using namespace lozsrame;
00032 
00033 SRAMFile::SRAMFile(const QString &filename) throw(InvalidSRAMFileException) :
00034     modified(false) {
00035     std::ifstream file(filename.toAscii().data(), 
00036         std::ios_base::in | std::ios_base::binary);
00037     
00038     if (!file) {
00039         throw InvalidSRAMFileException(ISFE_FILENOTFOUND);
00040     }
00041     
00042     file.seekg(0, std::ios_base::end);
00043     
00044     if (file.tellg() != static_cast<std::streampos>(SRAM_SIZE)) {
00045         throw InvalidSRAMFileException(ISFE_INVALIDSIZE);
00046     }
00047     
00048     file.seekg(0, std::ios_base::beg);
00049     file.read(sram, SRAM_SIZE);
00050     file.close();
00051     
00052     // checksum to determine valid games
00053     std::memset(valid, 0, 3 * sizeof(bool));
00054     
00055     bool foundValid = false;
00056     QString emptyName("        ");
00057     
00058     for (int game = 2; game >= 0; --game) {
00059         if (checksum(game) == getChecksum(game)) {
00060             valid[game] = true;
00061             setGame(game);
00062             
00063             if (getName() == emptyName) {
00064                 // empty slot
00065                 valid[game] = false;
00066             } else {
00067                 foundValid = true;
00068             }
00069         }
00070     }
00071         
00072     if (!foundValid) {
00073         throw InvalidSRAMFileException(ISFE_NOVALIDGAMES);
00074     }
00075 }
00076 
00077 /*
00078     $A3E1:A9 00     LDA #$00        ; set checksum counters to 0
00079     $A3E3:85 CE     STA $00CE
00080     $A3E5:85 CF     STA $00CF
00081 
00082     $A3E7:A0 07     LDY #$07        ; y = 7 (name counter)
00083 
00084     $A3E9:B1 C4     LDA ($C4),Y     ; load name byte
00085     $A3EB:20 2B A4  JSR $A42B       ; add to checksum
00086 
00087     $A3EE:88        DEY             ; --y
00088     $A3EF:10 F8     BPL $A3E9       ; loop for all name bytes
00089 
00090     $A3F1:A0 27     LDY #$27        ; y = 27 (inventory counter)
00091 
00092     $A3F3:B1 C0     LDA ($C0),Y     ; load inventory byte
00093     $A3F5:20 2B A4  JSR $A42B       ; add to checksum
00094 
00095     $A3F8:88        DEY             ; --y
00096     $A3F9:10 F8     BPL $A3F3       ; loop for all inventory bytes
00097 
00098     $A3FB:A9 80     LDA #$80        ; c1 = 80h
00099     $A3FD:85 C1     STA $00C1
00100     $A3FF:A9 01     LDA #$01        ; c0 = 1h (map counter)
00101     $A401:85 C0     STA $00C0
00102 
00103     $A403:A0 00     LDY #$00        ; y = 0 (needed for addressing mode)
00104 
00105     $A405:B1 C2     LDA ($C2),Y     ; load map byte
00106     $A407:20 2B A4  JSR $A42B       ; add to checksum
00107 
00108     $A40A:E6 C2     INC $00C2       ; loop for all map bytes
00109     $A40C:D0 02     BNE $A410
00110     $A40E:E6 C3     INC $00C3
00111     $A410:C6 C1     DEC $00C1
00112     $A412:D0 F1     BNE $A405
00113 
00114     $A414:C6 C0     DEC $00C0
00115     $A416:A5 C0     LDA $00C0
00116     $A418:10 EB     BPL $A405
00117 
00118     $A41A:B1 C6     LDA ($C6),Y     ; load misc byte
00119     $A41C:20 2B A4  JSR $A42B       ; add to checksum
00120 
00121     $A41F:B1 C8     LDA ($C8),Y
00122     $A421:20 2B A4  JSR $A42B
00123 
00124     $A424:B1 CA     LDA ($CA),Y
00125     $A426:20 2B A4  JSR $A42B
00126 
00127     $A429:B1 CC     LDA ($CC),Y
00128 
00129     $A42B:18        CLC             ; 16-bit big endian add
00130     $A42C:65 CF     ADC $00CF
00131     $A42E:85 CF     STA $00CF
00132     $A430:A5 CE     LDA $00CE
00133     $A432:69 00     ADC #$00
00134     $A434:85 CE     STA $00CE
00135     $A436:60        RTS
00136 */
00137 quint16 SRAMFile::checksum(int game) const {
00138     Q_ASSERT((game >= 0) && (game < 3));
00139     
00140     quint16 checksum = 0;
00141     
00142     // name data
00143     for (int i = 0; i < NAME_DATA_SIZE; ++i) {
00144         checksum += static_cast<unsigned char>
00145             (sram[NAME_DATA + i + (game * NAME_DATA_SIZE)]);
00146     }
00147     
00148     // inventory data
00149     for (int i = 0; i < INVENTORY_DATA_SIZE; ++i) {
00150         checksum += static_cast<unsigned char>
00151             (sram[INVENTORY_DATA + i + (game * INVENTORY_DATA_SIZE)]);
00152     }
00153     
00154     // map data
00155     for (int i = 0; i < MAP_DATA_SIZE; ++i) {
00156         checksum += static_cast<unsigned char>
00157             (sram[MAP_DATA + i + (game * MAP_DATA_SIZE)]);
00158     }
00159     
00160     // misc data (0x512, 0x515, 0x518, 0x51B)
00161     for (int i = 0; i < MISC_DATA_SIZE; ++i) {
00162         checksum += static_cast<unsigned char>
00163             (sram[MISC_DATA + (i * 3) + game]);
00164     }
00165     
00166     return checksum;
00167 }
00168 
00169 bool SRAMFile::save(const QString &filename) {
00170     for (int i = 0; i < 3; ++i) {
00171         if (isValid(i)) {
00172             setChecksum(i, checksum(i));
00173         }
00174     }
00175     
00176     std::ofstream file(filename.toAscii().data(),
00177                        std::ios_base::out | std::ios_base::binary);
00178     
00179     if (!file) {
00180         return false;
00181     }
00182     
00183     file.write(sram, SRAM_SIZE);
00184     
00185     if (file.tellp() != static_cast<std::streampos>(SRAM_SIZE)) {
00186         return false;
00187     }
00188     
00189     file.close();
00190     modified = false;
00191     
00192     return true;
00193 }
00194 
00195 enum sf_arrow SRAMFile::getArrows() const {
00196     Q_ASSERT(isValid(game));
00197     
00198     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00199     
00200     return static_cast<enum sf_arrow>(ptr[ARROWS_OFFSET]);
00201 }
00202 
00203 void SRAMFile::setArrows(sf_arrow arrows) {
00204     Q_ASSERT(isValid(game));
00205     
00206     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00207     
00208     ptr[ARROWS_OFFSET] = arrows;
00209     modified = true;
00210 }
00211 
00212 int SRAMFile::getBombCapacity() const {
00213     Q_ASSERT(isValid(game));
00214     
00215     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00216     
00217     return ptr[BOMBCAPACITY_OFFSET];
00218 }
00219 
00220 void SRAMFile::setBombCapacity(int capacity) {
00221     Q_ASSERT(isValid(game));
00222     Q_ASSERT((capacity >= 0) && (capacity <= 16));
00223     
00224     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00225     
00226     ptr[BOMBCAPACITY_OFFSET] = capacity;
00227     modified = true;
00228 }
00229 
00230 int SRAMFile::getBombs() const {
00231     Q_ASSERT(isValid(game));
00232     
00233     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00234     
00235     return ptr[BOMBS_OFFSET];
00236 }
00237 
00238 void SRAMFile::setBombs(int bombs) {
00239     Q_ASSERT(isValid(game));
00240     Q_ASSERT((bombs >= 0) && (bombs <= 16));
00241     
00242     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00243     
00244     ptr[BOMBS_OFFSET] = bombs;
00245     modified = true;
00246 }
00247 
00248 enum sf_candle SRAMFile::getCandle() const {
00249     Q_ASSERT(isValid(game));
00250     
00251     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00252     
00253     return static_cast<enum sf_candle>(ptr[CANDLE_OFFSET]);
00254 }
00255 
00256 void SRAMFile::setCandle(enum sf_candle candle) {
00257     Q_ASSERT(isValid(game));
00258     
00259     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00260     
00261     ptr[CANDLE_OFFSET] = candle;
00262     modified = true;
00263 }
00264 
00265 quint16 SRAMFile::getChecksum(int game) const {
00266     Q_ASSERT((game >= 0) && (game < 3));
00267     
00268     const quint16 *ptr = reinterpret_cast<const quint16 *>
00269         (sram + CHECKSUM_OFFSET);
00270     
00271     return qFromBigEndian(ptr[game]);
00272 }
00273 
00274 void SRAMFile::setChecksum(int game, quint16 checksum) {
00275     Q_ASSERT((game >= 0) && (game < 3));
00276     
00277     quint16 *ptr = reinterpret_cast<quint16 *>
00278         (sram + CHECKSUM_OFFSET);
00279         
00280     ptr[game] = qToBigEndian(checksum);
00281 }
00282 
00283 bool SRAMFile::hasCompass(int level) const {
00284     Q_ASSERT(isValid(game));
00285     Q_ASSERT((level >= 1) && (level <= 9));
00286     
00287     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00288     
00289     if (level == 9) {
00290         return (ptr[COMPASS9_OFFSET] == 1);
00291     }
00292     
00293     return (ptr[COMPASS_OFFSET] & (1 << (level - 1)));
00294 }
00295 
00296 void SRAMFile::setCompass(int level, bool give) {
00297     Q_ASSERT(isValid(game));
00298     Q_ASSERT((level >= 1) && (level <= 9));
00299     
00300     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00301     
00302     if (level == 9) {
00303         ptr[COMPASS9_OFFSET] = (give ? 1 : 0);
00304     }
00305     
00306     if (give) {
00307         ptr[COMPASS_OFFSET] |= (1 << (level - 1));
00308     } else {
00309         ptr[COMPASS_OFFSET] &= ~(1 << (level - 1));
00310     }
00311     
00312     modified = true;
00313 }
00314 
00315 int SRAMFile::getHeartContainers() const {
00316     Q_ASSERT(isValid(game));
00317     
00318     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00319     
00320     return ((static_cast<unsigned char>(ptr[HEARTCONTAINERS_OFFSET]) >> 4) + 1);
00321 }
00322 
00323 void SRAMFile::setHeartContainers(int containers) {
00324     Q_ASSERT(isValid(game));
00325     Q_ASSERT((containers > 0) && (containers <= 16));
00326     
00327     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00328     
00329     ptr[HEARTCONTAINERS_OFFSET] &= 0x0F;
00330     ptr[HEARTCONTAINERS_OFFSET] |= ((containers - 1) << 4);
00331     modified = true;
00332 }
00333 
00334 bool SRAMFile::hasItem(enum sf_item item) const {
00335     Q_ASSERT(isValid(game));
00336     
00337     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00338     
00339     return (ptr[item] == 1);
00340 }
00341 
00342 void SRAMFile::setItem(enum sf_item item, bool give) {
00343     Q_ASSERT(isValid(game));
00344     
00345     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00346     
00347     ptr[item] = (give ? 1 : 0);
00348     modified = true;
00349 }
00350 
00351 int SRAMFile::getKeys() const {
00352     Q_ASSERT(isValid(game));
00353     
00354     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00355     
00356     return ptr[KEYS_OFFSET];
00357 }
00358 
00359 void SRAMFile::setKeys(int keys) {
00360     Q_ASSERT(isValid(game));
00361     Q_ASSERT((keys >= 0) && (keys <= 99));
00362     
00363     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00364     
00365     ptr[KEYS_OFFSET] = keys;
00366     modified = true;
00367 }
00368 
00369 bool SRAMFile::hasMap(int level) const {
00370     Q_ASSERT(isValid(game));
00371     Q_ASSERT((level >= 1) && (level <= 9));
00372     
00373     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00374     
00375     if (level == 9) {
00376         return (ptr[MAP9_OFFSET] == 1);
00377     }
00378     
00379     return (ptr[MAP_OFFSET] & (1 << (level - 1)));
00380 }
00381 
00382 void SRAMFile::setMap(int level, bool give) {
00383     Q_ASSERT(isValid(game));
00384     Q_ASSERT((level >= 1) && (level <= 9));
00385     
00386     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00387     
00388     if (level == 9) {
00389         ptr[MAP9_OFFSET] = (give ? 1 : 0);
00390     }
00391     
00392     if (give) {
00393         ptr[MAP_OFFSET] |= (1 << (level - 1));
00394     } else {
00395         ptr[MAP_OFFSET] &= ~(1 << (level - 1));
00396     }
00397     
00398     modified = true;
00399 }
00400 
00401 QString SRAMFile::getName() const {
00402     Q_ASSERT(isValid(game));
00403     
00404     QString name;
00405     const char *ptr = (sram + NAME_DATA + (game * NAME_DATA_SIZE));
00406     
00407     for (int i = 0; i < NAME_DATA_SIZE; ++i) {
00408         char ch = ptr[i];
00409         
00410         if ((ch >= 0) && (ch <= 9)) {
00411             name += ('0' + ch);
00412         } else if ((ch >= 0xA) && (ch <= 0x23)) {
00413             name += ('A' + ch - 0xA);
00414         } else if (ch == 0x24) {
00415             name += ' ';
00416         } else if (ch == 0x28) {
00417             name += ',';
00418         } else if (ch == 0x29) {
00419             name += '!';
00420         } else if (ch == 0x2A) {
00421             name += '\'';
00422         } else if (ch == 0x2B) {
00423             name += '&';
00424         } else if (ch == 0x2C) {
00425             name += '.';
00426         } else if (ch == 0x2D) {
00427             name += '\"';
00428         } else if (ch == 0x2E) {
00429             name += '?';
00430         } else if (ch == 0x2F) {
00431             name += '_';
00432         } else {
00433             // invalid character
00434             Q_ASSERT(false);
00435         }
00436     }
00437     
00438     return name;
00439 }
00440 
00441 void SRAMFile::setName(const QString &name) {
00442     Q_ASSERT(isValid(game));
00443     
00444     char *ptr = (sram + NAME_DATA + (game * NAME_DATA_SIZE));
00445     
00446     for (int count = 0; count < 8; ++count) {
00447         if (name.length() > count) {
00448             char ch = name[count].toAscii();
00449             
00450             if ((ch >= '0') && (ch <= '9')) {
00451                 ptr[count] = (ch - '0');
00452             } else if ((ch >= 'A') && (ch <= 'Z')) {
00453                 ptr[count] = (ch - 'A' + 0xA);
00454             } else if (ch == ' ') {
00455                 ptr[count] = 0x24;
00456             } else if (ch == ',') {
00457                 ptr[count] = 0x28;
00458             } else if (ch == '!') {
00459                 ptr[count] = 0x29;
00460             } else if (ch == '\'') {
00461                 ptr[count] = 0x2A;
00462             } else if (ch == '&') {
00463                 ptr[count] = 0x2B;
00464             } else if (ch == '.') {
00465                 ptr[count] = 0x2C;
00466             } else if (ch == '\"') {
00467                 ptr[count] = 0x2D;
00468             } else if (ch == '?') {
00469                 ptr[count] = 0x2E;
00470             } else if (ch == '_') {
00471                 ptr[count] = 0x2F;
00472             } else {
00473                 Q_ASSERT(false);
00474             }
00475         } else {
00476             // pad with spaces
00477             ptr[count] = 0x24;
00478         }
00479     }
00480     
00481     modified = true;
00482 }
00483 
00484 enum sf_note SRAMFile::getNote() const {
00485     Q_ASSERT(isValid(game));
00486     
00487     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00488     
00489     return static_cast<enum sf_note>(ptr[NOTE_OFFSET]);
00490 }
00491 
00492 void SRAMFile::setNote(enum sf_note note) {
00493     Q_ASSERT(isValid(game));
00494     
00495     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00496     
00497     ptr[NOTE_OFFSET] = note;
00498     modified = true;
00499 }
00500 
00501 int SRAMFile::getPlayCount() const {
00502     Q_ASSERT(isValid(game));
00503     
00504     const unsigned char *ptr = (reinterpret_cast<const unsigned char *>(sram)
00505         + MISC_DATA + PLAYCOUNT_OFFSET);
00506     
00507     return ptr[game];
00508 }
00509 
00510 void SRAMFile::setPlayCount(int count) {
00511     Q_ASSERT(isValid(game));
00512     Q_ASSERT((count >= 0) && (count <= 255));
00513     
00514     unsigned char *ptr = (reinterpret_cast<unsigned char *>(sram)
00515         + MISC_DATA + PLAYCOUNT_OFFSET);
00516     
00517     ptr[game] = count;
00518     modified = true;
00519 }
00520 
00521 enum sf_potion SRAMFile::getPotion() const {
00522     Q_ASSERT(isValid(game));
00523     
00524     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00525     
00526     return static_cast<sf_potion>(ptr[POTION_OFFSET]);
00527 }
00528 
00529 void SRAMFile::setPotion(enum sf_potion potion) {
00530     Q_ASSERT(isValid(game));
00531     
00532     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00533     
00534     ptr[POTION_OFFSET] = potion;
00535     modified = true;
00536 }
00537 
00538 enum sf_quest SRAMFile::getQuest() const {
00539     Q_ASSERT(isValid(game));
00540     
00541     const char *ptr = (sram + MISC_DATA + QUEST_OFFSET);
00542     
00543     return static_cast<enum sf_quest>(ptr[game]);
00544 }
00545 
00546 void SRAMFile::setQuest(enum sf_quest quest) {
00547     Q_ASSERT(isValid(game));
00548     
00549     char *ptr = (sram + MISC_DATA + QUEST_OFFSET);
00550     
00551     ptr[game] = quest;
00552     modified = true;
00553 }
00554 
00555 enum sf_ring SRAMFile::getRing() const {
00556     Q_ASSERT(isValid(game));
00557     
00558     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00559     
00560     return static_cast<enum sf_ring>(ptr[RING_OFFSET]);
00561 }
00562 
00563 void SRAMFile::setRing(enum sf_ring ring) {
00564     Q_ASSERT(isValid(game));
00565     
00566     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00567     
00568     ptr[RING_OFFSET] = ring;
00569     modified = true;
00570 }
00571 
00572 int SRAMFile::getRupees() const {
00573     Q_ASSERT(isValid(game));
00574     
00575     const unsigned char *ptr = (reinterpret_cast<const unsigned char *>(sram)
00576         + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00577     
00578     return ptr[RUPEES_OFFSET];
00579 }
00580 
00581 void SRAMFile::setRupees(int rupees) {
00582     Q_ASSERT(isValid(game));
00583     Q_ASSERT((rupees >= 0) && (rupees <= 255));
00584     
00585     unsigned char *ptr = (reinterpret_cast<unsigned char *>(sram)
00586         + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00587     
00588     ptr[RUPEES_OFFSET] = rupees;
00589     modified = true;
00590 }
00591 
00592 enum sf_sword SRAMFile::getSword() const {
00593     Q_ASSERT(isValid(game));
00594     
00595     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00596     
00597     return static_cast<enum sf_sword>(ptr[SWORD_OFFSET]);
00598 }
00599 
00600 void SRAMFile::setSword(enum sf_sword sword) {
00601     Q_ASSERT(isValid(game));
00602     
00603     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00604     
00605     ptr[SWORD_OFFSET] = sword;
00606     modified = true;
00607 }
00608 
00609 bool SRAMFile::hasTriforce(int piece) const {
00610     Q_ASSERT(isValid(game));
00611     Q_ASSERT((piece >= 1) && (piece <= 8));
00612     
00613     const char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00614     
00615     return (ptr[TRIFORCE_OFFSET] & (1 << (piece - 1)));
00616 }
00617 
00618 void SRAMFile::setTriforce(int piece, bool give) {
00619     Q_ASSERT(isValid(game));
00620     Q_ASSERT((piece >= 1) && (piece <= 8));
00621     
00622     char *ptr = (sram + INVENTORY_DATA + (game * INVENTORY_DATA_SIZE));
00623     
00624     if (give) {
00625         ptr[TRIFORCE_OFFSET] |= (1 << (piece - 1));
00626     } else {
00627         ptr[TRIFORCE_OFFSET] &= ~(1 << (piece - 1));
00628     }
00629     
00630     modified = true;
00631 }
00632 

Generated on Fri Feb 1 09:29:51 2008 for Legend of Zelda SRAM Editor by  doxygen 1.5.3