2016-10-25 10:58:08 +08:00
|
|
|
/* POLE - Portable C++ library to access OLE Storage
|
|
|
|
Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org).
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
#include <cassert>
|
2016-10-25 10:58:08 +08:00
|
|
|
#include <cstring>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <list>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
#include <detail/pole.hpp>
|
2016-10-25 10:58:08 +08:00
|
|
|
|
|
|
|
// enable to activate debugging output
|
|
|
|
// #define POLE_DEBUG
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
// helper function: recursively find siblings of index
|
2016-12-24 23:04:57 +08:00
|
|
|
void dirtree_find_siblings(POLE::DirTree *dirtree, std::vector<std::size_t> &result, std::size_t index)
|
2016-11-10 08:52:18 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
auto e = dirtree->entry(index);
|
|
|
|
if (!e) return;
|
|
|
|
if (!e->valid) return;
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
// prevent infinite loop
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < result.size(); i++)
|
|
|
|
if (result[i] == index) return;
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
// add myself
|
2016-12-24 23:04:57 +08:00
|
|
|
result.push_back(index);
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
// visit previous sibling, don't go infinitely
|
|
|
|
std::size_t prev = e->prev;
|
2016-12-24 23:04:57 +08:00
|
|
|
if ((prev > 0) && (prev < dirtree->entryCount()))
|
2016-11-10 08:52:18 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < result.size(); i++)
|
|
|
|
if (result[i] == prev) prev = 0;
|
|
|
|
if (prev) dirtree_find_siblings(dirtree, result, prev);
|
2016-11-10 08:52:18 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
// visit next sibling, don't go infinitely
|
|
|
|
std::size_t next = e->next;
|
2016-12-24 23:04:57 +08:00
|
|
|
if ((next > 0) && (next < dirtree->entryCount()))
|
2016-11-10 08:52:18 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < result.size(); i++)
|
|
|
|
if (result[i] == next) next = 0;
|
|
|
|
if (next) dirtree_find_siblings(dirtree, result, next);
|
2016-11-10 08:52:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
namespace POLE {
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
} // namespace POLE
|
|
|
|
|
|
|
|
using namespace POLE;
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
static inline std::uint16_t readU16(const std::uint8_t *ptr)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-11-10 08:52:18 +08:00
|
|
|
return static_cast<std::uint16_t>(ptr[0] + (ptr[1] << 8));
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
static inline std::uint32_t readU32(const std::uint8_t *ptr)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-11-10 08:52:18 +08:00
|
|
|
return static_cast<std::uint32_t>(ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24));
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
static inline void writeU16(std::uint8_t *ptr, std::uint16_t data)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-11-10 08:52:18 +08:00
|
|
|
ptr[0] = static_cast<std::uint8_t>(data & 0xff);
|
|
|
|
ptr[1] = static_cast<std::uint8_t>((data >> 8) & 0xff);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
static inline void writeU32(std::uint8_t *ptr, std::uint32_t data)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-11-10 08:52:18 +08:00
|
|
|
ptr[0] = static_cast<std::uint8_t>(data & 0xff);
|
|
|
|
ptr[1] = static_cast<std::uint8_t>((data >> 8) & 0xff);
|
|
|
|
ptr[2] = static_cast<std::uint8_t>((data >> 16) & 0xff);
|
|
|
|
ptr[3] = static_cast<std::uint8_t>((data >> 24) & 0xff);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
static const std::uint8_t pole_magic[] = {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};
|
2016-10-25 10:58:08 +08:00
|
|
|
|
|
|
|
// =========== Header ==========
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
Header::Header()
|
|
|
|
: b_shift(9),
|
|
|
|
s_shift(6),
|
|
|
|
num_bat(0),
|
|
|
|
dirent_start(0),
|
|
|
|
threshold(4096),
|
|
|
|
sbat_start(0),
|
|
|
|
num_sbat(0),
|
|
|
|
mbat_start(0),
|
|
|
|
num_mbat(0)
|
|
|
|
{
|
|
|
|
for (std::size_t i = 0; i < 8; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
id[i] = pole_magic[i];
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < 109; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
bb_blocks[i] = AllocTable::Avail;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Header::valid()
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (threshold != 4096) return false;
|
|
|
|
if (num_bat == 0) return false;
|
|
|
|
if ((num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
|
|
|
|
if ((num_bat < 109) && (num_mbat != 0)) return false;
|
|
|
|
if (s_shift > b_shift) return false;
|
|
|
|
if (b_shift <= 6) return false;
|
|
|
|
if (b_shift >= 31) return false;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void Header::load(const std::uint8_t *buffer)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
b_shift = readU16(buffer + 0x1e);
|
|
|
|
s_shift = readU16(buffer + 0x20);
|
|
|
|
num_bat = readU32(buffer + 0x2c);
|
|
|
|
dirent_start = readU32(buffer + 0x30);
|
|
|
|
threshold = readU32(buffer + 0x38);
|
|
|
|
sbat_start = readU32(buffer + 0x3c);
|
|
|
|
num_sbat = readU32(buffer + 0x40);
|
|
|
|
mbat_start = readU32(buffer + 0x44);
|
|
|
|
num_mbat = readU32(buffer + 0x48);
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < 8; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
id[i] = buffer[i];
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < 109; i++)
|
|
|
|
bb_blocks[i] = readU32(buffer + 0x4C + i * 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Header::save(std::uint8_t *buffer)
|
|
|
|
{
|
|
|
|
memset(buffer, 0, 0x4c);
|
|
|
|
memcpy(buffer, pole_magic, 8); // ole signature
|
|
|
|
writeU32(buffer + 8, 0); // unknown
|
|
|
|
writeU32(buffer + 12, 0); // unknown
|
|
|
|
writeU32(buffer + 16, 0); // unknown
|
|
|
|
writeU16(buffer + 24, 0x003e); // revision ?
|
|
|
|
writeU16(buffer + 26, 3); // version ?
|
|
|
|
writeU16(buffer + 28, 0xfffe); // unknown
|
|
|
|
writeU16(buffer + 0x1e, b_shift);
|
|
|
|
writeU16(buffer + 0x20, s_shift);
|
|
|
|
writeU32(buffer + 0x2c, num_bat);
|
|
|
|
writeU32(buffer + 0x30, dirent_start);
|
|
|
|
writeU32(buffer + 0x38, threshold);
|
|
|
|
writeU32(buffer + 0x3c, sbat_start);
|
|
|
|
writeU32(buffer + 0x40, num_sbat);
|
|
|
|
writeU32(buffer + 0x44, mbat_start);
|
|
|
|
writeU32(buffer + 0x48, num_mbat);
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < 109; i++)
|
|
|
|
writeU32(buffer + 0x4C + i * 4, bb_blocks[i]);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Header::debug()
|
|
|
|
{
|
|
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "b_shift " << b_shift << std::endl;
|
|
|
|
std::cout << "s_shift " << s_shift << std::endl;
|
|
|
|
std::cout << "num_bat " << num_bat << std::endl;
|
|
|
|
std::cout << "dirent_start " << dirent_start << std::endl;
|
|
|
|
std::cout << "threshold " << threshold << std::endl;
|
|
|
|
std::cout << "sbat_start " << sbat_start << std::endl;
|
|
|
|
std::cout << "num_sbat " << num_sbat << std::endl;
|
|
|
|
std::cout << "mbat_start " << mbat_start << std::endl;
|
|
|
|
std::cout << "num_mbat " << num_mbat << std::endl;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
std::size_t s = (num_bat <= 109) ? num_bat : 109;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << "bat blocks: ";
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < s; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << bb_blocks[i] << " ";
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// =========== AllocTable ==========
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
const std::uint32_t AllocTable::Avail = 0xffffffff;
|
|
|
|
const std::uint32_t AllocTable::Eof = 0xfffffffe;
|
|
|
|
const std::uint32_t AllocTable::Bat = 0xfffffffd;
|
|
|
|
const std::uint32_t AllocTable::MetaBat = 0xfffffffc;
|
2016-10-25 10:58:08 +08:00
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
AllocTable::AllocTable()
|
|
|
|
: blockSize(4096), data()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// initial size
|
2016-12-24 23:04:57 +08:00
|
|
|
resize(128);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t AllocTable::count()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return data.size();
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::resize(std::size_t newsize)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t oldsize = data.size();
|
2016-12-24 23:04:57 +08:00
|
|
|
data.resize(newsize);
|
|
|
|
if (newsize > oldsize)
|
|
|
|
for (std::size_t i = oldsize; i < newsize; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
data[i] = Avail;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure there're still free blocks
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::preserve(std::size_t n)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::vector<std::size_t> pre;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < n; i++)
|
|
|
|
pre.push_back(unused());
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t AllocTable::operator[](std::size_t index)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t result;
|
2016-10-25 10:58:08 +08:00
|
|
|
result = data[index];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::set(std::size_t index, std::uint32_t value)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (index >= count()) resize(index + 1);
|
|
|
|
data[index] = value;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::setChain(std::vector<std::uint32_t> chain)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (chain.size())
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < chain.size() - 1; i++)
|
|
|
|
set(chain[i], chain[i + 1]);
|
|
|
|
set(chain[chain.size() - 1], AllocTable::Eof);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: optimize this with better search
|
2016-12-24 23:04:57 +08:00
|
|
|
static bool already_exist(const std::vector<std::size_t> &chain, std::size_t item)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < chain.size(); i++)
|
|
|
|
if (chain[i] == item) return true;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// follow
|
2016-12-24 23:04:57 +08:00
|
|
|
std::vector<std::size_t> AllocTable::follow(std::size_t start)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::vector<std::size_t> chain;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
if (start >= count()) return chain;
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t p = start;
|
2016-12-24 23:04:57 +08:00
|
|
|
while (p < count())
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (p == static_cast<std::size_t>(Eof)) break;
|
|
|
|
if (p == static_cast<std::size_t>(Bat)) break;
|
|
|
|
if (p == static_cast<std::size_t>(MetaBat)) break;
|
|
|
|
if (already_exist(chain, p)) break;
|
2016-10-25 10:58:08 +08:00
|
|
|
chain.push_back(p);
|
2016-12-24 23:04:57 +08:00
|
|
|
if (data[p] >= count()) break;
|
|
|
|
p = data[p];
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t AllocTable::unused()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// find first available block
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < data.size(); i++)
|
|
|
|
if (data[i] == Avail) return i;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// completely full, so enlarge the table
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t block = data.size();
|
2016-12-24 23:04:57 +08:00
|
|
|
resize(data.size() + 10);
|
2016-10-25 10:58:08 +08:00
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::load(const std::uint8_t *buffer, std::size_t len)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
resize(len / 4);
|
|
|
|
for (std::size_t i = 0; i < count(); i++)
|
|
|
|
set(i, readU32(buffer + i * 4));
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// return space required to save this dirtree
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t AllocTable::size()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return count() * 4;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void AllocTable::save(std::uint8_t *buffer)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < count(); i++)
|
|
|
|
writeU32(buffer + i * 4, data[i]);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void AllocTable::debug()
|
|
|
|
{
|
|
|
|
std::cout << "block size " << data.size() << std::endl;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < data.size(); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (data[i] == Avail) continue;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << i << ": ";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (data[i] == Eof)
|
|
|
|
std::cout << "[eof]";
|
|
|
|
else if (data[i] == Bat)
|
|
|
|
std::cout << "[bat]";
|
|
|
|
else if (data[i] == MetaBat)
|
|
|
|
std::cout << "[metabat]";
|
|
|
|
else
|
|
|
|
std::cout << data[i];
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// =========== DirTree ==========
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
const std::uint32_t DirTree::End = 0xffffffff;
|
2016-10-25 10:58:08 +08:00
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
DirTree::DirTree()
|
|
|
|
: entries()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DirTree::clear()
|
|
|
|
{
|
|
|
|
// leave only root entry
|
2016-12-24 23:04:57 +08:00
|
|
|
entries.resize(1);
|
2016-10-25 10:58:08 +08:00
|
|
|
entries[0].valid = true;
|
|
|
|
entries[0].name = "Root Entry";
|
|
|
|
entries[0].dir = true;
|
|
|
|
entries[0].size = 0;
|
|
|
|
entries[0].start = End;
|
|
|
|
entries[0].prev = End;
|
|
|
|
entries[0].next = End;
|
|
|
|
entries[0].child = End;
|
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t DirTree::entryCount()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return entries.size();
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *DirTree::entry(std::size_t index)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (index >= entryCount()) return nullptr;
|
|
|
|
return &entries[index];
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::ptrdiff_t DirTree::indexOf(DirEntry *e)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < entryCount(); i++)
|
|
|
|
if (entry(i) == e) return static_cast<std::ptrdiff_t>(i);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::ptrdiff_t DirTree::parent(std::size_t index)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// brute-force, basically we iterate for each entries, find its children
|
|
|
|
// and check if one of the children is 'index'
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t j = 0; j < entryCount(); j++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
std::vector<std::size_t> chi = children(j);
|
|
|
|
for (std::size_t i = 0; i < chi.size(); i++)
|
|
|
|
if (chi[i] == index) return static_cast<std::ptrdiff_t>(j);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::string DirTree::fullName(std::size_t index)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// don't use root name ("Root Entry"), just give "/"
|
2016-12-24 23:04:57 +08:00
|
|
|
if (index == 0) return "/";
|
|
|
|
|
|
|
|
std::string result = entry(index)->name;
|
|
|
|
result.insert(0, "/");
|
|
|
|
auto p = parent(index);
|
|
|
|
DirEntry *_entry = 0;
|
|
|
|
while (p > 0)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
_entry = entry(static_cast<std::size_t>(p));
|
2016-10-25 10:58:08 +08:00
|
|
|
if (_entry->dir && _entry->valid)
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
result.insert(0, _entry->name);
|
|
|
|
result.insert(0, "/");
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
--p;
|
2016-11-10 08:52:18 +08:00
|
|
|
index = static_cast<std::size_t>(p);
|
2016-12-24 23:04:57 +08:00
|
|
|
if (p <= 0) break;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
|
|
|
|
// if not found and create is false, return 0
|
|
|
|
// if create is true, a new entry is returned
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *DirTree::entry(const std::string &name, bool create)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!name.length()) return nullptr;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// quick check for "/" (that's root)
|
2016-12-24 23:04:57 +08:00
|
|
|
if (name == "/") return entry(0);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// split the names, e.g "/ObjectPool/_1020961869" will become:
|
|
|
|
// "ObjectPool" and "_1020961869"
|
|
|
|
std::list<std::string> names;
|
|
|
|
std::string::size_type start = 0, end = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (name[0] == '/') start++;
|
|
|
|
while (start < name.length())
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
end = name.find_first_of('/', start);
|
|
|
|
if (end == std::string::npos) end = name.length();
|
|
|
|
names.push_back(name.substr(start, end - start));
|
|
|
|
start = end + 1;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// start from root
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t index = 0;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// trace one by one
|
|
|
|
std::list<std::string>::iterator it;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
for (it = names.begin(); it != names.end(); ++it)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// find among the children of index
|
2016-12-24 23:04:57 +08:00
|
|
|
std::vector<std::size_t> chi = children(index);
|
2016-10-26 08:21:58 +08:00
|
|
|
std::ptrdiff_t child = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < chi.size(); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *ce = entry(chi[i]);
|
|
|
|
if (ce)
|
|
|
|
if (ce->valid && (ce->name.length() > 1))
|
|
|
|
if (ce->name == *it) child = static_cast<std::ptrdiff_t>(chi[i]);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// traverse to the child
|
2016-12-24 23:04:57 +08:00
|
|
|
if (child > 0)
|
|
|
|
index = static_cast<std::size_t>(child);
|
2016-10-25 10:58:08 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// not found among children
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!create) return nullptr;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// create a new entry
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t parent = index;
|
2016-12-24 23:04:57 +08:00
|
|
|
entries.push_back(DirEntry());
|
|
|
|
index = entryCount() - 1;
|
|
|
|
DirEntry *e = entry(index);
|
2016-10-25 10:58:08 +08:00
|
|
|
e->valid = true;
|
|
|
|
e->name = *it;
|
|
|
|
e->dir = false;
|
|
|
|
e->size = 0;
|
|
|
|
e->start = 0;
|
|
|
|
e->child = End;
|
|
|
|
e->prev = End;
|
|
|
|
e->next = entry(parent)->child;
|
2016-10-26 08:21:58 +08:00
|
|
|
entry(parent)->child = static_cast<std::uint32_t>(index);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
return entry(index);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::vector<std::size_t> DirTree::children(std::size_t index)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::vector<std::size_t> result;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
DirEntry *e = entry(index);
|
|
|
|
if (e)
|
|
|
|
if (e->valid && e->child < entryCount()) dirtree_find_siblings(this, result, e->child);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void DirTree::load(std::uint8_t *buffer, std::size_t size)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
entries.clear();
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
for (std::size_t i = 0; i < size / 128; i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t p = i * 128;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// parse name of this entry, which stored as Unicode 16-bit
|
|
|
|
std::string name;
|
2016-12-24 23:04:57 +08:00
|
|
|
auto name_len = static_cast<std::size_t>(readU16(buffer + 0x40 + p));
|
|
|
|
if (name_len > 64) name_len = 64;
|
|
|
|
for (std::size_t j = 0; (buffer[j + p]) && (j < name_len); j += 2)
|
|
|
|
name.append(1, static_cast<char>(buffer[j + p]));
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// first char isn't printable ? remove it...
|
2016-12-24 23:04:57 +08:00
|
|
|
if (buffer[p] < 32)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
name.erase(0, 1);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// 2 = file (aka stream), 1 = directory (aka storage), 5 = root
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t type = buffer[0x42 + p];
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
DirEntry e;
|
|
|
|
e.valid = true;
|
|
|
|
e.name = name;
|
2016-12-24 23:04:57 +08:00
|
|
|
e.start = readU32(buffer + 0x74 + p);
|
|
|
|
e.size = readU32(buffer + 0x78 + p);
|
|
|
|
e.prev = readU32(buffer + 0x44 + p);
|
|
|
|
e.next = readU32(buffer + 0x48 + p);
|
|
|
|
e.child = readU32(buffer + 0x4C + p);
|
|
|
|
e.dir = (type != 2);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// sanity checks
|
2016-12-24 23:04:57 +08:00
|
|
|
if ((type != 2) && (type != 1) && (type != 5)) e.valid = false;
|
|
|
|
if (name_len < 1) e.valid = false;
|
|
|
|
|
|
|
|
entries.push_back(e);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return space required to save this dirtree
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t DirTree::size()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return entryCount() * 128;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void DirTree::save(std::uint8_t *buffer)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
memset(buffer, 0, size());
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// root is fixed as "Root Entry"
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *root = entry(0);
|
2016-11-10 08:52:18 +08:00
|
|
|
std::string entry_name = "Root Entry";
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t j = 0; j < entry_name.length(); j++)
|
|
|
|
buffer[j * 2] = static_cast<std::uint8_t>(entry_name[j]);
|
|
|
|
writeU16(buffer + 0x40, static_cast<std::uint16_t>(entry_name.length() * 2 + 2));
|
|
|
|
writeU32(buffer + 0x74, 0xffffffff);
|
|
|
|
writeU32(buffer + 0x78, 0);
|
|
|
|
writeU32(buffer + 0x44, 0xffffffff);
|
|
|
|
writeU32(buffer + 0x48, 0xffffffff);
|
|
|
|
writeU32(buffer + 0x4c, root->child);
|
|
|
|
buffer[0x42] = 5;
|
|
|
|
buffer[0x43] = 1;
|
|
|
|
|
|
|
|
for (std::size_t i = 1; i < entryCount(); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *e = entry(i);
|
|
|
|
if (!e) continue;
|
|
|
|
if (e->dir)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
e->start = 0xffffffff;
|
|
|
|
e->size = 0;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// max length for name is 32 chars
|
2016-11-10 08:52:18 +08:00
|
|
|
entry_name = e->name;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (entry_name.length() > 32) entry_name.erase(32, entry_name.length());
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// write name as Unicode 16-bit
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t j = 0; j < entry_name.length(); j++)
|
|
|
|
buffer[i * 128 + j * 2] = static_cast<std::uint8_t>(entry_name[j]);
|
|
|
|
|
|
|
|
writeU16(buffer + i * 128 + 0x40, static_cast<std::uint16_t>(entry_name.length() * 2 + 2));
|
|
|
|
writeU32(buffer + i * 128 + 0x74, e->start);
|
|
|
|
writeU32(buffer + i * 128 + 0x78, e->size);
|
|
|
|
writeU32(buffer + i * 128 + 0x44, e->prev);
|
|
|
|
writeU32(buffer + i * 128 + 0x48, e->next);
|
|
|
|
writeU32(buffer + i * 128 + 0x4c, e->child);
|
|
|
|
buffer[i * 128 + 0x42] = e->dir ? 1 : 2;
|
|
|
|
buffer[i * 128 + 0x43] = 1; // always black
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DirTree::debug()
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < entryCount(); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *e = entry(i);
|
|
|
|
if (!e) continue;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << i << ": ";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!e->valid) std::cout << "INVALID ";
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << e->name << " ";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (e->dir)
|
|
|
|
std::cout << "(Dir) ";
|
|
|
|
else
|
|
|
|
std::cout << "(File) ";
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << e->size << " ";
|
|
|
|
std::cout << "s:" << e->start << " ";
|
|
|
|
std::cout << "(";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (e->child == End)
|
|
|
|
std::cout << "-";
|
|
|
|
else
|
|
|
|
std::cout << e->child;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << " ";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (e->prev == End)
|
|
|
|
std::cout << "-";
|
|
|
|
else
|
|
|
|
std::cout << e->prev;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << ":";
|
2016-12-24 23:04:57 +08:00
|
|
|
if (e->next == End)
|
|
|
|
std::cout << "-";
|
|
|
|
else
|
|
|
|
std::cout << e->next;
|
2016-10-25 10:58:08 +08:00
|
|
|
std::cout << ")";
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// =========== StorageIO ==========
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
StorageIO::StorageIO(Storage *st, char *bytes, std::size_t length)
|
|
|
|
: storage(st),
|
|
|
|
filedata(reinterpret_cast<std::uint8_t *>(bytes)),
|
|
|
|
dataLength(length),
|
|
|
|
result(Storage::Ok),
|
|
|
|
opened(false),
|
|
|
|
filesize(0),
|
|
|
|
header(new Header()),
|
|
|
|
dirtree(new DirTree()),
|
|
|
|
bbat(new AllocTable()),
|
|
|
|
sbat(new AllocTable()),
|
|
|
|
sb_blocks(),
|
|
|
|
streams()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
bbat->blockSize = static_cast<std::size_t>(1) << header->b_shift;
|
|
|
|
sbat->blockSize = static_cast<std::size_t>(1) << header->s_shift;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
StorageIO::~StorageIO()
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (opened) close();
|
2016-10-25 10:58:08 +08:00
|
|
|
delete sbat;
|
|
|
|
delete bbat;
|
|
|
|
delete dirtree;
|
|
|
|
delete header;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StorageIO::open()
|
|
|
|
{
|
|
|
|
// already opened ? close first
|
2016-12-24 23:04:57 +08:00
|
|
|
if (opened) close();
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
load();
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return result == Storage::Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorageIO::load()
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
std::uint8_t *buffer = 0;
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t buflen = 0;
|
|
|
|
std::vector<std::size_t> blocks;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// open the file, check for error
|
|
|
|
result = Storage::OpenFailed;
|
2016-12-24 23:04:57 +08:00
|
|
|
// FSTREAM file.open( filename.c_str(), std::ios::binary | std::ios::in );
|
|
|
|
// FSTREAM if( !file.good() ) return;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// find size of input file
|
2016-12-24 23:04:57 +08:00
|
|
|
// FSTREAM file.seekg( 0, std::ios::end );
|
|
|
|
// FSTREAM filesize = file.tellg();
|
2016-10-25 10:58:08 +08:00
|
|
|
filesize = dataLength;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// load header
|
2016-10-26 08:21:58 +08:00
|
|
|
buffer = new std::uint8_t[512];
|
2016-12-24 23:04:57 +08:00
|
|
|
// FSTREAM file.seekg( 0 );
|
|
|
|
// FSTREAM file.read( (char*)buffer, 512 );
|
2016-10-25 10:58:08 +08:00
|
|
|
memcpy(buffer, filedata, 512);
|
2016-12-24 23:04:57 +08:00
|
|
|
header->load(buffer);
|
2016-10-25 10:58:08 +08:00
|
|
|
delete[] buffer;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// check OLE magic id
|
|
|
|
result = Storage::NotOLE;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; i < 8; i++)
|
|
|
|
if (header->id[i] != pole_magic[i]) return;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// sanity checks
|
|
|
|
result = Storage::BadOLE;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!header->valid()) return;
|
|
|
|
if (header->threshold != 4096) return;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// important block size
|
2016-10-26 08:21:58 +08:00
|
|
|
bbat->blockSize = static_cast<std::size_t>(1) << header->b_shift;
|
|
|
|
sbat->blockSize = static_cast<std::size_t>(1) << header->s_shift;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// find blocks allocated to store big bat
|
|
|
|
// the first 109 blocks are in header, the rest in meta bat
|
|
|
|
blocks.clear();
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks.resize(header->num_bat);
|
|
|
|
for (std::size_t i = 0; i < 109; i++)
|
|
|
|
if (i >= header->num_bat)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
blocks[i] = header->bb_blocks[i];
|
|
|
|
if ((header->num_bat > 109) && (header->num_mbat > 0))
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
std::uint8_t *buffer2 = new std::uint8_t[bbat->blockSize];
|
2016-10-25 10:58:08 +08:00
|
|
|
memset(buffer2, 0, bbat->blockSize);
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t k = 109;
|
|
|
|
std::size_t mblock = header->mbat_start;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t r = 0; r < header->num_mbat; r++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
loadBigBlock(mblock, buffer2, bbat->blockSize);
|
|
|
|
for (std::size_t s = 0; s < bbat->blockSize - 4; s += 4)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (k >= header->num_bat)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
blocks[k++] = readU32(buffer2 + s);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
mblock = readU32(buffer2 + bbat->blockSize - 4);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
delete[] buffer2;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// load big bat
|
2016-12-24 23:04:57 +08:00
|
|
|
buflen = blocks.size() * bbat->blockSize;
|
|
|
|
if (buflen > 0)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
buffer = new std::uint8_t[buflen];
|
2016-10-25 10:58:08 +08:00
|
|
|
memset(buffer, 0, buflen);
|
2016-12-24 23:04:57 +08:00
|
|
|
loadBigBlocks(blocks, buffer, buflen);
|
|
|
|
bbat->load(buffer, buflen);
|
2016-10-25 10:58:08 +08:00
|
|
|
delete[] buffer;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// load small bat
|
|
|
|
blocks.clear();
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks = bbat->follow(header->sbat_start);
|
|
|
|
buflen = blocks.size() * bbat->blockSize;
|
|
|
|
if (buflen > 0)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
buffer = new std::uint8_t[buflen];
|
2016-10-25 10:58:08 +08:00
|
|
|
memset(buffer, 0, buflen);
|
2016-12-24 23:04:57 +08:00
|
|
|
loadBigBlocks(blocks, buffer, buflen);
|
|
|
|
sbat->load(buffer, buflen);
|
2016-10-25 10:58:08 +08:00
|
|
|
delete[] buffer;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// load directory tree
|
|
|
|
blocks.clear();
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks = bbat->follow(header->dirent_start);
|
|
|
|
buflen = blocks.size() * bbat->blockSize;
|
|
|
|
buffer = new std::uint8_t[buflen];
|
2016-10-25 10:58:08 +08:00
|
|
|
memset(buffer, 0, buflen);
|
2016-12-24 23:04:57 +08:00
|
|
|
loadBigBlocks(blocks, buffer, buflen);
|
|
|
|
dirtree->load(buffer, buflen);
|
|
|
|
std::size_t sb_start = readU32(buffer + 0x74);
|
2016-10-25 10:58:08 +08:00
|
|
|
delete[] buffer;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// fetch block chain as data for small-files
|
2016-12-24 23:04:57 +08:00
|
|
|
sb_blocks = bbat->follow(sb_start); // small files
|
|
|
|
|
|
|
|
// for troubleshooting, just enable this block
|
2016-10-25 10:58:08 +08:00
|
|
|
#if 0
|
|
|
|
header->debug();
|
|
|
|
sbat->debug();
|
|
|
|
bbat->debug();
|
|
|
|
dirtree->debug();
|
|
|
|
#endif
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// so far so good
|
|
|
|
result = Storage::Ok;
|
|
|
|
opened = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorageIO::create()
|
|
|
|
{
|
|
|
|
// std::cout << "Creating " << filename << std::endl;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
/*FSTREAM file.open( filename.c_str(), std::ios::out|std::ios::binary );
|
|
|
|
if( !file.good() )
|
|
|
|
{
|
|
|
|
std::cerr << "Can't create " << filename << std::endl;
|
|
|
|
result = Storage::OpenFailed;
|
|
|
|
return;
|
|
|
|
}*/
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// so far so good
|
|
|
|
opened = true;
|
|
|
|
result = Storage::Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorageIO::flush()
|
|
|
|
{
|
|
|
|
/* Note on Microsoft implementation:
|
|
|
|
- directory entries are stored in the last block(s)
|
|
|
|
- BATs are as second to the last
|
|
|
|
- Meta BATs are third to the last
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorageIO::close()
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!opened) return;
|
|
|
|
|
|
|
|
// FSTREAM file.close();
|
2016-10-25 10:58:08 +08:00
|
|
|
opened = false;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
std::list<Stream *>::iterator it;
|
|
|
|
for (it = streams.begin(); it != streams.end(); ++it)
|
2016-10-25 10:58:08 +08:00
|
|
|
delete *it;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
StreamIO *StorageIO::streamIO(const std::string &name)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sanity check
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!name.length()) return nullptr;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// search in the entries
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *entry = dirtree->entry(name);
|
|
|
|
// if( entry) std::cout << "FOUND\n";
|
|
|
|
if (!entry) return nullptr;
|
|
|
|
// if( !entry->dir ) std::cout << " NOT DIR\n";
|
|
|
|
if (entry->dir) return nullptr;
|
|
|
|
|
|
|
|
StreamIO *stream = new StreamIO(this, entry);
|
2016-11-10 08:52:18 +08:00
|
|
|
stream->fullName = name;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
return stream;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StorageIO::loadBigBlocks(std::vector<std::size_t> blocks, std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sentinel
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!data) return 0;
|
|
|
|
if (blocks.size() < 1) return 0;
|
|
|
|
if (maxlen == 0) return 0;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// read block one by one, seems fast enough
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t bytes = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; (i < blocks.size()) && (bytes < maxlen); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t block = blocks[i];
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t pos = bbat->blockSize * (block + 1);
|
|
|
|
std::size_t p = (bbat->blockSize < maxlen - bytes) ? bbat->blockSize : maxlen - bytes;
|
|
|
|
if (pos + p > filesize) p = filesize - pos;
|
|
|
|
// FSTREAM file.seekg( pos );
|
|
|
|
// FSTREAM file.read( (char*)data + bytes, p );
|
2016-11-10 08:52:18 +08:00
|
|
|
memcpy(reinterpret_cast<char *>(data) + bytes, filedata + pos, p);
|
2016-10-25 10:58:08 +08:00
|
|
|
bytes += p;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StorageIO::loadBigBlock(std::size_t block, std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sentinel
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!data) return 0;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// wraps call for loadBigBlocks
|
2016-10-26 08:21:58 +08:00
|
|
|
std::vector<std::size_t> blocks;
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks.resize(1);
|
|
|
|
blocks[0] = block;
|
|
|
|
|
|
|
|
return loadBigBlocks(blocks, data, maxlen);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// return number of bytes which has been read
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StorageIO::loadSmallBlocks(std::vector<std::size_t> blocks, std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sentinel
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!data) return 0;
|
|
|
|
if (blocks.size() < 1) return 0;
|
|
|
|
if (maxlen == 0) return 0;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// our own local buffer
|
2016-12-24 23:04:57 +08:00
|
|
|
std::uint8_t *buf = new std::uint8_t[bbat->blockSize];
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// read small block one by one
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t bytes = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
for (std::size_t i = 0; (i < blocks.size()) && (bytes < maxlen); i++)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t block = blocks[i];
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// find where the small-block exactly is
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t pos = block * sbat->blockSize;
|
|
|
|
std::size_t bbindex = pos / bbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (bbindex >= sb_blocks.size()) break;
|
|
|
|
|
|
|
|
loadBigBlock(sb_blocks[bbindex], buf, bbat->blockSize);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// copy the data
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t offset = pos % bbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t p = (maxlen - bytes < bbat->blockSize - offset) ? maxlen - bytes : bbat->blockSize - offset;
|
|
|
|
p = (sbat->blockSize < p) ? sbat->blockSize : p;
|
|
|
|
memcpy(data + bytes, buf + offset, p);
|
2016-10-25 10:58:08 +08:00
|
|
|
bytes += p;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
delete[] buf;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StorageIO::loadSmallBlock(std::size_t block, std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sentinel
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!data) return 0;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// wraps call for loadSmallBlocks
|
2016-10-26 08:21:58 +08:00
|
|
|
std::vector<std::size_t> blocks;
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks.resize(1);
|
|
|
|
blocks.assign(1, block);
|
|
|
|
|
|
|
|
return loadSmallBlocks(blocks, data, maxlen);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// =========== StreamIO ==========
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
StreamIO::StreamIO(StorageIO *s, DirEntry *e)
|
|
|
|
: io(s),
|
|
|
|
entry(e),
|
|
|
|
fullName(),
|
|
|
|
eof(false),
|
|
|
|
fail(false),
|
|
|
|
blocks(),
|
|
|
|
m_pos(0),
|
|
|
|
cache_data(0),
|
|
|
|
cache_size(4096), // optimal ?
|
|
|
|
cache_pos(0)
|
|
|
|
{
|
|
|
|
if (entry->size >= io->header->threshold)
|
|
|
|
blocks = io->bbat->follow(entry->start);
|
2016-10-25 10:58:08 +08:00
|
|
|
else
|
2016-12-24 23:04:57 +08:00
|
|
|
blocks = io->sbat->follow(entry->start);
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// prepare cache
|
2016-10-26 08:21:58 +08:00
|
|
|
cache_data = new std::uint8_t[cache_size];
|
2016-10-25 10:58:08 +08:00
|
|
|
updateCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME tell parent we're gone
|
|
|
|
StreamIO::~StreamIO()
|
|
|
|
{
|
|
|
|
delete[] cache_data;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void StreamIO::seek(std::size_t pos)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
m_pos = pos;
|
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t StreamIO::tell()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return m_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
int StreamIO::getch()
|
|
|
|
{
|
|
|
|
// past end-of-file ?
|
2016-12-24 23:04:57 +08:00
|
|
|
if (m_pos > entry->size) return -1;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// need to update cache ?
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!cache_size || (m_pos < cache_pos) || (m_pos >= cache_pos + cache_size)) updateCache();
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
// something bad if we don't get good cache
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!cache_size) return -1;
|
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
int data = cache_data[m_pos - cache_pos];
|
|
|
|
m_pos++;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StreamIO::read(std::size_t pos, std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// sanity checks
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!data) return 0;
|
|
|
|
if (maxlen == 0) return 0;
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t totalbytes = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
if (entry->size < io->header->threshold)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
// small file
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t index = pos / io->sbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
if (index >= blocks.size()) return 0;
|
|
|
|
|
|
|
|
std::uint8_t *buf = new std::uint8_t[io->sbat->blockSize];
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t offset = pos % io->sbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
while (totalbytes < maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (index >= blocks.size()) break;
|
|
|
|
io->loadSmallBlock(blocks[index], buf, io->bbat->blockSize);
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t count = io->sbat->blockSize - offset;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (count > maxlen - totalbytes) count = maxlen - totalbytes;
|
|
|
|
memcpy(data + totalbytes, buf + offset, count);
|
2016-10-25 10:58:08 +08:00
|
|
|
totalbytes += count;
|
|
|
|
offset = 0;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
delete[] buf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// big file
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t index = pos / io->bbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
if (index >= blocks.size()) return 0;
|
|
|
|
|
|
|
|
std::uint8_t *buf = new std::uint8_t[io->bbat->blockSize];
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t offset = pos % io->bbat->blockSize;
|
2016-12-24 23:04:57 +08:00
|
|
|
while (totalbytes < maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (index >= blocks.size()) break;
|
|
|
|
io->loadBigBlock(blocks[index], buf, io->bbat->blockSize);
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t count = io->bbat->blockSize - offset;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (count > maxlen - totalbytes) count = maxlen - totalbytes;
|
|
|
|
memcpy(data + totalbytes, buf + offset, count);
|
2016-10-25 10:58:08 +08:00
|
|
|
totalbytes += count;
|
|
|
|
index++;
|
|
|
|
offset = 0;
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
delete[] buf;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return totalbytes;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t StreamIO::read(std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t bytes = read(tell(), data, maxlen);
|
2016-10-25 10:58:08 +08:00
|
|
|
m_pos += bytes;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StreamIO::updateCache()
|
|
|
|
{
|
|
|
|
// sanity check
|
2016-12-24 23:04:57 +08:00
|
|
|
if (!cache_data) return;
|
|
|
|
|
|
|
|
cache_pos = m_pos - (m_pos % cache_size);
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t bytes = cache_size;
|
2016-12-24 23:04:57 +08:00
|
|
|
if (cache_pos + bytes > entry->size) bytes = entry->size - cache_pos;
|
|
|
|
cache_size = read(cache_pos, cache_data, bytes);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// =========== Storage ==========
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
Storage::Storage(char *bytes, std::size_t length)
|
|
|
|
: io(new StorageIO(this, bytes, length))
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Storage::~Storage()
|
|
|
|
{
|
|
|
|
delete io;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Storage::result()
|
|
|
|
{
|
|
|
|
return io->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Storage::open()
|
|
|
|
{
|
|
|
|
return io->open();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Storage::close()
|
|
|
|
{
|
|
|
|
io->close();
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::list<std::string> Storage::entries(const std::string &path)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
std::list<std::string> result;
|
2016-12-24 23:04:57 +08:00
|
|
|
DirTree *dt = io->dirtree;
|
|
|
|
DirEntry *e = dt->entry(path, false);
|
|
|
|
if (e && e->dir)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
auto parent = dt->indexOf(e);
|
|
|
|
std::vector<std::size_t> children = dt->children(static_cast<std::size_t>(parent));
|
|
|
|
for (std::size_t i = 0; i < children.size(); i++)
|
|
|
|
result.push_back(dt->entry(children[i])->name);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
bool Storage::isDirectory(const std::string &name)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
DirEntry *e = io->dirtree->entry(name, false);
|
2016-10-25 10:58:08 +08:00
|
|
|
return e ? e->dir : false;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
DirTree *Storage::dirTree()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return io->dirtree;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
StorageIO *Storage::storageIO()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return io;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::list<DirEntry *> Storage::dirEntries(const std::string &path)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
std::list<DirEntry *> result;
|
|
|
|
DirTree *dt = io->dirtree;
|
|
|
|
DirEntry *e = dt->entry(path, false);
|
|
|
|
if (e && e->dir)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
auto parent = dt->indexOf(e);
|
|
|
|
std::vector<std::size_t> children = dt->children(static_cast<std::size_t>(parent));
|
|
|
|
for (std::size_t i = 0; i < children.size(); i++)
|
|
|
|
result.push_back(dt->entry(children[i]));
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
2016-10-25 10:58:08 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// =========== Stream ==========
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
Stream::Stream(Storage *storage, const std::string &name)
|
|
|
|
: io(storage->io->streamIO(name))
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME tell parent we're gone
|
|
|
|
Stream::~Stream()
|
|
|
|
{
|
|
|
|
delete io;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Stream::fullName()
|
|
|
|
{
|
|
|
|
return io ? io->fullName : std::string();
|
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t Stream::tell()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return io ? io->tell() : 0;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
void Stream::seek(std::size_t newpos)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
if (io) io->seek(newpos);
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
2016-10-26 08:21:58 +08:00
|
|
|
std::size_t Stream::size()
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
|
|
|
return io ? io->entry->size : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Stream::getch()
|
|
|
|
{
|
|
|
|
return io ? io->getch() : 0;
|
|
|
|
}
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
std::size_t Stream::read(std::uint8_t *data, std::size_t maxlen)
|
2016-10-25 10:58:08 +08:00
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
return io ? io->read(data, maxlen) : 0;
|
2016-10-25 10:58:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Stream::eof()
|
|
|
|
{
|
|
|
|
return io ? io->eof : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Stream::fail()
|
|
|
|
{
|
|
|
|
return io ? io->fail : true;
|
|
|
|
}
|