rpcs3/rpcs3/Loader/TAR.cpp
2019-07-01 04:33:23 +03:00

132 lines
3 KiB
C++

#include "stdafx.h"
#include "TAR.h"
#include <cmath>
#include <cstdlib>
tar_object::tar_object(const fs::file& file, size_t offset)
: m_file(file)
, initial_offset(static_cast<int>(offset))
{
m_file.seek(initial_offset);
largest_offset = initial_offset;
}
TARHeader tar_object::read_header(u64 offset)
{
m_file.seek(offset);
TARHeader header;
m_file.read(header);
return header;
}
int octalToDecimal(int octalNumber)
{
int decimalNumber = 0, i = 0, rem;
while (octalNumber != 0)
{
rem = octalNumber % 10;
octalNumber /= 10;
decimalNumber += rem * pow(8, i);
++i;
}
return decimalNumber;
}
std::vector<std::string> tar_object::get_filenames()
{
std::vector<std::string> vec;
get_file("");
for (auto it = m_map.cbegin(); it != m_map.cend(); ++it)
{
vec.push_back(it->first);
}
return vec;
}
fs::file tar_object::get_file(std::string path)
{
if (!m_file) return fs::file();
auto it = m_map.find(path);
if (it != m_map.end())
{
TARHeader header = read_header(it->second);
int size = octalToDecimal(atoi(header.size));
std::vector<u8> buf(size);
m_file.read(buf, size);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset; // Always keep the offset aligned to 512 bytes + the initial offset.
m_file.seek(offset);
return fs::make_stream(std::move(buf));
}
else //continue scanning from last file entered
{
while (m_file.pos() < m_file.size())
{
TARHeader header = read_header(largest_offset);
if (std::string(header.magic).find("ustar") != std::string::npos)
m_map[header.name] = largest_offset;
int size = octalToDecimal(atoi(header.size));
if (path.compare(header.name) == 0) { //path is equal, read file and advance offset to start of next block
std::vector<u8> buf(size);
m_file.read(buf, size);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
m_file.seek(offset);
largest_offset = offset;
return fs::make_stream(std::move(buf));
}
else { // just advance offset to next block
m_file.seek(size, fs::seek_mode::seek_cur);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
m_file.seek(offset);
largest_offset = offset;
}
}
return fs::file();
}
}
bool tar_object::extract(std::string path, std::string ignore)
{
if (!m_file) return false;
get_file(""); //Make sure we have scanned all files
for (auto iter : m_map)
{
TARHeader header = read_header(iter.second);
if (!header.name[0]) continue;
std::string result = path + header.name;
if (result.compare(path.size(), ignore.size(), ignore) == 0)
{
result.erase(path.size(), ignore.size());
}
switch (header.filetype)
{
case '0':
{
fs::file file(result, fs::rewrite);
file.write(get_file(header.name).to_vector<u8>());
break;
}
case '5':
{
fs::create_dir(result);
break;
}
default:
LOG_ERROR(GENERAL, "TAR Loader: unknown file type: 0x%x", header.filetype);
return false;
}
}
return true;
}