Logo Search packages:      
Sourcecode: kamefu version File versions

snesrommetadata.cpp

/*
    snesrommetadata.cpp - Extract metadata(information) from SNES ROM file.

    Copyright (c) 2005      by Michaƫl Larouche       <michael.larouche@kdemail.net>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/
#include "snesrommetadata.h"

// Qt includes
#include <qdatastream.h>

// KDE includes
#include <klocale.h>
#include <kdebug.h>
#include <kmdcodec.h>

#include "snes_country.h"
#include "nintendomakerlist.h"

const int loRomHeaderlessLocation = 0x7fc0; // Without the 512 byte header
const int hiRomHeaderlessLocation = 0xffc0; // Without the 512 byte header
const int loRomHeaderLocation = 0x81c0; // With the 512 byte header
const int hiRomHeaderLocation = 0x101c0; // With the 512 byte header

SnesRomMetaData::SnesRomMetaData()
 : Kamefu::DefaultRomMetaData()
{
}


SnesRomMetaData::~SnesRomMetaData()
{
}


00044 Kamefu::RomMetaInformation SnesRomMetaData::extractMetaInformation(KFileItem* item)
{
    return DefaultRomMetaData::extractMetaInformation(item);
}

00049 Kamefu::RomMetaInformation SnesRomMetaData::extractMetaInformation(QIODevice* device)
{
      Q_UINT8 countryCode, editorCode;
      QString editorName(i18n("Unknown")), countryName(i18n("Unknown")), md5Hash;
      int makerCode, selectedLocation=0;
      
      // Generate the MD5 Hash
      KMD5 context(0L);
      context.update(*device);
      md5Hash = context.hexDigest();

      QDataStream stream(device);
      stream.setByteOrder(QDataStream::LittleEndian);

      // Get the best header location (header/headerless and HiROM or LoROM)
      selectedLocation = getBestHeaderLocation(device, stream);
      selectedLocation += 0x19;
      // Go directly to country code (faster reading).
      device->at( selectedLocation );

      // Read the countryCode
      stream >> countryCode;
      if(countryCode < 14)
            countryName = snesCountryList[countryCode];
      
      // Read the editor
      stream >> editorCode;
      kdDebug() << k_funcinfo << "Read editorCode: " << (uint)editorCode << endl;

      if(editorCode == 0x33) // This a special code for newer ROM, the editor is in ASCII below
            makerCode = getMakerCode(device, stream);
      else if(editorCode != 0)
            makerCode = editorCode;
      else 
            makerCode = 0;
      makerCode = (makerCode >> 4) * 36 + (makerCode & 0x0f); // from uCON64 source.
      
      if(makerCode > 0 || makerCode <= nintendoMakerListLength)
            editorName = nintendoMakerList[makerCode];

      kdDebug() << k_funcinfo << "Maker Code: " << (int)makerCode << endl;
      kdDebug() << k_funcinfo << "Editor Name: " << editorName << endl;

      Kamefu::RomMetaInformation romMetaData;
      romMetaData.setEditor(editorName);
      romMetaData.setCountry(countryName);
      romMetaData.setMd5Hash(md5Hash);

      return romMetaData;
}

00100 int SnesRomMetaData::getMakerCode(QIODevice *file, QDataStream &stream)
{
      int oldLocation = file->at();
      int makerLocation = oldLocation - 0x2b; // Maybe 0x2a
      int result = 0;

      QByteArray makerRaw(2);
      
      file->at(makerLocation);
      stream.readRawBytes(makerRaw.data(), makerRaw.size());
      
      bool ok;
      QString sMaker(makerRaw);
      kdDebug() << k_funcinfo << "QString maker: " << sMaker << endl;
      result = sMaker.toInt(&ok, 16);

      kdDebug() << k_funcinfo << "Returned Maker Code: " << result << endl;

      // Return to the latest location.
      file->at(oldLocation);

      return result;
}

00124 int SnesRomMetaData::getBestHeaderLocation(QIODevice *file, QDataStream &stream)
{
      int i, theBestLocation=0, tempScore=0;
      int scores[4];
      int locations[4] = {hiRomHeaderLocation, hiRomHeaderlessLocation, loRomHeaderLocation, loRomHeaderlessLocation};
      for(i=0; i<4; i++)
      {
            scores[i] = checkInformationValidity(locations[i], file, stream);
            kdDebug() << k_funcinfo << "0x" << QString::number(locations[i], 16) << " score: " << scores[i] << endl;
      }
      // Assume that the first location is the best
      tempScore = scores[0];
      theBestLocation = locations[0];
      // Find the best score
      for(i=1; i<4; i++)
      {
            if(scores[i] > tempScore)
            {
                  tempScore = scores[i];
                  theBestLocation = locations[i];
            }
      }
      
      kdDebug() << "The best header location: 0x" << QString::number(theBestLocation, 16) << endl;
      
      return theBestLocation;
}

00152 bool SnesRomMetaData::canPrint(const QByteArray &charArray)
{
      uint i=0;
      for(i=0; i<charArray.size()-1; i++)
      {
            char temp = charArray.at(i);
            // Make sure all the characters are in printable ASCII range.
            if(temp < 0x20 || temp > 0x7E)
                  return false;
      }
      return true;
}

00165 int SnesRomMetaData::checkInformationValidity(int location, QIODevice *file, QDataStream &stream)
{
      int score = 0;
      Q_UINT8 temp1, temp2;

      // Set the rom at test location.
      file->at(location);
      // Internal Name check
      QByteArray gameName(21);
      stream.readRawBytes(gameName.data(), gameName.size());

      if( canPrint(gameName) )
            score += 1;

      stream >> temp1;// Skip rom makeup
      stream >> temp1; 
      // map type
      if( (temp1 & 0xf) < 4)
            score += 2;
    // ROM size
      stream >> temp1;
      if (1 << (temp1 - 7) <= 64)
            score +=1;

      // SRAM size
      stream >> temp1;
      if(1 << temp1 <= 256)
            score += 1;
      // Country
      stream >> temp1;
      if(temp1 <= 13)
            score += 1;
      // Editor "escape code"
      stream >> temp1;
      if(temp1 == 0x33)
            score += 2;
      else
      {
            temp1 = (temp1 >> 4) * 36 + (temp1 & 0x0f);
            if( nintendoMakerList[temp1] != QString::null )
                  score += 2;
      }
      // Version
      stream >> temp1;
      if(temp1 <= 2)
            score += 2;

      Q_UINT16 checksum1, checksum2;
      // Check first checksum.
      stream >> temp1;
      stream >> temp2;
      checksum1 = temp1 + (temp2 << 8);
      // Check second checksum.
      stream >> temp1;
      stream >> temp2;
      checksum2  = temp1 + (temp2 << 8);
      
      if(checksum1 + checksum2 == 0xffff)
      {
            if( checksum1 == 0xffff || checksum2 == 0xffff )
                  score += 3;
            else
                  score += 4;
      }
      // Reset vector
      stream >> temp1;
      if(temp1 & 0x80)
            score += 3;

      return score;
}


Generated by  Doxygen 1.6.0   Back to index