mirror of
https://github.com/jclehner/bcm2-utils.git
synced 2025-01-30 08:11:24 +00:00
213 lines
4.5 KiB
C++
213 lines
4.5 KiB
C++
/**
|
|
* bcm2-utils
|
|
* Copyright (C) 2016-2018 Joseph Lehner <joseph.c.lehner@gmail.com>
|
|
*
|
|
* bcm2-utils 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* bcm2-utils is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with bcm2-utils. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <fstream>
|
|
#include "util.h"
|
|
#include "ps.h"
|
|
using namespace bcm2dump;
|
|
using namespace std;
|
|
|
|
namespace {
|
|
|
|
class mono_header
|
|
{
|
|
public:
|
|
struct raw
|
|
{
|
|
// 0x4d4f4e4f (MONO)
|
|
uint32_t magic;
|
|
// signature (similar to ProgramStore)
|
|
uint16_t signature;
|
|
uint16_t unk1;
|
|
// length including header
|
|
uint32_t length;
|
|
uint16_t unk2;
|
|
uint16_t unk3;
|
|
} __attribute__((packed));
|
|
|
|
mono_header& parse(const string& buf)
|
|
{
|
|
if (buf.size() < sizeof(raw)) {
|
|
throw invalid_argument("buffer too small to contain valid header");
|
|
}
|
|
|
|
memcpy(&m_raw, buf.data(), sizeof(m_raw));
|
|
return *this;
|
|
}
|
|
|
|
bool valid() const
|
|
{ return be_to_h(m_raw.magic) == 0x4d4f4e4f; }
|
|
|
|
uint16_t signature() const
|
|
{ return be_to_h(m_raw.signature); }
|
|
|
|
uint32_t length() const
|
|
{ return be_to_h(m_raw.length); }
|
|
|
|
uint16_t unk1() const
|
|
{ return be_to_h(m_raw.unk1); }
|
|
|
|
uint16_t unk2() const
|
|
{ return be_to_h(m_raw.unk2); }
|
|
|
|
uint16_t unk3() const
|
|
{ return be_to_h(m_raw.unk3); }
|
|
|
|
private:
|
|
raw m_raw;
|
|
};
|
|
|
|
void do_extract(istream& in, const ps_header& ps, size_t length = 0)
|
|
{
|
|
if (!length) {
|
|
length = ps.length();
|
|
}
|
|
|
|
auto buf = make_unique<char[]>(length);
|
|
|
|
if (!in.read(buf.get(), length)) {
|
|
throw runtime_error("read error (data)");
|
|
}
|
|
|
|
ofstream out(ps.filename());
|
|
|
|
out.write(reinterpret_cast<const char*>(ps.data()), sizeof(ps_header::raw));
|
|
out.write(buf.get(), length);
|
|
|
|
if (!out) {
|
|
throw runtime_error("write error");
|
|
}
|
|
}
|
|
|
|
string read_hbuf(istream& in)
|
|
{
|
|
string hbuf(sizeof(ps_header::raw), '\0');
|
|
if (!in.read(&hbuf[0], hbuf.size())) {
|
|
throw runtime_error("read error (header)");
|
|
}
|
|
|
|
return hbuf;
|
|
}
|
|
|
|
void extract_ps(istream& in, const ps_header& ps)
|
|
{
|
|
unsigned long off = static_cast<unsigned long>(in.tellg()) - sizeof(ps_header::raw);
|
|
logger::i("0x%07lx ", off);
|
|
logger::i() << "image: " << ps.filename() << ", " << ps.length() << " b";
|
|
logger::v(", %04x", ps.signature());
|
|
logger::i() << endl;
|
|
|
|
do_extract(in, ps);
|
|
}
|
|
|
|
bool extract_ps(istream& in)
|
|
{
|
|
ps_header ps;
|
|
|
|
if (!ps.parse(read_hbuf(in)).hcs_valid()) {
|
|
return false;
|
|
}
|
|
|
|
extract_ps(in, ps);
|
|
return true;
|
|
}
|
|
|
|
void extract_image(istream& in)
|
|
{
|
|
ps_header ps;
|
|
mono_header mono;
|
|
|
|
streamoff beg = in.tellg();
|
|
string hbuf = read_hbuf(in);
|
|
|
|
if (ps.parse(hbuf).hcs_valid()) {
|
|
extract_ps(in, ps);
|
|
} else {
|
|
logger::i("0x%07lx ", long(beg & 0xffffffff));
|
|
|
|
if (mono.parse(hbuf).valid()) {
|
|
logger::i() << "monolithic, " << mono.length() << " b";
|
|
logger::v(", %04x, ", mono.signature());
|
|
logger::v("(%04x %04x %04x)", mono.unk1(), mono.unk2(), mono.unk3());
|
|
logger::i() << endl;
|
|
|
|
streamoff end = beg + mono.length();
|
|
in.seekg(beg + sizeof(mono_header::raw));
|
|
|
|
while (!in.eof() && in.tellg() < end && extract_ps(in)) {
|
|
streamoff pos = in.tellg() - beg;
|
|
in.seekg(beg + align_right(pos, 0xffff + 1));
|
|
}
|
|
} else if (hbuf[0] == 0x30 && (hbuf[1] & 0xff) == 0x82) {
|
|
// add 7, because sizeof(type + len) is 4, and
|
|
// sizeof(end-of-data) is 2. add 1 for next data.
|
|
|
|
auto len = be_to_h(extract<uint16_t>(hbuf, 2)) + 7;
|
|
logger::i() << "asn.1 data, " << len << " b " << endl;
|
|
|
|
in.seekg(beg + len);
|
|
|
|
return extract_image(in);
|
|
} else {
|
|
logger::e() << "unknown image format" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
int do_main(int argc, char* argv[])
|
|
{
|
|
logger::loglevel(logger::debug);
|
|
|
|
if (argc < 2) {
|
|
logger::e() << "Usage: psextract <infile> [<offset1> ...]" << endl;
|
|
return 1;
|
|
}
|
|
|
|
ifstream in(argv[1]);
|
|
|
|
if (!in.good()) {
|
|
throw user_error("failed to open input file");
|
|
}
|
|
|
|
if (argc == 2) {
|
|
extract_image(in);
|
|
} else {
|
|
for (int i = 2; i < argc; ++i) {
|
|
if (!in.seekg(lexical_cast<unsigned>(argv[i], 0))) {
|
|
throw user_error("bad offset "s + argv[i]);
|
|
}
|
|
|
|
extract_image(in);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
try {
|
|
return do_main(argc, argv);
|
|
} catch (const exception& e) {
|
|
logger::e() << "error: " << e.what() << endl;
|
|
return 1;
|
|
}
|
|
}
|