1 Commits
main ... 0.1.2

Author SHA1 Message Date
ccc98bdf62 Implement GIF conversion 2025-09-27 14:06:57 +08:00
266 changed files with 2193 additions and 2926 deletions

9
.gitignore vendored
View File

@ -1,11 +1,8 @@
# idea # idea
.idea/ .idea/
cmake-build-*/ cmake-build-debug/
cmake-build-release/
# build仅根目录不影响 vendor/lz4/build 等子目录) cmake-build-minsizerel/
/build/
compile_commands.json
# vs # vs
.vs/ .vs/

View File

@ -2,31 +2,9 @@ cmake_minimum_required(VERSION 3.31)
project(expkg) project(expkg)
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 默认 Release 构建Debug 模式下 GIF/PNG 编码慢 3-5 倍) add_subdirectory(expkg)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) add_subdirectory(testApp)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# 第三方依赖
add_subdirectory(vendor/lz4/build/cmake)
# 源文件CONFIGURE_DEPENDS: 文件增删时自动重新配置)
file(GLOB_RECURSE SRC_SOURCE CONFIGURE_DEPENDS src/*.cpp)
file(GLOB STB_SOURCE CONFIGURE_DEPENDS vendor/stb/*.cpp)
# 可执行文件
add_executable(${PROJECT_NAME} ${SRC_SOURCE} ${STB_SOURCE})
# MSVC 默认编码设为 UTF-8
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /utf-8)
endif()
# 依赖与头文件路径
target_link_libraries(${PROJECT_NAME} PRIVATE lz4)
target_include_directories(${PROJECT_NAME}
PRIVATE vendor/stb vendor/gif-h src
)

View File

@ -1,12 +1,35 @@
# exPKG # exPKG
## useage ## useage
```bash > ```c++
expkg /path/to/file.pkg|file.mpkg [output/path] > #include "EXPKG/EXPKG.h"
``` >
> int main(int argc, char** argv) {
> CommandArgs args{ argc, argv};
>
> PKG::EXPKG decompress(args);
> PKG::EXPKG decompress("path/to/file");
> PKG::EXPKG decompress("path/to/file", "path/to/output/directory");
>
> return 0;
> }
>```
build ## Cmake
```bash >
cmake -B build > use as subdirectory
cmake --build build --config Release -j8 >
``` > ```cmake
> add_subdirectory(path/to/expkg)
>
>add_executable(${TARGET} ${SRC_SOURCE})
>target_link_libraries(${TARGET} expkg-static)
>
> # or use dll by
> # target_link_libraries(${TARGET} expkg-shared)
>```
> then
> ```bash
> cmake -B build
> cmake --build build --config Release -j8
>```

40
expkg/CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
set(TARGET expkg)
project(${TARGET})
add_subdirectory(vendor/lz4/build/cmake)
file(GLOB_RECURSE SRC_SOURCE src/**.cpp vendor/gif-h/gif.h)
file(GLOB STB_SOURCE vendor/stb/*.cpp)
# static
add_library(expkg-static STATIC
${SRC_SOURCE}
${STB_SOURCE}
)
target_link_libraries(expkg-static PRIVATE lz4)
target_include_directories(expkg-static PRIVATE vendor/stb vendor/gif-h)
target_include_directories(expkg-static PUBLIC src)
# shared
add_library(expkg-shared SHARED
${SRC_SOURCE}
${STB_SOURCE}
)
set_target_properties(expkg-shared PROPERTIES
OUTPUT_NAME "expkg"
PREFIX ""
)
target_link_libraries(expkg-shared PRIVATE lz4)
target_compile_definitions(expkg-shared PRIVATE -DPKG_SHARED -DPKG_BUILD_DLL)
target_include_directories(expkg-shared PRIVATE vendor/stb vendor/gif-h)
target_include_directories(expkg-shared PUBLIC src)

View File

@ -0,0 +1,97 @@
//
// Created by sfd on 25-8-4.
//
#include "BinaryReader.h"
#include <filesystem>
#include <iostream>
namespace PKG
{
BinaryReader::BinaryReader(const std::filesystem::path& fileName)
{
m_FilePath = fileName.string();
m_File.open(m_FilePath, std::ios::in | std::ios::binary);
if (!m_File.is_open())
{
std::cerr << "Failed to open file " << m_FilePath << std::endl;
system("pause");
exit(0);
}
}
BinaryReader::~BinaryReader()
{
if (m_File.is_open())
m_File.close();
}
int32_t BinaryReader::ReadInt32()
{
int32_t result = 0;
m_File.read(reinterpret_cast<char*>(&result), sizeof(int32_t));
return result;
}
uint32_t BinaryReader::ReadUInt32()
{
uint32_t result = 0;
m_File.read(reinterpret_cast<char*>(&result), sizeof(uint32_t));
return result;
}
float_t BinaryReader::ReadSingle()
{
float_t result = 0;
m_File.read(reinterpret_cast<char*>(&result), sizeof(float_t));
return result;
}
char BinaryReader::ReadChar()
{
char result;
m_File.read(&result, sizeof(char));
pos_type a = m_File.tellg();
return result;
}
void BinaryReader::ReadData(std::string& data, const uint32_t length)
{
data.resize(length);
m_File.read(data.data(), length);
}
void BinaryReader::ReadData(std::vector<uint8_t>& data, uint32_t length)
{
data.resize(length);
m_File.read(reinterpret_cast<char*>(data.data()), length);
}
std::string BinaryReader::ReadString(const uint32_t length)
{
std::vector<uint8_t> result;
result.resize(length);
m_File.read(reinterpret_cast<char*>(result.data()), length);
return std::filesystem::u8path(std::string(reinterpret_cast<const char*>(result.data()), length)).string();
}
std::string BinaryReader::ReadNString(const int32_t maxLength)
{
std::vector<uint8_t> result;
result.resize(0);
int count = maxLength <= 0 ? 16 : maxLength;
char chr = ReadChar();
while (chr != '\0' && (maxLength == -1 || count <= maxLength))
{
result.push_back(chr);
chr = ReadChar();
}
return std::string(reinterpret_cast<const char*>(result.data()), result.size());
}
}

View File

@ -0,0 +1,49 @@
//
// Created by sfd on 25-8-4.
//
#ifndef BINARYREADER_H
#define BINARYREADER_H
#include <filesystem>
#include <fstream>
#include <vector>
#include "Core.h"
namespace PKG
{
using pos_type = long long;
class PKG_API BinaryReader
{
public:
BinaryReader() = delete;
explicit BinaryReader(const std::filesystem::path& fileName);
~BinaryReader();
int32_t ReadInt32();
uint32_t ReadUInt32();
float_t ReadSingle();
char ReadChar();
std::string ReadString(uint32_t length);
std::string ReadNString(int32_t maxLength = -1);
void ReadData(std::string& data, uint32_t length);
void ReadData(std::vector<uint8_t>& data, uint32_t length);
void seekg(const pos_type pos) { m_File.seekg(pos); }
pos_type tellg() { return m_File.tellg(); }
std::string GetFilePath() const { return m_FilePath; }
std::string GetFileName() const { return m_FilePath.substr(m_FilePath.find_last_of("\\/") + 1); }
private:
std::ifstream m_File;
std::string m_FilePath;
};
}
#endif //BINARYREADER_H

View File

@ -0,0 +1,47 @@
//
// Created by sfd on 25-8-4.
//
#include "BinaryWriter.h"
#include <filesystem>
#include <iostream>
namespace PKG
{
BinaryWriter::BinaryWriter(const std::filesystem::path& fileName, const std::ios_base::openmode optMode)
{
m_FilePath = fileName.string();
const std::filesystem::path path(fileName.parent_path());
m_File.open(m_FilePath, optMode);
if (!m_File.is_open() && !std::filesystem::exists(path))
{
std::filesystem::create_directories(path);
m_File.open(m_FilePath, optMode);
if (!m_File.is_open())
std::cerr << "cound not create file: " << m_FilePath << std::endl;
}
}
BinaryWriter::~BinaryWriter()
{
close();
}
void BinaryWriter::WriteBytes(const char* data, const uint32_t size)
{
m_File.write(data, size);
}
void BinaryWriter::WriteString(const std::string& str)
{
m_File.write(str.c_str(), str.size());
}
void BinaryWriter::close()
{
if (!m_File.is_open())
m_File.close();
}
}

View File

@ -0,0 +1,38 @@
//
// Created by sfd on 25-8-4.
//
#ifndef BINARYWRITER_H
#define BINARYWRITER_H
#include <filesystem>
#include <fstream>
#include "Core.h"
namespace PKG
{
class PKG_API BinaryWriter
{
public:
BinaryWriter() = delete;
explicit BinaryWriter(const std::filesystem::path& fileName, std::ios_base::openmode optMode = std::ios::out);
~BinaryWriter();
void WriteBytes(const char* data, uint32_t size);
void WriteString(const std::string& str);
std::string GetFilePath() const { return m_FilePath; }
std::string GetFileName() const { return m_FilePath.substr(m_FilePath.find_last_of("\\/") + 1); }
void close();
private:
std::ofstream m_File;
std::string m_FilePath;
};
}
#endif //BINARYWRITER_H

View File

@ -0,0 +1,265 @@
//
// Created by sfd on 25-8-5.
//
#include "ImageReader.h"
#include <iostream>
#include "lz4.h"
namespace PKG
{
#include <vector>
std::vector<unsigned char> Lz4Decompress(const unsigned char* compressedData,
size_t compressedSize,
size_t decompressedSize)
{
// 准备输出缓冲区
std::vector<unsigned char> output(decompressedSize);
// 执行解压缩
int result = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressedData),
reinterpret_cast<char*>(output.data()),
static_cast<int>(compressedSize),
static_cast<int>(decompressedSize)
);
// 检查解压结果
if (result < 0 || static_cast<size_t>(result) != decompressedSize) {
throw std::runtime_error("LZ4 decompression failed");
}
return output;
}
TexImage ImageReader::ReadFrom(BinaryReader& reader, const TexImageContainer& container, const TexFormat format)
{
int mipMapCount = reader.ReadInt32();
auto aFormat = GetFormatFromTex(container.ImageFormat, format);
TexImage image{};
for (int i = 0; i < mipMapCount; i++)
{
TexMipMap mipmap;
switch (container.ImageContainerVersion)
{
case ImageContainerVersion::VERSION1:
mipmap = ReadMipMapV1(reader); break;
case ImageContainerVersion::VERSION2:
case ImageContainerVersion::VERSION3:
mipmap = ReadMipMapV2AndV3(reader); break;
case ImageContainerVersion::VERSION4:
mipmap = ReadMipMapV4(reader); break;
}
mipmap.Format = aFormat;
if (mipmap.IsZ4Compressed)
{
mipmap.Data = Lz4Decompress(mipmap.Data.data(), mipmap.Data.size(), mipmap.DecompressedDataCount);
mipmap.IsZ4Compressed = false;
}
image.Mipmaps.push_back(mipmap);
}
return image;
}
TexImageContainer ImageReader::ImageContainerReaderReadFrom(BinaryReader& reader, TexFormat texFormat)
{
TexImageContainer container;
container.Magic = reader.ReadNString();
int imageCount = reader.ReadInt32();
if (container.Magic == "TEXB0001")
{
}
else if (container.Magic == "TEXB0002")
{
}
else if (container.Magic == "TEXB0003")
{
container.ImageFormat = (FreeImageFormat)reader.ReadInt32();
}
else if (container.Magic == "TEXB0004")
{
auto format = (FreeImageFormat)reader.ReadInt32();
bool isVideoMp4 = reader.ReadInt32();
if (format == FreeImageFormat::FIF_UNKNOWN)
{
if (isVideoMp4)
format = FreeImageFormat::FIF_MP4;
}
container.ImageFormat = format;
}
else
{
std::cerr << "bad image format" << std::endl;
}
int version = std::stoi(container.Magic.substr(4, 4));
container.ImageContainerVersion = (ImageContainerVersion)version;
if (container.ImageContainerVersion == ImageContainerVersion::VERSION4 && container.ImageFormat != FreeImageFormat::FIF_MP4)
{
container.ImageContainerVersion = ImageContainerVersion::VERSION3;
}
for (int i = 0; i < imageCount; i++)
{
container.Images.push_back(ReadFrom(reader, container, texFormat));
}
return container;
}
TexMipMap ImageReader::ReadMipMapV1(BinaryReader& reader)
{
TexMipMap mipmap{};
mipmap.Width = reader.ReadInt32();
mipmap.Height = reader.ReadInt32();
mipmap.Data = ReadBytes(reader);
return mipmap;
}
TexMipMap ImageReader::ReadMipMapV2AndV3(BinaryReader& reader)
{
TexMipMap mipmap{};
mipmap.Width = reader.ReadInt32();
mipmap.Height = reader.ReadInt32();
mipmap.IsZ4Compressed = reader.ReadInt32() == 1;
mipmap.DecompressedDataCount = reader.ReadInt32();
mipmap.Data = ReadBytes(reader);
return mipmap;
}
TexMipMap ImageReader::ReadMipMapV4(BinaryReader& reader)
{
int param1 = reader.ReadInt32();
if (param1 != 1)
{
std::cerr << "ReadMipmapV4 unknow param1: " << param1 << std::endl;
}
int param2 = reader.ReadInt32();
if (param2 != 2)
{
std::cerr << "ReadMipmapV4 unknow param2: " << param1 << std::endl;
}
std::string condition = reader.ReadNString();
int param3 = reader.ReadInt32();
if (param3 != 1)
{
std::cerr << "ReadMipmapV4 unknow param3: " << param1 << std::endl;
}
TexMipMap mipmap{};
mipmap.Width = reader.ReadInt32();
mipmap.Height = reader.ReadInt32();
mipmap.IsZ4Compressed = reader.ReadInt32() == 1;
mipmap.DecompressedDataCount = reader.ReadInt32();
mipmap.Data = ReadBytes(reader);
return mipmap;
}
std::vector<uint8_t> ImageReader::ReadBytes(BinaryReader& reader)
{
int count = reader.ReadInt32();
std::vector<uint8_t> bytes;
reader.ReadData(bytes, count);
return bytes;
}
MipmapFormat ImageReader::GetFormatFromTex(FreeImageFormat imageFormat, TexFormat format)
{
if (imageFormat != FreeImageFormat::FIF_UNKNOWN)
{
return FreeImageFormatToMipmapFormat(imageFormat);
}
switch (format)
{
case TexFormat::RGBA8888: return MipmapFormat::RGBA8888;
case TexFormat::DXT5: return MipmapFormat::CompressedDXT5;
case TexFormat::DXT3: return MipmapFormat::CompressedDXT3;
case TexFormat::DXT1: return MipmapFormat::CompressedDXT1;
case TexFormat::RG88: return MipmapFormat::RG88;
case TexFormat::R8: return MipmapFormat::R8;
}
std::cerr << "unknow format" << std::endl;
return MipmapFormat::Invalid;
}
MipmapFormat ImageReader::FreeImageFormatToMipmapFormat(FreeImageFormat imageFormat)
{
switch (imageFormat)
{
case FreeImageFormat::FIF_BMP: return MipmapFormat::ImageBMP;
case FreeImageFormat::FIF_ICO: return MipmapFormat::ImageICO;
case FreeImageFormat::FIF_JPEG: return MipmapFormat::ImageJPEG;
case FreeImageFormat::FIF_JNG: return MipmapFormat::ImageJNG;
case FreeImageFormat::FIF_KOALA: return MipmapFormat::ImageKOALA;
case FreeImageFormat::FIF_LBM: return MipmapFormat::ImageLBM;
case FreeImageFormat::FIF_MNG: return MipmapFormat::ImageMNG;
case FreeImageFormat::FIF_PBM: return MipmapFormat::ImagePBM;
case FreeImageFormat::FIF_PBMRAW: return MipmapFormat::ImagePBMRAW;
case FreeImageFormat::FIF_PCD: return MipmapFormat::ImagePCD;
case FreeImageFormat::FIF_PCX: return MipmapFormat::ImagePCX;
case FreeImageFormat::FIF_PGM: return MipmapFormat::ImagePGM;
case FreeImageFormat::FIF_PGMRAW: return MipmapFormat::ImagePGMRAW;
case FreeImageFormat::FIF_PNG: return MipmapFormat::ImagePNG;
case FreeImageFormat::FIF_PPM: return MipmapFormat::ImagePPM;
case FreeImageFormat::FIF_PPMRAW: return MipmapFormat::ImagePPMRAW;
case FreeImageFormat::FIF_RAS: return MipmapFormat::ImageRAS;
case FreeImageFormat::FIF_TARGA: return MipmapFormat::ImageTARGA;
case FreeImageFormat::FIF_TIFF: return MipmapFormat::ImageTIFF;
case FreeImageFormat::FIF_WBMP: return MipmapFormat::ImageWBMP;
case FreeImageFormat::FIF_PSD: return MipmapFormat::ImagePSD;
case FreeImageFormat::FIF_CUT: return MipmapFormat::ImageCUT;
case FreeImageFormat::FIF_XBM: return MipmapFormat::ImageXBM;
case FreeImageFormat::FIF_XPM: return MipmapFormat::ImageXPM;
case FreeImageFormat::FIF_DDS: return MipmapFormat::ImageDDS;
case FreeImageFormat::FIF_GIF: return MipmapFormat::ImageGIF;
case FreeImageFormat::FIF_HDR: return MipmapFormat::ImageHDR;
case FreeImageFormat::FIF_FAXG3: return MipmapFormat::ImageFAXG3;
case FreeImageFormat::FIF_SGI: return MipmapFormat::ImageSGI;
case FreeImageFormat::FIF_EXR: return MipmapFormat::ImageEXR;
case FreeImageFormat::FIF_J2K: return MipmapFormat::ImageJ2K;
case FreeImageFormat::FIF_JP2: return MipmapFormat::ImageJP2;
case FreeImageFormat::FIF_PFM: return MipmapFormat::ImagePFM;
case FreeImageFormat::FIF_PICT: return MipmapFormat::ImagePICT;
case FreeImageFormat::FIF_RAW: return MipmapFormat::ImageRAW;
case FreeImageFormat::FIF_MP4: return MipmapFormat::VideoMp4;
}
std::cerr << "unknown format" << std::endl;
return MipmapFormat::Invalid;
}
}

View File

@ -0,0 +1,32 @@
//
// Created by sfd on 25-8-5.
//
#ifndef IMAGEREADER_H
#define IMAGEREADER_H
#include "BinaryOPT/BinaryReader.h"
#include "Tex/TexImageContainer.h"
namespace PKG
{
class PKG_API ImageReader
{
public:
static TexImage ReadFrom(BinaryReader& reader, const TexImageContainer& container, TexFormat format);
static TexImageContainer ImageContainerReaderReadFrom(BinaryReader& reader, TexFormat texFormat);
private:
static TexMipMap ReadMipMapV1(BinaryReader& reader);
static TexMipMap ReadMipMapV2AndV3(BinaryReader& reader);
static TexMipMap ReadMipMapV4(BinaryReader& reader);
static std::vector<uint8_t> ReadBytes(BinaryReader& reader);
static MipmapFormat GetFormatFromTex(FreeImageFormat imageFormat, TexFormat format);
static MipmapFormat FreeImageFormatToMipmapFormat(FreeImageFormat imageFormat);
};
}
#endif //IMAGEREADER_H

5
expkg/src/Core.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-8-4.
//
#include "Core.h"

19
expkg/src/Core.h Normal file
View File

@ -0,0 +1,19 @@
//
// Created by sfd on 25-8-4.
//
#ifndef CORE_H
#define CORE_H
#ifdef PKG_SHARED
#ifdef PKG_BUILD_DLL
#define PKG_API __declspec(dllexport)
#else
#define PKG_API __declspec(dllimport)
#endif
#else
#define PKG_API
#endif
#endif //CORE_H

230
expkg/src/DXT/DXT.cpp Normal file
View File

@ -0,0 +1,230 @@
//
// Created by sfd on 25-9-21.
//
#include "DXT.h"
namespace PKG
{
void DXT::DecompressImage(int width, int height, std::vector<uint8_t>& data, const DXTFlags flags)
{
std::vector<uint8_t> rgba(width * height * 4);
// uint8_t rgba[width * height * 4];
// init the block pos
int sourceBlockPos = 0;
int bytesPerBlock = flags == DXTFlags::DXT1 ? 8 : 16;
std::vector<uint8_t> targetRGBA(4 * 16);
// loop over blocks
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
// decompress the block
uint8_t targetRGBA_pos = 0;
if (data.size() == sourceBlockPos)
continue;
Decompress(targetRGBA, data, sourceBlockPos, flags);
// Write the decompressed pixels to the correct image locations
for (int py = 0; py < 4; py++)
{
for (int px = 0; px < 4; px++)
{
const int sx = x + px;
const int sy = y + py;
if (sx < width && sy < height)
{
const int targetPixel = 4 * (width * sy + sx);
rgba[targetPixel + 0] = targetRGBA[targetRGBA_pos + 0];
rgba[targetPixel + 1] = targetRGBA[targetRGBA_pos + 1];
rgba[targetPixel + 2] = targetRGBA[targetRGBA_pos + 2];
rgba[targetPixel + 3] = targetRGBA[targetRGBA_pos + 3];
targetRGBA_pos += 4;
}
else
{
// Ignore that pixel
targetRGBA_pos += 4;
}
}
}
sourceBlockPos += bytesPerBlock;
}
}
data = rgba;
}
void DXT::Decompress(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, const int blockIndex, const DXTFlags flags)
{
// get block locations
int colorBlockIndex = blockIndex;
if (flags == DXTFlags::DXT3 | flags == DXTFlags::DXT5)
colorBlockIndex += 8;
DecompressColor(rgba, block, colorBlockIndex, flags == DXTFlags::DXT1);
// decompress alpha separately is necessary
if (flags == DXTFlags::DXT3)
DecompressAlphaDxt3(rgba, block, blockIndex);
else if (flags == DXTFlags::DXT5)
DecompressAlphaDxt5(rgba, block, blockIndex);
}
void DXT::DecompressColor(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, const int blockIndex, const bool isDxt1)
{
// unpack end points
std::vector<uint8_t> codes(16);
const int a = UnPack565(block, blockIndex, 0, codes, 0);
const int b = UnPack565(block, blockIndex, 2, codes, 4);
// generate Midpoints
for (int i = 0; i < 3; i++)
{
const int c = codes[i];
const int d = codes[4 + i];
if (isDxt1 && a <= b)
{
codes[8 + i] = (uint8_t)((c + d) / 2);
codes[12 + i] = 0;
}
else
{
codes[8 + i] = (uint8_t)((2 * c + d) / 3);
codes[12 + i] = (uint8_t)((c + 2 * d) / 3);
}
}
// Fill in alpha for intermediate values
codes[8 + 3] = 255;
codes[12 + 3] = (isDxt1 && a <= b) ? (uint8_t)0 : (uint8_t)255;
// unpack the indices
std::vector<uint8_t> indices(16);
for (int i = 0; i < 4; i++)
{
const int packed = block[blockIndex + 4 + i];
indices[0 + i * 4] = (uint8_t)(packed & 0x3);
indices[1 + i * 4] = (uint8_t)((packed >> 2) & 0x3);
indices[2 + i * 4] = (uint8_t)((packed >> 4) & 0x3);
indices[3 + i * 4] = (uint8_t)((packed >> 6) & 0x3);
}
// store out the colours
for (int i = 0; i < 16; i++)
{
const int offset = 4 * indices[i];
rgba[4 * i + 0] = codes[offset + 0];
rgba[4 * i + 1] = codes[offset + 1];
rgba[4 * i + 2] = codes[offset + 2];
rgba[4 * i + 3] = codes[offset + 3];
}
}
void DXT::DecompressAlphaDxt3(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, const int blockIndex)
{
// Unpack the alpha values pairwise
for (int i = 0; i < 8; i++)
{
// Quantise down to 4 bits
int quant = block[blockIndex + i];
const uint8_t lo = (uint8_t)(quant & 0x0F);
const uint8_t hi = (uint8_t)(quant & 0xF0);
// Convert back up to bytes
rgba[8 * i + 3] = (uint8_t)(lo | (lo << 4));
rgba[8 * i + 7] = (uint8_t)(hi | (hi >> 4));
}
}
void DXT::DecompressAlphaDxt5(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, const int blockIndex)
{
// Get the two alpha values
uint8_t alpha0 = block[blockIndex + 0];
uint8_t alpha1 = block[blockIndex + 1];
// compare the values to build the codebook
std::vector<uint8_t> codes(8);
codes[0] = alpha0;
codes[1] = alpha1;
if (alpha0 <= alpha1)
{
// Use 5-Alpha Codebook
for (int i = 1; i < 5; i++)
codes[1 + i] = (uint8_t)(((5 - i) * alpha0 + i * alpha1) / 5);
codes[6] = 0;
codes[7] = 255;
}
else
{
// Use 7-Alpha Codebook
for (int i = 1; i < 7; i++)
{
codes[i + 1] = (uint8_t)(((7 - i) * alpha0 + i * alpha1) / 7);
}
}
// decode indices
std::vector<uint8_t> indices(16);
uint8_t blockSrc_pos = 2;
uint8_t indices_pos = 0;
for (int i = 0; i < 2; i++)
{
// grab 3 bytes
int value = 0;
for (int j = 0; j < 3; j++)
{
int _byte = block[blockIndex + blockSrc_pos++];
value |= (_byte << 8 * j);
}
// unpack 8 3-bit values from it
for (int j = 0; j < 8; j++)
{
int index = (value >> 3 * j) & 0x07;
indices[indices_pos++] = (uint8_t)index;
}
}
// write out the indexed codebook values
for (int i = 0; i < 16; i++)
{
rgba[4 * i + 3] = codes[indices[i]];
}
}
int DXT::UnPack565(std::vector<uint8_t>& block, const int blockIndex, const int packedOffset, std::vector<uint8_t>& color, const int colorOffset)
{
// build packed value
const int value = block[blockIndex + packedOffset] | (block[blockIndex + packedOffset + 1] << 8);
// get components in the stored range
const uint16_t red = ((value >> 11) & 0x1F);
const uint16_t green = ((value >> 5) & 0x3F);
const uint16_t blue = (value & 0x1F);
// Scale up to 8 Bit
color[0 + colorOffset] = (uint8_t)((red << 3) | (red >> 2));
color[1 + colorOffset] = (uint8_t)((green << 2) | (green >> 4));
color[2 + colorOffset] = (uint8_t)((blue << 3) | (blue >> 2));
color[3 + colorOffset] = 255;
return value;
}
}

38
expkg/src/DXT/DXT.h Normal file
View File

@ -0,0 +1,38 @@
//
// Created by sfd on 25-9-21.
//
#ifndef DXT_H
#define DXT_H
#include <cstdint>
#include <vector>
namespace PKG
{
enum class DXTFlags
{
DXT1 = 1,
DXT3 = 1 << 1,
DXT5 = 1 << 2,
};
class DXT
{
public:
// public static byte[] DecompressImage(int width, int height, byte[] data, DXTFlags flags)
static void DecompressImage(int width, int height, std::vector<uint8_t>& data, DXTFlags flags);
private:
static void Decompress(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, int blockIndex, DXTFlags flags);
static int UnPack565(std::vector<uint8_t>& block, int blockIndex, int packedOffset, std::vector<uint8_t>& color, int colorOffset);
static void DecompressColor(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, int blockIndex, bool isDxt1);
static void DecompressAlphaDxt3(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, int blockIndex);
static void DecompressAlphaDxt5(std::vector<uint8_t>& rgba, std::vector<uint8_t>& block, int blockIndex);
};
}
#endif //DXT_H

543
expkg/src/EXPKG/EXPKG.cpp Normal file
View File

@ -0,0 +1,543 @@
//
// Created by sfd on 25-8-4.
//
#include "EXPKG.h"
#include <iostream>
#include "gif.h"
#include "BinaryOPT/BinaryWriter.h"
#include "BinaryOPT/ImageReader.h"
#include "DXT/DXT.h"
#include "Tex/Tex.h"
extern "C" unsigned char* stbi_write_png_to_mem(const unsigned char* pixels, int stride_bytes, int x, int y, int n,
int* out_len);
namespace PKG
{
const char* help = R"(
usage:
expkg path/to/file.pkg [path/to/output](optional)
example:
expkg demo.pkg
expkg demo.pkg outdir
)";
enum class FILE_EXTENSION
{
TEX,
PKG,
UNKNOWN
};
EXPKG::EXPKG(const CommandArgs& commandArgs)
{
if (commandArgs.argc < 2)
{
std::cout << help << std::endl;
system("pause");
exit(0);
}
m_Reader = std::make_shared<BinaryReader>(commandArgs.argv[1]);
if (commandArgs.argc == 3)
{
m_OutDir = commandArgs.argv[2];
m_OutDir = m_OutDir.make_preferred();
}
else
{
m_OutDir = "out";
}
Run();
}
EXPKG::EXPKG(const std::string& filePath, const std::string& outDir)
{
m_Reader = std::make_shared<BinaryReader>(filePath);
m_OutDir = outDir;
m_OutDir = m_OutDir.make_preferred();
if (!m_Reader)
Run();
}
FILE_EXTENSION EXPKG::checkExtension(const std::filesystem::path& filePath)
{
if (filePath.extension() == ".pkg")
return FILE_EXTENSION::PKG;
if (filePath.extension() == ".mpkg")
return FILE_EXTENSION::PKG;
if (filePath.extension() == ".tex")
return FILE_EXTENSION::TEX;
std::cerr << "not a pkg file or mpkg file or tex file" << std::endl;
return FILE_EXTENSION::UNKNOWN;
}
void EXPKG::Run()
{
switch (checkExtension(m_Reader->GetFilePath()))
{
case FILE_EXTENSION::TEX:
ExtractTex();
break;
case FILE_EXTENSION::PKG:
ExtractPkg();
break;
case FILE_EXTENSION::UNKNOWN:
break;
}
}
void EXPKG::ExtractPkg()
{
// Read Binary resource
const int head = m_Reader->ReadInt32();
const std::string magicHeader = m_Reader->ReadString(head);
if (magicHeader.substr(0,3) != "PKG")
{
std::cerr << "unknown header: " << m_Reader->GetFilePath() << std::endl;
std::cerr << "not a pkg file " << std::endl;
exit(0);
}
const int count = m_Reader->ReadInt32();
if (count > 0)
{
for (int i = 0; i < count; i++)
{
Entry entry;
const int size = m_Reader->ReadInt32();
entry.FullPath = m_Reader->ReadString(size);
entry.FullPath = entry.FullPath.make_preferred(); // conflict with system "\\" or "/", use it to solve
entry.Offset = m_Reader->ReadInt32();
entry.Length = m_Reader->ReadInt32();
entry.Type = entry.FullPath.extension().string();
m_Entries.push_back(entry);
}
}
else
{
std::cerr << "could not get resource: " << m_Reader->GetFilePath() << std::endl;
exit(0);
}
// try Extract
uint32_t offsetPosition = m_Reader->tellg();
for (const auto& entry : m_Entries)
{
std::cout << "convert file: " << m_OutDir / entry.FullPath << std::endl;
m_Reader->seekg(entry.Offset + offsetPosition);
if (entry.Type == ".tex")
{
std::filesystem::path texPath = m_OutDir / entry.FullPath;
BinaryWriter writer(texPath, std::ios::binary);
std::string texdata;
m_Reader->ReadData(texdata, entry.Length);
writer.WriteBytes(texdata.data(), texdata.size());
writer.close();
ExtractTex(texPath);
}
else if (entry.Type == ".gif" ||
entry.Type == ".jpg" ||
entry.Type == ".png" ||
entry.Type == ".jpeg" ||
entry.Type == ".webp")
{
BinaryWriter writer(m_OutDir / entry.FullPath, std::ios::binary);
std::string texdata;
m_Reader->ReadData(texdata, entry.Length);
writer.WriteBytes(texdata.data(), texdata.size());
writer.close();
}
else
{
BinaryWriter writer(m_OutDir / entry.FullPath);
writer.WriteString(m_Reader->ReadString(entry.Length));
}
}
}
void EXPKG::ExtractTex(const std::filesystem::path& path) const
{
std::shared_ptr<BinaryReader> reader = m_Reader;
if (path != "")
{
reader = std::make_shared<BinaryReader>(path);
}
// Tex ReadHeader
Tex tex{};
tex.Magic1 = reader->ReadNString(16);
if (tex.Magic1 != "TEXV0005")
{
std::cerr << "bad magic" << std::endl;
return;
}
tex.Magic2 = reader->ReadNString(16);
if (tex.Magic2 != "TEXI0001")
{
std::cerr << "bad magic" << std::endl;
return;
}
tex.Header.Format = (TexFormat)reader->ReadInt32();
tex.Header.Flags = (TexType)reader->ReadInt32();
tex.Header.TextureWidth = reader->ReadInt32();
tex.Header.TextureHeight = reader->ReadInt32();
tex.Header.ImageWidth = reader->ReadInt32();
tex.Header.ImageHeight = reader->ReadInt32();
tex.Header.UnkInt0 = reader->ReadInt32();
if ((int)tex.Header.Flags & (int)TexType::IsGif) tex.IsGif = true;
if ((int)tex.Header.Flags & (int)TexType::IsVideoTexture) tex.IsVideoTexture = true;
tex.ImageContainer = ImageReader::ImageContainerReaderReadFrom(*reader, tex.Header.Format);
// ReadHeader end
if (!tex.ImageContainer.Images.empty())
{
// GetConvertFormat
MipmapFormat format;
if (tex.IsVideoTexture)
format = MipmapFormat::VideoMp4;
else
format = tex.ImageContainer.Images[0].Mipmaps[0].Format;
auto tmpfotmat = format;
for(auto& Image : tex.ImageContainer.Images)
{
auto& mipmap = Image.Mipmaps[0];
switch (tmpfotmat)
{
case MipmapFormat::CompressedDXT5:
DXT::DecompressImage(mipmap.Width, mipmap.Height, mipmap.Data, DXTFlags::DXT5);
mipmap.Format = MipmapFormat::RGBA8888; format = MipmapFormat::RGBA8888;
break;
case MipmapFormat::CompressedDXT3:
DXT::DecompressImage(mipmap.Width, mipmap.Height, mipmap.Data, DXTFlags::DXT3);
mipmap.Format = MipmapFormat::RGBA8888; format = MipmapFormat::RGBA8888;
break;
case MipmapFormat::CompressedDXT1:
DXT::DecompressImage(mipmap.Width, mipmap.Height, mipmap.Data, DXTFlags::DXT1);
mipmap.Format = MipmapFormat::RGBA8888; format = MipmapFormat::RGBA8888;
break;
default:
std::cerr << "raw mipmap meybe compressed" << std::endl;
break;
}
}
if ((int)format >= 1 && (int)format <= 3)
{
format = MipmapFormat::ImagePNG;
}
// GetConvertFormat end
// Convert source
if (tex.IsGif)
{
// convert gif
// tex.GifContainer
auto& container = tex.FrameInfoContainer;
container.Magic = reader->ReadNString(16);
int frameCount = reader->ReadInt32();
/*
switch (container.Magic)
{
case "TEXS0001":
case "TEXS0002":
break;
case "TEXS0003":
container.GifWidth = reader->ReadInt32();
container.GifHeight = reader->ReadInt32();
break;
default:
std::cerr << "bad magic" << std::endl;
}
*/
{
if (container.Magic == "TEXS0001" || container.Magic == "TEXS0002")
{
}else if (container.Magic == "TEXS0003")
{
container.GifWidth = reader->ReadInt32();
container.GifHeight = reader->ReadInt32();
}else
{
std::cerr << "bad magic" << std::endl;
}
}
/*
switch (container.Magic)
{
case "TEXS0001":
for (int i = 0; i < frameCount; i++)
{
TexFrameInfo frameInfo = {};
frameInfo.ImageId = reader->ReadInt32();
frameInfo.Frametime = reader->ReadSingle();
frameInfo.X = reader->ReadInt32();
frameInfo.Y = reader->ReadInt32();
frameInfo.Width = reader->ReadInt32();
frameInfo.WidthY = reader->ReadInt32();
frameInfo.HeightX = reader->ReadInt32();
frameInfo.Height = reader->ReadInt32();
container.Frames.push_back(frameInfo);
}
case "TEXS0002":
case "TEXS0003":
for (int i = 0; i < frameCount; i++)
{
TexFrameInfo frameInfo = {};
frameInfo.ImageId = reader->ReadInt32();
frameInfo.Frametime = reader->ReadSingle();
frameInfo.X = reader->ReadSingle();
frameInfo.Y = reader->ReadSingle();
frameInfo.Width = reader->ReadSingle();
frameInfo.WidthY = reader->ReadSingle();
frameInfo.HeightX = reader->ReadSingle();
frameInfo.Height = reader->ReadSingle();
container.Frames.push_back(frameInfo);
}
default:
std::cerr << "bad magic" << std::endl;
}
*/
{
if (container.Magic == "TEXS0001")
{
for (int i = 0; i < frameCount; i++)
{
TexFrameInfo frameInfo = {};
frameInfo.ImageId = reader->ReadInt32();
frameInfo.Frametime = reader->ReadSingle();
frameInfo.PosX = reader->ReadInt32();
frameInfo.PosY = reader->ReadInt32();
frameInfo.Width = reader->ReadInt32();
frameInfo.WidthY = reader->ReadInt32();
frameInfo.HeightX = reader->ReadInt32();
frameInfo.Height = reader->ReadInt32();
container.Frames.push_back(frameInfo);
}
}else if (container.Magic == "TEXS0002" || container.Magic == "TEXS0003")
{
for (int i = 0; i < frameCount; i++)
{
TexFrameInfo frameInfo = {};
frameInfo.ImageId = reader->ReadInt32();
frameInfo.Frametime = reader->ReadSingle();
frameInfo.PosX = reader->ReadSingle();
frameInfo.PosY = reader->ReadSingle();
frameInfo.Width = reader->ReadSingle();
frameInfo.WidthY = reader->ReadSingle();
frameInfo.HeightX = reader->ReadSingle();
frameInfo.Height = reader->ReadSingle();
container.Frames.push_back(frameInfo);
}
}else
{
std::cerr << "bad magic" << std::endl;
}
}
if (container.GifWidth == 0 || container.GifHeight == 0)
{
container.GifWidth = (int) container.Frames[0].Width;
container.GifHeight = (int) container.Frames[0].Height;
}
std::filesystem::path outPath = reader->GetFilePath();
outPath.replace_extension("gif");
std::cout << "convert file: " << outPath << std::endl;
GifWriter writer;
uint32_t delay = (int)(tex.FrameInfoContainer.Frames[0].Frametime * 100);
GifBegin(&writer,
outPath.string().c_str(),
(uint32_t)tex.FrameInfoContainer.Frames[0].Width,
(uint32_t)tex.FrameInfoContainer.Frames[0].Height,
delay);
int frameIndex = 0;
int imageIndex = 1;
for (const auto& [Mipmap] : tex.ImageContainer.Images)
{
const auto& Image = Mipmap[0];
int SingleImageFrameCount = (Image.Width / container.GifWidth) * (Image.Height / container.GifHeight);
for (; frameIndex < SingleImageFrameCount * imageIndex; frameIndex++)
{
const auto& frameInfo = container.Frames[frameIndex];
std::vector<uint8_t> frameImage;
for (int heightIndex = 0; heightIndex < container.GifHeight; heightIndex++)
{
auto lineDataStart = Image.Data.begin() + Image.Width * 4 * ((int)frameInfo.PosY + heightIndex)+ (int)frameInfo.PosX * 4;
auto lineData = std::vector<uint8_t>(lineDataStart, lineDataStart + (int)frameInfo.Width * 4);
frameImage.insert(frameImage.end(), lineData.begin(), lineData.end());
}
// output by gif
{
GifWriteFrame(&writer, frameImage.data(), (uint32_t)frameInfo.Width, (uint32_t)frameInfo.Height, delay);
}
// output one by one
/*
{
int len = 0;
static int index = 0;
auto* data = stbi_write_png_to_mem(frameImage.data(),
(int)frameInfo.Width * 4,
(int)frameInfo.Width,
(int)frameInfo.Height,
4,
&len);
std::filesystem::path outputfile = outPath;
std::string name = outputfile.filename().string();
outputfile = outputfile.parent_path();
std::string filename = std::to_string(index++) + "_" + name;
outputfile /= "out";
outputfile /= filename;
std::cout << "convert file: " << outputfile << std::endl;
BinaryWriter imageWriter(outputfile, std::ios::binary);
imageWriter.WriteBytes(reinterpret_cast<const char*>(data), len);
imageWriter.close();
free(data);
data = nullptr;
}
*/
}
imageIndex ++;
}
GifEnd(&writer);
}else
{
auto& sourceMipmap = tex.ImageContainer.Images[0].Mipmaps[0];
if (tex.IsVideoTexture)
{
if (sourceMipmap.Data.size() < 12)
{
std::cerr << "expect mp4 magic header" << std::endl;
}
std::string mp4Magic = std::string(reinterpret_cast<const char*>(&sourceMipmap.Data[4]), 8);
if (mp4Magic != "ftypisom" && mp4Magic != "ftypmsnv" && mp4Magic != "ftypmp42")
{
std::cerr << "bad mp4 magic header" << std::endl;
}
}
else
{
auto imgformat = sourceMipmap.Format;
if ((int)imgformat >= 1 && (int)imgformat <= 3)
{
int len = 0;
auto& imgData = tex.ImageContainer.Images[0].Mipmaps[0].Data;
uint8_t* data = nullptr;
int channel = 4;
switch (imgformat)
{
case MipmapFormat::R8: channel = 1; break;
case MipmapFormat::RG88: channel = 2; break;
case MipmapFormat::RGBA8888: channel = 4; break;
default: break;
}
data = stbi_write_png_to_mem(sourceMipmap.Data.data(),
sourceMipmap.Width * channel,
sourceMipmap.Width,
sourceMipmap.Height,
channel,
&len);
if (data)
{
imgData.assign(data, data + len);
free(data);
}
}
}
// return ImageResult
// data format
std::filesystem::path outPath = reader->GetFilePath();
outPath.replace_extension(GetFileExtension(format));
std::cout << "convert file: " << outPath << std::endl;
BinaryWriter imageWriter(outPath, std::ios::binary);
imageWriter.WriteBytes(reinterpret_cast<const char*>(sourceMipmap.Data.data()), sourceMipmap.Data.size());
imageWriter.close();
// Convert source end
}
}
}
}

48
expkg/src/EXPKG/EXPKG.h Normal file
View File

@ -0,0 +1,48 @@
//
// Created by sfd on 25-8-4.
//
#ifndef EXPKG_H
#define EXPKG_H
#include <vector>
#include "Entry.h"
typedef struct CommandArgs
{
int argc;
char** argv;
} CommandArgs;
namespace PKG
{
class BinaryReader;
enum class FILE_EXTENSION;
class PKG_API EXPKG
{
public:
EXPKG(const CommandArgs& commandArgs);
EXPKG(const std::string& filePath, const std::string& outDir = "out");
private:
static FILE_EXTENSION checkExtension(const std::filesystem::path& filePath);
void ExtractTex(const std::filesystem::path& path = "") const;
void ExtractPkg();
void Run();
private:
std::shared_ptr<BinaryReader> m_Reader;
std::filesystem::path m_OutDir;
std::vector<Entry> m_Entries;
};
}
#endif //EXPKG_H

5
expkg/src/Entry.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-8-4.
//
#include "Entry.h"

427
expkg/src/Entry.h Normal file
View File

@ -0,0 +1,427 @@
//
// Created by sfd on 25-8-4.
//
#ifndef ENTRY_H
#define ENTRY_H
#include <filesystem>
#include <string>
#include "Core.h"
namespace PKG
{
enum class TexFormat
{
RGBA8888 = 0,
DXT5 = 4,
DXT3 = 6,
DXT1 = 7,
RG88 = 8,
R8 = 9
};
enum class TexType
{
None = 0,
NoInterpolation = 1,
ClampUVs = 2,
IsGif = 4,
// Placeholders
Unk3 = 8,
Unk4 = 16,
IsVideoTexture = 32,
Unk6 = 64,
Unk7 = 128,
};
enum class MipmapFormat
{
Invalid = 0,
/// Raw pixels (4 bytes per pixel) (RGBA8888)
RGBA8888 = 1,
/// Raw pixels (1 byte per pixel) (R8)
R8 = 2,
/// Raw pixels (2 bytes per pixel) (RG88)
RG88 = 3,
/// Raw pixels compressed using DXT5
CompressedDXT5,
/// Raw pixels compressed using DXT3
CompressedDXT3,
/// Raw pixels compressed using DXT1
CompressedDXT1,
/// MP4 Video
VideoMp4,
/// Windows or OS/2 Bitmap File (*.BMP)
/// Keep '= 1000' because MipmapFormatExtensions.IsImage uses this to check if format is an image format
ImageBMP = 1000,
/// Windows Icon (*.ICO)
ImageICO,
/// Independent JPEG Group (*.JPG, *.JIF, *.JPEG, *.JPE)
ImageJPEG,
/// JPEG Network Graphics (*.JNG)
ImageJNG,
/// Commodore 64 Koala format (*.KOA)
ImageKOALA,
/// Amiga IFF (*.IFF, *.LBM)
ImageLBM,
/// Amiga IFF (*.IFF, *.LBM)
ImageIFF,
/// Multiple Network Graphics (*.MNG)
ImageMNG,
/// Portable Bitmap (ASCII) (*.PBM)
ImagePBM,
/// Portable Bitmap (BINARY) (*.PBM)
ImagePBMRAW,
/// Kodak PhotoCD (*.PCD)
ImagePCD,
/// Zsoft Paintbrush PCX bitmap format (*.PCX)
ImagePCX,
/// Portable Graymap (ASCII) (*.PGM)
ImagePGM,
/// Portable Graymap (BINARY) (*.PGM)
ImagePGMRAW,
/// Portable Network Graphics (*.PNG)
ImagePNG,
/// Portable Pixelmap (ASCII) (*.PPM)
ImagePPM,
/// Portable Pixelmap (BINARY) (*.PPM)
ImagePPMRAW,
/// Sun Rasterfile (*.RAS)
ImageRAS,
/// truevision Targa files (*.TGA, *.TARGA)
ImageTARGA,
/// Tagged Image File Format (*.TIF, *.TIFF)
ImageTIFF,
/// Wireless Bitmap (*.WBMP)
ImageWBMP,
/// Adobe Photoshop (*.PSD)
ImagePSD,
/// Dr. Halo (*.CUT)
ImageCUT,
/// X11 Bitmap Format (*.XBM)
ImageXBM,
/// X11 Pixmap Format (*.XPM)
ImageXPM,
/// DirectDraw Surface (*.DDS)
ImageDDS,
/// Graphics Interchange Format (*.GIF)
ImageGIF,
/// High Dynamic Range (*.HDR)
ImageHDR,
/// Raw Fax format CCITT G3 (*.G3)
ImageFAXG3,
/// Silicon Graphics SGI image format (*.SGI)
ImageSGI,
/// OpenEXR format (*.EXR)
ImageEXR,
/// JPEG-2000 format (*.J2K, *.J2C)
ImageJ2K,
/// JPEG-2000 format (*.JP2)
ImageJP2,
/// Portable FloatMap (*.PFM)
ImagePFM,
/// Macintosh PICT (*.PICT)
ImagePICT,
/// RAW camera image (*.*)
ImageRAW,
};
enum class FreeImageFormat {
/// <summary>
/// Unknown format (returned value only, never use it as input value)
/// </summary>
FIF_UNKNOWN = -1,
/// <summary>
/// Windows or OS/2 Bitmap File (*.BMP)
/// </summary>
FIF_BMP = 0,
/// <summary>
/// Windows Icon (*.ICO)
/// </summary>
FIF_ICO = 1,
/// <summary>
/// Independent JPEG Group (*.JPG, *.JIF, *.JPEG, *.JPE)
/// </summary>
FIF_JPEG = 2,
/// <summary>
/// JPEG Network Graphics (*.JNG)
/// </summary>
FIF_JNG = 3,
/// <summary>
/// Commodore 64 Koala format (*.KOA)
/// </summary>
FIF_KOALA = 4,
/// <summary>
/// Amiga IFF (*.IFF, *.LBM)
/// </summary>
FIF_LBM = 5,
/// <summary>
/// Amiga IFF (*.IFF, *.LBM)
/// </summary>
FIF_IFF = 5,
/// <summary>
/// Multiple Network Graphics (*.MNG)
/// </summary>
FIF_MNG = 6,
/// <summary>
/// Portable Bitmap (ASCII) (*.PBM)
/// </summary>
FIF_PBM = 7,
/// <summary>
/// Portable Bitmap (BINARY) (*.PBM)
/// </summary>
FIF_PBMRAW = 8,
/// <summary>
/// Kodak PhotoCD (*.PCD)
/// </summary>
FIF_PCD = 9,
/// <summary>
/// Zsoft Paintbrush PCX bitmap format (*.PCX)
/// </summary>
FIF_PCX = 10,
/// <summary>
/// Portable Graymap (ASCII) (*.PGM)
/// </summary>
FIF_PGM = 11,
/// <summary>
/// Portable Graymap (BINARY) (*.PGM)
/// </summary>
FIF_PGMRAW = 12,
/// <summary>
/// Portable Network Graphics (*.PNG)
/// </summary>
FIF_PNG = 13,
/// <summary>
/// Portable Pixelmap (ASCII) (*.PPM)
/// </summary>
FIF_PPM = 14,
/// <summary>
/// Portable Pixelmap (BINARY) (*.PPM)
/// </summary>
FIF_PPMRAW = 15,
/// <summary>
/// Sun Rasterfile (*.RAS)
/// </summary>
FIF_RAS = 16,
/// <summary>
/// truevision Targa files (*.TGA, *.TARGA)
/// </summary>
FIF_TARGA = 17,
/// <summary>
/// Tagged Image File Format (*.TIF, *.TIFF)
/// </summary>
FIF_TIFF = 18,
/// <summary>
/// Wireless Bitmap (*.WBMP)
/// </summary>
FIF_WBMP = 19,
/// <summary>
/// Adobe Photoshop (*.PSD)
/// </summary>
FIF_PSD = 20,
/// <summary>
/// Dr. Halo (*.CUT)
/// </summary>
FIF_CUT = 21,
/// <summary>
/// X11 Bitmap Format (*.XBM)
/// </summary>
FIF_XBM = 22,
/// <summary>
/// X11 Pixmap Format (*.XPM)
/// </summary>
FIF_XPM = 23,
/// <summary>
/// DirectDraw Surface (*.DDS)
/// </summary>
FIF_DDS = 24,
/// <summary>
/// Graphics Interchange Format (*.GIF)
/// </summary>
FIF_GIF = 25,
/// <summary>
/// High Dynamic Range (*.HDR)
/// </summary>
FIF_HDR = 26,
/// <summary>
/// Raw Fax format CCITT G3 (*.G3)
/// </summary>
FIF_FAXG3 = 27,
/// <summary>
/// Silicon Graphics SGI image format (*.SGI)
/// </summary>
FIF_SGI = 28,
/// <summary>
/// OpenEXR format (*.EXR)
/// </summary>
FIF_EXR = 29,
/// <summary>
/// JPEG-2000 format (*.J2K, *.J2C)
/// </summary>
FIF_J2K = 30,
/// <summary>
/// JPEG-2000 format (*.JP2)
/// </summary>
FIF_JP2 = 31,
/// <summary>
/// Portable FloatMap (*.PFM)
/// </summary>
FIF_PFM = 32,
/// <summary>
/// Macintosh PICT (*.PICT)
/// </summary>
FIF_PICT = 33,
/// <summary>
/// RAW camera image (*.*)
/// </summary>
FIF_RAW = 34,
/// <summary>
/// RAW camera MP4 (*.mp4)
/// </summary>
FIF_MP4 = 35,
};
struct PKG_API Entry
{
std::filesystem::path FullPath;
int32_t Offset{};
int32_t Length{};
std::string Type;
};
struct PKG_API TexHeader
{
TexFormat Format;
TexType Flags;
int32_t TextureWidth;
int32_t TextureHeight;
int32_t ImageWidth;
int32_t ImageHeight;
uint32_t UnkInt0;
};
}
#endif //ENTRY_H

5
expkg/src/Tex/Tex.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-8-5.
//
#include "Tex.h"

30
expkg/src/Tex/Tex.h Normal file
View File

@ -0,0 +1,30 @@
//
// Created by sfd on 25-8-5.
//
#ifndef TEX_H
#define TEX_H
#include "TexFrameInfoContainer.h"
#include "TexImageContainer.h"
namespace PKG
{
class Tex
{
public:
std::string Magic1;
std::string Magic2;
TexHeader Header = {};
TexImageContainer ImageContainer = {};
bool IsGif = false;
bool IsVideoTexture = false;
TexFrameInfoContainer FrameInfoContainer = {};
};
}
#endif //TEX_H

View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-9-21.
//
#include "TexFrameInfoContainer.h"

View File

@ -0,0 +1,39 @@
//
// Created by sfd on 25-9-21.
//
#ifndef TEXFRAMEINFOCONTAINER_H
#define TEXFRAMEINFOCONTAINER_H
#include <string>
#include <vector>
namespace PKG
{
struct TexFrameInfo
{
int ImageId;
float Frametime;
float PosX;
float PosY;
float Width;
float WidthY;
float HeightX;
float Height;
};
class TexFrameInfoContainer
{
public:
std::string Magic;
std::vector<TexFrameInfo> Frames;
int GifWidth;
int GifHeight;
};
}
#endif //TEXFRAMEINFOCONTAINER_H

View File

@ -0,0 +1,88 @@
//
// Created by sfd on 25-8-5.
//
#include "TexImage.h"
#include <iostream>
std::string PKG::GetFileExtension(const MipmapFormat format)
{
switch (format)
{
case MipmapFormat::ImageBMP:
return "bmp";
case MipmapFormat::ImageICO:
return "ico";
case MipmapFormat::ImageJPEG:
return "jpg";
case MipmapFormat::ImageJNG:
return "jng";
case MipmapFormat::ImageKOALA:
return "koa";
case MipmapFormat::ImageLBM:
return "lbm";
case MipmapFormat::ImageIFF:
return "iff";
case MipmapFormat::ImageMNG:
return "mng";
case MipmapFormat::ImagePBM:
case MipmapFormat::ImagePBMRAW:
return "pbm";
case MipmapFormat::ImagePCD:
return "pcd";
case MipmapFormat::ImagePCX:
return "pcx";
case MipmapFormat::ImagePGM:
case MipmapFormat::ImagePGMRAW:
return "pgm";
case MipmapFormat::ImagePNG:
return "png";
case MipmapFormat::ImagePPM:
case MipmapFormat::ImagePPMRAW:
return "ppm";
case MipmapFormat::ImageRAS:
return "ras";
case MipmapFormat::ImageTARGA:
return "tga";
case MipmapFormat::ImageTIFF:
return "tif";
case MipmapFormat::ImageWBMP:
return "wbmp";
case MipmapFormat::ImagePSD:
return "psd";
case MipmapFormat::ImageCUT:
return "cut";
case MipmapFormat::ImageXBM:
return "xbm";
case MipmapFormat::ImageXPM:
return "xpm";
case MipmapFormat::ImageDDS:
return "dds";
case MipmapFormat::ImageGIF:
return "gif";
case MipmapFormat::ImageHDR:
return "hdr";
case MipmapFormat::ImageFAXG3:
return "g3";
case MipmapFormat::ImageSGI:
return "sgi";
case MipmapFormat::ImageEXR:
return "exr";
case MipmapFormat::ImageJ2K:
return "j2k";
case MipmapFormat::ImageJP2:
return "jp2";
case MipmapFormat::ImagePFM:
return "pfm";
case MipmapFormat::ImagePICT:
return "pict";
case MipmapFormat::ImageRAW:
return "raw";
case MipmapFormat::VideoMp4:
return "mp4";
}
std::cerr << "unknown file type" << std::endl;
return ".unknown";
}

37
expkg/src/Tex/TexImage.h Normal file
View File

@ -0,0 +1,37 @@
//
// Created by sfd on 25-8-5.
//
#ifndef TEXIMAGE_H
#define TEXIMAGE_H
#include <vector>
#include "Entry.h"
namespace PKG
{
std::string GetFileExtension(MipmapFormat format);
class TexMipMap
{
public:
int Width;
int Height;
int DecompressedDataCount;
bool IsZ4Compressed = false;
MipmapFormat Format;
std::vector<uint8_t> Data;
};
class TexImage
{
public:
std::vector<TexMipMap> Mipmaps;
};
}
#endif //TEXIMAGE_H

View File

@ -0,0 +1,6 @@
//
// Created by sfd on 25-8-5.
//
#include "TexImageContainer.h"

View File

@ -0,0 +1,31 @@
//
// Created by sfd on 25-8-5.
//
#ifndef TEXIMAGECONTAINER_H
#define TEXIMAGECONTAINER_H
#include "TexImage.h"
namespace PKG
{
enum class ImageContainerVersion
{
VERSION1 = 1,
VERSION2 = 2,
VERSION3 = 3,
VERSION4 = 4,
};
class TexImageContainer
{
public:
std::string Magic;
FreeImageFormat ImageFormat{};
ImageContainerVersion ImageContainerVersion;
std::vector<TexImage> Images;
};
}
#endif //TEXIMAGECONTAINER_H

14
expkg/src/expkg.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef EXPKG_H
#define EXPKG_H
#include "Entry.h"
#include "BinaryOPT/BinaryReader.h"
#include "BinaryOPT/BinaryWriter.h"
#include "BinaryOPT/ImageReader.h"
#include "EXPKG/EXPKG.h"
#include "Tex/Tex.h"
#include "Tex/TexImage.h"
#include "Tex/TexImageContainer.h"
#endif // EXPKG_H

Some files were not shown because too many files have changed in this diff Show More