From 494a39e76a5a2a40712f13e412bf1861927c6b23 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 29 May 2016 08:22:20 -1000 Subject: [PATCH] Support for absolute PROJ offsets introduced in Battle for Naboo --- AudioUnit/CMakeLists.txt | 97 ++++++ CMakeLists.txt | 102 +----- VST/CMakeLists.txt | 0 include/amuse/AudioGroup.hpp | 4 +- include/amuse/AudioGroupData.hpp | 21 +- include/amuse/AudioGroupProject.hpp | 8 +- include/amuse/AudioGroupSampleDirectory.hpp | 5 +- include/amuse/ContainerRegistry.hpp | 2 + lib/AudioGroup.cpp | 12 +- lib/AudioGroupData.cpp | 3 +- lib/AudioGroupProject.cpp | 60 ++-- lib/AudioGroupSampleDirectory.cpp | 103 +++++- lib/ContainerRegistry.cpp | 327 +++++++++++++++++++- lib/Engine.cpp | 4 +- 14 files changed, 579 insertions(+), 169 deletions(-) create mode 100644 AudioUnit/CMakeLists.txt create mode 100644 VST/CMakeLists.txt diff --git a/AudioUnit/CMakeLists.txt b/AudioUnit/CMakeLists.txt new file mode 100644 index 0000000..13b4e71 --- /dev/null +++ b/AudioUnit/CMakeLists.txt @@ -0,0 +1,97 @@ +if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.10)) + set(APPLE_DEV_ID "" CACHE STRING "Mac Developer ID string 'Mac Developer: John Smith (XXXXXXXXXX)'") + set(APPLE_TEAM_ID "" CACHE STRING "Team ID string provisioned within Xcode / Apple's portal") + find_library(AVFOUNDATION_LIBRARY AVFoundation) + find_library(AUDIOUNIT_LIBRARY AudioUnit) + find_library(COREAUDIOKIT_LIBRARY CoreAudioKit) + if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND)) + set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}") + + # Search for provision profile to make AudioUnit extension on OS X + unset(PROV_PROFILE) + file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile") + foreach(FILE ${PROV_FILES}) + file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID}) + if(NAME) + set(PROV_PROFILE "${FILE}") + break() + endif() + endforeach() + + if(EXISTS "${PROV_PROFILE}") + + # Extension App + add_executable(amuse-au MACOSX_BUNDLE AudioUnitBackend.hpp AudioUnitBackend.mm + AudioUnitViewController.hpp AudioUnitViewController.mm + AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm) + + set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse.AudioUnit") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in + ${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements) + + target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY} + ${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core) + set_target_properties(amuse-au PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ExtensionInfo.plist" + BUNDLE_EXTENSION "appex" BUNDLE TRUE + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}" + LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension") + + + # Containing App + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib + COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts + --target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile + ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib + ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib + ) + add_executable(amuse-au-container MACOSX_BUNDLE AmuseContainingApp.mm + AudioUnitBackend.hpp AudioUnitBackend.mm + AudioUnitViewController.hpp AudioUnitViewController.mm + AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm + AmuseContainerMainMenu.nib) + set_source_files_properties(AudioUnitBackend.mm AudioUnitViewController.mm + AmuseContainingApp.mm AudioGroupFilePresenter.mm + PROPERTIES COMPILE_FLAGS -fobjc-arc) + target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY} + ${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core) + + set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in + ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements) + + set_target_properties(amuse-au-container PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ContainerInfo.plist" + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}") + + add_custom_command(TARGET amuse-au POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$/../embedded.provisionprofile" + COMMAND ${CMAKE_COMMAND} -E remove_directory "$/../PlugIns/amuse-au.appex" + COMMAND ${CMAKE_COMMAND} -E copy_directory "$/../.." + "$/../PlugIns/amuse-au.appex" + COMMAND codesign --force --sign + ${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements" + "$/../PlugIns/amuse-au.appex" + COMMAND codesign --force --sign ${APPLE_DEV_ID} + "$/../.." + VERBATIM + ) + + add_custom_command(TARGET amuse-au-container POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib" + "$/../Resources/AmuseContainerMainMenu.nib" + COMMAND codesign --force --sign + ${APPLE_DEV_ID} "$/../.." + VERBATIM + ) + + else() + message(WARNING "Unable to find developer provision profile; skipping Amuse-AU") + endif() + endif() +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 0663f22..38cc63a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,105 +75,13 @@ add_library(amuse ${EXTRAS}) if(TARGET boo) - # AudioUnit Target - if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.10)) - set(APPLE_DEV_ID "" CACHE STRING "Mac Developer ID string 'Mac Developer: John Smith (XXXXXXXXXX)'") - set(APPLE_TEAM_ID "" CACHE STRING "Team ID string provisioned within Xcode / Apple's portal") - find_library(AVFOUNDATION_LIBRARY AVFoundation) - find_library(AUDIOUNIT_LIBRARY AudioUnit) - find_library(COREAUDIOKIT_LIBRARY CoreAudioKit) - if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND)) - set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}") + # AudioUnit Target (OS X only) + add_subdirectory(AudioUnit) - # Search for provision profile to make AudioUnit extension on OS X - unset(PROV_PROFILE) - file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile") - foreach(FILE ${PROV_FILES}) - file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID}) - if(NAME) - set(PROV_PROFILE "${FILE}") - break() - endif() - endforeach() - - if(EXISTS "${PROV_PROFILE}") - - # Extension App - add_executable(amuse-au MACOSX_BUNDLE AudioUnit/AudioUnitBackend.hpp AudioUnit/AudioUnitBackend.mm - AudioUnit/AudioUnitViewController.hpp AudioUnit/AudioUnitViewController.mm - AudioUnit/AudioGroupFilePresenter.hpp AudioUnit/AudioGroupFilePresenter.mm) - - set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse.AudioUnit") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/AmuseExtension.entitlements.in - ${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements) - - target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY} - ${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core) - set_target_properties(amuse-au PROPERTIES - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/ExtensionInfo.plist" - BUNDLE_EXTENSION "appex" BUNDLE TRUE - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements" - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}" - LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension") - - - # Containing App - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib - COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts - --target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile - ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib - ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/AmuseContainerMainMenu.xib - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/AmuseContainerMainMenu.xib - ) - add_executable(amuse-au-container MACOSX_BUNDLE AudioUnit/AmuseContainingApp.mm - AudioUnit/AudioUnitBackend.hpp AudioUnit/AudioUnitBackend.mm - AudioUnit/AudioUnitViewController.hpp AudioUnit/AudioUnitViewController.mm - AudioUnit/AudioGroupFilePresenter.hpp AudioUnit/AudioGroupFilePresenter.mm - AmuseContainerMainMenu.nib) - set_source_files_properties(AudioUnit/AudioUnitBackend.mm AudioUnit/AudioUnitViewController.mm - AudioUnit/AmuseContainingApp.mm AudioUnit/AudioGroupFilePresenter.mm - PROPERTIES COMPILE_FLAGS -fobjc-arc) - target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY} - ${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core) - - set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/AmuseContainer.entitlements.in - ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements) - - set_target_properties(amuse-au-container PROPERTIES - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/AudioUnit/ContainerInfo.plist" - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements" - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}") - - add_custom_command(TARGET amuse-au POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$/../embedded.provisionprofile" - COMMAND ${CMAKE_COMMAND} -E remove_directory "$/../PlugIns/amuse-au.appex" - COMMAND ${CMAKE_COMMAND} -E copy_directory "$/../.." - "$/../PlugIns/amuse-au.appex" - COMMAND codesign --force --sign - ${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements" - "$/../PlugIns/amuse-au.appex" - COMMAND codesign --force --sign ${APPLE_DEV_ID} - "$/../.." - VERBATIM - ) - - add_custom_command(TARGET amuse-au-container POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib" - "$/../Resources/AmuseContainerMainMenu.nib" - COMMAND codesign --force --sign - ${APPLE_DEV_ID} "$/../.." - VERBATIM - ) - - else() - message(WARNING "Unable to find developer provision profile; skipping Amuse-AU") - endif() - endif() - endif() + # VST Target + add_subdirectory(VST) + # Multi-platform CLI tool add_executable(amuseplay WIN32 driver/main.cpp) target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES}) endif() diff --git a/VST/CMakeLists.txt b/VST/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp index 62b0839..ac79f78 100644 --- a/include/amuse/AudioGroup.hpp +++ b/include/amuse/AudioGroup.hpp @@ -24,8 +24,8 @@ class AudioGroup public: operator bool() const {return m_valid;} AudioGroup(const AudioGroupData& data, GCNDataTag); - AudioGroup(const AudioGroupData& data, N64DataTag); - AudioGroup(const AudioGroupData& data, PCDataTag); + AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag); + AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag); const Sample* getSample(int sfxId) const; const unsigned char* getSampleData(uint32_t offset) const; diff --git a/include/amuse/AudioGroupData.hpp b/include/amuse/AudioGroupData.hpp index 4b9f441..4328d70 100644 --- a/include/amuse/AudioGroupData.hpp +++ b/include/amuse/AudioGroupData.hpp @@ -16,20 +16,26 @@ protected: unsigned char* m_sdir; unsigned char* m_samp; DataFormat m_fmt; + bool m_absOffs; AudioGroupData(unsigned char* proj, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, DataFormat fmt) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(fmt) {} + unsigned char* sdir, unsigned char* samp, + DataFormat fmt, bool absOffs) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + m_fmt(fmt), m_absOffs(absOffs) {} public: AudioGroupData(unsigned char* proj, unsigned char* pool, unsigned char* sdir, unsigned char* samp, GCNDataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::GCN) {} + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + m_fmt(DataFormat::GCN), m_absOffs(true) {} AudioGroupData(unsigned char* proj, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, N64DataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::N64) {} + unsigned char* sdir, unsigned char* samp, bool absOffs, N64DataTag) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + m_fmt(DataFormat::N64), m_absOffs(absOffs) {} AudioGroupData(unsigned char* proj, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, PCDataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_fmt(DataFormat::PC) {} + unsigned char* sdir, unsigned char* samp, bool absOffs, PCDataTag) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + m_fmt(DataFormat::PC), m_absOffs(absOffs) {} const unsigned char* getProj() const {return m_proj;} const unsigned char* getPool() const {return m_pool;} @@ -42,6 +48,7 @@ public: } DataFormat getDataFormat() const {return m_fmt;} + bool getAbsoluteProjOffsets() const {return m_absOffs;} }; /** A buffer-owning version of AudioGroupData */ diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index 3b719fa..686e171 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -76,13 +76,13 @@ class AudioGroupProject std::unique_ptr m_convNormalPages; std::unique_ptr m_convDrumPages; std::unique_ptr[]> m_convMidiSetups; - void _allocateConvBuffers(const unsigned char* data, N64DataTag); - void _allocateConvBuffers(const unsigned char* data, PCDataTag); + void _allocateConvBuffers(const unsigned char* data, bool absOffs, N64DataTag); + void _allocateConvBuffers(const unsigned char* data, bool absOffs, PCDataTag); public: AudioGroupProject(const unsigned char* data, GCNDataTag); - AudioGroupProject(const unsigned char* data, N64DataTag); - AudioGroupProject(const unsigned char* data, PCDataTag); + AudioGroupProject(const unsigned char* data, bool absOffs, N64DataTag); + AudioGroupProject(const unsigned char* data, bool absOffs, PCDataTag); static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); const SongGroupIndex* getSongGroupIndex(int groupId) const; diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 1087bb8..7da4978 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -48,8 +48,9 @@ private: std::unordered_map> m_entries; public: AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag); - AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, N64DataTag); - AudioGroupSampleDirectory(const unsigned char* data, PCDataTag); + AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, + bool absOffs, N64DataTag); + AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag); }; } diff --git a/include/amuse/ContainerRegistry.hpp b/include/amuse/ContainerRegistry.hpp index 364e0ea..ca6b4fa 100644 --- a/include/amuse/ContainerRegistry.hpp +++ b/include/amuse/ContainerRegistry.hpp @@ -19,6 +19,8 @@ public: MetroidPrime2, RogueSquadronPC, RogueSquadronN64, + BattleForNabooPC, + BattleForNabooN64, RogueSquadron2, RogueSquadron3 }; diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index 7381b74..14da9cd 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -12,18 +12,18 @@ AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) m_fmt(DataFormat::GCN) {} -AudioGroup::AudioGroup(const AudioGroupData& data, N64DataTag) -: m_proj(data.getProj(), N64DataTag{}), +AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) +: m_proj(data.getProj(), absOffs, N64DataTag{}), m_pool(data.getPool()), - m_sdir(data.getSdir(), data.getSamp(), N64DataTag{}), + m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{}), m_samp(data.getSamp()), m_fmt(DataFormat::N64) {} -AudioGroup::AudioGroup(const AudioGroupData& data, PCDataTag) -: m_proj(data.getProj(), PCDataTag{}), +AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag) +: m_proj(data.getProj(), absOffs, PCDataTag{}), m_pool(data.getPool(), PCDataTag{}), - m_sdir(data.getSdir(), PCDataTag{}), + m_sdir(data.getSdir(), absOffs, PCDataTag{}), m_samp(data.getSamp()), m_fmt(DataFormat::PC) {} diff --git a/lib/AudioGroupData.cpp b/lib/AudioGroupData.cpp index 9a29713..a68a241 100644 --- a/lib/AudioGroupData.cpp +++ b/lib/AudioGroupData.cpp @@ -15,7 +15,7 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData() } IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) -: AudioGroupData(other.m_proj, other.m_pool, other.m_sdir, other.m_samp, other.m_fmt) +: AudioGroupData(other.m_proj, other.m_pool, other.m_sdir, other.m_samp, other.m_fmt, other.m_absOffs) { m_owns = other.m_owns; other.m_owns = false; @@ -39,6 +39,7 @@ IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupD m_sdir = other.m_sdir; m_samp = other.m_samp; m_fmt = other.m_fmt; + m_absOffs = other.m_absOffs; return *this; } diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 733f024..e91aa95 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -152,7 +152,7 @@ struct MusyX1MIDISetup } }; -void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataTag) +void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool absOffs, N64DataTag) { size_t normPageCount = 0; size_t drumPageCount = 0; @@ -161,7 +161,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataT const GroupHeader* group = reinterpret_cast(data); while (group->groupEndOff != 0xffffffff) { - const unsigned char* subData = data + 8; + const unsigned char* subData = absOffs ? data : data + 8; GroupHeader header = *group; header.swapBig(); @@ -194,8 +194,13 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataT } } - data += header.groupEndOff; - group = reinterpret_cast(data); + if (absOffs) + group = reinterpret_cast(data + header.groupEndOff); + else + { + data += header.groupEndOff; + group = reinterpret_cast(data); + } } if (normPageCount) @@ -206,9 +211,9 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataT m_convMidiSetups.reset(new std::array[midiSetupCount]); } -AudioGroupProject::AudioGroupProject(const unsigned char* data, N64DataTag) +AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N64DataTag) { - _allocateConvBuffers(data, N64DataTag{}); + _allocateConvBuffers(data, absOffs, N64DataTag{}); SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get(); SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get(); std::array* midiSetupsBuf = m_convMidiSetups.get(); @@ -216,7 +221,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, N64DataTag) const GroupHeader* group = reinterpret_cast(data); while (group->groupEndOff != 0xffffffff) { - const unsigned char* subData = data + 8; + const unsigned char* subData = absOffs ? data : data + 8; GroupHeader header = *group; header.swapBig(); @@ -289,12 +294,17 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, N64DataTag) bIdx->m_layersIndex = reinterpret_cast(subData + header.layerIdsOff); } - data += header.groupEndOff; - group = reinterpret_cast(data); + if (absOffs) + group = reinterpret_cast(data + header.groupEndOff); + else + { + data += header.groupEndOff; + group = reinterpret_cast(data); + } } } -void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTag) +void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool absOffs, PCDataTag) { size_t normPageCount = 0; size_t drumPageCount = 0; @@ -303,7 +313,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTa const GroupHeader* group = reinterpret_cast(data); while (group->groupEndOff != 0xffffffff) { - const unsigned char* subData = data + 8; + const unsigned char* subData = absOffs ? data : data + 8; if (group->type == GroupType::Song) { @@ -334,8 +344,13 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTa } } - data += group->groupEndOff; - group = reinterpret_cast(data); + if (absOffs) + group = reinterpret_cast(data + group->groupEndOff); + else + { + data += group->groupEndOff; + group = reinterpret_cast(data); + } } if (normPageCount) @@ -346,9 +361,9 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTa m_convMidiSetups.reset(new std::array[midiSetupCount]); } -AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) +AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, PCDataTag) { - _allocateConvBuffers(data, PCDataTag{}); + _allocateConvBuffers(data, absOffs, PCDataTag{}); SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get(); SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get(); std::array* midiSetupsBuf = m_convMidiSetups.get(); @@ -356,7 +371,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) const GroupHeader* group = reinterpret_cast(data); while (group->groupEndOff != 0xffffffff) { - const unsigned char* subData = data + 8; + const unsigned char* subData = absOffs ? data : data + 8; AudioGroupIndex* bIdx = nullptr; @@ -427,8 +442,13 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, PCDataTag) bIdx->m_layersIndex = reinterpret_cast(subData + group->layerIdsOff); } - data += group->groupEndOff; - group = reinterpret_cast(data); + if (absOffs) + group = reinterpret_cast(data + group->groupEndOff); + else + { + data += group->groupEndOff; + group = reinterpret_cast(data); + } } } @@ -439,9 +459,9 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupDat case DataFormat::GCN: return AudioGroupProject(data.getProj(), GCNDataTag{}); case DataFormat::N64: - return AudioGroupProject(data.getProj(), N64DataTag{}); + return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), N64DataTag{}); case DataFormat::PC: - return AudioGroupProject(data.getProj(), PCDataTag{}); + return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), PCDataTag{}); } } diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index 468561c..e0c9d33 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -94,36 +94,109 @@ struct MusyX1SdirEntry } }; +struct MusyX1AbsSdirEntry +{ + uint16_t m_sfxId; + uint32_t m_sampleOff; + uint32_t m_unk; + uint32_t m_pitchSampleRate; + uint32_t m_numSamples; + uint32_t m_loopStartSample; + uint32_t m_loopLengthSamples; + + void swapBig() + { + m_sfxId = SBig(m_sfxId); + m_sampleOff = SBig(m_sampleOff); + m_unk = SBig(m_unk); + m_pitchSampleRate = SBig(m_pitchSampleRate); + m_numSamples = SBig(m_numSamples); + m_loopStartSample = SBig(m_loopStartSample); + m_loopLengthSamples = SBig(m_loopLengthSamples); + } + + void setIntoMusyX2(AudioGroupSampleDirectory::Entry& ent) const + { + ent.m_sfxId = m_sfxId; + ent.m_sampleOff = m_sampleOff; + ent.m_unk = m_unk; + ent.m_pitch = m_pitchSampleRate >> 24; + ent.m_sampleRate = m_pitchSampleRate & 0xffff; + ent.m_numSamples = m_numSamples; + ent.m_loopStartSample = m_loopStartSample; + ent.m_loopLengthSamples = m_loopLengthSamples; + ent.m_adpcmParmOffset = 0; + } +}; + AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, - const unsigned char* sampData, N64DataTag) + const unsigned char* sampData, + bool absOffs, N64DataTag) { const unsigned char* cur = data; - while (*reinterpret_cast(cur) != 0xffffffff) + + if (absOffs) { - MusyX1SdirEntry ent = *reinterpret_cast(cur); - ent.swapBig(); + while (*reinterpret_cast(cur) != 0xffffffff) + { + MusyX1AbsSdirEntry ent = *reinterpret_cast(cur); + ent.swapBig(); - std::pair& store = m_entries[ent.m_sfxId]; - ent.setIntoMusyX2(store.first); + std::pair& store = m_entries[ent.m_sfxId]; + ent.setIntoMusyX2(store.first); - memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); - store.second.swapBigVADPCM(); + memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); + store.second.swapBigVADPCM(); - cur += 24; + cur += 28; + } + } + else + { + while (*reinterpret_cast(cur) != 0xffffffff) + { + MusyX1SdirEntry ent = *reinterpret_cast(cur); + ent.swapBig(); + + std::pair& store = m_entries[ent.m_sfxId]; + ent.setIntoMusyX2(store.first); + + memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); + store.second.swapBigVADPCM(); + + cur += 24; + } } } -AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, PCDataTag) +AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, + bool absOffs, PCDataTag) { const unsigned char* cur = data; - while (*reinterpret_cast(cur) != 0xffffffff) + + if (absOffs) { - const MusyX1SdirEntry* ent = reinterpret_cast(cur); + while (*reinterpret_cast(cur) != 0xffffffff) + { + const MusyX1AbsSdirEntry* ent = reinterpret_cast(cur); - std::pair& store = m_entries[ent->m_sfxId]; - ent->setIntoMusyX2(store.first); + std::pair& store = m_entries[ent->m_sfxId]; + ent->setIntoMusyX2(store.first); - cur += 24; + cur += 28; + } + } + else + { + while (*reinterpret_cast(cur) != 0xffffffff) + { + const MusyX1SdirEntry* ent = reinterpret_cast(cur); + + std::pair& store = m_entries[ent->m_sfxId]; + ent->setIntoMusyX2(store.first); + + cur += 24; + } } } diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index 6a9956a..8b32782 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -51,6 +51,10 @@ const char* ContainerRegistry::TypeToName(Type tp) return "Star Wars: Rogue Squadron (PC)"; case Type::RogueSquadronN64: return "Star Wars: Rogue Squadron (N64)"; + case Type::BattleForNabooPC: + return "Star Wars Episode I: Battle for Naboo (PC)"; + case Type::BattleForNabooN64: + return "Star Wars Episode I: Battle for Naboo (N64)"; case Type::RogueSquadron2: return "Star Wars: Rogue Squadron 2 (GCN)"; case Type::RogueSquadron3: @@ -371,13 +375,13 @@ static std::vector> LoadMP2(FILE fread(pool.get(), 1, poolSz, fp); std::unique_ptr proj(new uint8_t[projSz]); - fread(pool.get(), 1, projSz, fp); + fread(proj.get(), 1, projSz, fp); std::unique_ptr sdir(new uint8_t[sdirSz]); - fread(pool.get(), 1, sdirSz, fp); + fread(sdir.get(), 1, sdirSz, fp); std::unique_ptr samp(new uint8_t[sampSz]); - fread(pool.get(), 1, sampSz, fp); + fread(samp.get(), 1, sampSz, fp); ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), sdir.release(), samp.release(), GCNDataTag{}}); @@ -410,7 +414,14 @@ struct RS1FSTEntry } }; -static void SwapN64Rom(void* data, size_t size) +static void SwapN64Rom16(void* data, size_t size) +{ + uint16_t* words = reinterpret_cast(data); + for (size_t i=0 ; i(data); for (size_t i=0 ; i> LoadRS1PC(FI } ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), PCDataTag{}}); + sdir.release(), samp.release(), + false, PCDataTag{}}); } } @@ -522,11 +534,15 @@ static bool ValidateRS1N64(FILE* fp) fread(data.get(), 1, endPos, fp); if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80) - SwapN64Rom(data.get(), endPos); + SwapN64Rom32(data.get(), endPos); + else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80) + SwapN64Rom16(data.get(), endPos); +#if 0 const uint32_t* gameId = reinterpret_cast(&data[59]); - if (*gameId != 0x4e525345 && *gameId != 0x4553524e) - return false; /* GameId not 'NRSE' */ + if (*gameId != 0x4553524e && *gameId != 0x4a53524e && *gameId != 0x5053524e) + return false; /* GameId not 'NRSE', 'NRSJ', or 'NRSP' */ +#endif const uint8_t* dataSeg = reinterpret_cast(memmem(data.get(), endPos, "dbg_data\0\0\0\0\0\0\0\0", 16)); @@ -571,7 +587,9 @@ static std::vector> LoadRS1N64(F fread(data.get(), 1, endPos, fp); if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80) - SwapN64Rom(data.get(), endPos); + SwapN64Rom32(data.get(), endPos); + else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80) + SwapN64Rom16(data.get(), endPos); const uint8_t* dataSeg = reinterpret_cast(memmem(data.get(), endPos, "dbg_data\0\0\0\0\0\0\0\0", 16)); @@ -656,7 +674,261 @@ static std::vector> LoadRS1N64(F } ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), N64DataTag{}}); + sdir.release(), samp.release(), + false, N64DataTag{}}); + } + + return ret; +} + +static bool ValidateBFNPC(FILE* fp) +{ + size_t endPos = FileLength(fp); + + uint32_t fstOff; + uint32_t fstSz; + if (fread(&fstOff, 1, 4, fp) == 4 && fread(&fstSz, 1, 4, fp) == 4) + { + if (fstOff + fstSz <= endPos) + { + FSeek(fp, fstOff, SEEK_SET); + uint32_t elemCount = fstSz / 32; + std::unique_ptr entries(new RS1FSTEntry[elemCount]); + fread(entries.get(), fstSz, 1, fp); + + uint8_t foundComps = 0; + for (uint32_t i=0 ; i> LoadBFNPC(FILE* fp) +{ + std::vector> ret; + size_t endPos = FileLength(fp); + + uint32_t fstOff; + uint32_t fstSz; + if (fread(&fstOff, 1, 4, fp) == 4 && fread(&fstSz, 1, 4, fp) == 4) + { + if (fstOff + fstSz <= endPos) + { + FSeek(fp, fstOff, SEEK_SET); + uint32_t elemCount = fstSz / 32; + std::unique_ptr entries(new RS1FSTEntry[elemCount]); + fread(entries.get(), fstSz, 1, fp); + + std::unique_ptr proj; + std::unique_ptr pool; + std::unique_ptr sdir; + std::unique_ptr samp; + + for (uint32_t i=0 ; i 32 * 1024 * 1024) + return false; /* N64 ROM definitely won't exceed 32MB */ + + std::unique_ptr data(new uint8_t[endPos]); + fread(data.get(), 1, endPos, fp); + + if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80) + SwapN64Rom32(data.get(), endPos); + else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80) + SwapN64Rom16(data.get(), endPos); + +#if 0 + const uint32_t* gameId = reinterpret_cast(&data[59]); + if (*gameId != 0x4553524e && *gameId != 0x4a53524e && *gameId != 0x5053524e) + return false; /* GameId not 'NRSE', 'NRSJ', or 'NRSP' */ +#endif + + const uint8_t* dataSeg = reinterpret_cast(memmem(data.get(), endPos, + "dbg_data\0\0\0\0\0\0\0\0", 16)); + if (dataSeg) + { + dataSeg += 28; + size_t fstEnd = SBig(*reinterpret_cast(dataSeg)); + dataSeg += 4; + size_t fstOff = SBig(*reinterpret_cast(dataSeg)); + if (endPos <= size_t(dataSeg - data.get()) + fstOff || endPos <= size_t(dataSeg - data.get()) + fstEnd) + return false; + + const RS1FSTEntry* entry = reinterpret_cast(dataSeg + fstOff); + const RS1FSTEntry* lastEnt = reinterpret_cast(dataSeg + fstEnd); + + uint8_t foundComps = 0; + for (; entry != lastEnt ; ++entry) + { + if (!strncmp("proj", entry->name, 16)) + foundComps |= 1; + else if (!strncmp("pool", entry->name, 16)) + foundComps |= 2; + else if (!strncmp("sdir", entry->name, 16)) + foundComps |= 4; + else if (!strncmp("samp", entry->name, 16)) + foundComps |= 8; + } + + if (foundComps == 0xf) + return true; + } + + return false; +} + +static std::vector> LoadBFNN64(FILE* fp) +{ + std::vector> ret; + size_t endPos = FileLength(fp); + + std::unique_ptr data(new uint8_t[endPos]); + fread(data.get(), 1, endPos, fp); + + if ((data[0] & 0x80) != 0x80 && (data[3] & 0x80) == 0x80) + SwapN64Rom32(data.get(), endPos); + else if ((data[0] & 0x80) != 0x80 && (data[1] & 0x80) == 0x80) + SwapN64Rom16(data.get(), endPos); + + const uint8_t* dataSeg = reinterpret_cast(memmem(data.get(), endPos, + "dbg_data\0\0\0\0\0\0\0\0", 16)); + if (dataSeg) + { + dataSeg += 28; + size_t fstEnd = SBig(*reinterpret_cast(dataSeg)); + dataSeg += 4; + size_t fstOff = SBig(*reinterpret_cast(dataSeg)); + if (endPos <= size_t(dataSeg - data.get()) + fstOff || endPos <= size_t(dataSeg - data.get()) + fstEnd) + return ret; + + const RS1FSTEntry* entry = reinterpret_cast(dataSeg + fstOff); + const RS1FSTEntry* lastEnt = reinterpret_cast(dataSeg + fstEnd); + + std::unique_ptr proj; + std::unique_ptr pool; + std::unique_ptr sdir; + std::unique_ptr samp; + + for (; entry != lastEnt ; ++entry) + { + RS1FSTEntry ent = *entry; + ent.swapBig(); + + if (!strncmp("proj", ent.name, 16)) + { + if (ent.compSz == 0xffffffff) + { + proj.reset(new uint8_t[ent.decompSz]); + memcpy(proj.get(), dataSeg + ent.offset, ent.decompSz); + } + else + { + proj.reset(new uint8_t[ent.decompSz]); + uLongf outSz = ent.decompSz; + uncompress(proj.get(), &outSz, dataSeg + ent.offset, ent.compSz); + } + } + else if (!strncmp("pool", ent.name, 16)) + { + if (ent.compSz == 0xffffffff) + { + pool.reset(new uint8_t[ent.decompSz]); + memcpy(pool.get(), dataSeg + ent.offset, ent.decompSz); + } + else + { + pool.reset(new uint8_t[ent.decompSz]); + uLongf outSz = ent.decompSz; + uncompress(pool.get(), &outSz, dataSeg + ent.offset, ent.compSz); + } + } + else if (!strncmp("sdir", ent.name, 16)) + { + if (ent.compSz == 0xffffffff) + { + sdir.reset(new uint8_t[ent.decompSz]); + memcpy(sdir.get(), dataSeg + ent.offset, ent.decompSz); + } + else + { + sdir.reset(new uint8_t[ent.decompSz]); + uLongf outSz = ent.decompSz; + uncompress(sdir.get(), &outSz, dataSeg + ent.offset, ent.compSz); + } + } + else if (!strncmp("samp", ent.name, 16)) + { + if (ent.compSz == 0xffffffff) + { + samp.reset(new uint8_t[ent.decompSz]); + memcpy(samp.get(), dataSeg + ent.offset, ent.decompSz); + } + else + { + samp.reset(new uint8_t[ent.decompSz]); + uLongf outSz = ent.decompSz; + uncompress(samp.get(), &outSz, dataSeg + ent.offset, ent.compSz); + } + } + } + + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), + sdir.release(), samp.release(), + true, N64DataTag{}}); } return ret; @@ -989,6 +1261,18 @@ ContainerRegistry::Type ContainerRegistry::DetectContainerType(const char* path) return Type::RogueSquadronN64; } + if (ValidateBFNPC(fp)) + { + fclose(fp); + return Type::BattleForNabooPC; + } + + if (ValidateBFNN64(fp)) + { + fclose(fp); + return Type::BattleForNabooN64; + } + if (ValidateRS2(fp)) { fclose(fp); @@ -1103,13 +1387,16 @@ ContainerRegistry::LoadContainer(const char* path) /* SDIR-based format detection */ if (*reinterpret_cast(sdir.get() + 8) == 0x0) ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), GCNDataTag{}}); + sdir.release(), samp.release(), + GCNDataTag{}}); else if (sdir[9] == 0x0) ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), N64DataTag{}}); + sdir.release(), samp.release(), + false, N64DataTag{}}); else ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), PCDataTag{}}); + sdir.release(), samp.release(), + false, PCDataTag{}}); return ret; } @@ -1146,6 +1433,20 @@ ContainerRegistry::LoadContainer(const char* path) return ret; } + if (ValidateBFNPC(fp)) + { + auto ret = LoadBFNPC(fp); + fclose(fp); + return ret; + } + + if (ValidateBFNN64(fp)) + { + auto ret = LoadBFNN64(fp); + fclose(fp); + return ret; + } + if (ValidateRS2(fp)) { auto ret = LoadRS2(fp); diff --git a/lib/Engine.cpp b/lib/Engine.cpp index e420b85..62d474f 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -205,10 +205,10 @@ const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) grp = std::make_unique(data, GCNDataTag{}); break; case DataFormat::N64: - grp = std::make_unique(data, N64DataTag{}); + grp = std::make_unique(data, data.m_absOffs, N64DataTag{}); break; case DataFormat::PC: - grp = std::make_unique(data, PCDataTag{}); + grp = std::make_unique(data, data.m_absOffs, PCDataTag{}); break; } if (!grp)