/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_RomSoundArchive.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // #define NW_SND_DEBUG_PRINT_ENABLE namespace nw { namespace snd { /*---------------------------------------------------------------------------* Name: RomSoundArchive Description: コンストラクタ Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ RomSoundArchive::RomSoundArchive() : m_IsOpened( false ) { } /*---------------------------------------------------------------------------* Name: ~RomSoundArchive Description: デストラクタ Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ RomSoundArchive::~RomSoundArchive() { Close(); } bool RomSoundArchive::Open( const char* path ) { #ifdef NW_PLATFORM_CTRWIN // for PC_SDK { bool result = nn::fs::OpenFile( &m_FileInfo, path ); if ( result == false ) { NW_WARNING( false, "Cannot open file(%s)\n", path ); return false; } m_IsOpened = true; } #else // for CTR_SDK { m_FileReader.Initialize( path ); m_IsOpened = true; } #endif { bool result = LoadFileHeader(); if ( result == false ) { NW_WARNING( false, "Cannot load header\n" ); return false; } } std::size_t len = std::strlen( path ); for ( int i = static_cast( len ) - 1; i >= 0; i-- ) { const char ch = path[ i ] ; if ( ch == '/' || ch == '\\' ) { char dirBuffer[ FILE_PATH_MAX ]; NW_ASSERT( i < FILE_PATH_MAX ); if ( i >= FILE_PATH_MAX ) { return false; } ut::strncpy( dirBuffer, FILE_PATH_MAX, path, static_cast( i ) ); dirBuffer[ i ] = '\0'; SetExternalFileRoot( dirBuffer ); break; } } return true; } #if 0 bool RomSoundArchive::Open( const wchar_t* path ) { { m_FileReader.Initialize( path ); m_IsOpened = true; } { bool result = LoadFileHeader(); if ( result == false ) { NW_WARNING( false, "Cannot load header\n" ); return false; } } return true; } #endif /*---------------------------------------------------------------------------* Name: Close Description: サウンドアーカイブを閉じる Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void RomSoundArchive::Close() { if ( m_IsOpened ) { #ifdef NW_PLATFORM_CTRWIN nn::fs::CloseFile( &m_FileInfo ); #else m_FileReader.Finalize(); #endif m_ArchiveReader.Finalize(); m_IsOpened = false; } Finalize(); } io::FileStream* RomSoundArchive::OpenStream( void* buffer, int size, u32 begin, u32 length ) { #ifdef NW_PLATFORM_CTRWIN if ( ! m_IsOpened ) return NULL; if ( size < sizeof( RomFileStream ) ) return NULL; RomFileStream* stream = new( buffer ) RomFileStream( &m_FileInfo, begin, length ); return stream; #else if ( ! m_IsOpened ) return NULL; if ( size < sizeof( RomFileStream ) ) return NULL; RomFileStream* stream = new( buffer ) RomFileStream( &m_FileReader, begin, length ); return stream; #endif } io::FileStream* RomSoundArchive::OpenExtStream( void* buffer, int size, const char* extFilePath, // const wchar_t* extFilePath, u32 begin, u32 length ) const { if ( ! m_IsOpened ) return NULL; if ( size < sizeof( RomFileStream ) ) return NULL; RomFileStream* stream = new( buffer ) RomFileStream( extFilePath, begin, length ); return stream; } size_t RomSoundArchive::detail_GetRequiredStreamBufferSize() const { return sizeof( RomFileStream ); } /*---------------------------------------------------------------------------* Name: LoadFileHeader Description: サウンドアーカイブファイルのヘッダをロードする Arguments: None. Returns: 成功したら true 失敗したら false *---------------------------------------------------------------------------*/ bool RomSoundArchive::LoadFileHeader() { NW_ASSERT( m_IsOpened ); const unsigned long headerAlignSize = static_cast( ut::RoundUp( sizeof(internal::SoundArchiveFile::FileHeader), 32 ) ); u8 headerArea[ sizeof(internal::SoundArchiveFile::FileHeader) + 32*2 ]; // 前後32バイトずつの余裕を持つ void* file = ut::RoundUp( headerArea, 32 ); #ifdef NW_PLATFORM_CTRWIN s32 readSize = nn::fs::ReadFile( &m_FileInfo, file, static_cast(headerAlignSize) ); #else s32 readSize = m_FileReader.Read( file, static_cast(headerAlignSize) ); #endif if ( readSize != headerAlignSize ) { NW_WARNING( false, "RomSoundArchive::LoadFileHeader cannot read file.\n" ); return false; } m_ArchiveReader.Initialize( file ); Initialize( &m_ArchiveReader ); return true; } /*---------------------------------------------------------------------------* Name: LoadHeader Description: サウンドデータの情報テーブルをロードする Arguments: buffer - ロードアドレス size - バッファサイズ Returns: 成功したら true 失敗したら false *---------------------------------------------------------------------------*/ bool RomSoundArchive::LoadHeader( void* buffer, unsigned long size ) { NW_ASSERT( m_IsOpened ); const s32 infoChunkOffset = m_ArchiveReader.GetInfoBlockOffset(); const u32 infoChunkSize = m_ArchiveReader.GetInfoBlockSize(); if ( size < infoChunkSize ) { NW_WARNING( size >= infoChunkSize, "RomSoundArchive::LoadHeader buffer size is too small.\n" ); return false; } //----------------------------------------------------------------------------- // 情報テーブルのロード #ifdef NW_PLATFORM_CTRWIN nn::fs::SeekFile( &m_FileInfo, infoChunkOffset, nn::fs::FS_SEEK_SET ); s32 readSize = nn::fs::ReadFile( &m_FileInfo, buffer, static_cast(infoChunkSize) ); #else m_FileReader.Seek( infoChunkOffset, nn::fs::POSITION_BASE_BEGIN ); s32 readSize = m_FileReader.Read( buffer, static_cast(infoChunkSize) ); #endif if ( readSize != infoChunkSize ) { NW_WARNING( false, "RomSoundArchive::LoadHeader cannot read file.\n" ); return false; } m_ArchiveReader.SetInfoBlock( buffer /*, infoChunkSize */ ); #ifdef NW_SND_DEBUG_PRINT_ENABLE // デバッグ出力 { // サウンド情報 NN_LOG("### Sound INFO(%d)\n", m_ArchiveReader.GetSoundCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetSoundCount(); i++ ) { // サウンド共通情報 SoundArchive::ItemId soundId = GetSoundIdFromIndex( i ); SoundArchive::SoundType type = m_ArchiveReader.GetSoundType( soundId ); SoundArchive::SoundInfo info; bool ret = m_ArchiveReader.ReadSoundInfo( soundId, &info ); NN_LOG("[%08X]?(%d) fileId(%d) playerId(0x%08X) actorId(%d) type(%d)\n", soundId, ret, info.fileId, info.playerId, info.actorPlayerId, type ); NN_LOG(" *common* volume(%d) panMode(%d) panCurve (%d)\n", info.volume, info.panMode, info.panCurve ); // 3D サウンド情報 { SoundArchive::Sound3DInfo info3d; if ( ReadSound3DInfo( soundId, &info3d ) ) { u32 flag = info3d.flags; NN_LOG(" *3D* ra(%.2f) cv(%d) df(%d) vol(%d) pr(%d) pa(%d) sp(%d) bqf(%d)\n", info3d.decayRatio, info3d.decayCurve, info3d.dopplerFactor, (flag & Sound3DInfo::FLAG_CTRL_VOLUME) > 0, (flag & Sound3DInfo::FLAG_CTRL_PRIORITY) > 0, (flag & Sound3DInfo::FLAG_CTRL_PAN) > 0, (flag & Sound3DInfo::FLAG_CTRL_SPAN) > 0, (flag & Sound3DInfo::FLAG_CTRL_FILTER) > 0 ); } else { NN_LOG(" *3D* data not found\n"); } } // サウンド別個別情報 switch ( type ) { case SoundArchive::SOUND_TYPE_SEQ: { SoundArchive::SequenceSoundInfo seqInfo; bool retDetail = m_ArchiveReader.ReadSequenceSoundInfo( soundId, &seqInfo ); NN_LOG(" *SEQ* ret(%d) ofs(%d) bnk(0x%08X,0x%08X,0x%08X,0x%08X) trk(0x%x) chPrio(%d) rPrioFix(%d)\n", retDetail, seqInfo.startOffset, seqInfo.bankIds[0], seqInfo.bankIds[1], seqInfo.bankIds[2], seqInfo.bankIds[3], seqInfo.allocateTrackFlags, seqInfo.channelPriority, seqInfo.isReleasePriorityFix ); } break; case SoundArchive::SOUND_TYPE_STRM: { SoundArchive::StreamSoundInfo strmInfo; bool retDetail = m_ArchiveReader.ReadStreamSoundInfo( soundId, &strmInfo ); NN_LOG(" *STRM* ret(%d) trk(%d) channel(%d)\n", retDetail, strmInfo.allocTrackCount, strmInfo.allocChannelCount ); } break; case SoundArchive::SOUND_TYPE_WAVE: { SoundArchive::WaveSoundInfo wsdInfo; bool retDetail = m_ArchiveReader.ReadWaveSoundInfo( soundId, &wsdInfo ); NN_LOG(" *WSD* ret(%d) index(%d) trk(0x%x) chPrio(%d) rPrioFix(%d)\n", retDetail, wsdInfo.index, wsdInfo.allocTrackCount, wsdInfo.channelPriority, wsdInfo.isReleasePriorityFix ); } break; case SoundArchive::SOUND_TYPE_INVALID: { NN_LOG("Invalid SoundType (not STRM/WSD/SEQ)\n"); } break; } } // バンク情報 NN_LOG("### BANK Info(%d)\n", m_ArchiveReader.GetBankCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetBankCount(); i++ ) { SoundArchive::ItemId bankId = GetBankIdFromIndex( i ); SoundArchive::BankInfo info; bool ret = m_ArchiveReader.ReadBankInfo( bankId, &info ); NN_LOG("[%08X]?(%d) fileId(%d)\n", bankId, ret, info.fileId ); } // プレイヤー情報 NN_LOG("### PLAYER Info(%d)\n", m_ArchiveReader.GetPlayerCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetPlayerCount(); i++ ) { SoundArchive::ItemId playerId = GetPlayerIdFromIndex( i ); SoundArchive::PlayerInfo info; bool ret = m_ArchiveReader.ReadPlayerInfo( playerId, &info ); NN_LOG("[%08X]?(%d) max(%d) heapSize(%d)\n", playerId, ret, info.playableSoundMax, info.playerHeapSize ); } // サウンドグループ情報 NN_LOG("### SOUND-GROUP Info(%d)\n", m_ArchiveReader.GetSoundGroupCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetSoundGroupCount(); i++ ) { SoundArchive::ItemId soundGroupId = GetSoundGroupIdFromIndex( i ); SoundArchive::SoundGroupInfo info; bool ret = m_ArchiveReader.ReadSoundGroupInfo( soundGroupId, &info ); NN_LOG("[%08X]?(%d) startId(%08X) end(%08X) fileId:count(%d)", soundGroupId, ret, info.startId, info.endId, info.fileIdTable->count ); for ( u32 j = 0; j < info.fileIdTable->count; j++ ) { NN_LOG(" [%08X]", info.fileIdTable->item[j]); } NN_LOG("\n"); } // グループ情報 NN_LOG("### GROUP Info(%d)\n", m_ArchiveReader.GetGroupCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetGroupCount(); i++ ) { SoundArchive::ItemId groupId = GetGroupIdFromIndex( i ); SoundArchive::GroupInfo info; bool ret = m_ArchiveReader.ReadGroupInfo( groupId, &info ); NN_LOG("[%08X]?(%d) fileId(%d)\n", groupId, ret, info.fileId ); } // 波形アーカイブ情報 NN_LOG("### WAVE-ARCHIVE Info(%d)\n", m_ArchiveReader.GetWaveArchiveCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetWaveArchiveCount(); i++ ) { SoundArchive::ItemId warcId = GetWaveArchiveIdFromIndex( i ); SoundArchive::WaveArchiveInfo info; bool ret = m_ArchiveReader.ReadWaveArchiveInfo( warcId, &info ); NN_LOG("[%08X]?(%d) fileId(%d)\n", warcId, ret, info.fileId ); } // ファイル情報 NN_LOG("### FILE Info(%d)\n", m_ArchiveReader.GetFileCount() ); for ( u32 i = 0; i < m_ArchiveReader.GetFileCount(); i++ ) { SoundArchive::FileInfo info; bool ret = m_ArchiveReader.ReadFileInfo( i, &info ); if ( info.externalFilePath != NULL ) { NN_LOG("[%4d]?(%d) fileSize(%8d) ofs(%8d) path(%s)\n", i, ret, info.fileSize, info.offsetFromFileBlockHead, info.externalFilePath ); } else { NN_LOG("[%4d]?(%d) fileSize(%8d) ofs(%8d) path((null))\n", i, ret, info.fileSize, info.offsetFromFileBlockHead ); } } // サウンドアーカイブプレイヤー情報 NN_LOG("### SOUND-ARCHIVE-PLAYER Info\n"); { SoundArchive::SoundArchivePlayerInfo info; bool ret = m_ArchiveReader.ReadSoundArchivePlayerInfo( &info ); NN_LOG("sequenceSoundMax (%2d)\n", info.sequenceSoundMax ); NN_LOG("sequenceTrackMax (%2d)\n", info.sequenceTrackMax ); NN_LOG("streamSoundMax (%2d)\n", info.streamSoundMax ); NN_LOG("streamTrackMax (%2d)\n", info.streamTrackMax ); NN_LOG("streamChannelMax (%2d)\n", info.streamChannelMax ); NN_LOG("waveSoundMax (%2d)\n", info.waveSoundMax ); NN_LOG("waveTrackMax (%2d)\n", info.waveTrackMax ); } } #endif /* NW_SND_DEBUG_PRINT_ENABLE */ return true; } /*---------------------------------------------------------------------------* Name: LoadLabelStringData Description: ラベルデータをロードする Arguments: buffer - ロードアドレス size - バッファサイズ Returns: 成功したら true 失敗したら false *---------------------------------------------------------------------------*/ bool RomSoundArchive::LoadLabelStringData( void* buffer, unsigned long size ) { NW_ASSERT( m_IsOpened ); const s32 stringBlockOffset = m_ArchiveReader.GetStringBlockOffset(); const u32 stringBlockSize = m_ArchiveReader.GetStringBlockSize(); if ( stringBlockOffset == internal::Util::Reference::INVALID_OFFSET ) { // サウンドアーカイブの文字列ブロックが含まれていない return false; } if ( size < stringBlockSize ) { NW_WARNING( size >= stringBlockSize, "RomSoundArchive::LoadLabelStringData buffer size is too small." ); return false; } #ifdef NW_PLATFORM_CTRWIN nn::fs::SeekFile( &m_FileInfo, stringBlockOffset, nn::fs::FS_SEEK_SET ); s32 readSize = nn::fs::ReadFile( &m_FileInfo, buffer, static_cast(stringBlockSize) ); #else m_FileReader.Seek( stringBlockOffset, nn::fs::POSITION_BASE_BEGIN ); s32 readSize = m_FileReader.Read( buffer, static_cast(stringBlockSize) ); #endif if ( readSize != stringBlockSize ) { NW_WARNING( false, "RomSoundArchive::LoadLabelStringData cannot read file.\n" ); return false; } m_ArchiveReader.SetStringBlock( buffer/*, stringBlockSize*/ ); #ifdef NW_SND_DEBUG_PRINT_ENABLE // デバッグ出力 { NN_LOG("### PATRICIA-TREE Info\n"); m_ArchiveReader.DumpTree(); NN_LOG("### LABEL => ID\n"); int count = m_ArchiveReader.GetStringCount(); for ( int i = 0; i < count; i++ ) { const char* str = m_ArchiveReader.GetString( i ); NN_LOG("[%02d] (%-16s) => ItemId(0x%08X)\n", i, str, GetItemId( str ) ); #if 0 NN_LOG(" as Sound ID: (0x%08X)\n", GetSoundId( str ) ); NN_LOG(" as Bank ID: (0x%08X)\n", GetBankId( str ) ); NN_LOG(" as Player ID: (0x%08X)\n", GetPlayerId( str ) ); NN_LOG(" as SoundGroup ID: (0x%08X)\n", GetSoundGroupId( str ) ); NN_LOG(" as Group ID: (0x%08X)\n", GetGroupId( str ) ); NN_LOG(" as WaveArchive ID: (0x%08X)\n", GetWaveArchiveId( str ) ); #endif } NN_LOG("### ID => LABEL\n"); NN_LOG("[Sound]\n"); for ( u32 i = 0; i < GetSoundCount(); i++ ) { u32 id = GetSoundIdFromIndex( i ); NN_LOG(" [%08X] (%s)\n", id, GetItemLabel(id) ); } NN_LOG("[Bank]\n"); for ( u32 i = 0; i < GetBankCount(); i++ ) { u32 id = GetBankIdFromIndex( i ); NN_LOG(" [%08X] (%s)\n", id, GetItemLabel(id) ); } NN_LOG("[Player]\n"); for ( u32 i = 0; i < GetPlayerCount(); i++ ) { u32 id = GetPlayerIdFromIndex( i ); NN_LOG(" [%08X] (%s)\n", id, GetItemLabel(id) ); } NN_LOG("[SoundGroup]\n"); for ( u32 i = 0; i < GetSoundGroupCount(); i++ ) { u32 id = GetSoundGroupIdFromIndex( i ); NN_LOG(" [%08X] (%s)\n", id, GetItemLabel(id) ); } NN_LOG("[Group]\n"); for ( u32 i = 0; i < GetGroupCount(); i++ ) { u32 id = GetGroupIdFromIndex( i ); NN_LOG(" [%08X] (%s)\n", id, GetItemLabel(id) ); } NN_LOG("[WaveArchive]\n"); for ( u32 i = 0; i < GetWaveArchiveCount(); i++ ) { u32 id = GetWaveArchiveIdFromIndex( i ); const char* label = GetItemLabel( id ); if ( label != NULL ) { NN_LOG(" [%08X] (%s)\n", id, label ); } else { NN_LOG(" [%08X] ((anonymous))\n", id ); } } } #endif /* NW_SND_DEBUG_PRINT_ENABLE */ return true; } #if 0 /*---------------------------------------------------------------------------* Name: RomFileStream Description: コンストラクタ Arguments: entryNum - DVDエントリ番号 offset - DVDファイル先頭からのオフセット size - ストリームサイズ Returns: None. *---------------------------------------------------------------------------*/ RomSoundArchive::RomFileStream::RomFileStream( s32 entryNum, u32 offset, u32 size ) : ut::RomLockedFileStream( entryNum ), m_Offset( static_cast( offset ) ), m_Size( size ) { NW_ASSERT( m_Size <= ut::RomLockedFileStream::GetSize() ); ut::RomLockedFileStream::Seek( m_Offset, ut::FILE_STREAM_SEEK_BEGIN ); } #endif RomSoundArchive::RomFileStream::RomFileStream( const char* path, u32 offset, u32 size ) // RomSoundArchive::RomFileStream::RomFileStream( const wchar_t* path, u32 offset, u32 size ) : io::RomFileStream( path ), m_Offset( static_cast( offset ) ), m_Size( size ) { NW_ASSERT( m_Size <= io::RomFileStream::GetSize() ); if ( size == 0 ) { m_Size = io::RomFileStream::GetSize(); } io::RomFileStream::Seek( m_Offset, io::FILE_STREAM_SEEK_BEGIN ); } #ifdef NW_PLATFORM_CTRWIN // オープン済みのファイルからストリームを作成して内部で持つ RomSoundArchive::RomFileStream::RomFileStream( const nn::fs::File* fileInfo, u32 offset, u32 size ) : io::RomFileStream( fileInfo, false ), m_Offset( static_cast( offset ) ), m_Size( size ) { NW_ASSERT( m_Size <= io::RomFileStream::GetSize() ); if ( size == 0 ) { m_Size = io::RomFileStream::GetSize(); } io::RomFileStream::Seek( m_Offset, io::FILE_STREAM_SEEK_BEGIN ); } #else // オープン済みのファイルからストリームを作成して内部で持つ RomSoundArchive::RomFileStream::RomFileStream( nn::fs::FileReader* fileReader, u32 offset, u32 size ) : io::RomFileStream( fileReader, false ), m_Offset( static_cast( offset ) ), m_Size( size ) { NW_ASSERT( m_Size <= io::RomFileStream::GetSize() ); if ( size == 0 ) { m_Size = io::RomFileStream::GetSize(); } io::RomFileStream::Seek( m_Offset, io::FILE_STREAM_SEEK_BEGIN ); } #endif s32 RomSoundArchive::RomFileStream::Read( void* buf, u32 length ) { NW_ALIGN32_ASSERT( buf ); NW_ALIGN32_ASSERT( length ); u32 curPos = io::RomFileStream::Tell(); if ( curPos + length > m_Offset + m_Size ) { length = static_cast( ut::RoundUp( m_Offset + m_Size - curPos, 32 ) ); } return io::RomFileStream::Read( buf, length ); } void RomSoundArchive::RomFileStream::Seek( s32 offset, u32 origin ) { switch( origin ) { case io::FILE_STREAM_SEEK_BEGIN: offset += m_Offset; break; case io::FILE_STREAM_SEEK_CURRENT: offset += io::RomFileStream::Tell(); break; case io::FILE_STREAM_SEEK_END: offset = m_Offset + static_cast( m_Size ) - offset; break; default: NW_ASSERTMSG( false, "Unsupported Seek origin" ); return; } if ( offset < m_Offset ) { offset = m_Offset; } else if ( offset > m_Offset + static_cast( m_Size ) ) { offset = m_Offset + static_cast( m_Size ); } io::RomFileStream::Seek( offset, io::FILE_STREAM_SEEK_BEGIN ); } } // namespace nw::snd } // namespace nw