From 20af5ceb7076da54c319b9dc39e2342b3682c35e Mon Sep 17 00:00:00 2001 From: SegaraRai Date: Tue, 27 Aug 2019 00:32:13 +0900 Subject: [PATCH] initial commit (v0.1.0) --- .editorconfig | 16 + .gitignore | 355 ++++++++++ AVI.hpp | 163 +++++ ApproxFraction.hpp | 68 ++ CacheStorage.cpp | 100 +++ CacheStorage.hpp | 48 ++ EntisGLS4.vcxproj | 578 ++++++++++++++++ EntisGLS4.vcxproj.filters | 1158 +++++++++++++++++++++++++++++++++ Fraction.hpp | 10 + LICENSE.txt | 7 + MEIToAVI.cpp | 725 +++++++++++++++++++++ MEIToAVI.hpp | 54 ++ Main.cpp | 167 +++++ MeiToAvi.sln | 44 ++ MeiToAvi.vcxproj | 212 ++++++ MeiToAvi.vcxproj.filters | 108 +++ README.md | 84 +++ RIFF/RIFFBase.cpp | 18 + RIFF/RIFFBase.hpp | 29 + RIFF/RIFFChunk.cpp | 70 ++ RIFF/RIFFChunk.hpp | 40 ++ RIFF/RIFFList.cpp | 102 +++ RIFF/RIFFList.hpp | 45 ++ RIFF/RIFFRoot.cpp | 89 +++ RIFF/RIFFRoot.hpp | 37 ++ Source/CachedSource.cpp | 37 ++ Source/CachedSource.hpp | 26 + Source/ConcatenatedSource.cpp | 74 +++ Source/ConcatenatedSource.hpp | 89 +++ Source/MemorySource.cpp | 66 ++ Source/MemorySource.hpp | 28 + Source/SourceBase.hpp | 17 + Startup.cpp | 20 + _MultiMon.cpp | 6 + 34 files changed, 4690 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 AVI.hpp create mode 100644 ApproxFraction.hpp create mode 100644 CacheStorage.cpp create mode 100644 CacheStorage.hpp create mode 100644 EntisGLS4.vcxproj create mode 100644 EntisGLS4.vcxproj.filters create mode 100644 Fraction.hpp create mode 100644 LICENSE.txt create mode 100644 MEIToAVI.cpp create mode 100644 MEIToAVI.hpp create mode 100644 Main.cpp create mode 100644 MeiToAvi.sln create mode 100644 MeiToAvi.vcxproj create mode 100644 MeiToAvi.vcxproj.filters create mode 100644 README.md create mode 100644 RIFF/RIFFBase.cpp create mode 100644 RIFF/RIFFBase.hpp create mode 100644 RIFF/RIFFChunk.cpp create mode 100644 RIFF/RIFFChunk.hpp create mode 100644 RIFF/RIFFList.cpp create mode 100644 RIFF/RIFFList.hpp create mode 100644 RIFF/RIFFRoot.cpp create mode 100644 RIFF/RIFFRoot.hpp create mode 100644 Source/CachedSource.cpp create mode 100644 Source/CachedSource.hpp create mode 100644 Source/ConcatenatedSource.cpp create mode 100644 Source/ConcatenatedSource.hpp create mode 100644 Source/MemorySource.cpp create mode 100644 Source/MemorySource.hpp create mode 100644 Source/SourceBase.hpp create mode 100644 Startup.cpp create mode 100644 _MultiMon.cpp diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5ac0500 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +#charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +charset = utf-8 +trim_trailing_whitespace = false + +[*.txt] +charset = utf-8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bdde4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,355 @@ +Build/ +EntisGLS4*/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/AVI.hpp b/AVI.hpp new file mode 100644 index 0000000..b3f4776 --- /dev/null +++ b/AVI.hpp @@ -0,0 +1,163 @@ +#ifndef ML_AVI_HPP +#define ML_AVI_HPP + +#include + +// obtained from https://makiuchi-d.github.io/mksoft/doc/avifileformat.html + + +namespace AVI { +#pragma pack(push, 1) + + struct BITMAPINFOHEADER { + std::uint32_t biSize; + std::uint32_t biWidth; + std::uint32_t biHeight; + std::uint16_t biPlanes; + std::uint16_t biBitCount; + std::uint32_t biCompression; + std::uint32_t biSizeImage; + std::uint32_t biXPelsPerMeter; + std::uint32_t biYPelsPerMeter; + std::uint32_t biClrUsed; + std::uint32_t biClrImportant; + }; + + static_assert(sizeof(BITMAPINFOHEADER) == 4 * 10); + + + struct WAVEFORMATEX { + std::uint16_t wFormatTag; + std::uint16_t nChannels; + std::uint32_t nSamplesPerSec; + std::uint32_t nAvgBytesPerSec; + std::uint16_t nBlockAlign; + std::uint16_t wBitsPerSample; + }; + + static_assert(sizeof(WAVEFORMATEX) == 4 * 4); + + + struct MainAVIHeader { + std::uint32_t dwMicroSecPerFrame; // frame display rate + std::uint32_t dwMaxBytesPerSec; // maximum transfer rate + std::uint32_t dwPaddingGranularity; // pad to multiple of this size (can be 0) + std::uint32_t dwFlags; // flags + std::uint32_t dwTotalFrames; // frames in the RIFF-AVI list + std::uint32_t dwInitialFrames; + std::uint32_t dwStreams; + std::uint32_t dwSuggestedBufferSize; + std::uint32_t dwWidth; + std::uint32_t dwHeight; + std::uint32_t dwReserved[4]; + }; + + static_assert(sizeof(MainAVIHeader) == 4 * 14); + + + struct AVIStreamHeader { + std::uint32_t fccType; + std::uint32_t fccHandler; + std::uint32_t dwFlags; + std::uint16_t wPriority; + std::uint16_t wLanguage; + std::uint32_t dwInitialFrames; + std::uint32_t dwScale; + std::uint32_t dwRate; // dwRate / dwScale == samples/second + std::uint32_t dwStart; + std::uint32_t dwLength; // frame count or sample count + std::uint32_t dwSuggestedBufferSize; + std::uint32_t dwQuality; + std::uint32_t dwSampleSize; + struct { + std::uint16_t left; + std::uint16_t top; + std::uint16_t right; + std::uint16_t bottom; + } rcFrame; + }; + + static_assert(sizeof(AVIStreamHeader) == 4 * 14); + + + struct AVIINDEXENTRY { + std::uint32_t ckid; + std::uint32_t dwFlags; + std::uint32_t dwChunkOffset; + std::uint32_t dwChunkLength; + }; + + static_assert(sizeof(AVIINDEXENTRY) == 4 * 4); + + + struct AVISUPERINDEX { + std::uint16_t wLongsPerEntry; + std::uint8_t bIndexSubType; + std::uint8_t bIndexType; + std::uint32_t nEntriesInUse; + std::uint32_t dwChunkId; + std::uint32_t dwReserved[3]; + }; + + static_assert(sizeof(AVISUPERINDEX) == 4 * 6); + + // flexible array member cannot be used in C++ ... + struct AVISUPERINDEXENTRY { + std::uint64_t qwOffset; + std::uint32_t dwSize; + std::uint32_t dwDuration; + }; + + static_assert(sizeof(AVISUPERINDEXENTRY) == 4 * 4); + + + struct AVISTDINDEX { + std::uint16_t wLongsPerEntry; + std::uint8_t bIndexSubType; + std::uint8_t bIndexType; + std::uint32_t nEntriesInUse; + std::uint32_t dwChunkId; + std::uint64_t qwBaseOffset; + std::uint32_t dwReserved3; + }; + + static_assert(sizeof(AVISTDINDEX) == 4 * 6); + + // flexible array member cannot be used in C++ ... + struct AVISTDINDEXENTRY { + std::uint32_t dwoffset; + std::uint32_t dwSize; + }; + + static_assert(sizeof(AVISTDINDEXENTRY) == 4 * 2); + + + // + + struct AVIEXTHEADER { + std::uint32_t dwGrandFrames; + std::uint32_t dwFuture[61]; + }; + + static_assert(sizeof(AVIEXTHEADER) == 4 * 62); + +#pragma pack(pop) + + + constexpr std::uint32_t AVIF_HASINDEX = 0x00000010; + constexpr std::uint32_t AVIF_MUSTUSEINDEX = 0x00000020; + constexpr std::uint32_t AVIF_ISINTERLEAVED = 0x00000100; + constexpr std::uint32_t AVIF_TRUSTCKTYPE = 0x00000800; + constexpr std::uint32_t AVIF_WASCAPTUREFILE = 0x00010000; + constexpr std::uint32_t AVIF_COPYRIGHTED = 0x00020000; + + constexpr std::uint32_t AVIIF_INDEX = 0x00000010; + constexpr std::uint32_t AVIIF_NO_TIME = 0x00000100; + + constexpr std::uint32_t AVISF_DISABLED = 0x00000001; + constexpr std::uint32_t AVISF_VIDEO_PALCHANGES = 0x00010000; + constexpr std::uint32_t AVIIF_LIST = 0x00000001; + constexpr std::uint32_t AVIIF_KEYFRAME = 0x00000010; +} + +#endif diff --git a/ApproxFraction.hpp b/ApproxFraction.hpp new file mode 100644 index 0000000..080bd14 --- /dev/null +++ b/ApproxFraction.hpp @@ -0,0 +1,68 @@ +#ifndef ML_APPROX_FRACTION_HPP +#define ML_APPROX_FRACTION_HPP + +#include +#include +#include + +#include "Fraction.hpp" + + +constexpr Fraction ApproxFraction(Fraction fraction) { + constexpr std::array, 2> PredefinedCandidates = { + Fraction{1, 1}, + Fraction{30000, 1001}, // 29.97 + }; + + struct Candidate { + Fraction baseFraction; + long multiply; + double absError; + }; + + + auto myAbs = [](double value) constexpr { + return value >= 0. ? value : -value; + }; + + + const double value = static_cast(fraction.numerator) / fraction.denominator; + + // find the nearest one for each candidate + std::array candidates{}; + + for (std::size_t i = 0; i < candidates.size(); i++) { + candidates[i] = { + PredefinedCandidates[i], + 0, + 1.e10, + }; + } + + for (auto& candidate : candidates) { + for (long multiply = 0; ; multiply++) { + const auto currentValue = static_cast(candidate.baseFraction.numerator) * multiply / candidate.baseFraction.denominator; + const auto currentAbsError = myAbs(currentValue - value); + if (currentAbsError > candidate.absError) { + break; + } + candidate.absError = currentAbsError; + candidate.multiply = multiply; + } + } + + const auto& approxCandidate = *std::min_element(candidates.cbegin(), candidates.cend(), [] (const Candidate& a, const Candidate& b) { + return a.absError < b.absError; + }); + + return Fraction{ + approxCandidate.baseFraction.numerator * approxCandidate.multiply, + approxCandidate.baseFraction.denominator, + }; +} + + +//static_assert(ApproxFraction(Fraction{211 * 1000, 8792}).numerator == 24); // 24 (mov_sak.mei) +//static_assert(ApproxFraction(Fraction{577 * 1000, 19253}).numerator == 30000); // 29.97 (mov_usi.mei) + +#endif diff --git a/CacheStorage.cpp b/CacheStorage.cpp new file mode 100644 index 0000000..b5fb045 --- /dev/null +++ b/CacheStorage.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include + +#include "CacheStorage.hpp" + + +bool CacheStorage::CompareCacheInfo(const CacheInfo& a, const CacheInfo& b) { + return a.lastUsed > b.lastUsed; +} + + +CacheStorage::CacheStorage(std::size_t maxStorageSize, std::size_t maxStorageData) : + mTimeCount(0), + mLastId(1), + mTotalSize(0), + mMaxStorageSize(maxStorageSize), + mMaxStorageData(maxStorageData), + mCacheDataMap(), + mCacheInfoHeap() +{} + + +CacheStorage::Id CacheStorage::Remove() { + if (mCacheInfoHeap.empty()) { + throw std::runtime_error("CacheStorage: no data in cache storage"); + } + + std::pop_heap(mCacheInfoHeap.begin(), mCacheInfoHeap.end(), CacheStorage::CompareCacheInfo); + const auto cacheInfo = *(mCacheInfoHeap.end() - 1); + mCacheInfoHeap.pop_back(); + mCacheDataMap.erase(cacheInfo.id); + mTotalSize -= cacheInfo.size; + return cacheInfo.id; +} + + +CacheStorage::Id CacheStorage::Add(std::unique_ptr&& data, std::size_t size) { + if (size > mMaxStorageSize) { + throw std::runtime_error("CacheStorage: data too large"); + } + + while (mTotalSize + size > mMaxStorageSize || mCacheDataMap.size() + 1 > mMaxStorageData) { + Remove(); + } + + do { + mLastId++; + } while (mCacheDataMap.count(mLastId)); + + const auto id = mLastId; + + mCacheDataMap.emplace(id, CacheData{ + id, + size, + std::move(data), + }); + + mCacheInfoHeap.push_back(CacheInfo{ + id, + size, + mTimeCount, + }); + + std::push_heap(mCacheInfoHeap.begin(), mCacheInfoHeap.end(), CacheStorage::CompareCacheInfo); + + mTotalSize += size; + + mTimeCount++; + + return id; +} + + +CacheStorage::Id CacheStorage::Add(const std::uint8_t* data, std::size_t size) { + auto upData = std::make_unique(size); + std::memcpy(upData.get(), data, size); + return Add(std::move(upData), size); +} + + +const CacheStorage::CacheData* CacheStorage::Get(Id id) { + auto itrCahceInfo = std::find_if(mCacheInfoHeap.begin(), mCacheInfoHeap.end(), [id] (const CacheInfo& cacheInfo) { + return cacheInfo.id == id; + }); + + if (itrCahceInfo == mCacheInfoHeap.end()) { + return nullptr; + } + + itrCahceInfo->lastUsed = mTimeCount; + + std::make_heap(mCacheInfoHeap.begin(), mCacheInfoHeap.end(), CacheStorage::CompareCacheInfo); + + mTimeCount++; + return &mCacheDataMap.at(id); +} diff --git a/CacheStorage.hpp b/CacheStorage.hpp new file mode 100644 index 0000000..d4bc373 --- /dev/null +++ b/CacheStorage.hpp @@ -0,0 +1,48 @@ +#ifndef ML_CACHESTORAGE_HPP +#define ML_CACHESTORAGE_HPP + +#include +#include +#include +#include + + +class CacheStorage { +public: + using Id = std::size_t; + + struct CacheData { + Id id; + std::size_t size; + std::unique_ptr data; + }; + +private: + using TimeCount = unsigned long; + + struct CacheInfo { + Id id; + std::size_t size; + TimeCount lastUsed; + }; + + TimeCount mTimeCount; + Id mLastId; + std::size_t mTotalSize; + std::size_t mMaxStorageSize; + std::size_t mMaxStorageData; + std::unordered_map mCacheDataMap; + std::vector mCacheInfoHeap; + + static bool CompareCacheInfo(const CacheInfo& a, const CacheInfo& b); + +public: + CacheStorage(std::size_t maxStorageSize, std::size_t maxStorageData); + + Id Remove(); + Id Add(std::unique_ptr&& data, std::size_t size); + Id Add(const std::uint8_t* data, std::size_t size); + const CacheData* Get(Id id); +}; + +#endif diff --git a/EntisGLS4.vcxproj b/EntisGLS4.vcxproj new file mode 100644 index 0000000..627102f --- /dev/null +++ b/EntisGLS4.vcxproj @@ -0,0 +1,578 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4} + EntisGLS4 + 10.0 + + + + StaticLibrary + true + v142 + MultiByte + + + StaticLibrary + false + v142 + true + MultiByte + + + StaticLibrary + true + v142 + MultiByte + + + StaticLibrary + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + + Level3 + Disabled + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + true + 4996 + + + DISABLE_ENTIS_GLS4_EXPORTS;_MBCS;%(PreprocessorDefinitions) + false + MultiThreadedDebug + + + Console + + + Glu32.lib;Imm32.lib;Opengl32.lib;Wininet.lib;Winmm.lib;Ws2_32.lib + + + + + Level3 + Disabled + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + true + 4996 + + + DISABLE_ENTIS_GLS4_EXPORTS;_MBCS;%(PreprocessorDefinitions) + false + MultiThreadedDebug + + + Console + + + Glu32.lib;Imm32.lib;Opengl32.lib;Wininet.lib;Winmm.lib;Ws2_32.lib + + + + + Level3 + MaxSpeed + true + true + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + true + MultiThreaded + AnySuitable + Speed + true + true + 4996 + + + DISABLE_ENTIS_GLS4_EXPORTS;_MBCS;NDEBUG;%(PreprocessorDefinitions) + false + + + Console + true + true + + + Glu32.lib;Imm32.lib;Opengl32.lib;Wininet.lib;Winmm.lib;Ws2_32.lib + + + + + Level3 + MaxSpeed + true + true + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + true + MultiThreaded + AnySuitable + Speed + true + true + 4996 + + + DISABLE_ENTIS_GLS4_EXPORTS;_MBCS;NDEBUG;%(PreprocessorDefinitions) + false + + + Console + true + true + + + Glu32.lib;Imm32.lib;Opengl32.lib;Wininet.lib;Winmm.lib;Ws2_32.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EntisGLS4.vcxproj.filters b/EntisGLS4.vcxproj.filters new file mode 100644 index 0000000..133d156 --- /dev/null +++ b/EntisGLS4.vcxproj.filters @@ -0,0 +1,1158 @@ +サソ + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + \ No newline at end of file diff --git a/Fraction.hpp b/Fraction.hpp new file mode 100644 index 0000000..c94641f --- /dev/null +++ b/Fraction.hpp @@ -0,0 +1,10 @@ +#ifndef ML_FRACTION_HPP +#define ML_FRACTION_HPP + +template +struct Fraction { + T numerator; + T denominator; +}; + +#endif diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..5ed91ee --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2019 SegaraRai + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MEIToAVI.cpp b/MEIToAVI.cpp new file mode 100644 index 0000000..9f53b29 --- /dev/null +++ b/MEIToAVI.cpp @@ -0,0 +1,725 @@ +#define NOMINMAX + +// FFmpegっぽい構造で出力するモード(デバッグ用) +//#define LIKE_FFMPEG 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MeiToAVI.hpp" +#include "ApproxFraction.hpp" +#include "AVI.hpp" +#include "Fraction.hpp" +#include "RIFF/RIFFChunk.hpp" +#include "RIFF/RIFFList.hpp" +#include "RIFF/RIFFRoot.hpp" +#include "Source/CachedSource.hpp" +#include "Source/MemorySource.hpp" + +#include + +#include +#include + +using namespace std::literals; + + +namespace { + constexpr std::uint_fast32_t MaxRiffSizeAVI = 0x40000000; // 1 GiB + constexpr std::uint_fast32_t MaxRiffSizeAVIX = 0x40000000; // 1 GiB + + constexpr std::uint_fast32_t MaxStreamsAVI = 0xFFFFFFFF; + constexpr std::uint_fast32_t MaxStreamsAVIX = 0xFFFFFFFF; + + + void CheckError(SSystem::SError error, const std::string& message) { + if (error != SSystem::SError::errSuccess) { + throw std::runtime_error(message); + } + } + + + template + Fraction ReduceFraction(const Fraction& fraction) { + const T gcd = std::gcd(fraction.numerator, fraction.denominator); + return Fraction{ + fraction.numerator / gcd, + fraction.denominator / gcd, + }; + } + + + constexpr std::uint32_t GetFourCC(const char fourcc[5]) { + return + static_cast(fourcc[0]) << 0 | + static_cast(fourcc[1]) << 8 | + static_cast(fourcc[2]) << 16 | + static_cast(fourcc[3]) << 24; + } + + + template + std::shared_ptr AddChunk(RIFFList& parent, std::uint32_t chunkId, const T& data) { + auto memorySource = std::make_shared(reinterpret_cast(&data), sizeof(T)); + auto riffChunk = std::make_shared(&parent, chunkId, memorySource); + parent.AddChild(riffChunk); + return riffChunk; + } + + std::shared_ptr AddEmptyChunk(RIFFList& parent, std::uint32_t chunkId) { + auto riffChunk = std::make_shared(&parent, chunkId); + parent.AddChild(riffChunk); + return riffChunk; + } +} + + +MEIToAVI::FrameImageSource::FrameImageSource(ERISA::SGLMovieFilePlayer& movieFilePlayer, std::uint_fast32_t frameIndex) : + mPtrMovieFilePlayer(&movieFilePlayer), + mFrameIndex(frameIndex), + mSize(0) +{ + const auto size = mPtrMovieFilePlayer->CurrentFrame()->GetImageSize(); + mSize = size.w * size.h * 4; +} + + +std::streamsize MEIToAVI::FrameImageSource::GetSize() const { + return mSize; +} + + +void MEIToAVI::FrameImageSource::Read(std::uint8_t* data, std::size_t size, std::streamsize offset) { + mPtrMovieFilePlayer->SeekToFrame(mFrameIndex); + const auto ptrCurrentFrame = mPtrMovieFilePlayer->CurrentFrame(); + const auto ptrSmartImage = static_cast(ptrCurrentFrame); + const auto ptrImageBuffer = ptrSmartImage->GetImage(); + std::memcpy(data, ptrImageBuffer->ptrBuffer + offset, size); +} + + + +MEIToAVI::MEIToAVI(const std::wstring& filePath, const Options& options) : + mCacheStorage(options.cacheStorageSize, options.cacheStorageLimit), + mFile(), + mMovieFilePlayer(), + mRiffRoot() +{ + // open file + CheckError(mFile.Open(filePath.c_str(), SSystem::SFileOpener::OpenFlag::modeRead | SSystem::SFileOpener::OpenFlag::shareRead), "cannot open file"s); + + // open as video + CheckError(mMovieFilePlayer.OpenMovieFile(&mFile, false), "cannot open video"s); + + // get media + const auto& mediaFile = mMovieFilePlayer.GetMediaFile(); + + + // check if the file has audio data + bool hasAudio = false; + if (!(options.flags & NoAudio)) { + hasAudio = mediaFile.m_flagsRead & ERISA::SGLMediaFile::readSoundInfo; + } + + + // load audio + std::vector audioData; + unsigned int audioBitsPerSample = 0; + unsigned int audioNumChannels = 0; + unsigned int audioBlockSize = 0; + std::uint_fast32_t audioSamplingRate = 0; + std::uint_fast32_t audioNumSamples = 0; + + if (hasAudio) { + SSystem::SFile fileForSound; + CheckError(fileForSound.Open(filePath.c_str(), SSystem::SFileOpener::OpenFlag::modeRead | SSystem::SFileOpener::OpenFlag::shareRead), "cannot open file for audio"s); + + ERISA::SGLSoundFilePlayer soundFilePlayer; + CheckError(soundFilePlayer.OpenSoundFile(&fileForSound, false), "cannot open audio"s); + + audioBitsPerSample = soundFilePlayer.GetBitsPerSample(); + audioNumChannels = soundFilePlayer.GetChannelCount(); + audioSamplingRate = soundFilePlayer.GetFrequency(); + audioNumSamples = soundFilePlayer.GetTotalSampleCount(); + audioBlockSize = audioBitsPerSample / 8 * audioNumChannels; + + // 音声データを全て読み出す + // 予め読んでいるのは事前にチャンクの配置を決定しておく必要があるため + // meiファイルでの配置をそのままAVIにするのも考えたがそれはそれで面倒そうだった + + audioData.resize(audioNumSamples * audioBlockSize); + + SSystem::SArray audioBuffer; + std::uint_fast32_t offset = 0; + while (offset < audioData.size()) { + soundFilePlayer.GetNextWaveBuffer(audioBuffer); + std::memcpy(audioData.data() + offset, audioBuffer.GetArray(), audioBuffer.GetLength()); + offset += audioBuffer.GetLength(); + +#ifdef _DEBUG + OutputDebugStringW((L"Read audio: "s + std::to_wstring(offset) + L" / "s + std::to_wstring(audioData.size()) + L"\n"s).c_str()); +#endif + } + + soundFilePlayer.Close(); + fileForSound.Close(); + } + + + // load video + const bool videoHasAlpha = !(options.flags & NoAlpha) && mediaFile.m_eriInfoHeader.fdwFormatType == 0x04000001 /*ERI_RGBA_IMAGE*/; + const auto videoSize = mMovieFilePlayer.CurrentFrame()->GetImageSize(); + const auto videoBytesPerFrame = static_cast(videoSize.w * videoSize.h * 4); + const auto videoNumFrames = static_cast(mMovieFilePlayer.GetAllFrameCount()); + const auto videoDurationMillis = static_cast(mMovieFilePlayer.GetTotalTime()); + + Fraction videoFPS{ + videoNumFrames * static_cast(1000), + videoDurationMillis, + }; + if (!(options.flags & NoApproxFPS)) { + const auto orgFPS = videoFPS; + + videoFPS = ApproxFraction(videoFPS); + + if (!(options.flags & NoMessage)) { + std::wcerr << L"[info] fps is approximated as "sv + << videoFPS.numerator << L"/"sv << videoFPS.denominator << L" ("sv << (static_cast(videoFPS.numerator) / videoFPS.denominator) << L") from "sv + << orgFPS.numerator << L"/"sv << orgFPS.denominator << L" ("sv << (static_cast(orgFPS.numerator) / orgFPS.denominator) << L")"sv <(1); + + // RIFFはチャンクを2バイト単位で整列する必要があるのでとりあえず + // audioBlockSizeを加味すれば問題ないこともあるが一応 + +#ifdef LIKE_FFMPEG + audioSamplesPerFrame = 1024; +#endif + } + + + // construct RIFF + + // ## RIFF-AVI + + auto riffAvi = std::make_shared(&mRiffRoot, GetFourCC("RIFF"), GetFourCC("AVI ")); + mRiffRoot.AddChild(riffAvi); + + // ### LIST-hdrl + auto listHdrl = std::make_shared(riffAvi.get(), GetFourCC("LIST"), GetFourCC("hdrl")); + riffAvi->AddChild(listHdrl); + + // #### avih + AVI::MainAVIHeader avihData{ + static_cast(1.e6 * videoFPS.denominator / videoFPS.numerator), + static_cast(static_cast(videoBytesPerFrame) * videoFPS.numerator / videoFPS.denominator + static_cast(audioSamplingRate) * audioBlockSize), + 0u, + AVI::AVIF_HASINDEX | AVI::AVIF_ISINTERLEAVED | AVI::AVIF_TRUSTCKTYPE, + 0u, // filled later + 0u, + hasAudio ? 2u : 1u, + videoBytesPerFrame + 2 * 4, + static_cast(videoSize.w), + static_cast(videoSize.h), + }; + auto avihMemorySource = std::make_shared(reinterpret_cast(&avihData), sizeof(avihData)); + auto avih = std::make_shared(listHdrl.get(), GetFourCC("avih"), avihMemorySource); + listHdrl->AddChild(avih); + + // video + + // #### LIST-strl (video) + auto videoListStrl = std::make_shared(listHdrl.get(), GetFourCC("LIST"), GetFourCC("strl")); + listHdrl->AddChild(videoListStrl); + + // ##### strh (video) + AddChunk(*videoListStrl, GetFourCC("strh"), AVI::AVIStreamHeader{ + GetFourCC("vids"), + videoHasAlpha ? GetFourCC("RGBA") : GetFourCC("\0\0\0\0"), + 0u, + 0u, + 0u, + 0u, + videoFPS.denominator, + videoFPS.numerator, + 0u, + videoNumFrames, + videoBytesPerFrame, + 0xFFFFFFFFu, + 0u, + { + 0u, + 0u, + static_cast(videoSize.w), + static_cast(videoSize.h), + }, + }); + + // ##### strf (video) + AddChunk(*videoListStrl, GetFourCC("strf"), AVI::BITMAPINFOHEADER{ + sizeof(AVI::BITMAPINFOHEADER), + static_cast(videoSize.w), + static_cast(-videoSize.h), + 1u, + 32u, + 0u, // BI_RGB + static_cast(videoSize.w * videoSize.h * 4), + 0u, + 0u, + 0u, + 0u, + }); + + // ##### indx (video) + // set later + auto videoIndx = std::make_shared(videoListStrl.get(), GetFourCC("indx")); + videoListStrl->AddChild(videoIndx); + + // audio + + std::shared_ptr audioListStrl; + std::shared_ptr audioIndx; + + if (hasAudio) { + // #### LIST-strl (audio) + audioListStrl = std::make_shared(listHdrl.get(), GetFourCC("LIST"), GetFourCC("strl")); + listHdrl->AddChild(audioListStrl); + + // ##### strh (audio) + AddChunk(*audioListStrl, GetFourCC("strh"), AVI::AVIStreamHeader{ + GetFourCC("auds"), + GetFourCC("\1\0\0\0"), + 0u, + 0u, + 0u, + 0u, + 1u, + audioSamplingRate, + 0u, + audioNumSamples, + audioSamplesPerFrame * audioBlockSize, + 0xFFFFFFFFu, + audioBlockSize, + { + 0u, + 0u, + 0u, + 0u, + }, + }); + + // ##### strf (audio) + AddChunk(*audioListStrl, GetFourCC("strf"), AVI::WAVEFORMATEX{ + 0x0001u, // WAVE_FORMAT_PCM + static_cast(audioNumChannels), + audioSamplingRate, + audioSamplingRate * audioBlockSize, + static_cast(audioBlockSize), + static_cast(audioBitsPerSample), + }); + + // ##### indx (audio) + audioIndx = std::make_shared(audioListStrl.get(), GetFourCC("indx")); + audioListStrl->AddChild(audioIndx); + } + + // Open-DML + + // #### LIST-odml + auto listOdml = std::make_shared(listHdrl.get(), GetFourCC("LIST"), GetFourCC("odml")); + listHdrl->AddChild(listOdml); + + // ##### dmlh + AVI::AVIEXTHEADER dmlhData{ + static_cast(videoNumFrames), + {}, + }; + auto dmlhMemorySource = std::make_shared(reinterpret_cast(&dmlhData), sizeof(dmlhData)); + auto dmlh = std::make_shared(listOdml.get(), GetFourCC("dmlh"), dmlhMemorySource); + listOdml->AddChild(dmlh); + + // + + // ### LIST-INFO + auto listInfo = std::make_shared(riffAvi.get(), GetFourCC("LIST"), GetFourCC("INFO")); + riffAvi->AddChild(listInfo); + + // #### ISFT +#if LIKE_FFMPEG + const char isftStr[] = "MeiToAvi0.1.0"; + auto isftMemorySource = std::make_shared(reinterpret_cast(isftStr), 14); +#else + const char isftStr[] = "MeiToAvi v0.1.0"; + static_assert(sizeof(isftStr) == 16); + auto isftMemorySource = std::make_shared(reinterpret_cast(isftStr), sizeof(isftStr)); +#endif + auto isft = std::make_shared(listInfo.get(), GetFourCC("ISFT"), isftMemorySource); + listInfo->AddChild(isft); + +#if LIKE_FFMPEG + // ### JUNK (FFmpegのと合わせる用) + auto junk = std::make_shared(riffAvi.get(), GetFourCC("JUNK"), std::make_shared(1016)); + riffAvi->AddChild(junk); +#else + if (options.junkChunkSize) { + auto junk = std::make_shared(riffAvi.get(), GetFourCC("JUNK"), std::make_shared(options.junkChunkSize)); + riffAvi->AddChild(junk); + } +#endif + + // ### LIST-movi + auto listMovi = std::make_shared(riffAvi.get(), GetFourCC("LIST"), GetFourCC("movi")); + riffAvi->AddChild(listMovi); + + // #### idx1 + auto idx1 = std::make_shared(riffAvi.get(), GetFourCC("idx1")); + riffAvi->AddChild(idx1); + + + // ## RIFF-AVIX + + std::vector> standardIndices; // 後でqwBaseOffsetを修正する用 + + struct StandardChunkInfo { + std::shared_ptr chunk; + std::uint_fast32_t duration; + }; + + std::vector videoStandardIndexChunks; + std::vector audioStandardIndexChunks; + + std::uint_fast32_t audioSampleCount = 0; + std::uint_fast32_t videoFrameCount = 0; + + // + + constexpr std::uint_fast32_t InitialSizeCount = 8; + + std::shared_ptr riffAvix = riffAvi; + std::shared_ptr avixListMovi = listMovi; + + std::vector> videoDataChunks; // indx登録用 + std::vector> audioDataChunks; // indx登録用 + + struct ChunkInfo { + bool video; + std::shared_ptr chunk; + }; + std::vector chunks; + + std::uint_fast32_t streamCount = 0; + std::uint_fast32_t sizeCount = static_cast(riffAvix->GetSize()); + + std::uint_fast32_t videoDurationCount = 0; + std::uint_fast32_t audioDurationCount = 0; + + std::uint_fast64_t tempGcd = hasAudio ? std::gcd(audioSamplingRate, videoFPS.numerator) : 1; + std::uint_fast64_t timeCodeCoefAudio = hasAudio ? videoFPS.numerator / tempGcd : 1; + std::uint_fast64_t timeCodeCoefVideo = hasAudio ? static_cast(videoFPS.denominator) * (audioSamplingRate / tempGcd) : 1; + + while (true) { + const bool isAvix = riffAvix != riffAvi; + const bool finished = videoFrameCount == videoNumFrames && (!hasAudio || audioSampleCount == audioNumSamples); + bool startNextAvix = false; + + const auto maxStreams = isAvix ? MaxStreamsAVIX : MaxStreamsAVI; + const auto maxRiffSize = isAvix ? MaxRiffSizeAVIX : MaxRiffSizeAVI; + + const std::uint_fast64_t timeCodeVideo = videoFrameCount != videoNumFrames + ? videoFrameCount * timeCodeCoefVideo + : std::numeric_limits::max(); + + const std::uint_fast64_t timeCodeAudio = hasAudio && audioSampleCount != audioNumSamples + ? audioSampleCount * timeCodeCoefAudio + : std::numeric_limits::max(); + + // なるべく映像の方を先に置くようにする + const bool isNextChunkVideo = timeCodeVideo <= timeCodeAudio; + + // 次のチャンクの大きさ + const std::uint_fast32_t nextChunkSize = 8 + (isNextChunkVideo ? videoBytesPerFrame : std::min(audioSamplesPerFrame, audioNumSamples - audioSampleCount) * audioBlockSize); + + if (streamCount >= maxStreams || sizeCount + nextChunkSize >= maxRiffSize || finished) { + // finish this RIFF-AVIX (RIFF-AVI) list + + // AVI-RIFFリストより後かつLIST-moviリストより前の領域の大きさがまだ増加する可能性があるので、 + // AVI-RIFFリストを基準にする場合は最後にインデックス全体のオフセットを書き直す必要が生じる + // ここではLIST-moviリストを基準にすることでこの問題を避けている + // 当然だがLIST-moviリスト内の構造に変化がある場合は上述の問題が生じる(ここでは大丈夫) + + auto& baseRiff = *avixListMovi; + + // video + { + const std::size_t videoIndexSize = sizeof(AVI::AVISTDINDEX) + sizeof(AVI::AVISTDINDEXENTRY) * videoDataChunks.size(); + auto videoIndexData = std::make_unique(videoIndexSize); + *reinterpret_cast(videoIndexData.get()) = AVI::AVISTDINDEX{ + 2u, + 0u, + 1u, // AVI_INDEX_OF_CHUNKS + videoDataChunks.size(), + GetFourCC("00dc"), + 0u, // filled later + 0u, + }; + const auto videoIndexEntries = reinterpret_cast(videoIndexData.get() + sizeof(AVI::AVISTDINDEX)); + const auto videoBaseOffset = baseRiff.GetOffset(); + for (std::uint_fast32_t i = 0; i < videoDataChunks.size(); i++) { + auto& videoDataChunk = *videoDataChunks[i]; + videoIndexEntries[i] = AVI::AVISTDINDEXENTRY{ + static_cast(videoDataChunk.GetOffset() - videoBaseOffset + 8), + static_cast(videoDataChunk.GetSize() - 8), + }; + } + auto videoIndexMemorySource = std::make_shared(std::move(videoIndexData), videoIndexSize); + auto videoIndexChunk = std::make_shared(avixListMovi.get(), GetFourCC("ix00"), videoIndexMemorySource); + avixListMovi->AddChild(videoIndexChunk); + videoStandardIndexChunks.push_back(StandardChunkInfo{ + videoIndexChunk, + videoDurationCount, + }); + standardIndices.push_back(std::make_pair(videoIndexMemorySource.get(), &baseRiff)); + } + + // audio + if (hasAudio) { + const std::size_t audioIndexSize = sizeof(AVI::AVISTDINDEX) + sizeof(AVI::AVISTDINDEXENTRY) * audioDataChunks.size(); + auto audioIndexData = std::make_unique(audioIndexSize); + *reinterpret_cast(audioIndexData.get()) = AVI::AVISTDINDEX{ + 2u, + 0u, + 1u, // AVI_INDEX_OF_CHUNKS + audioDataChunks.size(), + GetFourCC("01wb"), + 0u, // filled later + 0u, + }; + const auto audioIndexEntries = reinterpret_cast(audioIndexData.get() + sizeof(AVI::AVISTDINDEX)); + const auto audioBaseOffset = baseRiff.GetOffset(); + for (std::uint_fast32_t i = 0; i < audioDataChunks.size(); i++) { + auto& audioDataChunk = *audioDataChunks[i]; + audioIndexEntries[i] = AVI::AVISTDINDEXENTRY{ + static_cast(audioDataChunk.GetOffset() - audioBaseOffset + 8), + static_cast(audioDataChunk.GetSize() - 8), + }; + } + auto audioIndexMemorySource = std::make_shared(std::move(audioIndexData), audioIndexSize); + auto audioIndexChunk = std::make_shared(avixListMovi.get(), GetFourCC("ix01"), audioIndexMemorySource); + avixListMovi->AddChild(audioIndexChunk); + audioStandardIndexChunks.push_back(StandardChunkInfo{ + audioIndexChunk, + audioDurationCount, + }); + standardIndices.push_back(std::make_pair(audioIndexMemorySource.get(), &baseRiff)); + } + + if (!isAvix) { + // idx1 + auto idx1MemorySource = std::make_shared(sizeof(AVI::AVIINDEXENTRY) * streamCount); + const auto indexEntries = reinterpret_cast(idx1MemorySource->GetData()); + const auto baseOffset = listMovi->GetOffset() + 8; // I don't know why +8, but FFmpeg does + for (std::uint_fast32_t i = 0; i < streamCount; i++) { + indexEntries[i] = AVI::AVIINDEXENTRY{ + chunks[i].video ? GetFourCC("00dc") : GetFourCC("01wb"), + AVI::AVIIF_KEYFRAME, // both video frames and audio blocks are keyframes + static_cast(chunks[i].chunk->GetOffset() - baseOffset), // relative to movi; (absolute position is permitted also) + static_cast(chunks[i].chunk->GetSize() - 8), // I don't know why -8, but FFmpeg does + }; + } + idx1->SetContentSource(idx1MemorySource); + + // avih + reinterpret_cast(avihMemorySource->GetData())->dwTotalFrames = videoFrameCount + 1; // I don't know why +1, but FFmpeg does + } + + startNextAvix = true; + } + + + // finish + if (finished) { + break; + } + + + if (startNextAvix) { + // start new RIFF-AVIX list + + riffAvix = std::make_shared(&mRiffRoot, GetFourCC("RIFF"), GetFourCC("AVIX")); + mRiffRoot.AddChild(riffAvix); + + avixListMovi = std::make_shared(riffAvix.get(), GetFourCC("LIST"), GetFourCC("movi")); + riffAvix->AddChild(avixListMovi); + + videoDataChunks.clear(); + audioDataChunks.clear(); + chunks.clear(); + + streamCount = 0; + sizeCount = static_cast(riffAvix->GetSize()); + + videoDurationCount = 0; + audioDurationCount = 0; + } + + + std::shared_ptr chunk; + + if (isNextChunkVideo) { + assert(videoFrameCount != videoNumFrames); // 終了していないことを確認 + + auto source = std::make_shared(mCacheStorage, std::make_shared(mMovieFilePlayer, videoFrameCount)); + chunk = std::make_shared(avixListMovi.get(), GetFourCC("00dc"), source); + videoDataChunks.push_back(chunk); + chunks.push_back({ + true, + chunk, + }); + videoFrameCount++; + videoDurationCount++; + } else { + assert(hasAudio); + assert(audioSampleCount != audioNumSamples); // 終了していないことを確認 + + const std::uint_fast32_t samples = std::min(audioSamplesPerFrame, audioNumSamples - audioSampleCount); + auto memorySource = std::make_shared(audioData.data() + audioSampleCount * audioBlockSize, samples * audioBlockSize); + chunk = std::make_shared(avixListMovi.get(), GetFourCC("01wb"), memorySource); + audioDataChunks.push_back(chunk); + chunks.push_back({ + false, + chunk, + }); + audioSampleCount += samples; + audioDurationCount += samples; + } + + assert(chunk); + avixListMovi->AddChild(chunk); + + streamCount++; + sizeCount += static_cast(chunk->GetSize()); + } + + // set indx chunks + + std::vector> superIndexEntries; // 後でqwOffsetを修正する用 + + // video + { +#if LIKE_FFMPEG + const std::size_t videoIndexSize = 4120; +#else + const std::size_t videoIndexSize = sizeof(AVI::AVISUPERINDEX) + sizeof(AVI::AVISUPERINDEXENTRY)* videoStandardIndexChunks.size(); +#endif + auto videoIndexData = std::make_unique(videoIndexSize); + *reinterpret_cast(videoIndexData.get()) = AVI::AVISUPERINDEX{ + 4u, + 0u, + 0u, // AVI_INDEX_OF_CHUNKS + videoStandardIndexChunks.size(), + GetFourCC("00dc"), + {}, + }; + const auto videoIndexEntries = reinterpret_cast(videoIndexData.get() + sizeof(AVI::AVISUPERINDEX)); + for (std::uint_fast32_t i = 0; i < videoStandardIndexChunks.size(); i++) { + auto& videoStandardIndexChunk = videoStandardIndexChunks[i]; + videoIndexEntries[i] = AVI::AVISUPERINDEXENTRY{ + 0u, // filled later + static_cast(videoStandardIndexChunk.chunk->GetSize()), // includes the size of a chunk header + static_cast(videoStandardIndexChunk.duration), + }; + + superIndexEntries.emplace_back(&videoIndexEntries[i].qwOffset, videoStandardIndexChunk.chunk.get()); + } + videoIndx->SetContentSource(std::make_shared(std::move(videoIndexData), videoIndexSize)); + } + + // audio + if (hasAudio) { +#if LIKE_FFMPEG + const std::size_t audioIndexSize = 4120; +#else + const std::size_t audioIndexSize = sizeof(AVI::AVISUPERINDEX) + sizeof(AVI::AVISUPERINDEXENTRY) * audioStandardIndexChunks.size(); +#endif + auto audioIndexData = std::make_unique(audioIndexSize); + *reinterpret_cast(audioIndexData.get()) = AVI::AVISUPERINDEX{ + 4u, + 0u, + 0u, // AVI_INDEX_OF_CHUNKS + audioStandardIndexChunks.size(), + GetFourCC("01wb"), + {}, + }; + const auto audioIndexEntries = reinterpret_cast(audioIndexData.get() + sizeof(AVI::AVISUPERINDEX)); + for (std::uint_fast32_t i = 0; i < audioStandardIndexChunks.size(); i++) { + auto& audioStandardIndexChunk = audioStandardIndexChunks[i]; + audioIndexEntries[i] = AVI::AVISUPERINDEXENTRY{ + 0u, // filled later + static_cast(audioStandardIndexChunk.chunk->GetSize()), // includes the size of a chunk header + static_cast(audioStandardIndexChunk.duration), + }; + + superIndexEntries.emplace_back(&audioIndexEntries[i].qwOffset, audioStandardIndexChunk.chunk.get()); + } + audioIndx->SetContentSource(std::make_shared(std::move(audioIndexData), audioIndexSize)); + } + + // 骨組み完成 + + // fix qwBaseOffset (standard index) + for (const auto [ptrMemorySource, ptrBaseRiff] : standardIndices) { + reinterpret_cast(ptrMemorySource->GetData())->qwBaseOffset = ptrBaseRiff->GetOffset(); + } + + // fix qwOffset (super index) + for (const auto [ptrQwOffset, ptrStandardIndexChunk] : superIndexEntries) { + *ptrQwOffset = ptrStandardIndexChunk->GetOffset(); + } + + // 完成 + +#ifdef _DEBUG + OutputDebugStringW(L"RIFF construction completed\n"); +#endif + + mRiffRoot.CreateSource(); +} + + +SourceBase& MEIToAVI::GetSource() { + return mRiffRoot.GetSource(); +} diff --git a/MEIToAVI.hpp b/MEIToAVI.hpp new file mode 100644 index 0000000..56198e7 --- /dev/null +++ b/MEIToAVI.hpp @@ -0,0 +1,54 @@ +#ifndef ML_MEITOAVi_HPP +#define ML_MEITOAVi_HPP + +#include "CacheStorage.hpp" +#include "RIFF/RIFFRoot.hpp" +#include "Source/SourceBase.hpp" + +#include +#include + +#include +#include + + +class MEIToAVI { +public: + static constexpr unsigned int NoMessage = 0x0001; + static constexpr unsigned int NoAudio = 0x0002; + static constexpr unsigned int NoAlpha = 0x0004; + static constexpr unsigned int NoApproxFPS = 0x0008; + + struct Options { + unsigned int flags; + std::size_t cacheStorageSize; + std::size_t cacheStorageLimit; + std::uint_fast32_t audioBlockSamples; + std::uint_fast32_t junkChunkSize; + }; + +private: + class FrameImageSource : public SourceBase { + ERISA::SGLMovieFilePlayer* mPtrMovieFilePlayer; + std::size_t mFrameIndex; + std::size_t mSize; + + public: + FrameImageSource(ERISA::SGLMovieFilePlayer& movieFilePlayer, std::uint_fast32_t frameIndex); + + std::streamsize GetSize() const override; + void Read(std::uint8_t* data, std::size_t size, std::streamsize offset) override; + }; + + CacheStorage mCacheStorage; + SSystem::SFile mFile; + ERISA::SGLMovieFilePlayer mMovieFilePlayer; + RIFFRoot mRiffRoot; + +public: + MEIToAVI(const std::wstring& filePath, const Options& options); + + SourceBase& GetSource(); +}; + +#endif diff --git a/Main.cpp b/Main.cpp new file mode 100644 index 0000000..1ee3156 --- /dev/null +++ b/Main.cpp @@ -0,0 +1,167 @@ +#define NOMINMAX + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MEIToAVI.hpp" + +using namespace std::literals; + + +namespace { + constexpr std::size_t CacheStorageSize = std::numeric_limits::max(); + constexpr std::size_t CacheStorageLimit = 2; + constexpr std::size_t DefaultAudioBlockSamples = 0; + constexpr std::size_t DefaultJunkSize = 4096; + constexpr std::size_t DefaultBufferSize = 64 * 1024; + + + int ShowUsage(wchar_t* program) { + std::wcerr << L"MeiToAvi v0.1.0"sv << std::endl; + std::wcerr << L"Copyright (c) 2019 SegaraRai"sv << std::endl; + std::wcerr << std::endl; + std::wcerr << L"usage: "sv << program << L" [-quiet] [-noaudio] [-noalpha] [-orgfps] [-ablock sample] [-junksize size] [-bufsize size] infile outfile"sv << std::endl; + std::wcerr << std::endl; + std::wcerr << L"-quiet suppress messages"sv << std::endl; + std::wcerr << L"-noaudio skip decoding audio"sv << std::endl; + std::wcerr << L"-noalpha assume that the source has no alpha channel"sv << std::endl; + std::wcerr << L"-orgfps use original frame rate"sv << std::endl; + std::wcerr << L"-ablock set the number of samples for each audio block (default: "sv << DefaultAudioBlockSamples << L", set 0 to calculate automatically)"sv << std::endl; + std::wcerr << L"-junksize set the size of JUNK chunk (default: "sv << DefaultJunkSize << L", set 0 to disable JUNK chunk)"sv << std::endl; + std::wcerr << L"-bufsize set buffer size for output (default: "sv << DefaultBufferSize << L")"sv << std::endl; + std::wcerr << std::endl; + std::wcerr << L"set outfile to \"-\" to output to stdout"sv << std::endl; + std::wcerr << std::endl; + std::wcerr << L"This program makes use of EntisGLS version 4s.05." << std::endl; + std::wcerr << std::endl; + std::wcerr << L"EntisGLS version 4s.05"sv << std::endl; + std::wcerr << L"Copyright (c) 1998-2014 Leshade Entis, Entis soft."sv << std::endl; + return 1; + } +} + + +int xwmain(int argc, wchar_t* argv[]) { + MEIToAVI::Options options{ + 0, + CacheStorageSize, + CacheStorageLimit, + DefaultAudioBlockSamples, + DefaultJunkSize, + }; + + std::size_t bufferSize = DefaultBufferSize; + + int argIndex = 1; + while (argIndex < argc) { + const std::wstring arg(argv[argIndex]); + argIndex++; + + if (arg == L"-quiet"sv) { + options.flags |= MEIToAVI::NoMessage; + continue; + } + + if (arg == L"-noaudio"sv) { + options.flags |= MEIToAVI::NoAudio; + continue; + } + + if (arg == L"-noalpha"sv) { + options.flags |= MEIToAVI::NoAlpha; + continue; + } + + if (arg == L"-orgfps"sv) { + options.flags |= MEIToAVI::NoApproxFPS; + continue; + } + + if (arg == L"-ablock"sv) { + const auto argSamples = std::stoll(argv[argIndex++]); + if (argSamples < 0) { + std::wcerr << L"sample must be greater than or equal to 0" << std::endl; + return 2; + } + options.audioBlockSamples = static_cast(argSamples); + continue; + } + + if (arg == L"-junksize"sv) { + const auto argJunkChunkSize = std::stoll(argv[argIndex++]); + if (argJunkChunkSize < 0) { + std::wcerr << L"size must be greater than or equal to 0" << std::endl; + return 2; + } + options.junkChunkSize = static_cast(argJunkChunkSize); + continue; + } + + if (arg == L"-bufsize"sv) { + const auto argBufferSize = std::stoll(argv[argIndex++]); + if (argBufferSize < 1) { + std::wcerr << L"size must be greater than 0" << std::endl; + return 2; + } + bufferSize = static_cast(argBufferSize); + continue; + } + + argIndex--; + + break; + }; + + if (argIndex + 2 != argc) { + return ShowUsage(argv[0]); + } + + const std::wstring inFile(argv[argIndex++]); + const std::wstring outFile(argv[argIndex++]); + + const bool useStdOut = outFile == L"-"sv; + + std::ofstream ofs; + if (useStdOut) { + _setmode(_fileno(stdout), _O_BINARY); + } else { + ofs.exceptions(std::ios::failbit | std::ios::badbit); + ofs.open(outFile, std::ios::binary); + } + + { + MEIToAVI meiToAvi(inFile, options); + auto& source = meiToAvi.GetSource(); + const std::streamsize totalSize = source.GetSize(); + + if (!(options.flags & MEIToAVI::NoMessage)) { + std::wcerr << L"[info] avi size = "sv << totalSize << L" bytes"sv << std::endl; + } + + auto buffer = std::make_unique(bufferSize); + std::streamsize offset = 0; + + std::ostream* ptrOs = useStdOut ? &std::cout : &ofs; + + while (offset != totalSize) { + const auto readSize = static_cast(std::min(bufferSize, totalSize - offset)); + source.Read(buffer.get(), readSize, offset); + ptrOs->write(reinterpret_cast(buffer.get()), readSize); + offset += readSize; + } + + ptrOs->flush(); + } + + if (!useStdOut) { + ofs.close(); + } + + return 0; +} diff --git a/MeiToAvi.sln b/MeiToAvi.sln new file mode 100644 index 0000000..2bfbda2 --- /dev/null +++ b/MeiToAvi.sln @@ -0,0 +1,44 @@ +サソ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29215.179 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EntisGLS4", "EntisGLS4.vcxproj", "{03F9F65D-75AB-4EED-B980-1DF35B9146C4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MeiToAvi", "MeiToAvi.vcxproj", "{59E42633-DAA2-4DB7-9D26-2CFB45BE8146}" + ProjectSection(ProjectDependencies) = postProject + {03F9F65D-75AB-4EED-B980-1DF35B9146C4} = {03F9F65D-75AB-4EED-B980-1DF35B9146C4} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Debug|x64.ActiveCfg = Debug|x64 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Debug|x64.Build.0 = Debug|x64 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Debug|x86.ActiveCfg = Debug|Win32 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Debug|x86.Build.0 = Debug|Win32 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Release|x64.ActiveCfg = Release|x64 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Release|x64.Build.0 = Release|x64 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Release|x86.ActiveCfg = Release|Win32 + {03F9F65D-75AB-4EED-B980-1DF35B9146C4}.Release|x86.Build.0 = Release|Win32 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Debug|x64.ActiveCfg = Debug|x64 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Debug|x64.Build.0 = Debug|x64 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Debug|x86.ActiveCfg = Debug|Win32 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Debug|x86.Build.0 = Debug|Win32 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Release|x64.ActiveCfg = Release|x64 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Release|x64.Build.0 = Release|x64 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Release|x86.ActiveCfg = Release|Win32 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {22FF9C2E-26F1-44E2-9D56-D9A9540FA8DB} + EndGlobalSection +EndGlobal diff --git a/MeiToAvi.vcxproj b/MeiToAvi.vcxproj new file mode 100644 index 0000000..62a6094 --- /dev/null +++ b/MeiToAvi.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 + {59E42633-DAA2-4DB7-9D26-2CFB45BE8146} + MeiToAvi + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(ProjectName)\$(Platform)\$(Configuration)\ + + + + Level3 + Disabled + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + stdcpp17 + true + MultiThreadedDebug + false + + + Console + $(OutDir) + EntisGLS4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + stdcpp17 + true + MultiThreadedDebug + false + + + Console + $(OutDir) + EntisGLS4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + stdcpp17 + MultiThreaded + true + AnySuitable + Speed + true + true + _MBCS;NDEBUG;%(PreprocessorDefinitions) + false + + + Console + true + true + $(OutDir) + EntisGLS4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + false + + + + + Level3 + MaxSpeed + true + true + true + true + EntisGLS4s.05\Include\opengl;EntisGLS4s.05\Include\win32;EntisGLS4s.05\Include\common + stdcpp17 + MultiThreaded + true + AnySuitable + Speed + true + true + _MBCS;NDEBUG;%(PreprocessorDefinitions) + false + + + Console + true + true + $(OutDir) + EntisGLS4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + false + + + + + + \ No newline at end of file diff --git a/MeiToAvi.vcxproj.filters b/MeiToAvi.vcxproj.filters new file mode 100644 index 0000000..e4dfd2b --- /dev/null +++ b/MeiToAvi.vcxproj.filters @@ -0,0 +1,108 @@ +サソ + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {55b55245-1676-4a95-8a6b-7480b31faefd} + + + {4421eb5e-d6a9-4a96-8a01-a92cd7182a2a} + + + {503dba2c-c4bc-4feb-9ed0-627a8df96e1e} + + + {b0bdc9b8-6fbe-4fc0-b88b-6657458263ca} + + + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\RIFF + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\RIFF + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\RIFF + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\RIFF + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\Source + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ + + + 繧ス繝シ繧ケ 繝輔ぃ繧、繝ォ\Source + + + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\RIFF + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\RIFF + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\RIFF + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\RIFF + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\Source + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\Source + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\Source + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ\Source + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + 繝倥ャ繝繝シ 繝輔ぃ繧、繝ォ + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..acd3f7f --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# MeiToAvi + +MEI繝輔ぃ繧、繝ォシ[ERI繝輔か繝シ繝槭ャ繝](https://www.entis.jp/eridev/)シ峨r辟。蝨ァ邵ョAVI繝輔ぃ繧、繝ォ縺ォ螟画鋤縺吶k繝励Ο繧ー繝ゥ繝縺ァ縺吶 + +## 菴ソ縺譁ケ + +### AVI繝輔ぃ繧、繝ォ縺ォ蜃コ蜉帙☆繧 + +莉・荳九ョ讒倥↓螳溯。後☆繧九→video.mei繧致ideo.avi縺ォ螟画鋤縺ァ縺阪∪縺吶 +蜃コ蜉帙&繧後kAVI繝輔ぃ繧、繝ォ縺ッ32繝薙ャ繝RGBシAシ峨ョ辟。蝨ァ邵ョ繝繝シ繧ソ縺ェ縺ョ縺ァ螳ケ驥上↓縺頑ー励r縺、縺代¥縺縺輔>縲 +騾壼クク縺ッ縺薙■繧峨〒縺ッ縺ェ縺上:Fmpeg繧堤畑縺縺ヲ螟画鋤縺吶k縺ョ縺瑚憶縺縺ァ縺励g縺縲 + +```bat +MeiToAvi.exe video.mei video.avi +``` + +### [FFmpeg](https://ffmpeg.org/)繧堤畑縺縺ヲ螟画鋤縺吶k + +MeiToAvi縺ァ蜃コ蜉帙ヵ繧。繧、繝ォ縺ォ`-`繧呈欠螳壹☆繧九%縺ィ縺ァ螟画鋤縺励◆繝繝シ繧ソ繧呈ィ呎コ門コ蜉帙↓蜃コ蜉帙〒縺阪∪縺吶 +縺薙l繧端FFmpeg](https://ffmpeg.org/)縺ィ繝代う繝励〒郢九£繧九%縺ィ縺ァ縲√ヵ繧。繧、繝ォ繧堤オ檎罰縺帙★縺ォMEI繝輔ぃ繧、繝ォ繧剃サ悶ョ蠖「蠑上↓螟画鋤縺ァ縺阪∪縺吶 + +莉・荳九↓縺ッ繧医¥菴ソ縺縺ィ諤昴o繧後k萓九r遉コ縺励∪縺吶′縲√%縺薙↓謖吶£縺滉サ・螟悶ョ螟画鋤繧ょ庄閭ス縺ァ縺吶 + +#### MP4縺ォ螟画鋤縺吶k + +video.mei繧団rf蛟、18縺ァvideo.mp4縺ォ螟画鋤縺励∪縺吶 + +```bat +MeiToAvi.exe video.mei - | ffmpeg -i pipe:0.avi -crf 18 video.mp4 +``` + +#### 髻ウ螢ー繧淡AV縺ァ蜿悶j蜃コ縺 + +video.mei縺ョ髻ウ螢ー繧致ideo.wav縺ォ蜿悶j蜃コ縺励∪縺吶 + +```bat +MeiToAvi.exe video.mei - | ffmpeg -i pipe:0.avi -vn -c:a copy video.wav +``` + +### [FFplay](https://ffmpeg.org/ffplay.html)縺ァ蜀咲函縺吶k + +video.mei繧端FFplay](https://ffmpeg.org/ffplay.html)縺ァ蜀咲函縺励∪縺吶 + +```bat +MeiToAvi.exe video.mei - | ffplay pipe:0.avi +``` + +## 繝薙Ν繝画婿豕 + +### 1. [EntisGLS](https://www.entis.jp/gls/)縺ョ蟆主・ + +[EntisGLS](https://www.entis.jp/gls/)縺ッ辟。險ア蜿ッ縺ァ縺ョ蜀埼榊ク繧堤ヲ√§縺ヲ縺繧九◆繧√√Μ繝昴ず繝医Μ縺ォ蜷ォ繧√※縺縺セ縺帙s縲 +譛蛻昴↓EntisGLS version 4s.05繧偵ム繧ヲ繝ウ繝ュ繝シ繝峨@縲・ntisGLS4s.05繝繧」繝ャ繧ッ繝医Μ繧偵た繝シ繧ケ繝繝ェ繝シ縺ョ繝ォ繝シ繝医↓鄂ョ縺縺ヲ縺上□縺輔>縲 + +縺セ縺溘√さ繝ウ繝代う繝ォ繧帝壹☆縺溘a縺ォEntisGLS4s.05蜀縺ョSource/common/sakura/ssys_std_ui.cpp繝輔ぃ繧、繝ォ縺ォ縺、縺縺ヲ縲〜ConvertFileDialogFilter`髢「謨ー縺ョ螳」險縺ィ螳夂セゥ縺ョ謌サ繧雁、繧蛋char *`縺九i`const char *`縺ォ螟画峩縺励※縺上□縺輔>シ357陦檎岼縺ィ506陦檎岼シ峨 + +### 2. 繝薙Ν繝 + +Visual Studio 2019縺ァMeiToAvi.sln繧帝幕縺阪√た繝ェ繝・繝シ繧キ繝ァ繝ウ繧偵ン繝ォ繝峨@縺セ縺吶 + +螳溯。後ヵ繧。繧、繝ォ縺ッBuild/Win32/Debug縺セ縺溘ッBuild/Win32/Release繝繧」繝ャ繧ッ繝医Μ縺ォ蜃コ蜉帙&繧後∪縺吶 + +## TODO + +- [ ] 繧「繝ォ繝輔ぃ繝√Ε繝ウ繝阪Ν莉倥″繝繝シ繧ソ縺ョ蜍穂ス懃「コ隱 +- [ ] YUV濶イ遨コ髢薙〒縺ョ蜃コ蜉 +- [ ] 繝代う繝礼ュ峨°繧峨ョmei繝輔ぃ繧、繝ォ縺ョ +- [ ] 蜷榊燕莉倥″繝代う繝励∈縺ョ蜃コ蜉 +- [ ] Windows莉・螟悶ョ繝励Λ繝繝医ヵ繧ゥ繝シ繝 +- [ ] x64迚 + +## 繝ゥ繧、繧サ繝ウ繧ケ + +MeiToAvi縺ッMIT License縺ョ繧ゅ→驟榊ク縺輔l縺セ縺吶 +隧ウ縺励¥縺ッLICENSE.txt繧偵#隕ァ縺上□縺輔>縲 + +縺溘□縺励∽スソ逕ィ縺励※縺繧九Λ繧、繝悶Λ繝ェ縺ォ縺、縺縺ヲ縺ッ縺昴ョ繝ゥ繧、繝悶Λ繝ェ縺ョ繝ゥ繧、繧サ繝ウ繧ケ縺碁←逕ィ縺輔l縺セ縺吶 + +## 繝ゥ繧、繝悶Λ繝ェ闡嶺ス懈ィゥ陦ィ遉コ + +MeiToAvi縺ッ[EntisGLS](https://www.entis.jp/gls/)繧剃スソ逕ィ縺励※縺縺セ縺吶 + +> EntisGLS version 4s.05 +> Copyright (C) 1998-2014 逅蠖ア, Entis soft. diff --git a/RIFF/RIFFBase.cpp b/RIFF/RIFFBase.cpp new file mode 100644 index 0000000..8928823 --- /dev/null +++ b/RIFF/RIFFBase.cpp @@ -0,0 +1,18 @@ +#include + +#include "RIFFBase.hpp" + + +RIFFBase::RIFFBase(RIFFBase* parent) : + parent(parent) +{} + + +std::streamsize RIFFBase::GetOffset() const { + return parent->GetOffset() + parent->GetOffsetOf(this); +} + + +void RIFFBase::CreateSource() { + // do nothing +} diff --git a/RIFF/RIFFBase.hpp b/RIFF/RIFFBase.hpp new file mode 100644 index 0000000..65f58f0 --- /dev/null +++ b/RIFF/RIFFBase.hpp @@ -0,0 +1,29 @@ +#ifndef ML_RIFFBASE_HPP +#define ML_RIFFBASE_HPP + +#include +#include +#include +#include + +#include "../Source/SourceBase.hpp" + + +class RIFFBase { +protected: + RIFFBase* parent; + + virtual std::streamsize GetOffsetOf(const RIFFBase* child) const = 0; + +public: + RIFFBase(RIFFBase* parent); + virtual ~RIFFBase() = default; + + virtual std::streamsize GetOffset() const; + virtual std::streamsize GetSize() const = 0; + virtual SourceBase& GetSource() = 0; + virtual std::shared_ptr GetSourceSp() = 0; + virtual void CreateSource(); +}; + +#endif diff --git a/RIFF/RIFFChunk.cpp b/RIFF/RIFFChunk.cpp new file mode 100644 index 0000000..e8ed0dc --- /dev/null +++ b/RIFF/RIFFChunk.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "RIFFChunk.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/MemorySource.hpp" +#include "../Source/SourceBase.hpp" + + +void RIFFChunk::CreateSource() { + if (mContentSource->GetSize() > std::numeric_limits::max()) { + throw std::runtime_error("CHUNK: content too large"); + } + + const Header header{ + mChunkId, + static_cast(mContentSource->GetSize()), + }; + + mSource = std::make_shared(std::array, 2>{ + std::make_shared(reinterpret_cast(&header), sizeof(header)), + mContentSource, + }); +} + + +std::streamsize RIFFChunk::GetOffsetOf(const RIFFBase* child) const { + throw std::runtime_error("CHUNK: GetOffsetOf is not supported for RIFFChunk"); +} + + +RIFFChunk::RIFFChunk(RIFFBase* parent, std::uint32_t chunkId, std::shared_ptr contentSource) : + RIFFBase(parent), + mChunkId(chunkId), + mContentSource(contentSource), + mSource() +{ + CreateSource(); +} + + +RIFFChunk::RIFFChunk(RIFFBase* parent, std::uint32_t chunkId) : + RIFFChunk(parent, chunkId, std::make_shared(0)) +{} + + +std::streamsize RIFFChunk::GetSize() const { + return mSource->GetSize(); +} + + +SourceBase& RIFFChunk::GetSource() { + return *mSource; +} + + +std::shared_ptr RIFFChunk::GetSourceSp() { + return mSource; +} + + +void RIFFChunk::SetContentSource(std::shared_ptr contentSource) { + mContentSource = contentSource; + CreateSource(); +} diff --git a/RIFF/RIFFChunk.hpp b/RIFF/RIFFChunk.hpp new file mode 100644 index 0000000..1ddc2e6 --- /dev/null +++ b/RIFF/RIFFChunk.hpp @@ -0,0 +1,40 @@ +#ifndef ML_RIFFCHUNK_HPP +#define ML_RIFFCHUNK_HPP + +#include +#include +#include +#include + +#include "RIFFBase.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/SourceBase.hpp" + + +class RIFFChunk : public RIFFBase { + struct Header { + std::uint32_t chunkId; + std::uint32_t size; + }; + + std::uint32_t mChunkId; + std::shared_ptr mContentSource; + std::shared_ptr mSource; + + void CreateSource(); + +protected: + std::streamsize GetOffsetOf(const RIFFBase* child) const override; + +public: + RIFFChunk(RIFFBase* parent, std::uint32_t chunkId, std::shared_ptr contentSource); + RIFFChunk(RIFFBase* parent, std::uint32_t chunkId); + + std::streamsize GetSize() const override; + SourceBase& GetSource() override; + std::shared_ptr GetSourceSp() override; + + void SetContentSource(std::shared_ptr contentSource); +}; + +#endif diff --git a/RIFF/RIFFList.cpp b/RIFF/RIFFList.cpp new file mode 100644 index 0000000..8874906 --- /dev/null +++ b/RIFF/RIFFList.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "RIFFList.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/MemorySource.hpp" +#include "../Source/SourceBase.hpp" + + +std::streamsize RIFFList::GetOffsetOf(const RIFFBase* child) const { + std::streamsize offset = sizeof(Header); + auto itrChildren = mChildren.cbegin(); + while (itrChildren->get() != child) { + offset += (*itrChildren)->GetSize(); + itrChildren++; + } + return offset; +} + + +RIFFList::RIFFList(RIFFBase* parent, std::uint32_t listId, std::uint32_t chunkId) : + RIFFBase(parent), + mListId(listId), + mChunkId(chunkId), + mHeader{ + listId, + 0, + chunkId, + }, + mSource() +{} + + +std::streamsize RIFFList::GetSize() const { + std::streamsize size = 0; + for (const auto& child : mChildren) { + size += child->GetSize(); + } + return size + sizeof(Header); +} + + +SourceBase& RIFFList::GetSource() { + if (!mSource) { + throw std::runtime_error("LIST: call CreateSource before GetSource"); + } + return *mSource; +} + + +std::shared_ptr RIFFList::GetSourceSp() { + if (!mSource) { + throw std::runtime_error("LIST: call CreateSource before GetSourceSp"); + } + return mSource; +} + + +void RIFFList::CreateSource() { + std::streamsize childrenSize = 0; + for (const auto& child : mChildren) { + child->CreateSource(); + childrenSize += child->GetSize(); + } + if (childrenSize + 4 > std::numeric_limits::max()) { + throw std::runtime_error("LIST: children too large"); + } + mHeader.size = static_cast(childrenSize + 4); + + std::vector> sources; + sources.reserve(mChildren.size() + 1); + sources.emplace_back(std::make_shared(reinterpret_cast(&mHeader), sizeof(mHeader))); + for (const auto& child : mChildren) { + sources.emplace_back(child->GetSourceSp()); + } + mSource = std::make_shared(sources); +} + + +std::size_t RIFFList::CountChildren() const { + return mChildren.size(); +} + + +RIFFBase* RIFFList::GetChild(std::size_t index) { + return mChildren[index].get(); +} + + +const RIFFBase* RIFFList::GetChild(std::size_t index) const { + return mChildren[index].get(); +} + + +void RIFFList::AddChild(std::shared_ptr child) { + mChildren.push_back(child); +} diff --git a/RIFF/RIFFList.hpp b/RIFF/RIFFList.hpp new file mode 100644 index 0000000..1918108 --- /dev/null +++ b/RIFF/RIFFList.hpp @@ -0,0 +1,45 @@ +#ifndef ML_RIFFLIST_HPP +#define ML_RIFFLIST_HPP + +#include +#include +#include +#include +#include + +#include "RIFFBase.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/SourceBase.hpp" + + +class RIFFList : public RIFFBase { + struct Header { + std::uint32_t listId; + std::uint32_t size; + std::uint32_t chunkId; + }; + + std::vector> mChildren; + std::uint32_t mListId; + std::uint32_t mChunkId; + Header mHeader; + std::shared_ptr mSource; + +protected: + std::streamsize GetOffsetOf(const RIFFBase* child) const override; + +public: + RIFFList(RIFFBase* parent, std::uint32_t listId, std::uint32_t chunkId); + + std::streamsize GetSize() const override; + SourceBase& GetSource() override; + std::shared_ptr GetSourceSp() override; + void CreateSource() override; + + std::size_t CountChildren() const; + RIFFBase* GetChild(std::size_t index); + const RIFFBase* GetChild(std::size_t index) const; + void AddChild(std::shared_ptr child); +}; + +#endif diff --git a/RIFF/RIFFRoot.cpp b/RIFF/RIFFRoot.cpp new file mode 100644 index 0000000..d6f871e --- /dev/null +++ b/RIFF/RIFFRoot.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "RIFFRoot.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/SourceBase.hpp" + + +std::streamsize RIFFRoot::GetOffsetOf(const RIFFBase* child) const { + std::streamsize offset = 0; + auto itrChildren = mChildren.cbegin(); + while (itrChildren->get() != child) { + offset += (*itrChildren)->GetSize(); + itrChildren++; + } + return offset; +} + + +RIFFRoot::RIFFRoot() : + RIFFBase(nullptr), + mSource() +{} + + +std::streamsize RIFFRoot::GetOffset() const { + return 0; +} + + +std::streamsize RIFFRoot::GetSize() const { + std::streamsize size = 0; + for (const auto& child : mChildren) { + size += child->GetSize(); + } + return size; +} + + +SourceBase& RIFFRoot::GetSource() { + if (!mSource) { + throw std::runtime_error("ROOT: call CreateSource before GetSource"); + } + return *mSource; +} + + +std::shared_ptr RIFFRoot::GetSourceSp() { + if (!mSource) { + throw std::runtime_error("ROOT: call CreateSource before GetSourceSp"); + } + return mSource; +} + + +void RIFFRoot::CreateSource() { + std::vector> sources; + sources.reserve(mChildren.size()); + for (const auto& child : mChildren) { + child->CreateSource(); + sources.emplace_back(child->GetSourceSp()); + } + mSource = std::make_shared(sources); +} + + +std::size_t RIFFRoot::CountChildren() const { + return mChildren.size(); +} + + +RIFFBase* RIFFRoot::GetChild(std::size_t index) { + return mChildren[index].get(); +} + + +const RIFFBase* RIFFRoot::GetChild(std::size_t index) const { + return mChildren[index].get(); +} + + +void RIFFRoot::AddChild(std::shared_ptr child) { + mChildren.push_back(child); +} diff --git a/RIFF/RIFFRoot.hpp b/RIFF/RIFFRoot.hpp new file mode 100644 index 0000000..2c767ca --- /dev/null +++ b/RIFF/RIFFRoot.hpp @@ -0,0 +1,37 @@ +#ifndef ML_RIFFROOT_HPP +#define ML_RIFFROOT_HPP + +#include +#include +#include +#include +#include + +#include "RIFFBase.hpp" +#include "../Source/ConcatenatedSource.hpp" +#include "../Source/SourceBase.hpp" + + +class RIFFRoot : public RIFFBase { + std::vector> mChildren; + std::shared_ptr mSource; + +protected: + std::streamsize GetOffsetOf(const RIFFBase* child) const override; + +public: + RIFFRoot(); + + std::streamsize GetOffset() const override; + std::streamsize GetSize() const override; + SourceBase& GetSource() override; + std::shared_ptr GetSourceSp() override; + void CreateSource() override; + + std::size_t CountChildren() const; + RIFFBase* GetChild(std::size_t index); + const RIFFBase* GetChild(std::size_t index) const; + void AddChild(std::shared_ptr child); +}; + +#endif diff --git a/Source/CachedSource.cpp b/Source/CachedSource.cpp new file mode 100644 index 0000000..41c7885 --- /dev/null +++ b/Source/CachedSource.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include + +#include "CachedSource.hpp" + + +CachedSource::CachedSource(CacheStorage& cacheStorage, std::shared_ptr source) : + mPtrCacheStorage(&cacheStorage), + mSource(source), + mSize(static_cast(mSource->GetSize())), + mCacheId(0) +{} + + +std::streamsize CachedSource::GetSize() const { + return mSize; +} + + +void CachedSource::Read(std::uint8_t* data, std::size_t size, std::streamsize offset) { + const auto ptr = mPtrCacheStorage->Get(mCacheId); + if (ptr) { + std::memcpy(data, ptr->data.get() + offset, size); + //std::wcerr << L"cache hit" << std::endl; + return; + } + auto sourceData = std::make_unique(mSize); + mSource->Read(sourceData.get(), mSize, 0); + std::memcpy(data, sourceData.get() + offset, size); + mCacheId = mPtrCacheStorage->Add(std::move(sourceData), mSize); + //std::wcerr << L"cache miss" << std::endl; + return; +} diff --git a/Source/CachedSource.hpp b/Source/CachedSource.hpp new file mode 100644 index 0000000..9bb18f1 --- /dev/null +++ b/Source/CachedSource.hpp @@ -0,0 +1,26 @@ +#ifndef ML_CACHEDSOURCE_HPP +#define ML_CACHEDSOURCE_HPP + +#include +#include +#include +#include + +#include "SourceBase.hpp" +#include "../CacheStorage.hpp" + + +class CachedSource : public SourceBase { + CacheStorage* mPtrCacheStorage; + std::shared_ptr mSource; + std::size_t mSize; + CacheStorage::Id mCacheId; + +public: + CachedSource(CacheStorage& cacheStorage, std::shared_ptr source); + + std::streamsize GetSize() const override; + void Read(std::uint8_t* data, std::size_t size, std::streamsize offset) override; +}; + +#endif diff --git a/Source/ConcatenatedSource.cpp b/Source/ConcatenatedSource.cpp new file mode 100644 index 0000000..c8a7e26 --- /dev/null +++ b/Source/ConcatenatedSource.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +#include "ConcatenatedSource.hpp" +#include "SourceBase.hpp" + + +void ConcatenatedSource::ConstructBinarySearchTree(std::size_t treeIndex, const PartialSource* sources, std::size_t sourceOffset, std::size_t numSources) { + // treeIndex starts at 1 + + if (numSources == 0) { + return; + } + + // the middle element (use the right one if the size is even) + const auto middleIndex = numSources / 2; + + const auto absoluteMiddleIndex = sourceOffset + middleIndex; + mOffsetBinarySearchTree[treeIndex] = OffsetInfo{ + sources[absoluteMiddleIndex].offset, + sources[absoluteMiddleIndex].size, + absoluteMiddleIndex, + }; + + // left subtree + ConstructBinarySearchTree(treeIndex * 2, sources, sourceOffset, middleIndex); + // right subtree + ConstructBinarySearchTree(treeIndex * 2 + 1, sources, sourceOffset + middleIndex + 1, numSources - middleIndex - 1); +} + + +std::size_t ConcatenatedSource::GetIndexFromOffset(std::streamsize offset) const { + assert(0 <= offset && offset < mTotalSize); + + std::size_t treeIndex = 1; + while (true) { + assert(treeIndex < mOffsetBinarySearchTree.size()); + + const auto& node = mOffsetBinarySearchTree[treeIndex]; + assert(node.offset != UnusedOffset); + + if (node.offset <= offset && offset < node.offset + node.size) { + // found + return node.index; + } + treeIndex = node.offset > offset ? treeIndex * 2 : treeIndex * 2 + 1; + } +} + + +std::streamsize ConcatenatedSource::GetSize() const { + return mTotalSize; +} + + +void ConcatenatedSource::Read(std::uint8_t* data, std::size_t size, std::streamsize offset) { + const auto firstIndex = GetIndexFromOffset(offset); + const std::streamsize offsetEnd = offset + size; + std::streamsize currentOffset = offset; + std::size_t dataOffset = 0; + auto itrPartialSource = mPartialSources.cbegin() + firstIndex; + while (currentOffset != offsetEnd) { + const auto streamOffset = currentOffset - itrPartialSource->offset; + const auto readSize = static_cast(std::min(itrPartialSource->size - streamOffset, offsetEnd - currentOffset)); + itrPartialSource->source->Read(data + dataOffset, readSize, streamOffset); + itrPartialSource++; + currentOffset += readSize; + dataOffset += readSize; + } +} diff --git a/Source/ConcatenatedSource.hpp b/Source/ConcatenatedSource.hpp new file mode 100644 index 0000000..a147f49 --- /dev/null +++ b/Source/ConcatenatedSource.hpp @@ -0,0 +1,89 @@ +#ifndef ML_CONCATENATEDSOURCE_HPP +#define ML_CONCATENATEDSOURCE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SourceBase.hpp" + + +class ConcatenatedSource : public SourceBase { + struct PartialSource { + std::streamsize offset; + std::streamsize size; + std::shared_ptr source; + }; + + struct OffsetInfo { + std::streamsize offset; + std::streamsize size; + std::size_t index; + }; + + + static constexpr std::streamsize UnusedOffset = -1; + + static constexpr OffsetInfo UnusedOffsetInfo{ + UnusedOffset, + 0, + std::numeric_limits::max(), + }; + + + std::vector mOffsetBinarySearchTree; // represent binary tree in the way of heap + std::vector mPartialSources; + std::streamsize mTotalSize; + + void ConstructBinarySearchTree(std::size_t treeIndex, const PartialSource* sources, std::size_t sourceOffset, std::size_t numSources); + std::size_t GetIndexFromOffset(std::streamsize offset) const; + +public: + template + ConcatenatedSource(const T& sources) : + mOffsetBinarySearchTree(), + mPartialSources(), + mTotalSize(0) + { + const auto numSources = std::size(sources); + + // 2^ceil(lb(N)) - 1 + // for convenience of heap index calculation, 0 is not used, so 1 is added + std::size_t treeSize = 1; + while (treeSize - 1 < numSources) { + treeSize *= 2; + } + + mPartialSources.reserve(numSources); + + std::streamoff offset = 0; + for (const auto& source : sources) { + const auto size = source->GetSize(); + assert(size >= 0); + if (size == 0) { + continue; + } + mPartialSources.push_back({ + offset, + size, + source, + }); + offset += size; + } + mTotalSize = offset; + + // construct binary search tree + mOffsetBinarySearchTree.resize(treeSize, UnusedOffsetInfo); + ConstructBinarySearchTree(1, mPartialSources.data(), 0, mPartialSources.size()); + } + + std::streamsize GetSize() const override; + void Read(std::uint8_t* data, std::size_t size, std::streamsize offset) override; +}; + +#endif diff --git a/Source/MemorySource.cpp b/Source/MemorySource.cpp new file mode 100644 index 0000000..cd6e8f9 --- /dev/null +++ b/Source/MemorySource.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "MemorySource.hpp" + + +MemorySource::MemorySource(const std::uint8_t* data, std::size_t size) : + mSize(size), + mData(mSize ? std::make_unique(mSize) : nullptr) +{ + if (mSize) { + std::memcpy(mData.get(), data, mSize); + } +} + + +MemorySource::MemorySource(std::size_t size) : + mSize(size), + mData(mSize ? std::make_unique(mSize) : nullptr) +{ + if (mSize) { + std::memset(mData.get(), 0, mSize); + } +} + +MemorySource::MemorySource(std::unique_ptr&& data, std::size_t size) : + mSize(size), + mData(std::move(data)) +{} + + +MemorySource::MemorySource(SourceBase& source) : + mSize(static_cast(source.GetSize())), + mData(mSize ? std::make_unique(mSize) : nullptr) +{ + if (mSize) { + source.Read(mData.get(), mSize, 0); + } +} + + +std::streamsize MemorySource::GetSize() const { + return static_cast(mSize); +} + + +void MemorySource::Read(std::uint8_t* data, std::size_t size, std::streamsize offset) { + if (!size) { + return; + } + + assert(offset >= 0); + assert(offset + size <= mSize); + + std::memcpy(data, mData.get() + offset, size); +} + + +std::uint8_t* MemorySource::GetData() { + return mData.get(); +} diff --git a/Source/MemorySource.hpp b/Source/MemorySource.hpp new file mode 100644 index 0000000..4b6e951 --- /dev/null +++ b/Source/MemorySource.hpp @@ -0,0 +1,28 @@ +#ifndef ML_MEMORYSOURCE_HPP +#define ML_MEMORYSOURCE_HPP + +#include +#include +#include +#include + +#include "SourceBase.hpp" + + +class MemorySource : public SourceBase { + std::size_t mSize; + std::unique_ptr mData; + +public: + MemorySource(const std::uint8_t* data, std::size_t size); + MemorySource(std::size_t size); + MemorySource(std::unique_ptr&& data, std::size_t size); + MemorySource(SourceBase& source); + + std::streamsize GetSize() const override; + void Read(std::uint8_t* data, std::size_t size, std::streamsize offset) override; + + std::uint8_t* GetData(); +}; + +#endif diff --git a/Source/SourceBase.hpp b/Source/SourceBase.hpp new file mode 100644 index 0000000..f4dcc5d --- /dev/null +++ b/Source/SourceBase.hpp @@ -0,0 +1,17 @@ +#ifndef ML_SOURCEBASE_HPP +#define ML_SOURCEBASE_HPP + +#include +#include +#include + + +class SourceBase { +public: + virtual ~SourceBase() = default; + + virtual std::streamsize GetSize() const = 0; + virtual void Read(std::uint8_t* data, std::size_t size, std::streamsize offset) = 0; +}; + +#endif diff --git a/Startup.cpp b/Startup.cpp new file mode 100644 index 0000000..d5e0d5d --- /dev/null +++ b/Startup.cpp @@ -0,0 +1,20 @@ +#include +#include + + +int xwmain(int argc, wchar_t* argv[]); + + +int wmain(int argc, wchar_t* argv[]) { + int ret = 0; + + SakuraGL::Initialize(); + + SSystem::SetMemoryAllocationMode(SSystem::mallocModeGlobal); + + ret = xwmain(argc, argv); + + SakuraGL::Finalize(); + + return ret; +} diff --git a/_MultiMon.cpp b/_MultiMon.cpp new file mode 100644 index 0000000..ac0820d --- /dev/null +++ b/_MultiMon.cpp @@ -0,0 +1,6 @@ +#pragma warning(disable:4996) + +#include + +#define COMPILE_MULTIMON_STUBS +#include