Skip to content

Commit

Permalink
Support writing V1 and V2 VPKs from single dll
Browse files Browse the repository at this point in the history
  • Loading branch information
SCell555 committed Jun 19, 2022
1 parent cde87a9 commit fd4c9cd
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 13 deletions.
54 changes: 46 additions & 8 deletions Handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class CHandler :
public IOutArchive, public ISetProperties, public IMultiVolumeOutArchive, // writing
public CMyUnknownImp
{
public:
CHandler( bool writeV1 = false ) : writeV1( writeV1 ) {}

MY_UNKNOWN_IMP5( IInArchive, IInArchiveGetStream, IOutArchive, ISetProperties, IMultiVolumeOutArchive )
INTERFACE_IInArchive( override )
INTERFACE_IOutArchive( override )
Expand All @@ -30,6 +33,7 @@ class CHandler :
STDMETHOD( GetMultiArchiveNameFmt )( PROPVARIANT* nameMod, PROPVARIANT* prefix, PROPVARIANT* postfix, BOOL* numberAfterExt, UInt32* digitCount ) override;

private:
const bool writeV1;
libvpk::VPKSet vpk;
CMyComPtr<IInStream> basePak;
CObjectVector<CMyComPtr<IInStream>> paks;
Expand Down Expand Up @@ -765,7 +769,7 @@ static constexpr std::string_view standardDirs[] =
class VpkWriter
{
public:
VpkWriter() = default;
VpkWriter( bool writeV1 ) : writeV1( writeV1 ) {}

HRESULT addItem( AString internalPath, UInt32 size, IInStream* stream )
{
Expand All @@ -777,7 +781,7 @@ class VpkWriter
return E_FAIL;
const auto extOffset = path.rfind( '.' );
const auto nameOffset = path.rfind( '/', extOffset );
const auto name = nameOffset != std::string_view::npos ? std::string{ path.substr( nameOffset + 1, extOffset - nameOffset - 1 ) } : std::string{ path.substr( 0, extOffset ) };
auto name = nameOffset != std::string_view::npos ? std::string{ path.substr( nameOffset + 1, extOffset - nameOffset - 1 ) } : std::string{ path.substr( 0, extOffset ) };
m_exts.try_emplace( extOffset != std::string_view::npos ? std::string{ path.substr(extOffset + 1) } : " "s ).
first->second.try_emplace( nameOffset != std::string_view::npos ? std::string{ path.substr(0, nameOffset) } : " "s ).
first->second.try_emplace( name.empty() ? " "s : std::move( name ), size, 0, stream );
Expand Down Expand Up @@ -817,7 +821,15 @@ class VpkWriter
return E_OUTOFMEMORY;
RINOK( stream->Init( outStream, stream_ ) );

if ( m_needFixup == 1 )
const auto checkNeedFixup = [&]
{
for ( auto& ext : m_exts )
for ( auto& dir : ext.second )
if ( dir.first != m_lastDir )
return true;
return false;
};
if ( m_needFixup == 1 && checkNeedFixup() )
{
for ( auto &ext : m_exts )
{
Expand Down Expand Up @@ -882,7 +894,7 @@ class VpkWriter
}
RINOK( write<std::string>( stream, {} ) );

RINOK( stream->Seek( treeSize + sizeof( libvpk::meta::VPKHeader ), STREAM_SEEK_SET, nullptr ) );
RINOK( stream->Seek( treeSize + ( writeV1 ? sizeof( libvpk::meta::VPKHeader1 ) : sizeof( libvpk::meta::VPKHeader2 ) ), STREAM_SEEK_SET, nullptr ) );

if ( !volSize )
{
Expand Down Expand Up @@ -947,9 +959,19 @@ class VpkWriter
return stream->Write( string.c_str(), static_cast<UInt32>( string.size() + 1 ), nullptr );
}

static HRESULT writeHeader( IOutStream* stream, UInt32 size, UInt32 treeSize )
HRESULT writeHeader( IOutStream* stream, UInt32 size, UInt32 treeSize )
{
libvpk::meta::VPKHeader header;
if ( writeV1 )
{
libvpk::meta::VPKHeader1 header;
header.signature = libvpk::meta::VPKHeader::ValidSignature;
header.version = 1;
header.treeSize = static_cast<Int32>( treeSize );

return stream->Write( &header, sizeof( header ), nullptr );
}

libvpk::meta::VPKHeader2 header;
header.signature = libvpk::meta::VPKHeader::ValidSignature;
header.version = 2;
header.treeSize = static_cast<Int32>( treeSize );
Expand Down Expand Up @@ -984,6 +1006,7 @@ class VpkWriter
return crc;
}

const bool writeV1;
robin_hood::unordered_node_map<std::string, robin_hood::unordered_node_map<std::string, robin_hood::unordered_node_map<std::string, File>>> m_exts;
signed char m_needFixup = 0;
std::string m_lastDir;
Expand Down Expand Up @@ -1013,7 +1036,7 @@ STDMETHODIMP CHandler::UpdateItems( ISequentialOutStream* outStream, UInt32 numI
callback2->GetVolumeSize( 0, &volSize );

UInt64 totalSize = 0;
VpkWriter writer;
VpkWriter writer( writeV1 );
for ( UInt32 i = 0; i < numItems; i++ )
{
NWindows::NCOM::CPropVariant prop;
Expand Down Expand Up @@ -1107,4 +1130,19 @@ REGISTER_ARC_IO(
0,
NArcInfoFlags::kMultiSignature | NArcInfoFlags::kUseGlobalOffset | NArcInfoFlags::kPureStartOpen | NArcInfoFlags::kByExtOnlyOpen,
IsArc_Vpk, 1
)
)

#define REGISTER_ARC_IO2(n, e, ae, id, sig, offs, flags, isArc, mask) \
static IInArchive *CreateArc2() { return new CHandler( true ); } \
static IOutArchive *CreateArcOut2() { return new CHandler( true ); } \
static const CArcInfo g_ArcInfo2 = { flags, id, ARRAYSIZE(sig), offs, mask, sig, n, e, ae, CreateArc2, CreateArcOut2, isArc } ; \
struct CRegisterArc2 { CRegisterArc2() { RegisterArc(&g_ArcInfo2); }}; \
static CRegisterArc2 g_RegisterArc2;

REGISTER_ARC_IO2(
"VPKv1", "vpk", 0, 2,
k_Signature,
0,
NArcInfoFlags::kMultiSignature | NArcInfoFlags::kUseGlobalOffset | NArcInfoFlags::kPureStartOpen | NArcInfoFlags::kByExtOnlyOpen,
IsArc_Vpk, 1
)
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# VPK format provider for 7-Zip
Provides ability for 7-Zip to fully read V1 and V2, and write (with some limitations) V2 VPK files
Provides ability for 7-Zip to fully read and write (with some limitations) V1 and V2 VPK files

## Limitations
Creation of VPK files is limited to single VPK mode, because of 7-Zip's own multi-pack handling.

<br/>

Creation of VPK files is limited to single VPK mode, because of 7-Zip's own multi-pack handling. If you want to write multi-pack VPKs use [my fork of 7-Zip](https://github.com/SCell555/7-Zip-zstd) with necessary functionality exposed.

## Screenshots

![Vpk Read](./screenshots/vpk_handler.png)
Expand Down

0 comments on commit fd4c9cd

Please sign in to comment.