uint32 CIoman::Seek(uint32 handle, uint32 position, uint32 whence) { CLog::GetInstance().Print(LOG_NAME, "Seek(handle = %d, position = 0x%X, whence = %d);\r\n", handle, position, whence); uint32 result = 0xFFFFFFFF; try { Framework::CStream* stream = GetFileStream(handle); switch(whence) { case SEEK_DIR_SET: whence = Framework::STREAM_SEEK_SET; break; case SEEK_DIR_CUR: whence = Framework::STREAM_SEEK_CUR; break; case SEEK_DIR_END: whence = Framework::STREAM_SEEK_END; break; } stream->Seek(position, static_cast<Framework::STREAM_SEEK_DIRECTION>(whence)); result = static_cast<uint32>(stream->Tell()); } catch(const std::exception& except) { CLog::GetInstance().Print(LOG_NAME, "%s: Error occured while trying to seek file : %s\r\n", __FUNCTION__, except.what()); } return result; }
void CDialog::WriteString(const std::wstring& str, Framework::CStream& stream) { for(wchar_t character : str) { stream.Write16(character); } stream.Write16(0); }
void CPwibSection::Read(Framework::CStream& inputStream) { uint32 signature = inputStream.Read32(); assert(signature == 'BIWP'); uint32 fileSize = inputStream.Read32_MSBF(); uint32 unknown = inputStream.Read32_MSBF(); m_dataOffset = inputStream.Read32_MSBF(); auto child = CSectionLoader::ReadSection(shared_from_this(), inputStream); }
void CStreamChunk::Read(Framework::CStream& inputStream) { CBaseChunk::Read(inputStream); m_numElements = inputStream.Read32_MSBF(); m_vertexCount = inputStream.Read32_MSBF(); m_bytesPerVertex = inputStream.Read32_MSBF(); m_unknown1 = inputStream.Read32_MSBF(); ReadElements(inputStream); ReadStream(inputStream); }
std::wstring CDialog::ReadString(Framework::CStream& stream) { std::wstring str; wchar_t ch = stream.Read16(); while(ch) { str += ch; ch = stream.Read16(); } return str; }
void CContainerChunk::LoadChildren(Framework::CStream& inputStream) { for(uint32 i = 0; i < m_childrenCount; i++) { uint64 baseOffset = inputStream.Tell(); auto chunk = CChunkLoader::Load(shared_from_this(), inputStream); AddChild(chunk); uint64 nextOffset = baseOffset + chunk->GetNextChunkOffset(); inputStream.Seek(nextOffset, Framework::STREAM_SEEK_SET); } }
void CContainerChunk::Read(Framework::CStream& inputStream) { CBaseChunk::Read(inputStream); m_childrenCount = inputStream.Read32_MSBF(); m_unknown1 = inputStream.Read32_MSBF(); m_unknown2 = inputStream.Read32_MSBF(); m_unknown3 = inputStream.Read32_MSBF(); LoadChildren(inputStream); }
void CDialog::WriteSzOrOrd(const SZ_OR_ORD& szOrOrd, Framework::CStream& stream) { stream.Write16(szOrOrd.isString); if(szOrOrd.isString == 0xFFFF) { stream.Write16(szOrOrd.ord); } else if(szOrOrd.isString != 0) { WriteString(szOrOrd.str, stream); } }
void CPatchFile::ExtractCompressed(Framework::CStream& outputStream, Framework::CStream& inputStream, uint32 compressedSize) { z_stream zStream; zStream.zalloc = Z_NULL; zStream.zfree = Z_NULL; zStream.opaque = Z_NULL; zStream.avail_in = 0; zStream.next_in = Z_NULL; if(inflateInit(&zStream) != Z_OK) { throw std::runtime_error("zlib stream initialization error."); } const uint32 bufferSize = 0x4000; uint8 inBuffer[bufferSize]; uint8 outBuffer[bufferSize]; while(1) { if(zStream.avail_in == 0) { if(compressedSize == 0) { //EOF break; } uint32 toRead = std::min<uint32>(bufferSize, compressedSize); inputStream.Read(inBuffer, toRead); compressedSize -= toRead; zStream.avail_in = toRead; zStream.next_in = inBuffer; } zStream.avail_out = bufferSize; zStream.next_out = outBuffer; int ret = inflate(&zStream, Z_NO_FLUSH); if(ret < 0) { throw std::runtime_error("Error occured while inflating."); break; } int have = bufferSize - zStream.avail_out; outputStream.Write(outBuffer, have); if(ret == Z_STREAM_END) { break; } } inflateEnd(&zStream); }
void CPatchFile::ExtractUncompressed(Framework::CStream& outputStream, Framework::CStream& inputStream, uint32 compressedSize) { const uint32 bufferSize = 0x4000; uint8 buffer[bufferSize]; while(compressedSize != 0) { uint32 toRead = std::min<uint32>(bufferSize, compressedSize); inputStream.Read(buffer, toRead); outputStream.Write(buffer, toRead); compressedSize -= toRead; } }
void CDdsImage::Read(Framework::CStream& inputStream) { uint32 signature = inputStream.Read32(); if(signature != ' SDD') { assert(0); throw std::runtime_error("Invalid DDS image (Invalid signature)."); } inputStream.Read(&m_header, sizeof(DDS_HEADER)); if(m_header.size != sizeof(DDS_HEADER)) { assert(0); throw std::runtime_error("Invalid DDS image (Invalid header size)."); } if(m_header.ddspf.size != sizeof(DDS_PIXELFORMAT)) { assert(0); throw std::runtime_error("Invalid DDS image (Invalid header size)."); } const uint32 mandatoryFlags = DDS_HEADER_FLAG_CAPS | DDS_HEADER_FLAG_HEIGHT | DDS_HEADER_FLAG_WIDTH | DDS_HEADER_FLAG_PIXELFORMAT; if((m_header.flags & mandatoryFlags) != mandatoryFlags) { assert(0); throw std::runtime_error("Invalid DDS image (Missing flags)."); } assert((m_header.ddspf.flags & (DDS_PIXELFORMAT_FLAG_RGB | DDS_PIXELFORMAT_FLAG_ALPHAPIXELS)) != 0); uint32 surfaceSize = (m_header.ddspf.rgbBitCount / 8) * m_header.width * m_header.height; if(m_header.caps2 & DDS_HEADER_CAP2_CUBEMAP) { const uint32 fullCubeMapFlags = DDS_HEADER_CAP2_CUBEMAP_POSITIVE_X | DDS_HEADER_CAP2_CUBEMAP_NEGATIVE_X | DDS_HEADER_CAP2_CUBEMAP_POSITIVE_Y | DDS_HEADER_CAP2_CUBEMAP_NEGATIVE_Y | DDS_HEADER_CAP2_CUBEMAP_POSITIVE_Z | DDS_HEADER_CAP2_CUBEMAP_NEGATIVE_Z; assert((m_header.caps2 & fullCubeMapFlags) == fullCubeMapFlags); for(unsigned int i = 0; i < 6; i++) { SurfaceByteArray surface(surfaceSize); inputStream.Read(surface.data(), surfaceSize); m_surfaces.push_back(std::move(surface)); } } }
CDialog::SZ_OR_ORD CDialog::ReadSzOrOrd(Framework::CStream& stream) { SZ_OR_ORD szOrOrd; szOrOrd.ord = 0; szOrOrd.isString = stream.Read16(); if(szOrOrd.isString == 0xFFFF) { szOrOrd.ord = stream.Read16(); } else if(szOrOrd.isString != 0) { szOrOrd.str = ReadString(stream); } return szOrOrd; }
uint32 CIoman::Read(uint32 handle, uint32 size, void* buffer) { CLog::GetInstance().Print(LOG_NAME, "Read(handle = %d, size = 0x%X, buffer = ptr);\r\n", handle, size); uint32 result = 0xFFFFFFFF; try { Framework::CStream* stream = GetFileStream(handle); result = static_cast<uint32>(stream->Read(buffer, size)); } catch(const std::exception& except) { CLog::GetInstance().Print(LOG_NAME, "%s: Error occured while trying to read file : %s\r\n", __FUNCTION__, except.what()); } return result; }
void CTextureSection::Read(Framework::CStream& inputStream) { CBaseSection::Read(inputStream); inputStream.Read(m_header, sizeof(m_header)); auto gtex = std::make_shared<CGtexData>(); AddChild(gtex); gtex->Read(inputStream); }
void CBoundingBoxChunk::Read(Framework::CStream& inputStream) { CBaseChunk::Read(inputStream); uint32 minXInt = inputStream.Read32_MSBF(); uint32 minYInt = inputStream.Read32_MSBF(); uint32 minZInt = inputStream.Read32_MSBF(); uint32 maxXInt = inputStream.Read32_MSBF(); uint32 maxYInt = inputStream.Read32_MSBF(); uint32 maxZInt = inputStream.Read32_MSBF(); m_minX = *reinterpret_cast<float*>(&minXInt); m_minY = *reinterpret_cast<float*>(&minYInt); m_minZ = *reinterpret_cast<float*>(&minZInt); m_maxX = *reinterpret_cast<float*>(&maxXInt); m_maxY = *reinterpret_cast<float*>(&maxYInt); m_maxZ = *reinterpret_cast<float*>(&maxZInt); }
uint32 CIoman::Write(uint32 handle, uint32 size, const void* buffer) { CLog::GetInstance().Print(LOG_NAME, "Write(handle = %d, size = 0x%X, buffer = ptr);\r\n", handle, size); uint32 result = 0xFFFFFFFF; try { Framework::CStream* stream = GetFileStream(handle); result = static_cast<uint32>(stream->Write(buffer, size)); if((handle == FID_STDOUT) || (handle == FID_STDERR)) { //Force flusing stdout and stderr stream->Flush(); } } catch(const std::exception& except) { if((handle != FID_STDOUT) && (handle != FID_STDERR)) { CLog::GetInstance().Print(LOG_NAME, "%s: Error occured while trying to write file : %s\r\n", __FUNCTION__, except.what()); } } return result; }
void CPatchFile::ExecuteDELD(Framework::CStream& stream) { uint32 pathSize = stream.Read32_MSBF(); std::vector<char> pathData(pathSize); stream.Read(pathData.data(), pathSize); std::string path(std::begin(pathData), std::end(pathData)); uint32 otherData[4]; stream.Read(otherData, sizeof(otherData)); auto fullDirPath = m_gameLocationPath / path; fullDirPath.make_preferred(); if(!boost::filesystem::exists(fullDirPath)) { m_result.messages.push_back(string_format("Warning: Directory '%s' deletion requested but directory doesn't exist.", fullDirPath.string().c_str() )); } else { boost::filesystem::remove_all(fullDirPath); } }
CPathTableRecord::CPathTableRecord(Framework::CStream& stream) { m_nameLength = stream.Read8(); m_exLength = stream.Read8(); m_location = stream.Read32(); m_parentDir = stream.Read16(); m_directory = stream.ReadString(m_nameLength); if(m_nameLength & 1) { stream.Seek(1, Framework::STREAM_SEEK_CUR); } }
void CStreamChunk::ReadElements(Framework::CStream& inputStream) { for(unsigned int i = 0; i < m_numElements; i++) { ELEMENT element; element.unknown1 = inputStream.Read16_MSBF(); element.offsetInVertex = inputStream.Read16_MSBF(); element.dataFormat = static_cast<ELEMENT_DATA_FORMAT>(inputStream.Read32_MSBF()); element.numElements = inputStream.Read32_MSBF(); element.dataType = static_cast<ELEMENT_DATA_TYPE>(inputStream.Read16_MSBF()); element.unknown2 = inputStream.Read16_MSBF(); m_elements.push_back(element); } assert(std::is_sorted(std::begin(m_elements), std::end(m_elements), [](const ELEMENT& el1, const ELEMENT& el2) { return el1.offsetInVertex < el2.offsetInVertex; } )); }
CBspFile::CBspFile(Framework::CStream& inputStream) { uint8 signature[4]; inputStream.Read(signature, 4); if(memcmp(signature, "IBSP", 4)) { throw std::runtime_error("Invalid BSP. Bad signature."); } uint32 version = inputStream.Read32(); assert(version == 0x2E); DIRENTRY dirEntries[DIRENTRY_COUNT]; for(unsigned int i = 0; i < DIRENTRY_COUNT; i++) { dirEntries[i].offset = inputStream.Read32(); dirEntries[i].length = inputStream.Read32(); } ReadLumpData(inputStream, dirEntries[LUMP_ENTITIES], m_entities); ReadLumpData(inputStream, dirEntries[LUMP_PLANES], m_planes); ReadLumpData(inputStream, dirEntries[LUMP_NODES], m_nodes); ReadLumpData(inputStream, dirEntries[LUMP_LEAVES], m_leaves); ReadLumpData(inputStream, dirEntries[LUMP_LEAFFACES], m_leafFaces); ReadLumpData(inputStream, dirEntries[LUMP_TEXTURES], m_textures); ReadLumpData(inputStream, dirEntries[LUMP_VERTICES], m_vertices); ReadLumpData(inputStream, dirEntries[LUMP_MESHVERTICES], m_meshVertices); ReadLumpData(inputStream, dirEntries[LUMP_EFFECTS], m_effects); ReadLumpData(inputStream, dirEntries[LUMP_FACES], m_faces); ReadLumpData(inputStream, dirEntries[LUMP_LIGHTMAPS], m_lightMaps); //Read VISDATA { inputStream.Seek(dirEntries[LUMP_VISDATA].offset, Framework::STREAM_SEEK_SET); m_visData.vectorCount = inputStream.Read32(); m_visData.vectorSize = inputStream.Read32(); uint32 visDataSize = m_visData.vectorCount * m_visData.vectorSize; m_visData.vectors.resize(visDataSize); inputStream.Read(&m_visData.vectors[0], visDataSize); } }
void CNameChunk::Read(Framework::CStream& inputStream) { CBaseChunk::Read(inputStream); m_name = inputStream.ReadString(); }
void CPatchFile::DoExecute(Framework::CStream& stream) { uint8 patchHeader[0x10]; stream.Read(patchHeader, sizeof(patchHeader)); if( patchHeader[0] != 0x91 && patchHeader[1] != 'Z' && patchHeader[2] != 'I' && patchHeader[3] != 'P' && patchHeader[4] != 'A' && patchHeader[5] != 'T' && patchHeader[6] != 'C' && patchHeader[7] != 'H') { throw std::runtime_error("Invalid patch file."); } while(1) { char command[5]; stream.Read(command, 4); command[4] = 0; if(stream.IsEOF()) break; if(!strcmp(command, "FHDR")) { ExecuteFHDR(stream); } else if(!strcmp(command, "DIFF")) { ExecuteDIFF(stream); } else if(!strcmp(command, "HIST")) { ExecuteHIST(stream); } else if(!strcmp(command, "APLY")) { ExecuteAPLY(stream); } else if(!strcmp(command, "ADIR")) { ExecuteADIR(stream); } else if(!strcmp(command, "DELD")) { ExecuteDELD(stream); } else if(!strcmp(command, "ETRY")) { ExecuteETRY(stream); } else { throw std::runtime_error("Unhandled command encountered."); } if(stream.IsEOF()) break; } m_result.succeeded = true; }
void CStreamChunk::ReadStream(Framework::CStream& inputStream) { m_data.resize(m_vertexCount * m_bytesPerVertex); inputStream.Read(m_data.data(), m_vertexCount * m_bytesPerVertex); }
void CPatchFile::ExecuteETRY(Framework::CStream& inputStream) { uint32 pathSize = inputStream.Read32_MSBF(); std::vector<char> pathData(pathSize); inputStream.Read(pathData.data(), pathSize); std::string path(std::begin(pathData), std::end(pathData)); auto fullFilePath = m_gameLocationPath / path; auto fullFileDirectory = fullFilePath; fullFileDirectory.remove_leaf(); fullFileDirectory.make_preferred(); fullFilePath.make_preferred(); if(!boost::filesystem::exists(fullFileDirectory)) { m_result.messages.push_back(string_format("Warning: Directory '%s' doesn't exist. Creating.", fullFileDirectory.string().c_str() )); boost::filesystem::create_directories(fullFileDirectory); } if(!boost::filesystem::exists(fullFilePath)) { m_result.messages.push_back(string_format("Warning: File '%s' doesn't exist. Creating.", fullFilePath.string().c_str())); } uint32 itemCount = inputStream.Read32_MSBF(); for(unsigned int i = 0; i < itemCount; i++) { //0x41 = last hash, 0x44 = first hash, 0x4D = both hashes? uint32 hashMode = inputStream.Read32(); assert(hashMode == 0x41 || hashMode == 0x44 || hashMode == 0x4D); uint8 srcFileHash[0x14]; uint8 dstFileHash[0x14]; inputStream.Read(srcFileHash, sizeof(srcFileHash)); inputStream.Read(dstFileHash, sizeof(dstFileHash)); //4E is no compression //5A is zlib compression uint32 compressionMode = inputStream.Read32(); assert((compressionMode == 0x4E) || (compressionMode == 0x5A)); uint32 compressedFileSize = inputStream.Read32_MSBF(); uint32 previousFileSize = inputStream.Read32_MSBF(); uint32 newFileSize = inputStream.Read32_MSBF(); if(i != (itemCount - 1)) { assert(compressedFileSize == 0); } if(compressedFileSize == 0) continue; //Data starts here { //Retrying here because explorer.exe can sometimes open the ffxiv*.exe files to load //the icons making the open operation fail if we need to patch it again. auto outputStream = CreateOutputStdStreamWithRetry(fullFilePath.native()); if(compressionMode == 0x4E) { ExtractUncompressed(outputStream, inputStream, compressedFileSize); } else if(compressionMode == 0x5A) { ExtractCompressed(outputStream, inputStream, compressedFileSize); } else { throw std::runtime_error("Unknown compression type."); } } } inputStream.Seek(0x08, Framework::STREAM_SEEK_CUR); }
CDialog::DIALOGTEMPLATE CDialog::ReadDialogTemplate(Framework::CStream& stream) { DIALOGTEMPLATE dialog; dialog.dlgVer = stream.Read16(); dialog.signature = stream.Read16(); assert(dialog.dlgVer == 1); assert(dialog.signature == 0xFFFF); dialog.helpID = stream.Read32(); dialog.exStyle = stream.Read32(); dialog.style = stream.Read32(); dialog.cDlgItems = stream.Read16(); dialog.x = stream.Read16(); dialog.y = stream.Read16(); dialog.cx = stream.Read16(); dialog.cy = stream.Read16(); assert((stream.Tell() & 0x01) == 0); dialog.menu = ReadSzOrOrd(stream); assert((stream.Tell() & 0x01) == 0); dialog.windowClass = ReadSzOrOrd(stream); assert((stream.Tell() & 0x01) == 0); dialog.title = ReadString(stream); dialog.pointsize = stream.Read16(); dialog.weight = stream.Read16(); dialog.italic = stream.Read8(); dialog.charset = stream.Read8(); assert((stream.Tell() & 0x01) == 0); dialog.typeface = ReadString(stream); //Struct has padding for alignment to DWORD boundary (only if there's other items to read) if(stream.GetRemainingLength() != 0) { auto currentBytePos = stream.Tell() & 0x3; if(currentBytePos != 0) { stream.Seek(4 - currentBytePos, Framework::STREAM_SEEK_CUR); } assert((stream.Tell() & 0x03) == 0); } uint32 itemDataLength = static_cast<uint32>(stream.GetRemainingLength()); if(itemDataLength != 0) { dialog.dialogItemData.resize(itemDataLength); stream.Read(&dialog.dialogItemData[0], itemDataLength); } return dialog; }
void CPatchFile::ExecuteAPLY(Framework::CStream& stream) { uint32 values[5]; stream.Read(values, sizeof(values)); }
void CPatchFile::ExecuteFHDR(Framework::CStream& stream) { //Version number? uint32 unknown = stream.Read32_MSBF(); assert(unknown == 0x0200); }
void CBaseSection::Read(Framework::CStream& inputStream) { inputStream.Read(&m_header, sizeof(SEDB_HEADER)); assert(m_header.sectionId == 'BDES'); }
void CStringChunk::Read(Framework::CStream& inputStream) { CBaseChunk::Read(inputStream); m_string = inputStream.ReadString(); }
void CDialog::WriteDialogTemplate(DIALOGTEMPLATE& dialog, Framework::CStream& stream) { stream.Write16(dialog.dlgVer); stream.Write16(dialog.signature); stream.Write32(dialog.helpID); stream.Write32(dialog.exStyle); stream.Write32(dialog.style); stream.Write16(dialog.cDlgItems); stream.Write16(dialog.x); stream.Write16(dialog.y); stream.Write16(dialog.cx); stream.Write16(dialog.cy); WriteSzOrOrd(dialog.menu, stream); WriteSzOrOrd(dialog.windowClass, stream); WriteString(dialog.title, stream); stream.Write16(dialog.pointsize); stream.Write16(dialog.weight); stream.Write8(dialog.italic); stream.Write8(dialog.charset); WriteString(dialog.typeface, stream); //Pad struct for alignment to DWORD boundary { auto currentBytePos = stream.Tell() & 0x3; if(currentBytePos != 0) { for(unsigned int i = 0; i < (4 - currentBytePos); i++) { stream.Write8(0); } } assert((stream.Tell() & 0x03) == 0); } if(!dialog.dialogItemData.empty()) { stream.Write(dialog.dialogItemData.data(), dialog.dialogItemData.size()); } }