mirror of
https://github.com/Toastconcern/FrameStudio.git
synced 2026-10-29 04:02:56 +01:00
Frame Studio now builds for Windows (x64, x86, ARM64), Linux (x64, x86, ARM64)
and macOS (Apple Silicon, Intel):
- studio/platform.h with Win32 and POSIX implementations (dialogs through
tinyfiledialogs on Linux/macOS), a GLFW window beside the Win32 one, and a
platform-neutral frame pacer
- MoltenVK support in the renderer (portability enumeration) and bundled in
the macOS app
- 32-bit builds: large-address-aware, LZMA packing within a 32-bit address space
- unaligned file reads in the loaders go through loadUnaligned (no UB)
- scripts/build-studio.sh, studio/build_windows.bat [x64|x86|arm64],
cmake/llvm-mingw.cmake, and a GitHub Actions workflow building every target
Frame Home installs without Studio: frame/install-frame-home.{sh,ps1} and
"Install Frame Home.bat" (packaged as FrameHome-installer.zip).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
2.8 KiB
C++
73 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include "cook/zip_safety.h"
|
|
#include "miniz.h"
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace zipread {
|
|
|
|
inline const char* statusText(hslcook::zipsafety::ArchiveReadStatus status) {
|
|
using Status = hslcook::zipsafety::ArchiveReadStatus;
|
|
switch (status) {
|
|
case Status::Ok: return "ok";
|
|
case Status::TooManyEntries: return "too many entries";
|
|
case Status::EntryTooLarge: return "entry is too large";
|
|
case Status::TotalTooLarge: return "declared uncompressed total is too large";
|
|
case Status::UnsafeName: return "unsafe entry name";
|
|
case Status::DuplicateName: return "duplicate normalized entry name";
|
|
}
|
|
return "unknown archive error";
|
|
}
|
|
|
|
// Validate the entire central directory before any entry is extracted. This
|
|
// adapter intentionally stays tiny; the policy and arithmetic live in the pure
|
|
// zip_safety helper so they can be unit-tested without miniz.
|
|
inline bool validateArchive(mz_zip_archive& zip,
|
|
const hslcook::zipsafety::ArchiveReadLimits& limits,
|
|
std::string* error = nullptr) {
|
|
using namespace hslcook::zipsafety;
|
|
ArchiveReadBudget budget(limits);
|
|
const mz_uint count = mz_zip_reader_get_num_files(&zip);
|
|
if (static_cast<uint64_t>(count) > limits.maxEntries) {
|
|
if (error) *error = "too many entries";
|
|
return false;
|
|
}
|
|
|
|
for (mz_uint i = 0; i < count; ++i) {
|
|
mz_zip_archive_file_stat stat{};
|
|
if (!mz_zip_reader_file_stat(&zip, i, &stat)) {
|
|
if (error) *error = "invalid central-directory entry " + std::to_string(i);
|
|
return false;
|
|
}
|
|
|
|
const mz_uint nameBytesWithNull = mz_zip_reader_get_filename(&zip, i, nullptr, 0);
|
|
if (nameBytesWithNull == 0 || nameBytesWithNull > kZip16Max + 1) {
|
|
if (error) *error = "invalid or overlong entry name at index " + std::to_string(i);
|
|
return false;
|
|
}
|
|
std::vector<char> name(nameBytesWithNull);
|
|
if (mz_zip_reader_get_filename(&zip, i, name.data(), nameBytesWithNull) == 0) {
|
|
if (error) *error = "failed to read entry name at index " + std::to_string(i);
|
|
return false;
|
|
}
|
|
|
|
const bool directory = mz_zip_reader_is_file_a_directory(&zip, i) != 0;
|
|
const ArchiveReadResult result = budget.add(
|
|
std::string_view(name.data(), nameBytesWithNull - 1),
|
|
static_cast<uint64_t>(stat.m_uncomp_size), directory);
|
|
if (result.status != ArchiveReadStatus::Ok) {
|
|
if (error) {
|
|
*error = std::string(statusText(result.status)) + " at entry " +
|
|
std::to_string(i) + " ('" + name.data() + "')";
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace zipread
|