/** * Decodes RA/TS Style MIX headers. Assumes you have already checked if * header is encrypted and that mix is seeked to the start of the WSKey * * @param mix pointer to vfile for the mixfile * @param header pointer to header object that will store the mix's header * @param tscheck if equal to check_ts, will check if mix is from Tiberian Sun. * @return pointer to MixRecord */ MixRecord* MIXFiles::decodeHeader(VFile* mix, MixHeader* header, tscheck_ tscheck) { Uint8 WSKey[80]; // 80-byte Westwood key Uint8 BFKey[56]; // 56-byte blow fish key Uint8 Block[8]; // 8-byte block to store blowfish stuff in Cblowfish bf; Uint8 *e; MixRecord* mindex; //bool aligned = true; mix->readByte(WSKey, 80); get_blowfish_key((const Uint8 *)&WSKey, (Uint8 *)&BFKey); bf.set_key((const Uint8 *)&BFKey, 56); mix->readByte(Block, 8); bf.decipher(&Block, &Block, 8); // Extract the header from Block memcpy(&header->c_files, &Block[0], sizeof(Uint16)); memcpy(&header->size, &Block[sizeof(Uint16)], sizeof(Uint32)); #if SDL_BYTEORDER == SDL_BIG_ENDIAN header->c_files = SDL_Swap16(header->c_files); header->size = SDL_Swap32(header->size); #endif // Decrypt all indexes const int m_size = sizeof(MixRecord) * header->c_files; const int m_f = (m_size + 5) & ~7; mindex = new MixRecord[header->c_files]; e = new Uint8[m_f]; //fread(e, m_f, 1, mix); mix->readByte(e, m_f); memcpy(mindex, &Block[6], 2); bf.decipher(e, e, m_f); memcpy(reinterpret_cast<Uint8 *>(mindex) + 2, e, m_size - 2); delete[] e; for (int i = 0; i < header->c_files; i++) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN mindex[i].id = SDL_Swap32(mindex[i].id); mindex[i].offset = SDL_Swap32(mindex[i].offset); mindex[i].size = SDL_Swap32(mindex[i].size); #endif #if 0 if (check_ts == tscheck) { if (mindex[i].offset & 0xf) aligned = false; if (mindex[i].id == TS_ID) game = game_ts; } #endif /* 92 = 4 byte flag + 6 byte header + 80 byte key + 2 bytes (?) */ mindex[i].offset += 92 + m_f; /* re-center offset to be absolute offset */ } /* if (aligned) game = game_ts; */ return mindex; }
bool MixHeader::readEncrypted(std::fstream& fh) { t_mix_entry entry; std::pair<t_mix_index_iter,bool> rv; Cblowfish blfish; int block_count; char pblkbuf[8]; //header is at least 84 bytes long at this point due to key source m_header_size = 84; //read keysource to obtain blowfish key //fh.seekg(4, std::ios::beg); readKeySource(fh); blfish.set_key(reinterpret_cast<uint8_t*>(m_key), 56); //read first block to get file count, needed to calculate header size fh.read(pblkbuf, 8); blfish.decipher(reinterpret_cast<void*>(pblkbuf), reinterpret_cast<void*>(pblkbuf), 8); memcpy(reinterpret_cast<char*>(&m_file_count), pblkbuf, 2); memcpy(reinterpret_cast<char*>(&m_body_size), pblkbuf + 2 , 4); //workout size of our header and how much we need to decrypt //take into account 2 bytes left from getting the file count block_count = ((m_file_count * 12) - 2) / 8; if (((m_file_count * 12) - 2) % 8) block_count++; //add 8 to compensate for block we already decrypted m_header_size += block_count * 8 + 8; //prepare our buffer and copy in first 2 bytes we got from first block //index_buffer.resize(block_count * 8 + 2); char pindbuf[block_count * 8 + 2]; memcpy(pindbuf, pblkbuf + 6 , 2); //loop to decrypt index into index buffer for(int i = 0; i < block_count; i++) { fh.read(pblkbuf, 8); blfish.decipher(reinterpret_cast<void*>(pblkbuf), reinterpret_cast<void*>(pblkbuf), 8); memcpy(pindbuf + 2 + 8 * i, pblkbuf, 8); } //read entries for (uint16_t i = 0; i < m_file_count; i++) { memcpy(reinterpret_cast<char*>(&entry.first), pindbuf + i * 12, sizeof(int32_t)); memcpy(reinterpret_cast<char*>(&entry.second), pindbuf + 4 + i * 12, sizeof(t_index_info)); rv = m_index.insert(entry); if(!rv.second) { std::cout << "Error reading header, duplicate ID" << std::endl; return false; } } return true; }
bool MixHeader::writeEncrypted(std::fstream& fh) { Cblowfish blfish; //std::vector<char> block_buff(8); //std::vector<char> index_buffer(8); int block_count; int offset = 0; //char* pindbuf = &index_buffer[0]; //char* pblkbuf = &block_buff[0]; char pblkbuf[8]; //always write flags if encrypted. fh.write(reinterpret_cast<char*>(&m_header_flags), 4); //encrypted file needs its key_source fh.write(m_keysource, 80); //work out how much data needs encrypting, set vector size block_count = ((m_file_count * 12) + 6) / 8; if(((m_file_count * 12) + 6) % 8) block_count++; //index_buffer.resize(block_count * 8); char pindbuf[block_count * 8]; //fill the buffer memcpy(pindbuf + offset, reinterpret_cast<char*>(&m_file_count), 2); offset += 2; memcpy(pindbuf + offset, reinterpret_cast<char*>(&m_body_size), 4); offset += 4; for(t_mix_index_iter it = m_index.begin(); it != m_index.end(); ++it) { memcpy(pindbuf + offset, reinterpret_cast<const char*>(&(it->first)), 4); offset += 4; memcpy(pindbuf + offset, reinterpret_cast<const char*>(&(it->second)), 8); offset += 8; } //prepare blowfish blfish.set_key(reinterpret_cast<const uint8_t*>(m_key), 56); //encrypt and write to file. offset = 0; while(block_count--) { memcpy(pblkbuf, pindbuf + offset, 8); blfish.encipher(pblkbuf, pblkbuf, 8); fh.write(pblkbuf, 8); offset += 8; } return true; }