bool PalmBinaryLoader::loadFromMemory(QByteArray &img) { const int size = img.size(); m_image = reinterpret_cast<uint8_t *>(img.data()); if (static_cast<unsigned long>(size) < sizeof(PRCHeader) + sizeof(PRCRecordList)) { LOG_ERROR("This is not a standard .prc file"); return false; } PRCHeader *prcHeader = reinterpret_cast<PRCHeader *>(img.data()); // Check type at offset 0x3C; should be "appl" (or "palm"; ugh!) if ((strncmp(prcHeader->type, "appl", 4) != 0) && (strncmp(prcHeader->type, "panl", 4) != 0) && (strncmp(prcHeader->type, "libr", 4) != 0)) { LOG_ERROR("This is not a standard .prc file"); return false; } addTrapSymbols(); // Get the number of resource headers (one section per resource) PRCRecordList *records = reinterpret_cast<PRCRecordList *>(m_image + sizeof(PRCHeader)); if (records->nextRecordListOffset != 0) { LOG_ERROR("Reading PRC files with multiple record lists is not supported!"); return false; } const SWord numSections = Util::readWord(&records->resourceCount, Endian::Big); // Iterate through the resource headers (generating section info structs) PRCResource *resource = reinterpret_cast<PRCResource *>(m_image + sizeof(PRCHeader) + sizeof(PRCRecordList)); std::vector<SectionProperties> sectionProperties; for (unsigned i = 0; i < numSections; i++) { char buf[5]; strncpy(buf, reinterpret_cast<char *>(&resource[i].type), 4); buf[4] = 0; SWord id = Util::readWord(&resource[i].id, Endian::Big); QString name = QString("%1%2").arg(buf).arg(id); DWord dataOffset = Util::readDWord(&resource[i].dataOffset, Endian::Big); Address startAddr(dataOffset); if (i > 0) { sectionProperties[i - 1].to = startAddr; } sectionProperties.push_back( { name, startAddr, Address::INVALID, HostAddress(m_image + dataOffset) }); } // last section extends until eof sectionProperties[numSections - 1].to = Address(size); for (SectionProperties props : sectionProperties) { assert(props.to != Address::INVALID); BinarySection *sect = m_binaryImage->createSection(props.name, props.from, props.to); if (sect) { // Decide if code or data; note that code0 is a special case (not code) sect->setHostAddr(props.hostAddr); sect->setCode((props.name != "code0") && (props.name.startsWith("code"))); sect->setData(props.name.startsWith("data")); sect->setEndian(Endian::Little); // little endian sect->setEntrySize(1); // No info available sect->addDefinedArea(props.from, props.to); // no BSS } } // Create a separate, uncompressed, initialised data section BinarySection *dataSection = m_binaryImage->getSectionByName("data0"); if (dataSection == nullptr) { LOG_ERROR("No data section found!"); return false; } const BinarySection *code0Section = m_binaryImage->getSectionByName("code0"); if (code0Section == nullptr) { LOG_ERROR("No code 0 section found!"); return false; } // When the info is all boiled down, the two things we need from the // code 0 section are at offset 0, the size of data above a5, and at // offset 4, the size below. Save the size below as a member variable m_sizeBelowA5 = UINT4ADDR(code0Section->getHostAddr() + 4); // Total size is this plus the amount above (>=) a5 unsigned sizeData = m_sizeBelowA5 + UINT4ADDR(code0Section->getHostAddr()); // Allocate a new data section m_data = new unsigned char[sizeData]; if (m_data == nullptr) { LOG_FATAL("Could not allocate %1 bytes for data section", sizeData); } // Uncompress the data. Skip first long (offset of CODE1 "xrefs") Byte *p = reinterpret_cast<Byte *>((dataSection->getHostAddr() + 4).value()); int start = static_cast<int>(UINT4(p)); p += 4; unsigned char *q = (m_data + m_sizeBelowA5 + start); bool done = false; while (!done && (p < reinterpret_cast<unsigned char *>( (dataSection->getHostAddr() + dataSection->getSize()).value()))) { unsigned char rle = *p++; if (rle == 0) { done = true; break; } else if (rle == 1) { // 0x01 b_0 b_1 // => 0x00 0x00 0x00 0x00 0xFF 0xFF b_0 b_1 *q++ = 0x00; *q++ = 0x00; *q++ = 0x00; *q++ = 0x00; *q++ = 0xFF; *q++ = 0xFF; *q++ = *p++; *q++ = *p++; } else if (rle == 2) { // 0x02 b_0 b_1 b_2 // => 0x00 0x00 0x00 0x00 0xFF b_0 b_1 b_2 *q++ = 0x00; *q++ = 0x00; *q++ = 0x00; *q++ = 0x00; *q++ = 0xFF; *q++ = *p++; *q++ = *p++; *q++ = *p++; } else if (rle == 3) { // 0x03 b_0 b_1 b_2 // => 0xA9 0xF0 0x00 0x00 b_0 b_1 0x00 b_2 *q++ = 0xA9; *q++ = 0xF0; *q++ = 0x00; *q++ = 0x00; *q++ = *p++; *q++ = *p++; *q++ = 0x00; *q++ = *p++; } else if (rle == 4) { // 0x04 b_0 b_1 b_2 b_3 // => 0xA9 axF0 0x00 b_0 b_1 b_3 0x00 b_3 *q++ = 0xA9; *q++ = 0xF0; *q++ = 0x00; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = 0x00; *q++ = *p++; } else if (rle < 0x10) { // 5-0xF are invalid. assert(false); } else if (rle >= 0x80) { // n+1 bytes of literal data for (int k = 0; k <= (rle - 0x80); k++) { *q++ = *p++; } } else if (rle >= 40) { // n+1 repetitions of 0 for (int k = 0; k <= (rle - 0x40); k++) { *q++ = 0x00; } } else if (rle >= 20) { // n+2 repetitions of b unsigned char b = *p++; for (int k = 0; k < (rle - 0x20 + 2); k++) { *q++ = b; } } else { // 0x10: n+1 repetitions of 0xFF for (int k = 0; k <= (rle - 0x10); k++) { *q++ = 0xFF; } } } if (!done) { LOG_WARN("Compressed data section premature end"); } LOG_VERBOSE("Used %1 bytes of %2 in decompressing data section", p - reinterpret_cast<unsigned char *>(dataSection->getHostAddr().value()), dataSection->getSize()); // Replace the data pointer and size with the uncompressed versions dataSection->setHostAddr(HostAddress(m_data)); dataSection->resize(sizeData); m_symbols->createSymbol(getMainEntryPoint(), "PilotMain")->setAttribute("EntryPoint", true); return true; }
bool PalmBinaryFile::RealLoad(const char* sName) { FILE *fp; char buf[32]; m_pFileName = sName; if ((fp = fopen(sName, "rb")) == NULL) { fprintf(stderr, "Could not open binary file %s\n", sName); return false; } fseek(fp, 0, SEEK_END); long size = ftell(fp); // Allocate a buffer for the image m_pImage = new unsigned char[size]; if (m_pImage == 0) { fprintf(stderr, "Could not allocate %ld bytes for image\n", size); return false; } memset(m_pImage, size, 0); fseek(fp, 0, SEEK_SET); if (fread(m_pImage, 1, size, fp) != (unsigned)size) { fprintf(stderr, "Error reading binary file %s\n", sName); return false; } // Check type at offset 0x3C; should be "appl" (or "palm"; ugh!) if ((strncmp((char*)(m_pImage+0x3C), "appl", 4) != 0) && (strncmp((char*)(m_pImage+0x3C), "panl", 4) != 0) && (strncmp((char*)(m_pImage+0x3C), "libr", 4) != 0)) { fprintf(stderr, "%s is not a standard .prc file\n", sName); return false; } // Get the number of resource headers (one section per resource) m_iNumSections = (m_pImage[0x4C] << 8) + m_pImage[0x4D]; // Allocate the section information m_pSections = new SectionInfo[m_iNumSections]; if (m_pSections == 0) { fprintf(stderr, "Could not allocate section info array of %d items\n", m_iNumSections); if (m_pImage) { delete m_pImage; m_pImage = 0; } } // Iterate through the resource headers (generating section info structs) unsigned char* p = m_pImage + 0x4E; // First resource header unsigned off = 0; for (int i=0; i < m_iNumSections; i++) { // First get the name (4 alpha) strncpy(buf, (char*)p, 4); buf[4] = '\0'; std::string name(buf); // Now get the identifier (2 byte binary) unsigned id = (p[4] << 8) + p[5]; sprintf(buf, "%d", id); // Join the id to the name, e.g. code0, data12 name += buf; m_pSections[i].pSectionName = new char[name.size()+1]; strcpy(m_pSections[i].pSectionName, name.c_str()); p += 4+2; off = UINT4(p); p += 4; m_pSections[i].uNativeAddr = off; m_pSections[i].uHostAddr = off + m_pImage; // Guess the length if (i > 0) { m_pSections[i-1].uSectionSize = off - m_pSections[i-1].uNativeAddr; m_pSections[i].uSectionEntrySize = 1; // No info available } // Decide if code or data; note that code0 is a special case (not code) m_pSections[i].bCode = (name != "code0") && (name.substr(0, 4) == "code"); m_pSections[i].bData = name.substr(0, 4) == "data"; } // Set the length for the last section m_pSections[m_iNumSections-1].uSectionSize = size - off; // Create a separate, uncompressed, initialised data section SectionInfo* pData = GetSectionInfoByName("data0"); if (pData == 0) { fprintf(stderr, "No data section!\n"); return false; } SectionInfo* pCode0 = GetSectionInfoByName("code0"); if (pCode0 == 0) { fprintf(stderr, "No code 0 section!\n"); return false; } // When the info is all boiled down, the two things we need from the // code 0 section are at offset 0, the size of data above a5, and at // offset 4, the size below. Save the size below as a member variable m_SizeBelowA5 = UINT4(pCode0->uHostAddr+4); // Total size is this plus the amount above (>=) a5 unsigned sizeData = m_SizeBelowA5 + UINT4(pCode0->uHostAddr); // Allocate a new data section m_pData = new unsigned char[sizeData]; if (m_pData == 0) { fprintf(stderr, "Could not allocate %u bytes for data section\n", sizeData); } // Uncompress the data. Skip first long (offset of CODE1 "xrefs") p = (unsigned char*)(pData->uHostAddr+4); int start = (int) UINT4(p); p += 4; unsigned char* q = (m_pData + m_SizeBelowA5 + start); bool done = false; while (!done && (p < (unsigned char*)(pData->uHostAddr + pData->uSectionSize))) { unsigned char rle = *p++; if (rle == 0) { done = true; break; } else if (rle == 1) { // 0x01 b_0 b_1 // => 0x00 0x00 0x00 0x00 0xFF 0xFF b_0 b_1 *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0xFF; *q++ = 0xFF; *q++ = *p++; *q++ = *p++; } else if (rle == 2) { // 0x02 b_0 b_1 b_2 // => 0x00 0x00 0x00 0x00 0xFF b_0 b_1 b_2 *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0xFF; *q++ = *p++; *q++ = *p++; *q++ = *p++; } else if (rle == 3) { // 0x03 b_0 b_1 b_2 // => 0xA9 0xF0 0x00 0x00 b_0 b_1 0x00 b_2 *q++ = 0xA9; *q++ = 0xF0; *q++ = 0; *q++ = 0; *q++ = *p++; *q++ = *p++; *q++ = 0; *q++ = *p++; } else if (rle == 4) { // 0x04 b_0 b_1 b_2 b_3 // => 0xA9 axF0 0x00 b_0 b_1 b_3 0x00 b_3 *q++ = 0xA9; *q++ = 0xF0; *q++ = 0; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = 0; *q++ = *p++; } else if (rle < 0x10) { // 5-0xF are invalid. assert(0); } else if (rle >= 0x80) { // n+1 bytes of literal data for (int k=0; k <= (rle-0x80); k++) *q++ = *p++; } else if (rle >= 40) { // n+1 repetitions of 0 for (int k=0; k <= (rle-0x40); k++) *q++ = 0; } else if (rle >= 20) { // n+2 repetitions of b unsigned char b = *p++; for (int k=0; k < (rle-0x20+2); k++) *q++ = b; } else { // 0x10: n+1 repetitions of 0xFF for (int k=0; k <= (rle-0x10); k++) *q++ = 0xFF; } } if (!done) fprintf(stderr, "Warning! Compressed data section premature end\n"); //printf("Used %u bytes of %u in decompressing data section\n", //p-(unsigned char*)pData->uHostAddr, pData->uSectionSize); // Replace the data pointer and size with the uncompressed versions pData->uHostAddr = m_pData; pData->uSectionSize = sizeData; // May as well make the native address zero; certainly the offset in the // file is no longer appropriate (and is confusing) pData->uNativeAddr = 0; return true; }