From 7abc73fd072b73c460cbd9d46673234419a03210 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 13:49:51 -0800 Subject: [PATCH 001/104] Update .travis.xml Hopefully fix building again. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 52b885663..e4cc8e4be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -osx_image: xcode7.3 +osx_image: xcode9.2 language: objective-c xcode_project: Cog.xcodeproj xcode_scheme: Cog From b5330c2839e185e97e1737e7473a6e56d3cf4141 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 15:16:27 -0800 Subject: [PATCH 002/104] Fix BASSMODS IT input, and add MO3 signature check. --- Plugins/BASSMODS/BASSMODS/BASSDecoder.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/BASSMODS/BASSMODS/BASSDecoder.mm b/Plugins/BASSMODS/BASSMODS/BASSDecoder.mm index 3398a249d..dd765ccfc 100755 --- a/Plugins/BASSMODS/BASSMODS/BASSDecoder.mm +++ b/Plugins/BASSMODS/BASSMODS/BASSDecoder.mm @@ -76,6 +76,8 @@ static void SyncProc( HSYNC handle, DWORD channel, DWORD data, void *user ) - (BOOL)open:(id)s { + [self setSource:s]; + [source seek:0 whence:SEEK_END]; long size = [source tell]; [source seek:0 whence:SEEK_SET]; @@ -89,14 +91,12 @@ static void SyncProc( HSYNC handle, DWORD channel, DWORD data, void *user ) data = try_data; } - if (memcmp(data, "IMPM", 4) != 0) { + if (size < 4 || (memcmp(data, "mo3", 3) != 0 && memcmp(data, "IMPM", 4) != 0)) { ALog(@"BASS was passed a non-IT module"); free(data); return NO; } - [self setSource:s]; - NSURL * url = [s url]; int track_num; if ([[url fragment] length] == 0) From 6d4572579f1f48683fff77b3885b2fd82b5818d6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 15:40:59 -0800 Subject: [PATCH 003/104] Update .travis.xml Try this again. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e4cc8e4be..0b08f1ad6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -osx_image: xcode9.2 +osx_image: xcode8.3 language: objective-c xcode_project: Cog.xcodeproj xcode_scheme: Cog From 7c30545bd4419fe8911df841afb027e0fa1a52d6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 15:45:11 -0800 Subject: [PATCH 004/104] Update .travis.xml Try again. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0b08f1ad6..264b6f3bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ -osx_image: xcode8.3 +osx_image: xcode9.2 language: objective-c xcode_project: Cog.xcodeproj xcode_scheme: Cog +before_install: + - brew update + - brew outdated xctool || brew upgrade xctool From 413ef368d92f656ab75a3ef9cc892a04dec9734c Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 15:53:22 -0800 Subject: [PATCH 005/104] Update .travis.xml Try one more time. --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 264b6f3bc..d1d319776 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ +os: osx osx_image: xcode9.2 language: objective-c -xcode_project: Cog.xcodeproj -xcode_scheme: Cog -before_install: - - brew update - - brew outdated xctool || brew upgrade xctool +script: + - xcodebuild clean build -workspace Cog.xcodeproj/project.xcworkspace -scheme Cog CODE_SIGNING_REQUIRED=NO From 25cc0002a03dc202dcb14e3ff09c345b1b7c4295 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 17:14:32 -0800 Subject: [PATCH 006/104] Hopefully fix Travis CI building this time. --- .travis.yml | 2 +- Scripts/build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d1d319776..01c359b72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,4 @@ os: osx osx_image: xcode9.2 language: objective-c script: - - xcodebuild clean build -workspace Cog.xcodeproj/project.xcworkspace -scheme Cog CODE_SIGNING_REQUIRED=NO + - Scripts/build.sh CODE_SIGNING_REQUIRED=NO diff --git a/Scripts/build.sh b/Scripts/build.sh index d9be111d2..17980edc2 100755 --- a/Scripts/build.sh +++ b/Scripts/build.sh @@ -6,5 +6,5 @@ popd BUILDPRODUCTS="$BASE"/build/Build/Products/Release -xcodebuild -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build +xcodebuild -quiet -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build $1 From 871fa82620b672c8ac3f8207e8913e1b7f3bb3ea Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 17:33:52 -0800 Subject: [PATCH 007/104] Hopefully fix Travis CI building once and for all. --- .travis.yml | 2 +- Cog.xcodeproj/project.pbxproj | 2 +- Scripts/build.sh | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01c359b72..4288fb3e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,4 @@ os: osx osx_image: xcode9.2 language: objective-c script: - - Scripts/build.sh CODE_SIGNING_REQUIRED=NO + - Scripts/build.sh -n diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index e50feae7e..cedce24d9 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -2505,7 +2505,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PROJECT_DIR}/Scripts/genversion.sh\ncodesign -f -s \"${CODE_SIGN_IDENTITY}\" --deep \"${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app\""; + shellScript = "${PROJECT_DIR}/Scripts/genversion.sh\nif [ "$CODE_SIGN_IDENTITY" -ne "" ]; then codesign -f -s \"${CODE_SIGN_IDENTITY}\" --deep \"${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app\"; fi"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/Scripts/build.sh b/Scripts/build.sh index 17980edc2..662f44edf 100755 --- a/Scripts/build.sh +++ b/Scripts/build.sh @@ -4,7 +4,23 @@ pushd $(dirname $0) BASE=`pwd -P` popd +sign=true + +while getopts ":hn" option; do + case $option in + h) echo "usage: $0 [-h] [-n]"; exit ;; + n) sign=false ;; + ?) echo "error: option -$OPTARG is not implemented"; exit ;; + esac +done + +SIGNARGS="" + +if [ "$sign" = true ] ; then + SIGNARGS=('CODE_SIGN_IDENTITY=""' 'CODE_SIGNING_REQUIRED=NO') +fi + BUILDPRODUCTS="$BASE"/build/Build/Products/Release -xcodebuild -quiet -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build $1 +xcodebuild -quiet -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build ${SIGNARGS[*]} From 11adbbedcb9bc8e54295ba00bc3d58912055f10b Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 17:38:00 -0800 Subject: [PATCH 008/104] Oops. --- Cog.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index cedce24d9..34bf2b8bd 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -2505,7 +2505,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PROJECT_DIR}/Scripts/genversion.sh\nif [ "$CODE_SIGN_IDENTITY" -ne "" ]; then codesign -f -s \"${CODE_SIGN_IDENTITY}\" --deep \"${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app\"; fi"; + shellScript = "${PROJECT_DIR}/Scripts/genversion.sh\nif [ \"$CODE_SIGN_IDENTITY\" -ne \"\" ]; then codesign -f -s \"${CODE_SIGN_IDENTITY}\" --deep \"${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.app\"; fi"; }; /* End PBXShellScriptBuildPhase section */ From 13df28fef45faf22d6d3a7008e2d6e8d9d2fccd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Doncam=20Demian=20L=C3=B3pez=20Brante?= Date: Wed, 27 Dec 2017 18:51:44 -0800 Subject: [PATCH 009/104] Implement proper fullscreen enter and exit animations. --- Application/AppController.m | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Application/AppController.m b/Application/AppController.m index 2b02a736c..798200245 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -472,16 +472,23 @@ NSRect contentRect = [contentView frame]; const NSSize windowSize = [contentView convertSize:[mainWindow frame].size fromView: nil]; - NSRect nowPlayingFrame = [[nowPlaying view] frame]; - nowPlayingFrame.size.width = windowSize.width; - [[nowPlaying view] setFrame: nowPlayingFrame]; - [contentView addSubview: [nowPlaying view]]; - [[nowPlaying view] setFrameOrigin: NSMakePoint(0.0, NSMaxY(contentRect) - nowPlayingFrame.size.height)]; + [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) { + [context setDuration:0.25]; + NSRect nowPlayingFrame = [[nowPlaying view] frame]; + nowPlayingFrame.size.width = windowSize.width; + [[nowPlaying view] setFrame: nowPlayingFrame]; + [[nowPlaying view] setFrameOrigin: NSMakePoint(0.0, NSMaxY(contentRect) - nowPlayingFrame.size.height)]; + + NSRect mainViewFrame = [mainView frame]; + mainViewFrame.size.height -= nowPlayingFrame.size.height; + [[mainView animator] setFrame:mainViewFrame]; + + } completionHandler:^{ + + }]; + - NSRect mainViewFrame = [mainView frame]; - mainViewFrame.size.height -= nowPlayingFrame.size.height; - [mainView setFrame:mainViewFrame]; [[nowPlaying text] bind:@"value" toObject:currentEntryController withKeyPath:@"content.display" options:nil]; } @@ -495,11 +502,17 @@ NSRect nowPlayingFrame = [[nowPlaying view] frame]; NSRect mainViewFrame = [mainView frame]; mainViewFrame.size.height += nowPlayingFrame.size.height; - [mainView setFrame:mainViewFrame]; + //[mainView setFrame:mainViewFrame]; // [mainView setFrameOrigin:NSMakePoint(0.0, 0.0)]; - [[nowPlaying view] removeFromSuperview]; - nowPlaying = nil; + [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) { + [context setDuration:0.25]; + [[mainView animator] setFrame:mainViewFrame]; + + } completionHandler:^{ + [[nowPlaying view] removeFromSuperview]; + nowPlaying = nil; + }]; } } From 3647ff6e02fc2ed5332431c113663033654d398e Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 18:57:41 -0800 Subject: [PATCH 010/104] Update Secret Sauce functionality. --- Plugins/MIDI/MIDI/MIDIDecoder.mm | 2 +- Preferences/General/MIDIPane.m | 3 ++- Preferences/General/MIDIPluginFlavorTransformer.m | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index 34207f815..dccd0dbb2 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -202,7 +202,7 @@ static OSType getOSType(const char * in_) componentSubType = getOSType(cplugin); componentManufacturer = getOSType(cplugin + 4); - if (componentManufacturer == 'rolD' && componentSubType == 'Sc55') + if ((componentManufacturer == 'rolD' || componentManufacturer == 'RoCl') && componentSubType == 'Sc55') { SCPlayer * scplayer = new SCPlayer; diff --git a/Preferences/General/MIDIPane.m b/Preferences/General/MIDIPane.m index 36c857cd2..0a9cdc38f 100644 --- a/Preferences/General/MIDIPane.m +++ b/Preferences/General/MIDIPane.m @@ -41,7 +41,8 @@ - (IBAction)setMidiPlugin:(id)sender { - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"] isEqualToString:@"Sc55rolD"]) + NSString * plugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"]; + if ([plugin isEqualToString:@"Sc55rolD"] || [plugin isEqualToString:@"Sc55RoCl"]) [midiFlavorControl setEnabled:YES]; else [midiFlavorControl setEnabled:NO]; diff --git a/Preferences/General/MIDIPluginFlavorTransformer.m b/Preferences/General/MIDIPluginFlavorTransformer.m index d0a3f9cc1..74fe363a7 100644 --- a/Preferences/General/MIDIPluginFlavorTransformer.m +++ b/Preferences/General/MIDIPluginFlavorTransformer.m @@ -17,6 +17,6 @@ - (id)transformedValue:(id)value { if (value == nil) return nil; - return [NSNumber numberWithBool:[value isEqualToString:@"Sc55rolD"]]; + return [NSNumber numberWithBool:([value isEqualToString:@"Sc55rolD"] || [value isEqualToString:@"Sc55RoCl"])]; } @end From d17e504cbb1599201b2c8ae738fe7a0d9217992e Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 18:58:15 -0800 Subject: [PATCH 011/104] Remove badge from README.md. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 314959c31..fc495f694 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,3 @@ Up to date binaries will be available at the following link: http://kode54.net/cog/ --Christopher Snowhill (chris@kode54.net) - -[![Build Status](https://travis-ci.org/kode54/Cog.svg?branch=master)](https://travis-ci.org/kode54/Cog) From 4ea71d96f08e12226e0c83b6495fadfbc7ffe7ee Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 27 Dec 2017 19:20:19 -0800 Subject: [PATCH 012/104] Remove last traces of Travis CI support. --- .travis.yml | 5 ----- Scripts/build.sh | 18 +----------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4288fb3e7..000000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -os: osx -osx_image: xcode9.2 -language: objective-c -script: - - Scripts/build.sh -n diff --git a/Scripts/build.sh b/Scripts/build.sh index 662f44edf..fea0265c0 100755 --- a/Scripts/build.sh +++ b/Scripts/build.sh @@ -4,23 +4,7 @@ pushd $(dirname $0) BASE=`pwd -P` popd -sign=true - -while getopts ":hn" option; do - case $option in - h) echo "usage: $0 [-h] [-n]"; exit ;; - n) sign=false ;; - ?) echo "error: option -$OPTARG is not implemented"; exit ;; - esac -done - -SIGNARGS="" - -if [ "$sign" = true ] ; then - SIGNARGS=('CODE_SIGN_IDENTITY=""' 'CODE_SIGNING_REQUIRED=NO') -fi - BUILDPRODUCTS="$BASE"/build/Build/Products/Release -xcodebuild -quiet -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build ${SIGNARGS[*]} +xcodebuild -quiet -workspace "$BASE"/../Cog.xcodeproj/project.xcworkspace -scheme Cog -configuration Release -derivedDataPath "$BASE"/build From 66e9a1fa145db19fe2b2dd828027ca9f9e7b3771 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 4 Jan 2018 18:00:39 -0800 Subject: [PATCH 013/104] Replaced DUMB and company with libopenmpt. --- .gitmodules | 3 + Cog.xcodeproj/project.pbxproj | 202 +-- Frameworks/OpenMPT/Info.plist | 26 + Frameworks/OpenMPT/OpenMPT | 1 + .../OpenMPT/OpenMPT.xcodeproj/project.pbxproj | 1496 +++++++++++++++++ Frameworks/OpenMPT/config.h | 17 + .../OpenMPT/OpenMPT.xcodeproj/project.pbxproj | 375 +++++ Plugins/OpenMPT/OpenMPT/Info.plist | 26 + Plugins/OpenMPT/OpenMPT/OMPTContainer.h | 17 + Plugins/OpenMPT/OpenMPT/OMPTContainer.mm | 79 + Plugins/OpenMPT/OpenMPT/OMPTDecoder.h | 29 + Plugins/OpenMPT/OpenMPT/OMPTDecoder.mm | 201 +++ Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.h | 17 + Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm | 97 ++ 14 files changed, 2427 insertions(+), 159 deletions(-) create mode 100644 Frameworks/OpenMPT/Info.plist create mode 160000 Frameworks/OpenMPT/OpenMPT create mode 100644 Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj create mode 100644 Frameworks/OpenMPT/config.h create mode 100644 Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj create mode 100644 Plugins/OpenMPT/OpenMPT/Info.plist create mode 100755 Plugins/OpenMPT/OpenMPT/OMPTContainer.h create mode 100755 Plugins/OpenMPT/OpenMPT/OMPTContainer.mm create mode 100755 Plugins/OpenMPT/OpenMPT/OMPTDecoder.h create mode 100755 Plugins/OpenMPT/OpenMPT/OMPTDecoder.mm create mode 100644 Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.h create mode 100644 Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm diff --git a/.gitmodules b/.gitmodules index d9b9a4b8b..1a9a85783 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "Frameworks/mGBA/mGBA/mgba"] path = Frameworks/mGBA/mGBA/mgba url = https://github.com/kode54/mgba.git +[submodule "Frameworks/OpenMPT/OpenMPT"] + path = Frameworks/OpenMPT/OpenMPT + url = https://gitlab.kode54.net/kode54/OpenMPT.git diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 34bf2b8bd..618ff85c1 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -98,7 +98,6 @@ 17C8099A0C3BD233005707C4 /* FileSource.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808790C3BD167005707C4 /* FileSource.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 17C809E60C3BD487005707C4 /* CoreAudio.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C809E30C3BD46D005707C4 /* CoreAudio.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 17C8F3CF0CBED66C008D969D /* GME.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C8F3CD0CBED663008D969D /* GME.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 17C8F7D80CBEF3EF008D969D /* Dumb.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C8F7D70CBEF3E8008D969D /* Dumb.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 17D1B0D20F6320EA00694C57 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; }; 17D1B1010F63255200694C57 /* InfoWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D1B1000F63255200694C57 /* InfoWindowController.m */; }; 17D1B1680F632ABB00694C57 /* BlankZeroFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D1B1630F632ABB00694C57 /* BlankZeroFormatter.m */; }; @@ -139,7 +138,6 @@ 8355D6B6180612F300D05687 /* NSData+MD5.m in Sources */ = {isa = PBXBuildFile; fileRef = 8355D6B5180612F300D05687 /* NSData+MD5.m */; }; 8355D6B8180613FB00D05687 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8355D6B7180613FB00D05687 /* Security.framework */; }; 8359009D17FF06570060F3ED /* ArchiveSource.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8359FF3117FEF35D0060F3ED /* ArchiveSource.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 835CBC8118DA7A520087A03E /* modplay.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 835CBC7618DA79F80087A03E /* modplay.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 8360EF6D17F92E56005208A4 /* HighlyComplete.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8360EF0517F92B24005208A4 /* HighlyComplete.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 836D28A818086386005B7299 /* MiniModeMenuTitleTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */; }; 836F5BF91A357A01002730CC /* sidplay.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8314D6411A354DFF00EEE8E6 /* sidplay.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -183,12 +181,11 @@ 838491871808591F00E7332D /* NDHotKey.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8384917E1808585D00E7332D /* NDHotKey.framework */; }; 838491881808593200E7332D /* NDHotKey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8384917E1808585D00E7332D /* NDHotKey.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; - 839BD010196521E600947767 /* BASSMODS.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 839BCFEC1965133F00947767 /* BASSMODS.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 83A0F4E31816DBF900119DB4 /* playptmod.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83A0F4891816CE5E00119DB4 /* playptmod.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066A1180D5669008E3612 /* MIDI.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; }; 83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */; }; + 83E5FE771FFF0C9D00659F0F /* OpenMPT.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E5EFB11FFEF78300659F0F /* OpenMPT.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83E6B7621816136F00D4576D /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E6B759181612FD00D4576D /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83E6B7651816178200D4576D /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E6B759181612FD00D4576D /* Sparkle.framework */; }; 83EEAB241C965C56002761C5 /* Syntrax.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83EEAAFA1C9651D8002761C5 /* Syntrax.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -363,20 +360,6 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = "GME Plugin"; }; - 17C8F7D60CBEF3E8008D969D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17C8F7D20CBEF3E8008D969D /* Dumb.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8D5B49B6048680CD000E48DA; - remoteInfo = Dumb; - }; - 17C8F7D90CBEF3F9008D969D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17C8F7D20CBEF3E8008D969D /* Dumb.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8D5B49AC048680CD000E48DA; - remoteInfo = Dumb; - }; 17F3BB870CBC565100864489 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */; @@ -461,13 +444,6 @@ remoteGlobalIDString = 8359FF1717FEF35C0060F3ED; remoteInfo = ArchiveSource; }; - 835CBC7518DA79F80087A03E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 835CBC7118DA79F80087A03E /* modplay.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83F4D51518D8206A009B2DE6; - remoteInfo = modplay; - }; 8360EF0417F92B24005208A4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8360EF0017F92B23005208A4 /* HighlyComplete.xcodeproj */; @@ -538,34 +514,6 @@ remoteGlobalIDString = 32F1615514E6BB3B00D6AB2F; remoteInfo = NDHotKey; }; - 839BCFEB1965133F00947767 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 839BCFE71965133E00947767 /* BASSMODS.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 839BCFD21965133E00947767; - remoteInfo = BASSMODS; - }; - 839BD011196521FD00947767 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 839BCFE71965133E00947767 /* BASSMODS.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 839BCFD11965133E00947767; - remoteInfo = BASSMODS; - }; - 83A0F4881816CE5E00119DB4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83A0F46F1816CE5E00119DB4; - remoteInfo = playptmod; - }; - 83A0F4E11816DBE800119DB4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 83A0F46E1816CE5E00119DB4; - remoteInfo = playptmod; - }; 83B066A0180D5669008E3612 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */; @@ -594,6 +542,20 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = "FFMPEG Plugin"; }; + 83E5EFB01FFEF78300659F0F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83E5EFA31FFEF78100659F0F; + remoteInfo = OpenMPT; + }; + 83E5FE781FFF0CAC00659F0F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83E5EFA21FFEF78100659F0F; + remoteInfo = OpenMPT; + }; 83E6B758181612FD00D4576D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83E6B750181612FD00D4576D /* Sparkle.xcodeproj */; @@ -717,21 +679,18 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 83E5FE771FFF0C9D00659F0F /* OpenMPT.bundle in CopyFiles */, 83EEAB241C965C56002761C5 /* Syntrax.bundle in CopyFiles */, 83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */, 836F5BF91A357A01002730CC /* sidplay.bundle in CopyFiles */, - 839BD010196521E600947767 /* BASSMODS.bundle in CopyFiles */, - 835CBC8118DA7A520087A03E /* modplay.bundle in CopyFiles */, 836F706218BDD1230095E648 /* vgmstream.bundle in CopyFiles */, 836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */, - 83A0F4E31816DBF900119DB4 /* playptmod.bundle in CopyFiles */, 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */, 8375B36517FFEF130092A79F /* Opus.bundle in CopyFiles */, 8359009D17FF06570060F3ED /* ArchiveSource.bundle in CopyFiles */, 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */, 8360EF6D17F92E56005208A4 /* HighlyComplete.bundle in CopyFiles */, 99EAACA80DD1BB7A00423C38 /* APL.bundle in CopyFiles */, - 17C8F7D80CBEF3EF008D969D /* Dumb.bundle in CopyFiles */, 17C8F3CF0CBED66C008D969D /* GME.bundle in CopyFiles */, 17F3BB890CBC565900864489 /* CueSheet.bundle in CopyFiles */, 8E8D41C80CBB0DA900135C1B /* Pls.bundle in CopyFiles */, @@ -896,7 +855,6 @@ 17C808B70C3BD1D2005707C4 /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = Plugins/Vorbis/Vorbis.xcodeproj; sourceTree = ""; }; 17C808C00C3BD1DD005707C4 /* WavPack.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = WavPack.xcodeproj; path = Plugins/WavPack/WavPack.xcodeproj; sourceTree = ""; }; 17C8F3C80CBED663008D969D /* GME.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GME.xcodeproj; path = Plugins/GME/GME.xcodeproj; sourceTree = ""; }; - 17C8F7D20CBEF3E8008D969D /* Dumb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Dumb.xcodeproj; path = Plugins/Dumb/Dumb.xcodeproj; sourceTree = ""; }; 17D1B0D10F6320EA00694C57 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/InfoInspector.xib; sourceTree = ""; }; 17D1B0FF0F63255200694C57 /* InfoWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoWindowController.h; path = InfoInspector/InfoWindowController.h; sourceTree = ""; }; 17D1B1000F63255200694C57 /* InfoWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoWindowController.m; path = InfoInspector/InfoWindowController.m; sourceTree = ""; }; @@ -975,7 +933,6 @@ 8355D6B5180612F300D05687 /* NSData+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+MD5.m"; sourceTree = ""; }; 8355D6B7180613FB00D05687 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ArchiveSource.xcodeproj; path = Plugins/ArchiveSource/ArchiveSource.xcodeproj; sourceTree = ""; }; - 835CBC7118DA79F80087A03E /* modplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = modplay.xcodeproj; path = Plugins/modplay/modplay.xcodeproj; sourceTree = ""; }; 8360EF0017F92B23005208A4 /* HighlyComplete.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HighlyComplete.xcodeproj; path = Plugins/HighlyComplete/HighlyComplete.xcodeproj; sourceTree = ""; }; 836D28A618086386005B7299 /* MiniModeMenuTitleTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MiniModeMenuTitleTransformer.h; path = Window/MiniModeMenuTitleTransformer.h; sourceTree = ""; }; 836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MiniModeMenuTitleTransformer.m; path = Window/MiniModeMenuTitleTransformer.m; sourceTree = ""; }; @@ -1024,11 +981,10 @@ 838491791808585C00E7332D /* NDHotKey.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = NDHotKey.xcodeproj; path = Frameworks/NDHotKey/NDHotKey.xcodeproj; sourceTree = ""; }; 8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = ""; }; 8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = ""; }; - 839BCFE71965133E00947767 /* BASSMODS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BASSMODS.xcodeproj; path = Plugins/BASSMODS/BASSMODS.xcodeproj; sourceTree = ""; }; - 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = playptmod.xcodeproj; path = Plugins/playptmod/playptmod.xcodeproj; sourceTree = ""; }; 83B0669C180D5668008E3612 /* MIDI.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MIDI.xcodeproj; path = Plugins/MIDI/MIDI.xcodeproj; sourceTree = ""; }; 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOffTemplate.pdf; path = Images/miniModeOffTemplate.pdf; sourceTree = ""; }; 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOnTemplate.pdf; path = Images/miniModeOnTemplate.pdf; sourceTree = ""; }; + 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = Plugins/OpenMPT/OpenMPT.xcodeproj; sourceTree = ""; }; 83E6B750181612FD00D4576D /* Sparkle.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sparkle.xcodeproj; path = Frameworks/Sparkle/Sparkle.xcodeproj; sourceTree = ""; }; 83EEAAF51C9651D8002761C5 /* Syntrax.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Syntrax.xcodeproj; path = Plugins/Syntrax/Syntrax.xcodeproj; sourceTree = ""; }; 83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = ""; }; @@ -1306,12 +1262,11 @@ 17B619FF0B909ED400BC003F /* PlugIns */ = { isa = PBXGroup; children = ( + 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */, 83EEAAF51C9651D8002761C5 /* Syntrax.xcodeproj */, - 835CBC7118DA79F80087A03E /* modplay.xcodeproj */, 8360EF0017F92B23005208A4 /* HighlyComplete.xcodeproj */, B09E94300D747F7B0064F138 /* FFMPEG.xcodeproj */, 566D32160D538550004466A5 /* APL.xcodeproj */, - 17C8F7D20CBEF3E8008D969D /* Dumb.xcodeproj */, 17C8F3C80CBED663008D969D /* GME.xcodeproj */, 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */, 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */, @@ -1328,10 +1283,8 @@ 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */, 8375B05117FFEA400092A79F /* Opus.xcodeproj */, 83B0669C180D5668008E3612 /* MIDI.xcodeproj */, - 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */, 836FB5421820538700B3AD2D /* Hively.xcodeproj */, 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */, - 839BCFE71965133E00947767 /* BASSMODS.xcodeproj */, 8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */, 83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */, ); @@ -1418,14 +1371,6 @@ name = Products; sourceTree = ""; }; - 17C8F7D30CBEF3E8008D969D /* Products */ = { - isa = PBXGroup; - children = ( - 17C8F7D70CBEF3E8008D969D /* Dumb.bundle */, - ); - name = Products; - sourceTree = ""; - }; 17D1B0FE0F63252900694C57 /* InfoInspector */ = { isa = PBXGroup; children = ( @@ -1665,14 +1610,6 @@ name = Products; sourceTree = ""; }; - 835CBC7218DA79F80087A03E /* Products */ = { - isa = PBXGroup; - children = ( - 835CBC7618DA79F80087A03E /* modplay.bundle */, - ); - name = Products; - sourceTree = ""; - }; 8360EF0117F92B23005208A4 /* Products */ = { isa = PBXGroup; children = ( @@ -1732,22 +1669,6 @@ name = Products; sourceTree = ""; }; - 839BCFE81965133E00947767 /* Products */ = { - isa = PBXGroup; - children = ( - 839BCFEC1965133F00947767 /* BASSMODS.bundle */, - ); - name = Products; - sourceTree = ""; - }; - 83A0F4851816CE5E00119DB4 /* Products */ = { - isa = PBXGroup; - children = ( - 83A0F4891816CE5E00119DB4 /* playptmod.bundle */, - ); - name = Products; - sourceTree = ""; - }; 83B0669D180D5668008E3612 /* Products */ = { isa = PBXGroup; children = ( @@ -1756,6 +1677,14 @@ name = Products; sourceTree = ""; }; + 83E5EFAD1FFEF78100659F0F /* Products */ = { + isa = PBXGroup; + children = ( + 83E5EFB11FFEF78300659F0F /* OpenMPT.bundle */, + ); + name = Products; + sourceTree = ""; + }; 83E6B751181612FD00D4576D /* Products */ = { isa = PBXGroup; children = ( @@ -1919,12 +1848,11 @@ buildRules = ( ); dependencies = ( + 83E5FE791FFF0CAC00659F0F /* PBXTargetDependency */, 83EEAB261C965C61002761C5 /* PBXTargetDependency */, 83F9D8061A884C33007ABEC2 /* PBXTargetDependency */, 836F5BEE1A3579F5002730CC /* PBXTargetDependency */, - 839BD012196521FD00947767 /* PBXTargetDependency */, 836FB5A618206F1500B3AD2D /* PBXTargetDependency */, - 83A0F4E21816DBE800119DB4 /* PBXTargetDependency */, 83E6B7641816138800D4576D /* PBXTargetDependency */, 83B06703180D5776008E3612 /* PBXTargetDependency */, 838491861808591400E7332D /* PBXTargetDependency */, @@ -1947,7 +1875,6 @@ 8E8D41CC0CBB0DD200135C1B /* PBXTargetDependency */, 17F3BB8B0CBC566200864489 /* PBXTargetDependency */, 17C8F44C0CBEDD37008D969D /* PBXTargetDependency */, - 17C8F7DA0CBEF3F9008D969D /* PBXTargetDependency */, 17643CBC0D5BD44900F0A9FE /* PBXTargetDependency */, ); name = Cog; @@ -1996,10 +1923,6 @@ ProductGroup = 8359FF2D17FEF35C0060F3ED /* Products */; ProjectRef = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */; }, - { - ProductGroup = 839BCFE81965133E00947767 /* Products */; - ProjectRef = 839BCFE71965133E00947767 /* BASSMODS.xcodeproj */; - }, { ProductGroup = 17F5612B0C3BD4DC0019975C /* Products */; ProjectRef = 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */; @@ -2012,10 +1935,6 @@ ProductGroup = 17F3BB840CBC565100864489 /* Products */; ProjectRef = 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */; }, - { - ProductGroup = 17C8F7D30CBEF3E8008D969D /* Products */; - ProjectRef = 17C8F7D20CBEF3E8008D969D /* Dumb.xcodeproj */; - }, { ProductGroup = B09E94310D747F7B0064F138 /* Products */; ProjectRef = B09E94300D747F7B0064F138 /* FFMPEG.xcodeproj */; @@ -2056,10 +1975,6 @@ ProductGroup = 83B0669D180D5668008E3612 /* Products */; ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */; }, - { - ProductGroup = 835CBC7218DA79F80087A03E /* Products */; - ProjectRef = 835CBC7118DA79F80087A03E /* modplay.xcodeproj */; - }, { ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */; ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */; @@ -2069,12 +1984,12 @@ ProjectRef = 838491791808585C00E7332D /* NDHotKey.xcodeproj */; }, { - ProductGroup = 8375B05217FFEA400092A79F /* Products */; - ProjectRef = 8375B05117FFEA400092A79F /* Opus.xcodeproj */; + ProductGroup = 83E5EFAD1FFEF78100659F0F /* Products */; + ProjectRef = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */; }, { - ProductGroup = 83A0F4851816CE5E00119DB4 /* Products */; - ProjectRef = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; + ProductGroup = 8375B05217FFEA400092A79F /* Products */; + ProjectRef = 8375B05117FFEA400092A79F /* Opus.xcodeproj */; }, { ProductGroup = 8E8D41C30CBB0DA000135C1B /* Products */; @@ -2195,13 +2110,6 @@ remoteRef = 17C8F3CC0CBED663008D969D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 17C8F7D70CBEF3E8008D969D /* Dumb.bundle */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = Dumb.bundle; - remoteRef = 17C8F7D60CBEF3E8008D969D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 17F3BB880CBC565100864489 /* CueSheet.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2265,13 +2173,6 @@ remoteRef = 8359FF3017FEF35D0060F3ED /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 835CBC7618DA79F80087A03E /* modplay.bundle */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = modplay.bundle; - remoteRef = 835CBC7518DA79F80087A03E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 8360EF0517F92B24005208A4 /* HighlyComplete.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2307,20 +2208,6 @@ remoteRef = 8384917D1808585D00E7332D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 839BCFEC1965133F00947767 /* BASSMODS.bundle */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = BASSMODS.bundle; - remoteRef = 839BCFEB1965133F00947767 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 83A0F4891816CE5E00119DB4 /* playptmod.bundle */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = playptmod.bundle; - remoteRef = 83A0F4881816CE5E00119DB4 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 83B066A1180D5669008E3612 /* MIDI.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2328,6 +2215,13 @@ remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 83E5EFB11FFEF78300659F0F /* OpenMPT.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = OpenMPT.bundle; + remoteRef = 83E5EFB01FFEF78300659F0F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 83E6B759181612FD00D4576D /* Sparkle.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -2655,11 +2549,6 @@ name = "GME Plugin"; targetProxy = 17C8F44B0CBEDD37008D969D /* PBXContainerItemProxy */; }; - 17C8F7DA0CBEF3F9008D969D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Dumb; - targetProxy = 17C8F7D90CBEF3F9008D969D /* PBXContainerItemProxy */; - }; 17F3BB8B0CBC566200864489 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = CueSheet; @@ -2700,16 +2589,6 @@ name = NDHotKey; targetProxy = 838491851808591400E7332D /* PBXContainerItemProxy */; }; - 839BD012196521FD00947767 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = BASSMODS; - targetProxy = 839BD011196521FD00947767 /* PBXContainerItemProxy */; - }; - 83A0F4E21816DBE800119DB4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = playptmod; - targetProxy = 83A0F4E11816DBE800119DB4 /* PBXContainerItemProxy */; - }; 83B06703180D5776008E3612 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = MIDI; @@ -2725,6 +2604,11 @@ name = "FFMPEG Plugin"; targetProxy = 83BCB8DC17FC96FC00760340 /* PBXContainerItemProxy */; }; + 83E5FE791FFF0CAC00659F0F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = OpenMPT; + targetProxy = 83E5FE781FFF0CAC00659F0F /* PBXContainerItemProxy */; + }; 83E6B7641816138800D4576D /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Sparkle; diff --git a/Frameworks/OpenMPT/Info.plist b/Frameworks/OpenMPT/Info.plist new file mode 100644 index 000000000..4ca0406e0 --- /dev/null +++ b/Frameworks/OpenMPT/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2016 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/OpenMPT/OpenMPT b/Frameworks/OpenMPT/OpenMPT new file mode 160000 index 000000000..bf3d5ea38 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT @@ -0,0 +1 @@ +Subproject commit bf3d5ea380157da7aa0b7bc58c3d889ac7e31b37 diff --git a/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj new file mode 100644 index 000000000..69f6e4430 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -0,0 +1,1496 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 83E5EFD01FFEF9D200659F0F /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5EFCE1FFEF9D200659F0F /* config.h */; }; + 83E5EFD11FFEF9D200659F0F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 83E5EFCF1FFEF9D200659F0F /* Info.plist */; }; + 83E5F8801FFEF9E400659F0F /* minimp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5F2461FFEF9E100659F0F /* minimp3.c */; }; + 83E5F8811FFEF9E400659F0F /* minimp3.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5F2471FFEF9E100659F0F /* minimp3.h */; }; + 83E5F8821FFEF9E400659F0F /* OpenMPT.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83E5F2481FFEF9E100659F0F /* OpenMPT.txt */; }; + 83E5F8831FFEF9E400659F0F /* LGPL.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83E5F2491FFEF9E100659F0F /* LGPL.txt */; }; + 83E5F8841FFEF9E400659F0F /* libc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5F24A1FFEF9E100659F0F /* libc.h */; }; + 83E5F9831FFEF9E400659F0F /* stb_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5F3691FFEF9E100659F0F /* stb_vorbis.c */; }; + 83E5F9841FFEF9E400659F0F /* OpenMPT.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83E5F36A1FFEF9E100659F0F /* OpenMPT.txt */; }; + 83E5FC621FFEFA0D00659F0F /* typedefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */; }; + 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */; }; + 83E5FC641FFEFA0D00659F0F /* mptLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */; }; + 83E5FC651FFEFA0D00659F0F /* mptIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */; }; + 83E5FC661FFEFA0D00659F0F /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2D1FFEFA0D00659F0F /* version.h */; }; + 83E5FC671FFEFA0D00659F0F /* mptStringParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC2E1FFEFA0D00659F0F /* mptStringParse.cpp */; }; + 83E5FC681FFEFA0D00659F0F /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2F1FFEFA0D00659F0F /* stdafx.h */; }; + 83E5FC691FFEFA0D00659F0F /* ComponentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC301FFEFA0D00659F0F /* ComponentManager.h */; }; + 83E5FC6A1FFEFA0D00659F0F /* Endianness.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC311FFEFA0D00659F0F /* Endianness.h */; }; + 83E5FC6B1FFEFA0D00659F0F /* mptLibrary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */; }; + 83E5FC6C1FFEFA0D00659F0F /* mptStringFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */; }; + 83E5FC6D1FFEFA0D00659F0F /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC341FFEFA0D00659F0F /* Logging.cpp */; }; + 83E5FC6E1FFEFA0D00659F0F /* typedefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC351FFEFA0D00659F0F /* typedefs.h */; }; + 83E5FC6F1FFEFA0D00659F0F /* Profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */; }; + 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC371FFEFA0D00659F0F /* mptMutex.h */; }; + 83E5FC711FFEFA0D00659F0F /* version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC381FFEFA0D00659F0F /* version.cpp */; }; + 83E5FC721FFEFA0D00659F0F /* StringFixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC391FFEFA0D00659F0F /* StringFixer.h */; }; + 83E5FC731FFEFA0D00659F0F /* mptUUID.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */; }; + 83E5FC741FFEFA0D00659F0F /* BuildSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */; }; + 83E5FC751FFEFA0D00659F0F /* WriteMemoryDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */; }; + 83E5FC761FFEFA0D00659F0F /* mptCPU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */; }; + 83E5FC771FFEFA0D00659F0F /* mptTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */; }; + 83E5FC781FFEFA0D00659F0F /* FileReaderFwd.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */; }; + 83E5FC791FFEFA0D00659F0F /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC401FFEFA0D00659F0F /* Logging.h */; }; + 83E5FC7A1FFEFA0D00659F0F /* ComponentManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */; }; + 83E5FC7B1FFEFA0D00659F0F /* mptWine.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC421FFEFA0D00659F0F /* mptWine.h */; }; + 83E5FC7C1FFEFA0D00659F0F /* misc_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC431FFEFA0D00659F0F /* misc_util.h */; }; + 83E5FC7D1FFEFA0D00659F0F /* mptOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */; }; + 83E5FC7E1FFEFA0D00659F0F /* mptTypeTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */; }; + 83E5FC7F1FFEFA0D00659F0F /* serialization_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */; }; + 83E5FC801FFEFA0D00659F0F /* mptFileIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */; }; + 83E5FC811FFEFA0D00659F0F /* mptStringFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */; }; + 83E5FC821FFEFA0D00659F0F /* FileReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC491FFEFA0D00659F0F /* FileReader.cpp */; }; + 83E5FC831FFEFA0D00659F0F /* serialization_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC4A1FFEFA0D00659F0F /* serialization_utils.h */; }; + 83E5FC841FFEFA0D00659F0F /* mptWine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC4B1FFEFA0D00659F0F /* mptWine.cpp */; }; + 83E5FC851FFEFA0D00659F0F /* mptThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC4C1FFEFA0D00659F0F /* mptThread.h */; }; + 83E5FC861FFEFA0D00659F0F /* mptPathString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */; }; + 83E5FC871FFEFA0D00659F0F /* mptUUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC4E1FFEFA0D00659F0F /* mptUUID.cpp */; }; + 83E5FC881FFEFA0D00659F0F /* mptTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC4F1FFEFA0D00659F0F /* mptTime.cpp */; }; + 83E5FC891FFEFA0D00659F0F /* mptString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC501FFEFA0D00659F0F /* mptString.cpp */; }; + 83E5FC8A1FFEFA0D00659F0F /* FlagSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC511FFEFA0D00659F0F /* FlagSet.h */; }; + 83E5FC8B1FFEFA0D00659F0F /* mptFileIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC521FFEFA0D00659F0F /* mptFileIO.cpp */; }; + 83E5FC8C1FFEFA0D00659F0F /* mptString.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC531FFEFA0D00659F0F /* mptString.h */; }; + 83E5FC8D1FFEFA0D00659F0F /* mptStringParse.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */; }; + 83E5FC8E1FFEFA0D00659F0F /* mptRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC551FFEFA0D00659F0F /* mptRandom.h */; }; + 83E5FC8F1FFEFA0D00659F0F /* CompilerDetect.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */; }; + 83E5FC901FFEFA0D00659F0F /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */; }; + 83E5FC911FFEFA0D00659F0F /* FileReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC581FFEFA0D00659F0F /* FileReader.h */; }; + 83E5FC921FFEFA0D00659F0F /* mptPathString.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC591FFEFA0D00659F0F /* mptPathString.h */; }; + 83E5FC931FFEFA0D00659F0F /* Profiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */; }; + 83E5FC941FFEFA0D00659F0F /* mptRandom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */; }; + 83E5FC951FFEFA0D00659F0F /* mptOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */; }; + 83E5FC961FFEFA0D00659F0F /* mptIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC5D1FFEFA0D00659F0F /* mptIO.cpp */; }; + 83E5FC971FFEFA0D00659F0F /* mptCPU.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC5E1FFEFA0D00659F0F /* mptCPU.h */; }; + 83E5FC981FFEFA0D00659F0F /* mptBufferIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC5F1FFEFA0D00659F0F /* mptBufferIO.h */; }; + 83E5FC991FFEFA0D00659F0F /* versionNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC601FFEFA0D00659F0F /* versionNumber.h */; }; + 83E5FC9A1FFEFA0D00659F0F /* misc_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC611FFEFA0D00659F0F /* misc_util.cpp */; }; + 83E5FCCB1FFEFA1A00659F0F /* libopenmpt_impl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC9C1FFEFA1A00659F0F /* libopenmpt_impl.hpp */; }; + 83E5FCCC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC9D1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h */; }; + 83E5FCCD1FFEFA1A00659F0F /* libopenmpt.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC9E1FFEFA1A00659F0F /* libopenmpt.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; + 83E5FCCE1FFEFA1A00659F0F /* libopenmpt.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC9F1FFEFA1A00659F0F /* libopenmpt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83E5FCCF1FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */; }; + 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */; }; + 83E5FCD21FFEFA1A00659F0F /* libopenmpt_modplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */; }; + 83E5FCD31FFEFA1A00659F0F /* libopenmpt_c.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA41FFEFA1A00659F0F /* libopenmpt_c.cpp */; }; + 83E5FCD51FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA61FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp */; }; + 83E5FCD71FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */; }; + 83E5FCD81FFEFA1A00659F0F /* libopenmpt_config.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA91FFEFA1A00659F0F /* libopenmpt_config.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83E5FCDB1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCAC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h */; }; + 83E5FCDC1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCAD1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp */; }; + 83E5FCDD1FFEFA1A00659F0F /* libopenmpt_ext.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCAE1FFEFA1A00659F0F /* libopenmpt_ext.h */; }; + 83E5FCEC1FFEFA1A00659F0F /* libopenmpt_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCBF1FFEFA1A00659F0F /* libopenmpt_impl.cpp */; }; + 83E5FCED1FFEFA1A00659F0F /* libopenmpt_ext.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCC01FFEFA1A00659F0F /* libopenmpt_ext.hpp */; }; + 83E5FCF01FFEFA1A00659F0F /* libopenmpt_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCC51FFEFA1A00659F0F /* libopenmpt_version.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83E5FCF31FFEFA1A00659F0F /* libopenmpt_cxx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCC81FFEFA1A00659F0F /* libopenmpt_cxx.cpp */; }; + 83E5FCF51FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCCA1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h */; }; + 83E5FCFA1FFEFA7400659F0F /* SampleFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCF71FFEFA7400659F0F /* SampleFormat.h */; }; + 83E5FCFB1FFEFA7400659F0F /* SampleFormatCopy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */; }; + 83E5FCFC1FFEFA7400659F0F /* SampleFormatConverters.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCF91FFEFA7400659F0F /* SampleFormatConverters.h */; }; + 83E5FD061FFEFA7D00659F0F /* AGC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCFE1FFEFA7D00659F0F /* AGC.cpp */; }; + 83E5FD071FFEFA7D00659F0F /* Reverb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCFF1FFEFA7D00659F0F /* Reverb.h */; }; + 83E5FD081FFEFA7D00659F0F /* EQ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD001FFEFA7D00659F0F /* EQ.cpp */; }; + 83E5FD091FFEFA7D00659F0F /* DSP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD011FFEFA7D00659F0F /* DSP.cpp */; }; + 83E5FD0A1FFEFA7D00659F0F /* EQ.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD021FFEFA7D00659F0F /* EQ.h */; }; + 83E5FD0B1FFEFA7D00659F0F /* Reverb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD031FFEFA7D00659F0F /* Reverb.cpp */; }; + 83E5FD0C1FFEFA7D00659F0F /* DSP.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD041FFEFA7D00659F0F /* DSP.h */; }; + 83E5FD0D1FFEFA7D00659F0F /* AGC.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD051FFEFA7D00659F0F /* AGC.h */; }; + 83E5FDB71FFEFA8500659F0F /* WAVTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD0F1FFEFA8400659F0F /* WAVTools.cpp */; }; + 83E5FDB81FFEFA8500659F0F /* ITTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD101FFEFA8400659F0F /* ITTools.cpp */; }; + 83E5FDB91FFEFA8500659F0F /* AudioCriticalSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD111FFEFA8400659F0F /* AudioCriticalSection.cpp */; }; + 83E5FDBA1FFEFA8500659F0F /* MixerInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD121FFEFA8400659F0F /* MixerInterface.h */; }; + 83E5FDBB1FFEFA8500659F0F /* Load_stm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD131FFEFA8400659F0F /* Load_stm.cpp */; }; + 83E5FDBC1FFEFA8500659F0F /* MixerLoops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD141FFEFA8400659F0F /* MixerLoops.cpp */; }; + 83E5FDBD1FFEFA8500659F0F /* Load_dbm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD151FFEFA8400659F0F /* Load_dbm.cpp */; }; + 83E5FDBE1FFEFA8500659F0F /* ModChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD161FFEFA8400659F0F /* ModChannel.cpp */; }; + 83E5FDBF1FFEFA8500659F0F /* Load_gdm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD171FFEFA8400659F0F /* Load_gdm.cpp */; }; + 83E5FDC01FFEFA8500659F0F /* SoundFilePlayConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD181FFEFA8400659F0F /* SoundFilePlayConfig.h */; }; + 83E5FDC11FFEFA8500659F0F /* Snd_fx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD191FFEFA8400659F0F /* Snd_fx.cpp */; }; + 83E5FDC21FFEFA8500659F0F /* ModSample.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD1A1FFEFA8400659F0F /* ModSample.h */; }; + 83E5FDC31FFEFA8500659F0F /* MIDIEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD1B1FFEFA8400659F0F /* MIDIEvents.h */; }; + 83E5FDC41FFEFA8500659F0F /* Load_mid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD1C1FFEFA8400659F0F /* Load_mid.cpp */; }; + 83E5FDC51FFEFA8500659F0F /* ModSampleCopy.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD1D1FFEFA8400659F0F /* ModSampleCopy.h */; }; + 83E5FDC61FFEFA8500659F0F /* mod_specifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD1E1FFEFA8400659F0F /* mod_specifications.cpp */; }; + 83E5FDC71FFEFA8500659F0F /* patternContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD1F1FFEFA8400659F0F /* patternContainer.h */; }; + 83E5FDC81FFEFA8500659F0F /* Snd_flt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD201FFEFA8400659F0F /* Snd_flt.cpp */; }; + 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD211FFEFA8400659F0F /* ChunkReader.h */; }; + 83E5FDCA1FFEFA8500659F0F /* ITCompression.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD221FFEFA8400659F0F /* ITCompression.h */; }; + 83E5FDCB1FFEFA8500659F0F /* Load_psm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */; }; + 83E5FDCC1FFEFA8500659F0F /* Dither.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD241FFEFA8400659F0F /* Dither.h */; }; + 83E5FDCD1FFEFA8500659F0F /* S3MTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD251FFEFA8400659F0F /* S3MTools.h */; }; + 83E5FDCE1FFEFA8500659F0F /* Load_far.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD261FFEFA8400659F0F /* Load_far.cpp */; }; + 83E5FDCF1FFEFA8500659F0F /* patternContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */; }; + 83E5FDD01FFEFA8500659F0F /* Load_med.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD281FFEFA8400659F0F /* Load_med.cpp */; }; + 83E5FDD11FFEFA8500659F0F /* Load_dmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD291FFEFA8400659F0F /* Load_dmf.cpp */; }; + 83E5FDD21FFEFA8500659F0F /* MPEGFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD2A1FFEFA8400659F0F /* MPEGFrame.h */; }; + 83E5FDD31FFEFA8500659F0F /* Paula.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD2B1FFEFA8400659F0F /* Paula.cpp */; }; + 83E5FDD41FFEFA8500659F0F /* WAVTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD2C1FFEFA8400659F0F /* WAVTools.h */; }; + 83E5FDD51FFEFA8500659F0F /* mod_specifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD2D1FFEFA8400659F0F /* mod_specifications.h */; }; + 83E5FDD61FFEFA8500659F0F /* modcommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD2E1FFEFA8400659F0F /* modcommand.cpp */; }; + 83E5FDD71FFEFA8500659F0F /* Message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD2F1FFEFA8400659F0F /* Message.cpp */; }; + 83E5FDD81FFEFA8500659F0F /* ITTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD301FFEFA8400659F0F /* ITTools.h */; }; + 83E5FDD91FFEFA8500659F0F /* SoundFilePlayConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD311FFEFA8400659F0F /* SoundFilePlayConfig.cpp */; }; + 83E5FDDA1FFEFA8500659F0F /* RowVisitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD321FFEFA8400659F0F /* RowVisitor.h */; }; + 83E5FDDB1FFEFA8500659F0F /* Load_uax.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD331FFEFA8400659F0F /* Load_uax.cpp */; }; + 83E5FDDC1FFEFA8500659F0F /* PluginMixBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD351FFEFA8400659F0F /* PluginMixBuffer.h */; }; + 83E5FDDD1FFEFA8500659F0F /* PluginStructs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD361FFEFA8400659F0F /* PluginStructs.h */; }; + 83E5FDDE1FFEFA8500659F0F /* LFOPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD371FFEFA8400659F0F /* LFOPlugin.h */; }; + 83E5FDDF1FFEFA8500659F0F /* PlugInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD381FFEFA8400659F0F /* PlugInterface.h */; }; + 83E5FDE01FFEFA8500659F0F /* DigiBoosterEcho.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD391FFEFA8400659F0F /* DigiBoosterEcho.h */; }; + 83E5FDE11FFEFA8500659F0F /* PlugInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */; }; + 83E5FDE21FFEFA8500659F0F /* LFOPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */; }; + 83E5FDE31FFEFA8500659F0F /* OpCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */; }; + 83E5FDE41FFEFA8500659F0F /* PluginEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */; }; + 83E5FDE51FFEFA8500659F0F /* PluginManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */; }; + 83E5FDE61FFEFA8500659F0F /* DigiBoosterEcho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */; }; + 83E5FDE71FFEFA8500659F0F /* DMOPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD411FFEFA8400659F0F /* DMOPlugin.cpp */; }; + 83E5FDE81FFEFA8500659F0F /* Flanger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD421FFEFA8400659F0F /* Flanger.cpp */; }; + 83E5FDE91FFEFA8500659F0F /* Distortion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD431FFEFA8400659F0F /* Distortion.cpp */; }; + 83E5FDEA1FFEFA8500659F0F /* ParamEq.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD441FFEFA8400659F0F /* ParamEq.cpp */; }; + 83E5FDEB1FFEFA8500659F0F /* Echo.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD451FFEFA8400659F0F /* Echo.h */; }; + 83E5FDEC1FFEFA8500659F0F /* Gargle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD461FFEFA8400659F0F /* Gargle.cpp */; }; + 83E5FDED1FFEFA8500659F0F /* I3DL2Reverb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD471FFEFA8400659F0F /* I3DL2Reverb.h */; }; + 83E5FDEE1FFEFA8500659F0F /* I3DL2Reverb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD481FFEFA8400659F0F /* I3DL2Reverb.cpp */; }; + 83E5FDEF1FFEFA8500659F0F /* Compressor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD491FFEFA8400659F0F /* Compressor.cpp */; }; + 83E5FDF01FFEFA8500659F0F /* WavesReverb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD4A1FFEFA8400659F0F /* WavesReverb.h */; }; + 83E5FDF11FFEFA8500659F0F /* ParamEq.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD4B1FFEFA8400659F0F /* ParamEq.h */; }; + 83E5FDF21FFEFA8500659F0F /* WavesReverb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD4C1FFEFA8400659F0F /* WavesReverb.cpp */; }; + 83E5FDF31FFEFA8500659F0F /* Gargle.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD4D1FFEFA8400659F0F /* Gargle.h */; }; + 83E5FDF41FFEFA8500659F0F /* DMOPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD4E1FFEFA8400659F0F /* DMOPlugin.h */; }; + 83E5FDF51FFEFA8500659F0F /* Echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD4F1FFEFA8400659F0F /* Echo.cpp */; }; + 83E5FDF61FFEFA8500659F0F /* Chorus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD501FFEFA8400659F0F /* Chorus.cpp */; }; + 83E5FDF71FFEFA8500659F0F /* Chorus.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD511FFEFA8400659F0F /* Chorus.h */; }; + 83E5FDF81FFEFA8500659F0F /* Compressor.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD521FFEFA8400659F0F /* Compressor.h */; }; + 83E5FDF91FFEFA8500659F0F /* Distortion.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD531FFEFA8400659F0F /* Distortion.h */; }; + 83E5FDFA1FFEFA8500659F0F /* Flanger.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD541FFEFA8400659F0F /* Flanger.h */; }; + 83E5FDFB1FFEFA8500659F0F /* PluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD551FFEFA8400659F0F /* PluginManager.h */; }; + 83E5FDFC1FFEFA8500659F0F /* Load_ams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */; }; + 83E5FDFD1FFEFA8500659F0F /* tuningbase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */; }; + 83E5FDFE1FFEFA8500659F0F /* ContainerUMX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */; }; + 83E5FDFF1FFEFA8500659F0F /* Load_ptm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */; }; + 83E5FE001FFEFA8500659F0F /* SampleIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */; }; + 83E5FE011FFEFA8500659F0F /* Container.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD5B1FFEFA8400659F0F /* Container.h */; }; + 83E5FE021FFEFA8500659F0F /* ModSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD5C1FFEFA8400659F0F /* ModSequence.h */; }; + 83E5FE031FFEFA8500659F0F /* ContainerXPK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */; }; + 83E5FE041FFEFA8500659F0F /* SampleFormatMP3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */; }; + 83E5FE051FFEFA8500659F0F /* tuning.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */; }; + 83E5FE061FFEFA8500659F0F /* Sndfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD601FFEFA8400659F0F /* Sndfile.cpp */; }; + 83E5FE071FFEFA8500659F0F /* ContainerMMCMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD611FFEFA8400659F0F /* ContainerMMCMP.cpp */; }; + 83E5FE081FFEFA8500659F0F /* Load_amf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD621FFEFA8400659F0F /* Load_amf.cpp */; }; + 83E5FE091FFEFA8500659F0F /* Load_669.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD631FFEFA8400659F0F /* Load_669.cpp */; }; + 83E5FE0A1FFEFA8500659F0F /* UMXTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD641FFEFA8400659F0F /* UMXTools.h */; }; + 83E5FE0B1FFEFA8500659F0F /* modsmp_ctrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD651FFEFA8400659F0F /* modsmp_ctrl.cpp */; }; + 83E5FE0C1FFEFA8500659F0F /* Load_mtm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD661FFEFA8400659F0F /* Load_mtm.cpp */; }; + 83E5FE0D1FFEFA8500659F0F /* Message.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD671FFEFA8400659F0F /* Message.h */; }; + 83E5FE0E1FFEFA8500659F0F /* OggStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD681FFEFA8400659F0F /* OggStream.cpp */; }; + 83E5FE0F1FFEFA8500659F0F /* Load_plm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD691FFEFA8400659F0F /* Load_plm.cpp */; }; + 83E5FE101FFEFA8500659F0F /* modcommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD6A1FFEFA8400659F0F /* modcommand.h */; }; + 83E5FE111FFEFA8500659F0F /* Tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD6B1FFEFA8400659F0F /* Tables.cpp */; }; + 83E5FE121FFEFA8500659F0F /* XMTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD6C1FFEFA8400659F0F /* XMTools.h */; }; + 83E5FE131FFEFA8500659F0F /* Load_mod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD6D1FFEFA8400659F0F /* Load_mod.cpp */; }; + 83E5FE141FFEFA8500659F0F /* Load_sfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD6E1FFEFA8400659F0F /* Load_sfx.cpp */; }; + 83E5FE151FFEFA8500659F0F /* Sndmix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD6F1FFEFA8400659F0F /* Sndmix.cpp */; }; + 83E5FE161FFEFA8500659F0F /* load_j2b.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD701FFEFA8400659F0F /* load_j2b.cpp */; }; + 83E5FE171FFEFA8500659F0F /* ModSequence.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD711FFEFA8400659F0F /* ModSequence.cpp */; }; + 83E5FE181FFEFA8500659F0F /* Snd_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD721FFEFA8400659F0F /* Snd_defs.h */; }; + 83E5FE191FFEFA8500659F0F /* MixFuncTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD731FFEFA8400659F0F /* MixFuncTable.h */; }; + 83E5FE1A1FFEFA8500659F0F /* pattern.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD741FFEFA8400659F0F /* pattern.h */; }; + 83E5FE1B1FFEFA8500659F0F /* SampleFormatFLAC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */; }; + 83E5FE1C1FFEFA8500659F0F /* modsmp_ctrl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD761FFEFA8400659F0F /* modsmp_ctrl.h */; }; + 83E5FE1D1FFEFA8500659F0F /* ModInstrument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD771FFEFA8400659F0F /* ModInstrument.cpp */; }; + 83E5FE1E1FFEFA8500659F0F /* Load_mo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD781FFEFA8400659F0F /* Load_mo3.cpp */; }; + 83E5FE1F1FFEFA8500659F0F /* ModSample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD791FFEFA8400659F0F /* ModSample.cpp */; }; + 83E5FE201FFEFA8500659F0F /* Dlsbank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */; }; + 83E5FE211FFEFA8500659F0F /* Tagging.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD7B1FFEFA8400659F0F /* Tagging.h */; }; + 83E5FE221FFEFA8500659F0F /* tuningcollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */; }; + 83E5FE231FFEFA8500659F0F /* Mixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD7D1FFEFA8400659F0F /* Mixer.h */; }; + 83E5FE241FFEFA8500659F0F /* FloatMixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD7E1FFEFA8400659F0F /* FloatMixer.h */; }; + 83E5FE251FFEFA8500659F0F /* Load_itp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD7F1FFEFA8400659F0F /* Load_itp.cpp */; }; + 83E5FE261FFEFA8500659F0F /* AudioCriticalSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD801FFEFA8400659F0F /* AudioCriticalSection.h */; }; + 83E5FE271FFEFA8500659F0F /* Tables.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD811FFEFA8400659F0F /* Tables.h */; }; + 83E5FE281FFEFA8500659F0F /* UpgradeModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD821FFEFA8400659F0F /* UpgradeModule.cpp */; }; + 83E5FE291FFEFA8500659F0F /* tuningbase.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD831FFEFA8400659F0F /* tuningbase.h */; }; + 83E5FE2A1FFEFA8500659F0F /* MIDIMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD841FFEFA8400659F0F /* MIDIMacros.cpp */; }; + 83E5FE2B1FFEFA8500659F0F /* WindowedFIR.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD851FFEFA8400659F0F /* WindowedFIR.h */; }; + 83E5FE2C1FFEFA8500659F0F /* Sndfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD861FFEFA8400659F0F /* Sndfile.h */; }; + 83E5FE2D1FFEFA8500659F0F /* Paula.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD871FFEFA8400659F0F /* Paula.h */; }; + 83E5FE2E1FFEFA8500659F0F /* ContainerPP20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */; }; + 83E5FE2F1FFEFA8500659F0F /* RowVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD891FFEFA8400659F0F /* RowVisitor.cpp */; }; + 83E5FE301FFEFA8500659F0F /* Load_imf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD8A1FFEFA8400659F0F /* Load_imf.cpp */; }; + 83E5FE311FFEFA8500659F0F /* SampleFormatVorbis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */; }; + 83E5FE321FFEFA8500659F0F /* Load_dsm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD8C1FFEFA8400659F0F /* Load_dsm.cpp */; }; + 83E5FE331FFEFA8500659F0F /* ModInstrument.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD8D1FFEFA8400659F0F /* ModInstrument.h */; }; + 83E5FE341FFEFA8500659F0F /* Load_mt2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD8E1FFEFA8400659F0F /* Load_mt2.cpp */; }; + 83E5FE351FFEFA8500659F0F /* MixerSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD8F1FFEFA8400659F0F /* MixerSettings.cpp */; }; + 83E5FE361FFEFA8500659F0F /* S3MTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */; }; + 83E5FE371FFEFA8500659F0F /* Dlsbank.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD911FFEFA8400659F0F /* Dlsbank.h */; }; + 83E5FE381FFEFA8500659F0F /* Load_xm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD921FFEFA8400659F0F /* Load_xm.cpp */; }; + 83E5FE391FFEFA8500659F0F /* IntMixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD931FFEFA8400659F0F /* IntMixer.h */; }; + 83E5FE3A1FFEFA8500659F0F /* MIDIEvents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD941FFEFA8400659F0F /* MIDIEvents.cpp */; }; + 83E5FE3B1FFEFA8500659F0F /* pattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD951FFEFA8400659F0F /* pattern.cpp */; }; + 83E5FE3C1FFEFA8500659F0F /* Load_digi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD961FFEFA8400659F0F /* Load_digi.cpp */; }; + 83E5FE3D1FFEFA8500659F0F /* Load_s3m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */; }; + 83E5FE3E1FFEFA8500659F0F /* tuningCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */; }; + 83E5FE3F1FFEFA8500659F0F /* SampleIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */; }; + 83E5FE401FFEFA8500659F0F /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */; }; + 83E5FE411FFEFA8500659F0F /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9B1FFEFA8400659F0F /* Resampler.h */; }; + 83E5FE421FFEFA8500659F0F /* ModChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */; }; + 83E5FE431FFEFA8500659F0F /* MixerSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */; }; + 83E5FE441FFEFA8500659F0F /* AudioReadTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9E1FFEFA8400659F0F /* AudioReadTarget.h */; }; + 83E5FE451FFEFA8500659F0F /* Load_mdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD9F1FFEFA8400659F0F /* Load_mdl.cpp */; }; + 83E5FE461FFEFA8500659F0F /* WindowedFIR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA01FFEFA8400659F0F /* WindowedFIR.cpp */; }; + 83E5FE471FFEFA8500659F0F /* SampleFormats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */; }; + 83E5FE481FFEFA8500659F0F /* Load_wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA21FFEFA8400659F0F /* Load_wav.cpp */; }; + 83E5FE491FFEFA8500659F0F /* MixerLoops.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FDA31FFEFA8400659F0F /* MixerLoops.h */; }; + 83E5FE4A1FFEFA8500659F0F /* Load_it.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA41FFEFA8400659F0F /* Load_it.cpp */; }; + 83E5FE4B1FFEFA8500659F0F /* tuning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FDA51FFEFA8400659F0F /* tuning.h */; }; + 83E5FE4C1FFEFA8500659F0F /* UMXTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA61FFEFA8400659F0F /* UMXTools.cpp */; }; + 83E5FE4D1FFEFA8500659F0F /* Load_stp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA71FFEFA8400659F0F /* Load_stp.cpp */; }; + 83E5FE4E1FFEFA8500659F0F /* Load_okt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA81FFEFA8400659F0F /* Load_okt.cpp */; }; + 83E5FE4F1FFEFA8500659F0F /* Load_ult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDA91FFEFA8400659F0F /* Load_ult.cpp */; }; + 83E5FE501FFEFA8500659F0F /* MIDIMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FDAA1FFEFA8400659F0F /* MIDIMacros.h */; }; + 83E5FE511FFEFA8500659F0F /* MixFuncTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDAB1FFEFA8400659F0F /* MixFuncTable.cpp */; }; + 83E5FE521FFEFA8500659F0F /* SampleFormatOpus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */; }; + 83E5FE531FFEFA8500659F0F /* OggStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FDAD1FFEFA8400659F0F /* OggStream.h */; }; + 83E5FE541FFEFA8500659F0F /* Fastmix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */; }; + 83E5FE551FFEFA8500659F0F /* Loaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FDAF1FFEFA8400659F0F /* Loaders.h */; }; + 83E5FE561FFEFA8500659F0F /* Tagging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */; }; + 83E5FE571FFEFA8500659F0F /* ITCompression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB11FFEFA8400659F0F /* ITCompression.cpp */; }; + 83E5FE581FFEFA8500659F0F /* Load_dtm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB21FFEFA8400659F0F /* Load_dtm.cpp */; }; + 83E5FE591FFEFA8500659F0F /* MPEGFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB31FFEFA8400659F0F /* MPEGFrame.cpp */; }; + 83E5FE5A1FFEFA8500659F0F /* XMTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB41FFEFA8400659F0F /* XMTools.cpp */; }; + 83E5FE5B1FFEFA8500659F0F /* SampleFormatMediaFoundation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */; }; + 83E5FE5C1FFEFA8500659F0F /* InstrumentExtensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FDB61FFEFA8400659F0F /* InstrumentExtensions.cpp */; }; + 83E5FE611FFEFEA600659F0F /* svn_version.template.subwcrev.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FE5E1FFEFEA600659F0F /* svn_version.template.subwcrev.h */; }; + 83E5FE621FFEFEA600659F0F /* update_svn_version_vs_premake.cmd in Resources */ = {isa = PBXBuildFile; fileRef = 83E5FE5F1FFEFEA600659F0F /* update_svn_version_vs_premake.cmd */; }; + 83E5FE631FFEFEA600659F0F /* svn_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FE601FFEFEA600659F0F /* svn_version.h */; }; + 83E5FE661FFEFFA500659F0F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E5FE651FFEFFA500659F0F /* libz.tbd */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 83E5EFBD1FFEF7CC00659F0F /* OpenMPT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OpenMPT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 83E5EFCE1FFEF9D200659F0F /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = SOURCE_ROOT; }; + 83E5EFCF1FFEF9D200659F0F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; + 83E5F2461FFEF9E100659F0F /* minimp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minimp3.c; sourceTree = ""; }; + 83E5F2471FFEF9E100659F0F /* minimp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minimp3.h; sourceTree = ""; }; + 83E5F2481FFEF9E100659F0F /* OpenMPT.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OpenMPT.txt; sourceTree = ""; }; + 83E5F2491FFEF9E100659F0F /* LGPL.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LGPL.txt; sourceTree = ""; }; + 83E5F24A1FFEF9E100659F0F /* libc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libc.h; sourceTree = ""; }; + 83E5F3691FFEF9E100659F0F /* stb_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stb_vorbis.c; sourceTree = ""; }; + 83E5F36A1FFEF9E100659F0F /* OpenMPT.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OpenMPT.txt; sourceTree = ""; }; + 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = typedefs.cpp; sourceTree = ""; }; + 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptCRC.h; sourceTree = ""; }; + 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptLibrary.h; sourceTree = ""; }; + 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptIO.h; sourceTree = ""; }; + 83E5FC2D1FFEFA0D00659F0F /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; + 83E5FC2E1FFEFA0D00659F0F /* mptStringParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptStringParse.cpp; sourceTree = ""; }; + 83E5FC2F1FFEFA0D00659F0F /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = ""; }; + 83E5FC301FFEFA0D00659F0F /* ComponentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComponentManager.h; sourceTree = ""; }; + 83E5FC311FFEFA0D00659F0F /* Endianness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Endianness.h; sourceTree = ""; }; + 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptLibrary.cpp; sourceTree = ""; }; + 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptStringFormat.h; sourceTree = ""; }; + 83E5FC341FFEFA0D00659F0F /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = ""; }; + 83E5FC351FFEFA0D00659F0F /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = ""; }; + 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Profiler.cpp; sourceTree = ""; }; + 83E5FC371FFEFA0D00659F0F /* mptMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptMutex.h; sourceTree = ""; }; + 83E5FC381FFEFA0D00659F0F /* version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version.cpp; sourceTree = ""; }; + 83E5FC391FFEFA0D00659F0F /* StringFixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringFixer.h; sourceTree = ""; }; + 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptUUID.h; sourceTree = ""; }; + 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuildSettings.h; sourceTree = ""; }; + 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WriteMemoryDump.h; sourceTree = ""; }; + 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptCPU.cpp; sourceTree = ""; }; + 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptTime.h; sourceTree = ""; }; + 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileReaderFwd.h; sourceTree = ""; }; + 83E5FC401FFEFA0D00659F0F /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; + 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComponentManager.cpp; sourceTree = ""; }; + 83E5FC421FFEFA0D00659F0F /* mptWine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptWine.h; sourceTree = ""; }; + 83E5FC431FFEFA0D00659F0F /* misc_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = misc_util.h; sourceTree = ""; }; + 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptOS.cpp; sourceTree = ""; }; + 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptTypeTraits.h; sourceTree = ""; }; + 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization_utils.cpp; sourceTree = ""; }; + 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptFileIO.h; sourceTree = ""; }; + 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptStringFormat.cpp; sourceTree = ""; }; + 83E5FC491FFEFA0D00659F0F /* FileReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileReader.cpp; sourceTree = ""; }; + 83E5FC4A1FFEFA0D00659F0F /* serialization_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = serialization_utils.h; sourceTree = ""; }; + 83E5FC4B1FFEFA0D00659F0F /* mptWine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptWine.cpp; sourceTree = ""; }; + 83E5FC4C1FFEFA0D00659F0F /* mptThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptThread.h; sourceTree = ""; }; + 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptPathString.cpp; sourceTree = ""; }; + 83E5FC4E1FFEFA0D00659F0F /* mptUUID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptUUID.cpp; sourceTree = ""; }; + 83E5FC4F1FFEFA0D00659F0F /* mptTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptTime.cpp; sourceTree = ""; }; + 83E5FC501FFEFA0D00659F0F /* mptString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptString.cpp; sourceTree = ""; }; + 83E5FC511FFEFA0D00659F0F /* FlagSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlagSet.h; sourceTree = ""; }; + 83E5FC521FFEFA0D00659F0F /* mptFileIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptFileIO.cpp; sourceTree = ""; }; + 83E5FC531FFEFA0D00659F0F /* mptString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptString.h; sourceTree = ""; }; + 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptStringParse.h; sourceTree = ""; }; + 83E5FC551FFEFA0D00659F0F /* mptRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptRandom.h; sourceTree = ""; }; + 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompilerDetect.h; sourceTree = ""; }; + 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; }; + 83E5FC581FFEFA0D00659F0F /* FileReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileReader.h; sourceTree = ""; }; + 83E5FC591FFEFA0D00659F0F /* mptPathString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptPathString.h; sourceTree = ""; }; + 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Profiler.h; sourceTree = ""; }; + 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptRandom.cpp; sourceTree = ""; }; + 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptOS.h; sourceTree = ""; }; + 83E5FC5D1FFEFA0D00659F0F /* mptIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptIO.cpp; sourceTree = ""; }; + 83E5FC5E1FFEFA0D00659F0F /* mptCPU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptCPU.h; sourceTree = ""; }; + 83E5FC5F1FFEFA0D00659F0F /* mptBufferIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptBufferIO.h; sourceTree = ""; }; + 83E5FC601FFEFA0D00659F0F /* versionNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = versionNumber.h; sourceTree = ""; }; + 83E5FC611FFEFA0D00659F0F /* misc_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc_util.cpp; sourceTree = ""; }; + 83E5FC9C1FFEFA1A00659F0F /* libopenmpt_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_impl.hpp; sourceTree = ""; }; + 83E5FC9D1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_stream_callbacks_fd.h; sourceTree = ""; }; + 83E5FC9E1FFEFA1A00659F0F /* libopenmpt.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt.hpp; sourceTree = ""; }; + 83E5FC9F1FFEFA1A00659F0F /* libopenmpt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt.h; sourceTree = ""; }; + 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_plugin_settings.hpp; sourceTree = ""; }; + 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_internal.h; sourceTree = ""; }; + 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libopenmpt_modplug.c; sourceTree = ""; }; + 83E5FCA41FFEFA1A00659F0F /* libopenmpt_c.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_c.cpp; sourceTree = ""; }; + 83E5FCA61FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_ext_impl.hpp; sourceTree = ""; }; + 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_modplug_cpp.cpp; sourceTree = ""; }; + 83E5FCA91FFEFA1A00659F0F /* libopenmpt_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_config.h; sourceTree = ""; }; + 83E5FCAC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_stream_callbacks_file.h; sourceTree = ""; }; + 83E5FCAD1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_ext_impl.cpp; sourceTree = ""; }; + 83E5FCAE1FFEFA1A00659F0F /* libopenmpt_ext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_ext.h; sourceTree = ""; }; + 83E5FCBF1FFEFA1A00659F0F /* libopenmpt_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_impl.cpp; sourceTree = ""; }; + 83E5FCC01FFEFA1A00659F0F /* libopenmpt_ext.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_ext.hpp; sourceTree = ""; }; + 83E5FCC51FFEFA1A00659F0F /* libopenmpt_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_version.h; sourceTree = ""; }; + 83E5FCC81FFEFA1A00659F0F /* libopenmpt_cxx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_cxx.cpp; sourceTree = ""; }; + 83E5FCCA1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_stream_callbacks_buffer.h; sourceTree = ""; }; + 83E5FCF71FFEFA7400659F0F /* SampleFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleFormat.h; sourceTree = ""; }; + 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleFormatCopy.h; sourceTree = ""; }; + 83E5FCF91FFEFA7400659F0F /* SampleFormatConverters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleFormatConverters.h; sourceTree = ""; }; + 83E5FCFE1FFEFA7D00659F0F /* AGC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AGC.cpp; sourceTree = ""; }; + 83E5FCFF1FFEFA7D00659F0F /* Reverb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reverb.h; sourceTree = ""; }; + 83E5FD001FFEFA7D00659F0F /* EQ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EQ.cpp; sourceTree = ""; }; + 83E5FD011FFEFA7D00659F0F /* DSP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DSP.cpp; sourceTree = ""; }; + 83E5FD021FFEFA7D00659F0F /* EQ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EQ.h; sourceTree = ""; }; + 83E5FD031FFEFA7D00659F0F /* Reverb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Reverb.cpp; sourceTree = ""; }; + 83E5FD041FFEFA7D00659F0F /* DSP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DSP.h; sourceTree = ""; }; + 83E5FD051FFEFA7D00659F0F /* AGC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AGC.h; sourceTree = ""; }; + 83E5FD0F1FFEFA8400659F0F /* WAVTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WAVTools.cpp; sourceTree = ""; }; + 83E5FD101FFEFA8400659F0F /* ITTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ITTools.cpp; sourceTree = ""; }; + 83E5FD111FFEFA8400659F0F /* AudioCriticalSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioCriticalSection.cpp; sourceTree = ""; }; + 83E5FD121FFEFA8400659F0F /* MixerInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixerInterface.h; sourceTree = ""; }; + 83E5FD131FFEFA8400659F0F /* Load_stm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_stm.cpp; sourceTree = ""; }; + 83E5FD141FFEFA8400659F0F /* MixerLoops.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MixerLoops.cpp; sourceTree = ""; }; + 83E5FD151FFEFA8400659F0F /* Load_dbm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_dbm.cpp; sourceTree = ""; }; + 83E5FD161FFEFA8400659F0F /* ModChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModChannel.cpp; sourceTree = ""; }; + 83E5FD171FFEFA8400659F0F /* Load_gdm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_gdm.cpp; sourceTree = ""; }; + 83E5FD181FFEFA8400659F0F /* SoundFilePlayConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SoundFilePlayConfig.h; sourceTree = ""; }; + 83E5FD191FFEFA8400659F0F /* Snd_fx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Snd_fx.cpp; sourceTree = ""; }; + 83E5FD1A1FFEFA8400659F0F /* ModSample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModSample.h; sourceTree = ""; }; + 83E5FD1B1FFEFA8400659F0F /* MIDIEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIEvents.h; sourceTree = ""; }; + 83E5FD1C1FFEFA8400659F0F /* Load_mid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mid.cpp; sourceTree = ""; }; + 83E5FD1D1FFEFA8400659F0F /* ModSampleCopy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModSampleCopy.h; sourceTree = ""; }; + 83E5FD1E1FFEFA8400659F0F /* mod_specifications.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mod_specifications.cpp; sourceTree = ""; }; + 83E5FD1F1FFEFA8400659F0F /* patternContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = patternContainer.h; sourceTree = ""; }; + 83E5FD201FFEFA8400659F0F /* Snd_flt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Snd_flt.cpp; sourceTree = ""; }; + 83E5FD211FFEFA8400659F0F /* ChunkReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChunkReader.h; sourceTree = ""; }; + 83E5FD221FFEFA8400659F0F /* ITCompression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ITCompression.h; sourceTree = ""; }; + 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_psm.cpp; sourceTree = ""; }; + 83E5FD241FFEFA8400659F0F /* Dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dither.h; sourceTree = ""; }; + 83E5FD251FFEFA8400659F0F /* S3MTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3MTools.h; sourceTree = ""; }; + 83E5FD261FFEFA8400659F0F /* Load_far.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_far.cpp; sourceTree = ""; }; + 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = patternContainer.cpp; sourceTree = ""; }; + 83E5FD281FFEFA8400659F0F /* Load_med.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_med.cpp; sourceTree = ""; }; + 83E5FD291FFEFA8400659F0F /* Load_dmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_dmf.cpp; sourceTree = ""; }; + 83E5FD2A1FFEFA8400659F0F /* MPEGFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEGFrame.h; sourceTree = ""; }; + 83E5FD2B1FFEFA8400659F0F /* Paula.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paula.cpp; sourceTree = ""; }; + 83E5FD2C1FFEFA8400659F0F /* WAVTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAVTools.h; sourceTree = ""; }; + 83E5FD2D1FFEFA8400659F0F /* mod_specifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mod_specifications.h; sourceTree = ""; }; + 83E5FD2E1FFEFA8400659F0F /* modcommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modcommand.cpp; sourceTree = ""; }; + 83E5FD2F1FFEFA8400659F0F /* Message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Message.cpp; sourceTree = ""; }; + 83E5FD301FFEFA8400659F0F /* ITTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ITTools.h; sourceTree = ""; }; + 83E5FD311FFEFA8400659F0F /* SoundFilePlayConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SoundFilePlayConfig.cpp; sourceTree = ""; }; + 83E5FD321FFEFA8400659F0F /* RowVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RowVisitor.h; sourceTree = ""; }; + 83E5FD331FFEFA8400659F0F /* Load_uax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_uax.cpp; sourceTree = ""; }; + 83E5FD351FFEFA8400659F0F /* PluginMixBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginMixBuffer.h; sourceTree = ""; }; + 83E5FD361FFEFA8400659F0F /* PluginStructs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginStructs.h; sourceTree = ""; }; + 83E5FD371FFEFA8400659F0F /* LFOPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFOPlugin.h; sourceTree = ""; }; + 83E5FD381FFEFA8400659F0F /* PlugInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlugInterface.h; sourceTree = ""; }; + 83E5FD391FFEFA8400659F0F /* DigiBoosterEcho.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigiBoosterEcho.h; sourceTree = ""; }; + 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlugInterface.cpp; sourceTree = ""; }; + 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LFOPlugin.cpp; sourceTree = ""; }; + 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpCodes.h; sourceTree = ""; }; + 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginEventQueue.h; sourceTree = ""; }; + 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginManager.cpp; sourceTree = ""; }; + 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DigiBoosterEcho.cpp; sourceTree = ""; }; + 83E5FD411FFEFA8400659F0F /* DMOPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DMOPlugin.cpp; sourceTree = ""; }; + 83E5FD421FFEFA8400659F0F /* Flanger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Flanger.cpp; sourceTree = ""; }; + 83E5FD431FFEFA8400659F0F /* Distortion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Distortion.cpp; sourceTree = ""; }; + 83E5FD441FFEFA8400659F0F /* ParamEq.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParamEq.cpp; sourceTree = ""; }; + 83E5FD451FFEFA8400659F0F /* Echo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Echo.h; sourceTree = ""; }; + 83E5FD461FFEFA8400659F0F /* Gargle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Gargle.cpp; sourceTree = ""; }; + 83E5FD471FFEFA8400659F0F /* I3DL2Reverb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = I3DL2Reverb.h; sourceTree = ""; }; + 83E5FD481FFEFA8400659F0F /* I3DL2Reverb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = I3DL2Reverb.cpp; sourceTree = ""; }; + 83E5FD491FFEFA8400659F0F /* Compressor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Compressor.cpp; sourceTree = ""; }; + 83E5FD4A1FFEFA8400659F0F /* WavesReverb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WavesReverb.h; sourceTree = ""; }; + 83E5FD4B1FFEFA8400659F0F /* ParamEq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParamEq.h; sourceTree = ""; }; + 83E5FD4C1FFEFA8400659F0F /* WavesReverb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WavesReverb.cpp; sourceTree = ""; }; + 83E5FD4D1FFEFA8400659F0F /* Gargle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gargle.h; sourceTree = ""; }; + 83E5FD4E1FFEFA8400659F0F /* DMOPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DMOPlugin.h; sourceTree = ""; }; + 83E5FD4F1FFEFA8400659F0F /* Echo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Echo.cpp; sourceTree = ""; }; + 83E5FD501FFEFA8400659F0F /* Chorus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Chorus.cpp; sourceTree = ""; }; + 83E5FD511FFEFA8400659F0F /* Chorus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chorus.h; sourceTree = ""; }; + 83E5FD521FFEFA8400659F0F /* Compressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Compressor.h; sourceTree = ""; }; + 83E5FD531FFEFA8400659F0F /* Distortion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Distortion.h; sourceTree = ""; }; + 83E5FD541FFEFA8400659F0F /* Flanger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Flanger.h; sourceTree = ""; }; + 83E5FD551FFEFA8400659F0F /* PluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginManager.h; sourceTree = ""; }; + 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_ams.cpp; sourceTree = ""; }; + 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tuningbase.cpp; sourceTree = ""; }; + 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerUMX.cpp; sourceTree = ""; }; + 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_ptm.cpp; sourceTree = ""; }; + 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleIO.h; sourceTree = ""; }; + 83E5FD5B1FFEFA8400659F0F /* Container.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Container.h; sourceTree = ""; }; + 83E5FD5C1FFEFA8400659F0F /* ModSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModSequence.h; sourceTree = ""; }; + 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerXPK.cpp; sourceTree = ""; }; + 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatMP3.cpp; sourceTree = ""; }; + 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tuning.cpp; sourceTree = ""; }; + 83E5FD601FFEFA8400659F0F /* Sndfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sndfile.cpp; sourceTree = ""; }; + 83E5FD611FFEFA8400659F0F /* ContainerMMCMP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerMMCMP.cpp; sourceTree = ""; }; + 83E5FD621FFEFA8400659F0F /* Load_amf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_amf.cpp; sourceTree = ""; }; + 83E5FD631FFEFA8400659F0F /* Load_669.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_669.cpp; sourceTree = ""; }; + 83E5FD641FFEFA8400659F0F /* UMXTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UMXTools.h; sourceTree = ""; }; + 83E5FD651FFEFA8400659F0F /* modsmp_ctrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modsmp_ctrl.cpp; sourceTree = ""; }; + 83E5FD661FFEFA8400659F0F /* Load_mtm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mtm.cpp; sourceTree = ""; }; + 83E5FD671FFEFA8400659F0F /* Message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Message.h; sourceTree = ""; }; + 83E5FD681FFEFA8400659F0F /* OggStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OggStream.cpp; sourceTree = ""; }; + 83E5FD691FFEFA8400659F0F /* Load_plm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_plm.cpp; sourceTree = ""; }; + 83E5FD6A1FFEFA8400659F0F /* modcommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modcommand.h; sourceTree = ""; }; + 83E5FD6B1FFEFA8400659F0F /* Tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tables.cpp; sourceTree = ""; }; + 83E5FD6C1FFEFA8400659F0F /* XMTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMTools.h; sourceTree = ""; }; + 83E5FD6D1FFEFA8400659F0F /* Load_mod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mod.cpp; sourceTree = ""; }; + 83E5FD6E1FFEFA8400659F0F /* Load_sfx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_sfx.cpp; sourceTree = ""; }; + 83E5FD6F1FFEFA8400659F0F /* Sndmix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sndmix.cpp; sourceTree = ""; }; + 83E5FD701FFEFA8400659F0F /* load_j2b.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = load_j2b.cpp; sourceTree = ""; }; + 83E5FD711FFEFA8400659F0F /* ModSequence.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModSequence.cpp; sourceTree = ""; }; + 83E5FD721FFEFA8400659F0F /* Snd_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Snd_defs.h; sourceTree = ""; }; + 83E5FD731FFEFA8400659F0F /* MixFuncTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixFuncTable.h; sourceTree = ""; }; + 83E5FD741FFEFA8400659F0F /* pattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pattern.h; sourceTree = ""; }; + 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatFLAC.cpp; sourceTree = ""; }; + 83E5FD761FFEFA8400659F0F /* modsmp_ctrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modsmp_ctrl.h; sourceTree = ""; }; + 83E5FD771FFEFA8400659F0F /* ModInstrument.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModInstrument.cpp; sourceTree = ""; }; + 83E5FD781FFEFA8400659F0F /* Load_mo3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mo3.cpp; sourceTree = ""; }; + 83E5FD791FFEFA8400659F0F /* ModSample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModSample.cpp; sourceTree = ""; }; + 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dlsbank.cpp; sourceTree = ""; }; + 83E5FD7B1FFEFA8400659F0F /* Tagging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tagging.h; sourceTree = ""; }; + 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tuningcollection.h; sourceTree = ""; }; + 83E5FD7D1FFEFA8400659F0F /* Mixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mixer.h; sourceTree = ""; }; + 83E5FD7E1FFEFA8400659F0F /* FloatMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FloatMixer.h; sourceTree = ""; }; + 83E5FD7F1FFEFA8400659F0F /* Load_itp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_itp.cpp; sourceTree = ""; }; + 83E5FD801FFEFA8400659F0F /* AudioCriticalSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioCriticalSection.h; sourceTree = ""; }; + 83E5FD811FFEFA8400659F0F /* Tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tables.h; sourceTree = ""; }; + 83E5FD821FFEFA8400659F0F /* UpgradeModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UpgradeModule.cpp; sourceTree = ""; }; + 83E5FD831FFEFA8400659F0F /* tuningbase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tuningbase.h; sourceTree = ""; }; + 83E5FD841FFEFA8400659F0F /* MIDIMacros.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MIDIMacros.cpp; sourceTree = ""; }; + 83E5FD851FFEFA8400659F0F /* WindowedFIR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowedFIR.h; sourceTree = ""; }; + 83E5FD861FFEFA8400659F0F /* Sndfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sndfile.h; sourceTree = ""; }; + 83E5FD871FFEFA8400659F0F /* Paula.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paula.h; sourceTree = ""; }; + 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerPP20.cpp; sourceTree = ""; }; + 83E5FD891FFEFA8400659F0F /* RowVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RowVisitor.cpp; sourceTree = ""; }; + 83E5FD8A1FFEFA8400659F0F /* Load_imf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_imf.cpp; sourceTree = ""; }; + 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatVorbis.cpp; sourceTree = ""; }; + 83E5FD8C1FFEFA8400659F0F /* Load_dsm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_dsm.cpp; sourceTree = ""; }; + 83E5FD8D1FFEFA8400659F0F /* ModInstrument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModInstrument.h; sourceTree = ""; }; + 83E5FD8E1FFEFA8400659F0F /* Load_mt2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mt2.cpp; sourceTree = ""; }; + 83E5FD8F1FFEFA8400659F0F /* MixerSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MixerSettings.cpp; sourceTree = ""; }; + 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S3MTools.cpp; sourceTree = ""; }; + 83E5FD911FFEFA8400659F0F /* Dlsbank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dlsbank.h; sourceTree = ""; }; + 83E5FD921FFEFA8400659F0F /* Load_xm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_xm.cpp; sourceTree = ""; }; + 83E5FD931FFEFA8400659F0F /* IntMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntMixer.h; sourceTree = ""; }; + 83E5FD941FFEFA8400659F0F /* MIDIEvents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MIDIEvents.cpp; sourceTree = ""; }; + 83E5FD951FFEFA8400659F0F /* pattern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pattern.cpp; sourceTree = ""; }; + 83E5FD961FFEFA8400659F0F /* Load_digi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_digi.cpp; sourceTree = ""; }; + 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_s3m.cpp; sourceTree = ""; }; + 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tuningCollection.cpp; sourceTree = ""; }; + 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleIO.cpp; sourceTree = ""; }; + 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dither.cpp; sourceTree = ""; }; + 83E5FD9B1FFEFA8400659F0F /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Resampler.h; sourceTree = ""; }; + 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModChannel.h; sourceTree = ""; }; + 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixerSettings.h; sourceTree = ""; }; + 83E5FD9E1FFEFA8400659F0F /* AudioReadTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioReadTarget.h; sourceTree = ""; }; + 83E5FD9F1FFEFA8400659F0F /* Load_mdl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_mdl.cpp; sourceTree = ""; }; + 83E5FDA01FFEFA8400659F0F /* WindowedFIR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WindowedFIR.cpp; sourceTree = ""; }; + 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormats.cpp; sourceTree = ""; }; + 83E5FDA21FFEFA8400659F0F /* Load_wav.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_wav.cpp; sourceTree = ""; }; + 83E5FDA31FFEFA8400659F0F /* MixerLoops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixerLoops.h; sourceTree = ""; }; + 83E5FDA41FFEFA8400659F0F /* Load_it.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_it.cpp; sourceTree = ""; }; + 83E5FDA51FFEFA8400659F0F /* tuning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tuning.h; sourceTree = ""; }; + 83E5FDA61FFEFA8400659F0F /* UMXTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UMXTools.cpp; sourceTree = ""; }; + 83E5FDA71FFEFA8400659F0F /* Load_stp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_stp.cpp; sourceTree = ""; }; + 83E5FDA81FFEFA8400659F0F /* Load_okt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_okt.cpp; sourceTree = ""; }; + 83E5FDA91FFEFA8400659F0F /* Load_ult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_ult.cpp; sourceTree = ""; }; + 83E5FDAA1FFEFA8400659F0F /* MIDIMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIMacros.h; sourceTree = ""; }; + 83E5FDAB1FFEFA8400659F0F /* MixFuncTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MixFuncTable.cpp; sourceTree = ""; }; + 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatOpus.cpp; sourceTree = ""; }; + 83E5FDAD1FFEFA8400659F0F /* OggStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OggStream.h; sourceTree = ""; }; + 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Fastmix.cpp; sourceTree = ""; }; + 83E5FDAF1FFEFA8400659F0F /* Loaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Loaders.h; sourceTree = ""; }; + 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tagging.cpp; sourceTree = ""; }; + 83E5FDB11FFEFA8400659F0F /* ITCompression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ITCompression.cpp; sourceTree = ""; }; + 83E5FDB21FFEFA8400659F0F /* Load_dtm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_dtm.cpp; sourceTree = ""; }; + 83E5FDB31FFEFA8400659F0F /* MPEGFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MPEGFrame.cpp; sourceTree = ""; }; + 83E5FDB41FFEFA8400659F0F /* XMTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XMTools.cpp; sourceTree = ""; }; + 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatMediaFoundation.cpp; sourceTree = ""; }; + 83E5FDB61FFEFA8400659F0F /* InstrumentExtensions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InstrumentExtensions.cpp; sourceTree = ""; }; + 83E5FE5E1FFEFEA600659F0F /* svn_version.template.subwcrev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svn_version.template.subwcrev.h; sourceTree = ""; }; + 83E5FE5F1FFEFEA600659F0F /* update_svn_version_vs_premake.cmd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = update_svn_version_vs_premake.cmd; sourceTree = ""; }; + 83E5FE601FFEFEA600659F0F /* svn_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svn_version.h; sourceTree = ""; }; + 83E5FE651FFEFFA500659F0F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83E5EFB91FFEF7CC00659F0F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5FE661FFEFFA500659F0F /* libz.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83E5EFB31FFEF7CC00659F0F = { + isa = PBXGroup; + children = ( + 83E5EFBF1FFEF7CC00659F0F /* OpenMPT */, + 83E5EFBE1FFEF7CC00659F0F /* Products */, + 83E5FE641FFEFFA400659F0F /* Frameworks */, + ); + sourceTree = ""; + }; + 83E5EFBE1FFEF7CC00659F0F /* Products */ = { + isa = PBXGroup; + children = ( + 83E5EFBD1FFEF7CC00659F0F /* OpenMPT.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83E5EFBF1FFEF7CC00659F0F /* OpenMPT */ = { + isa = PBXGroup; + children = ( + 83E5FE5D1FFEFEA600659F0F /* svn_version */, + 83E5FD0E1FFEFA8400659F0F /* soundlib */, + 83E5FCFD1FFEFA7D00659F0F /* sounddsp */, + 83E5FCF61FFEFA7400659F0F /* soundbase */, + 83E5FC9B1FFEFA1A00659F0F /* libopenmpt */, + 83E5FC281FFEFA0D00659F0F /* common */, + 83E5EFD21FFEF9E000659F0F /* include */, + 83E5EFCE1FFEF9D200659F0F /* config.h */, + 83E5EFCF1FFEF9D200659F0F /* Info.plist */, + ); + path = OpenMPT; + sourceTree = ""; + }; + 83E5EFD21FFEF9E000659F0F /* include */ = { + isa = PBXGroup; + children = ( + 83E5F2451FFEF9E100659F0F /* minimp3 */, + 83E5F3681FFEF9E100659F0F /* stb_vorbis */, + ); + path = include; + sourceTree = ""; + }; + 83E5F2451FFEF9E100659F0F /* minimp3 */ = { + isa = PBXGroup; + children = ( + 83E5F2461FFEF9E100659F0F /* minimp3.c */, + 83E5F2471FFEF9E100659F0F /* minimp3.h */, + 83E5F2481FFEF9E100659F0F /* OpenMPT.txt */, + 83E5F2491FFEF9E100659F0F /* LGPL.txt */, + 83E5F24A1FFEF9E100659F0F /* libc.h */, + ); + path = minimp3; + sourceTree = ""; + }; + 83E5F3681FFEF9E100659F0F /* stb_vorbis */ = { + isa = PBXGroup; + children = ( + 83E5F3691FFEF9E100659F0F /* stb_vorbis.c */, + 83E5F36A1FFEF9E100659F0F /* OpenMPT.txt */, + ); + path = stb_vorbis; + sourceTree = ""; + }; + 83E5FC281FFEFA0D00659F0F /* common */ = { + isa = PBXGroup; + children = ( + 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */, + 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */, + 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */, + 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */, + 83E5FC2D1FFEFA0D00659F0F /* version.h */, + 83E5FC2E1FFEFA0D00659F0F /* mptStringParse.cpp */, + 83E5FC2F1FFEFA0D00659F0F /* stdafx.h */, + 83E5FC301FFEFA0D00659F0F /* ComponentManager.h */, + 83E5FC311FFEFA0D00659F0F /* Endianness.h */, + 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */, + 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */, + 83E5FC341FFEFA0D00659F0F /* Logging.cpp */, + 83E5FC351FFEFA0D00659F0F /* typedefs.h */, + 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */, + 83E5FC371FFEFA0D00659F0F /* mptMutex.h */, + 83E5FC381FFEFA0D00659F0F /* version.cpp */, + 83E5FC391FFEFA0D00659F0F /* StringFixer.h */, + 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */, + 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */, + 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */, + 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */, + 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */, + 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */, + 83E5FC401FFEFA0D00659F0F /* Logging.h */, + 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */, + 83E5FC421FFEFA0D00659F0F /* mptWine.h */, + 83E5FC431FFEFA0D00659F0F /* misc_util.h */, + 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */, + 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */, + 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */, + 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */, + 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */, + 83E5FC491FFEFA0D00659F0F /* FileReader.cpp */, + 83E5FC4A1FFEFA0D00659F0F /* serialization_utils.h */, + 83E5FC4B1FFEFA0D00659F0F /* mptWine.cpp */, + 83E5FC4C1FFEFA0D00659F0F /* mptThread.h */, + 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */, + 83E5FC4E1FFEFA0D00659F0F /* mptUUID.cpp */, + 83E5FC4F1FFEFA0D00659F0F /* mptTime.cpp */, + 83E5FC501FFEFA0D00659F0F /* mptString.cpp */, + 83E5FC511FFEFA0D00659F0F /* FlagSet.h */, + 83E5FC521FFEFA0D00659F0F /* mptFileIO.cpp */, + 83E5FC531FFEFA0D00659F0F /* mptString.h */, + 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */, + 83E5FC551FFEFA0D00659F0F /* mptRandom.h */, + 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */, + 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */, + 83E5FC581FFEFA0D00659F0F /* FileReader.h */, + 83E5FC591FFEFA0D00659F0F /* mptPathString.h */, + 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */, + 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */, + 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */, + 83E5FC5D1FFEFA0D00659F0F /* mptIO.cpp */, + 83E5FC5E1FFEFA0D00659F0F /* mptCPU.h */, + 83E5FC5F1FFEFA0D00659F0F /* mptBufferIO.h */, + 83E5FC601FFEFA0D00659F0F /* versionNumber.h */, + 83E5FC611FFEFA0D00659F0F /* misc_util.cpp */, + ); + path = common; + sourceTree = ""; + }; + 83E5FC9B1FFEFA1A00659F0F /* libopenmpt */ = { + isa = PBXGroup; + children = ( + 83E5FCA41FFEFA1A00659F0F /* libopenmpt_c.cpp */, + 83E5FCA91FFEFA1A00659F0F /* libopenmpt_config.h */, + 83E5FCC81FFEFA1A00659F0F /* libopenmpt_cxx.cpp */, + 83E5FCAD1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp */, + 83E5FCA61FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp */, + 83E5FCAE1FFEFA1A00659F0F /* libopenmpt_ext.h */, + 83E5FCC01FFEFA1A00659F0F /* libopenmpt_ext.hpp */, + 83E5FCBF1FFEFA1A00659F0F /* libopenmpt_impl.cpp */, + 83E5FC9C1FFEFA1A00659F0F /* libopenmpt_impl.hpp */, + 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */, + 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */, + 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */, + 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */, + 83E5FCCA1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h */, + 83E5FC9D1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h */, + 83E5FCAC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h */, + 83E5FCC51FFEFA1A00659F0F /* libopenmpt_version.h */, + 83E5FC9F1FFEFA1A00659F0F /* libopenmpt.h */, + 83E5FC9E1FFEFA1A00659F0F /* libopenmpt.hpp */, + ); + path = libopenmpt; + sourceTree = ""; + }; + 83E5FCF61FFEFA7400659F0F /* soundbase */ = { + isa = PBXGroup; + children = ( + 83E5FCF71FFEFA7400659F0F /* SampleFormat.h */, + 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */, + 83E5FCF91FFEFA7400659F0F /* SampleFormatConverters.h */, + ); + path = soundbase; + sourceTree = ""; + }; + 83E5FCFD1FFEFA7D00659F0F /* sounddsp */ = { + isa = PBXGroup; + children = ( + 83E5FCFE1FFEFA7D00659F0F /* AGC.cpp */, + 83E5FCFF1FFEFA7D00659F0F /* Reverb.h */, + 83E5FD001FFEFA7D00659F0F /* EQ.cpp */, + 83E5FD011FFEFA7D00659F0F /* DSP.cpp */, + 83E5FD021FFEFA7D00659F0F /* EQ.h */, + 83E5FD031FFEFA7D00659F0F /* Reverb.cpp */, + 83E5FD041FFEFA7D00659F0F /* DSP.h */, + 83E5FD051FFEFA7D00659F0F /* AGC.h */, + ); + path = sounddsp; + sourceTree = ""; + }; + 83E5FD0E1FFEFA8400659F0F /* soundlib */ = { + isa = PBXGroup; + children = ( + 83E5FD0F1FFEFA8400659F0F /* WAVTools.cpp */, + 83E5FD101FFEFA8400659F0F /* ITTools.cpp */, + 83E5FD111FFEFA8400659F0F /* AudioCriticalSection.cpp */, + 83E5FD121FFEFA8400659F0F /* MixerInterface.h */, + 83E5FD131FFEFA8400659F0F /* Load_stm.cpp */, + 83E5FD141FFEFA8400659F0F /* MixerLoops.cpp */, + 83E5FD151FFEFA8400659F0F /* Load_dbm.cpp */, + 83E5FD161FFEFA8400659F0F /* ModChannel.cpp */, + 83E5FD171FFEFA8400659F0F /* Load_gdm.cpp */, + 83E5FD181FFEFA8400659F0F /* SoundFilePlayConfig.h */, + 83E5FD191FFEFA8400659F0F /* Snd_fx.cpp */, + 83E5FD1A1FFEFA8400659F0F /* ModSample.h */, + 83E5FD1B1FFEFA8400659F0F /* MIDIEvents.h */, + 83E5FD1C1FFEFA8400659F0F /* Load_mid.cpp */, + 83E5FD1D1FFEFA8400659F0F /* ModSampleCopy.h */, + 83E5FD1E1FFEFA8400659F0F /* mod_specifications.cpp */, + 83E5FD1F1FFEFA8400659F0F /* patternContainer.h */, + 83E5FD201FFEFA8400659F0F /* Snd_flt.cpp */, + 83E5FD211FFEFA8400659F0F /* ChunkReader.h */, + 83E5FD221FFEFA8400659F0F /* ITCompression.h */, + 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */, + 83E5FD241FFEFA8400659F0F /* Dither.h */, + 83E5FD251FFEFA8400659F0F /* S3MTools.h */, + 83E5FD261FFEFA8400659F0F /* Load_far.cpp */, + 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */, + 83E5FD281FFEFA8400659F0F /* Load_med.cpp */, + 83E5FD291FFEFA8400659F0F /* Load_dmf.cpp */, + 83E5FD2A1FFEFA8400659F0F /* MPEGFrame.h */, + 83E5FD2B1FFEFA8400659F0F /* Paula.cpp */, + 83E5FD2C1FFEFA8400659F0F /* WAVTools.h */, + 83E5FD2D1FFEFA8400659F0F /* mod_specifications.h */, + 83E5FD2E1FFEFA8400659F0F /* modcommand.cpp */, + 83E5FD2F1FFEFA8400659F0F /* Message.cpp */, + 83E5FD301FFEFA8400659F0F /* ITTools.h */, + 83E5FD311FFEFA8400659F0F /* SoundFilePlayConfig.cpp */, + 83E5FD321FFEFA8400659F0F /* RowVisitor.h */, + 83E5FD331FFEFA8400659F0F /* Load_uax.cpp */, + 83E5FD341FFEFA8400659F0F /* plugins */, + 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */, + 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */, + 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */, + 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */, + 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */, + 83E5FD5B1FFEFA8400659F0F /* Container.h */, + 83E5FD5C1FFEFA8400659F0F /* ModSequence.h */, + 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */, + 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */, + 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */, + 83E5FD601FFEFA8400659F0F /* Sndfile.cpp */, + 83E5FD611FFEFA8400659F0F /* ContainerMMCMP.cpp */, + 83E5FD621FFEFA8400659F0F /* Load_amf.cpp */, + 83E5FD631FFEFA8400659F0F /* Load_669.cpp */, + 83E5FD641FFEFA8400659F0F /* UMXTools.h */, + 83E5FD651FFEFA8400659F0F /* modsmp_ctrl.cpp */, + 83E5FD661FFEFA8400659F0F /* Load_mtm.cpp */, + 83E5FD671FFEFA8400659F0F /* Message.h */, + 83E5FD681FFEFA8400659F0F /* OggStream.cpp */, + 83E5FD691FFEFA8400659F0F /* Load_plm.cpp */, + 83E5FD6A1FFEFA8400659F0F /* modcommand.h */, + 83E5FD6B1FFEFA8400659F0F /* Tables.cpp */, + 83E5FD6C1FFEFA8400659F0F /* XMTools.h */, + 83E5FD6D1FFEFA8400659F0F /* Load_mod.cpp */, + 83E5FD6E1FFEFA8400659F0F /* Load_sfx.cpp */, + 83E5FD6F1FFEFA8400659F0F /* Sndmix.cpp */, + 83E5FD701FFEFA8400659F0F /* load_j2b.cpp */, + 83E5FD711FFEFA8400659F0F /* ModSequence.cpp */, + 83E5FD721FFEFA8400659F0F /* Snd_defs.h */, + 83E5FD731FFEFA8400659F0F /* MixFuncTable.h */, + 83E5FD741FFEFA8400659F0F /* pattern.h */, + 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */, + 83E5FD761FFEFA8400659F0F /* modsmp_ctrl.h */, + 83E5FD771FFEFA8400659F0F /* ModInstrument.cpp */, + 83E5FD781FFEFA8400659F0F /* Load_mo3.cpp */, + 83E5FD791FFEFA8400659F0F /* ModSample.cpp */, + 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */, + 83E5FD7B1FFEFA8400659F0F /* Tagging.h */, + 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */, + 83E5FD7D1FFEFA8400659F0F /* Mixer.h */, + 83E5FD7E1FFEFA8400659F0F /* FloatMixer.h */, + 83E5FD7F1FFEFA8400659F0F /* Load_itp.cpp */, + 83E5FD801FFEFA8400659F0F /* AudioCriticalSection.h */, + 83E5FD811FFEFA8400659F0F /* Tables.h */, + 83E5FD821FFEFA8400659F0F /* UpgradeModule.cpp */, + 83E5FD831FFEFA8400659F0F /* tuningbase.h */, + 83E5FD841FFEFA8400659F0F /* MIDIMacros.cpp */, + 83E5FD851FFEFA8400659F0F /* WindowedFIR.h */, + 83E5FD861FFEFA8400659F0F /* Sndfile.h */, + 83E5FD871FFEFA8400659F0F /* Paula.h */, + 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */, + 83E5FD891FFEFA8400659F0F /* RowVisitor.cpp */, + 83E5FD8A1FFEFA8400659F0F /* Load_imf.cpp */, + 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */, + 83E5FD8C1FFEFA8400659F0F /* Load_dsm.cpp */, + 83E5FD8D1FFEFA8400659F0F /* ModInstrument.h */, + 83E5FD8E1FFEFA8400659F0F /* Load_mt2.cpp */, + 83E5FD8F1FFEFA8400659F0F /* MixerSettings.cpp */, + 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */, + 83E5FD911FFEFA8400659F0F /* Dlsbank.h */, + 83E5FD921FFEFA8400659F0F /* Load_xm.cpp */, + 83E5FD931FFEFA8400659F0F /* IntMixer.h */, + 83E5FD941FFEFA8400659F0F /* MIDIEvents.cpp */, + 83E5FD951FFEFA8400659F0F /* pattern.cpp */, + 83E5FD961FFEFA8400659F0F /* Load_digi.cpp */, + 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */, + 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */, + 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */, + 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */, + 83E5FD9B1FFEFA8400659F0F /* Resampler.h */, + 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */, + 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */, + 83E5FD9E1FFEFA8400659F0F /* AudioReadTarget.h */, + 83E5FD9F1FFEFA8400659F0F /* Load_mdl.cpp */, + 83E5FDA01FFEFA8400659F0F /* WindowedFIR.cpp */, + 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */, + 83E5FDA21FFEFA8400659F0F /* Load_wav.cpp */, + 83E5FDA31FFEFA8400659F0F /* MixerLoops.h */, + 83E5FDA41FFEFA8400659F0F /* Load_it.cpp */, + 83E5FDA51FFEFA8400659F0F /* tuning.h */, + 83E5FDA61FFEFA8400659F0F /* UMXTools.cpp */, + 83E5FDA71FFEFA8400659F0F /* Load_stp.cpp */, + 83E5FDA81FFEFA8400659F0F /* Load_okt.cpp */, + 83E5FDA91FFEFA8400659F0F /* Load_ult.cpp */, + 83E5FDAA1FFEFA8400659F0F /* MIDIMacros.h */, + 83E5FDAB1FFEFA8400659F0F /* MixFuncTable.cpp */, + 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */, + 83E5FDAD1FFEFA8400659F0F /* OggStream.h */, + 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */, + 83E5FDAF1FFEFA8400659F0F /* Loaders.h */, + 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */, + 83E5FDB11FFEFA8400659F0F /* ITCompression.cpp */, + 83E5FDB21FFEFA8400659F0F /* Load_dtm.cpp */, + 83E5FDB31FFEFA8400659F0F /* MPEGFrame.cpp */, + 83E5FDB41FFEFA8400659F0F /* XMTools.cpp */, + 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */, + 83E5FDB61FFEFA8400659F0F /* InstrumentExtensions.cpp */, + ); + path = soundlib; + sourceTree = ""; + }; + 83E5FD341FFEFA8400659F0F /* plugins */ = { + isa = PBXGroup; + children = ( + 83E5FD351FFEFA8400659F0F /* PluginMixBuffer.h */, + 83E5FD361FFEFA8400659F0F /* PluginStructs.h */, + 83E5FD371FFEFA8400659F0F /* LFOPlugin.h */, + 83E5FD381FFEFA8400659F0F /* PlugInterface.h */, + 83E5FD391FFEFA8400659F0F /* DigiBoosterEcho.h */, + 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */, + 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */, + 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */, + 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */, + 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */, + 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */, + 83E5FD401FFEFA8400659F0F /* dmo */, + 83E5FD551FFEFA8400659F0F /* PluginManager.h */, + ); + path = plugins; + sourceTree = ""; + }; + 83E5FD401FFEFA8400659F0F /* dmo */ = { + isa = PBXGroup; + children = ( + 83E5FD411FFEFA8400659F0F /* DMOPlugin.cpp */, + 83E5FD421FFEFA8400659F0F /* Flanger.cpp */, + 83E5FD431FFEFA8400659F0F /* Distortion.cpp */, + 83E5FD441FFEFA8400659F0F /* ParamEq.cpp */, + 83E5FD451FFEFA8400659F0F /* Echo.h */, + 83E5FD461FFEFA8400659F0F /* Gargle.cpp */, + 83E5FD471FFEFA8400659F0F /* I3DL2Reverb.h */, + 83E5FD481FFEFA8400659F0F /* I3DL2Reverb.cpp */, + 83E5FD491FFEFA8400659F0F /* Compressor.cpp */, + 83E5FD4A1FFEFA8400659F0F /* WavesReverb.h */, + 83E5FD4B1FFEFA8400659F0F /* ParamEq.h */, + 83E5FD4C1FFEFA8400659F0F /* WavesReverb.cpp */, + 83E5FD4D1FFEFA8400659F0F /* Gargle.h */, + 83E5FD4E1FFEFA8400659F0F /* DMOPlugin.h */, + 83E5FD4F1FFEFA8400659F0F /* Echo.cpp */, + 83E5FD501FFEFA8400659F0F /* Chorus.cpp */, + 83E5FD511FFEFA8400659F0F /* Chorus.h */, + 83E5FD521FFEFA8400659F0F /* Compressor.h */, + 83E5FD531FFEFA8400659F0F /* Distortion.h */, + 83E5FD541FFEFA8400659F0F /* Flanger.h */, + ); + path = dmo; + sourceTree = ""; + }; + 83E5FE5D1FFEFEA600659F0F /* svn_version */ = { + isa = PBXGroup; + children = ( + 83E5FE5E1FFEFEA600659F0F /* svn_version.template.subwcrev.h */, + 83E5FE5F1FFEFEA600659F0F /* update_svn_version_vs_premake.cmd */, + 83E5FE601FFEFEA600659F0F /* svn_version.h */, + ); + name = svn_version; + path = build/svn_version; + sourceTree = ""; + }; + 83E5FE641FFEFFA400659F0F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83E5FE651FFEFFA500659F0F /* libz.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 83E5EFBA1FFEF7CC00659F0F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5FCCE1FFEFA1A00659F0F /* libopenmpt.h in Headers */, + 83E5FCCD1FFEFA1A00659F0F /* libopenmpt.hpp in Headers */, + 83E5FCD81FFEFA1A00659F0F /* libopenmpt_config.h in Headers */, + 83E5FCF01FFEFA1A00659F0F /* libopenmpt_version.h in Headers */, + 83E5FC751FFEFA0D00659F0F /* WriteMemoryDump.h in Headers */, + 83E5FDC51FFEFA8500659F0F /* ModSampleCopy.h in Headers */, + 83E5FC7E1FFEFA0D00659F0F /* mptTypeTraits.h in Headers */, + 83E5FE611FFEFEA600659F0F /* svn_version.template.subwcrev.h in Headers */, + 83E5FE2C1FFEFA8500659F0F /* Sndfile.h in Headers */, + 83E5FE191FFEFA8500659F0F /* MixFuncTable.h in Headers */, + 83E5FE531FFEFA8500659F0F /* OggStream.h in Headers */, + 83E5FC8D1FFEFA0D00659F0F /* mptStringParse.h in Headers */, + 83E5FDD51FFEFA8500659F0F /* mod_specifications.h in Headers */, + 83E5FE1C1FFEFA8500659F0F /* modsmp_ctrl.h in Headers */, + 83E5FD071FFEFA7D00659F0F /* Reverb.h in Headers */, + 83E5FE371FFEFA8500659F0F /* Dlsbank.h in Headers */, + 83E5FCD51FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp in Headers */, + 83E5FDDC1FFEFA8500659F0F /* PluginMixBuffer.h in Headers */, + 83E5FDF71FFEFA8500659F0F /* Chorus.h in Headers */, + 83E5FE0A1FFEFA8500659F0F /* UMXTools.h in Headers */, + 83E5FDF31FFEFA8500659F0F /* Gargle.h in Headers */, + 83E5FC6C1FFEFA0D00659F0F /* mptStringFormat.h in Headers */, + 83E5FDBA1FFEFA8500659F0F /* MixerInterface.h in Headers */, + 83E5FDD81FFEFA8500659F0F /* ITTools.h in Headers */, + 83E5FE241FFEFA8500659F0F /* FloatMixer.h in Headers */, + 83E5FDCC1FFEFA8500659F0F /* Dither.h in Headers */, + 83E5FC681FFEFA0D00659F0F /* stdafx.h in Headers */, + 83E5FDD21FFEFA8500659F0F /* MPEGFrame.h in Headers */, + 83E5FDCA1FFEFA8500659F0F /* ITCompression.h in Headers */, + 83E5FDDF1FFEFA8500659F0F /* PlugInterface.h in Headers */, + 83E5FC731FFEFA0D00659F0F /* mptUUID.h in Headers */, + 83E5FDF11FFEFA8500659F0F /* ParamEq.h in Headers */, + 83E5FC661FFEFA0D00659F0F /* version.h in Headers */, + 83E5FC831FFEFA0D00659F0F /* serialization_utils.h in Headers */, + 83E5FC651FFEFA0D00659F0F /* mptIO.h in Headers */, + 83E5FE1A1FFEFA8500659F0F /* pattern.h in Headers */, + 83E5FC8F1FFEFA0D00659F0F /* CompilerDetect.h in Headers */, + 83E5FE211FFEFA8500659F0F /* Tagging.h in Headers */, + 83E5FE101FFEFA8500659F0F /* modcommand.h in Headers */, + 83E5FE331FFEFA8500659F0F /* ModInstrument.h in Headers */, + 83E5FC781FFEFA0D00659F0F /* FileReaderFwd.h in Headers */, + 83E5FDF41FFEFA8500659F0F /* DMOPlugin.h in Headers */, + 83E5FE2B1FFEFA8500659F0F /* WindowedFIR.h in Headers */, + 83E5FCFB1FFEFA7400659F0F /* SampleFormatCopy.h in Headers */, + 83E5FE001FFEFA8500659F0F /* SampleIO.h in Headers */, + 83E5FC721FFEFA0D00659F0F /* StringFixer.h in Headers */, + 83E5FE441FFEFA8500659F0F /* AudioReadTarget.h in Headers */, + 83E5FC8E1FFEFA0D00659F0F /* mptRandom.h in Headers */, + 83E5FE491FFEFA8500659F0F /* MixerLoops.h in Headers */, + 83E5FE411FFEFA8500659F0F /* Resampler.h in Headers */, + 83E5FC8A1FFEFA0D00659F0F /* FlagSet.h in Headers */, + 83E5FDCD1FFEFA8500659F0F /* S3MTools.h in Headers */, + 83E5FCCC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h in Headers */, + 83E5FDD41FFEFA8500659F0F /* WAVTools.h in Headers */, + 83E5FCDB1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h in Headers */, + 83E5FDC31FFEFA8500659F0F /* MIDIEvents.h in Headers */, + 83E5FDF81FFEFA8500659F0F /* Compressor.h in Headers */, + 83E5FE221FFEFA8500659F0F /* tuningcollection.h in Headers */, + 83E5FE121FFEFA8500659F0F /* XMTools.h in Headers */, + 83E5FCCB1FFEFA1A00659F0F /* libopenmpt_impl.hpp in Headers */, + 83E5FC981FFEFA0D00659F0F /* mptBufferIO.h in Headers */, + 83E5FC741FFEFA0D00659F0F /* BuildSettings.h in Headers */, + 83E5FDC01FFEFA8500659F0F /* SoundFilePlayConfig.h in Headers */, + 83E5FCCF1FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp in Headers */, + 83E5FE4B1FFEFA8500659F0F /* tuning.h in Headers */, + 83E5FDFA1FFEFA8500659F0F /* Flanger.h in Headers */, + 83E5FC921FFEFA0D00659F0F /* mptPathString.h in Headers */, + 83E5FC6E1FFEFA0D00659F0F /* typedefs.h in Headers */, + 83E5FC931FFEFA0D00659F0F /* Profiler.h in Headers */, + 83E5FDE31FFEFA8500659F0F /* OpCodes.h in Headers */, + 83E5FCFA1FFEFA7400659F0F /* SampleFormat.h in Headers */, + 83E5FE2D1FFEFA8500659F0F /* Paula.h in Headers */, + 83E5FDDD1FFEFA8500659F0F /* PluginStructs.h in Headers */, + 83E5FDDE1FFEFA8500659F0F /* LFOPlugin.h in Headers */, + 83E5FDC71FFEFA8500659F0F /* patternContainer.h in Headers */, + 83E5FC791FFEFA0D00659F0F /* Logging.h in Headers */, + 83E5FC971FFEFA0D00659F0F /* mptCPU.h in Headers */, + 83E5FC951FFEFA0D00659F0F /* mptOS.h in Headers */, + 83E5FDE01FFEFA8500659F0F /* DigiBoosterEcho.h in Headers */, + 83E5FD0A1FFEFA7D00659F0F /* EQ.h in Headers */, + 83E5FD0D1FFEFA7D00659F0F /* AGC.h in Headers */, + 83E5F8811FFEF9E400659F0F /* minimp3.h in Headers */, + 83E5FE181FFEFA8500659F0F /* Snd_defs.h in Headers */, + 83E5FC641FFEFA0D00659F0F /* mptLibrary.h in Headers */, + 83E5FCDD1FFEFA1A00659F0F /* libopenmpt_ext.h in Headers */, + 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */, + 83E5FDE41FFEFA8500659F0F /* PluginEventQueue.h in Headers */, + 83E5FDF91FFEFA8500659F0F /* Distortion.h in Headers */, + 83E5FE261FFEFA8500659F0F /* AudioCriticalSection.h in Headers */, + 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */, + 83E5FC991FFEFA0D00659F0F /* versionNumber.h in Headers */, + 83E5FC771FFEFA0D00659F0F /* mptTime.h in Headers */, + 83E5FC8C1FFEFA0D00659F0F /* mptString.h in Headers */, + 83E5FE501FFEFA8500659F0F /* MIDIMacros.h in Headers */, + 83E5FC911FFEFA0D00659F0F /* FileReader.h in Headers */, + 83E5FE291FFEFA8500659F0F /* tuningbase.h in Headers */, + 83E5FDEB1FFEFA8500659F0F /* Echo.h in Headers */, + 83E5FC691FFEFA0D00659F0F /* ComponentManager.h in Headers */, + 83E5FC6A1FFEFA0D00659F0F /* Endianness.h in Headers */, + 83E5FCED1FFEFA1A00659F0F /* libopenmpt_ext.hpp in Headers */, + 83E5FE0D1FFEFA8500659F0F /* Message.h in Headers */, + 83E5FCFC1FFEFA7400659F0F /* SampleFormatConverters.h in Headers */, + 83E5FE421FFEFA8500659F0F /* ModChannel.h in Headers */, + 83E5FC851FFEFA0D00659F0F /* mptThread.h in Headers */, + 83E5FE021FFEFA8500659F0F /* ModSequence.h in Headers */, + 83E5FC7C1FFEFA0D00659F0F /* misc_util.h in Headers */, + 83E5FE431FFEFA8500659F0F /* MixerSettings.h in Headers */, + 83E5FE271FFEFA8500659F0F /* Tables.h in Headers */, + 83E5FE551FFEFA8500659F0F /* Loaders.h in Headers */, + 83E5FE631FFEFEA600659F0F /* svn_version.h in Headers */, + 83E5FC801FFEFA0D00659F0F /* mptFileIO.h in Headers */, + 83E5EFD01FFEF9D200659F0F /* config.h in Headers */, + 83E5FE011FFEFA8500659F0F /* Container.h in Headers */, + 83E5F8841FFEF9E400659F0F /* libc.h in Headers */, + 83E5FE391FFEFA8500659F0F /* IntMixer.h in Headers */, + 83E5FDF01FFEFA8500659F0F /* WavesReverb.h in Headers */, + 83E5FDED1FFEFA8500659F0F /* I3DL2Reverb.h in Headers */, + 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */, + 83E5FD0C1FFEFA7D00659F0F /* DSP.h in Headers */, + 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */, + 83E5FDC21FFEFA8500659F0F /* ModSample.h in Headers */, + 83E5FC7B1FFEFA0D00659F0F /* mptWine.h in Headers */, + 83E5FE231FFEFA8500659F0F /* Mixer.h in Headers */, + 83E5FDDA1FFEFA8500659F0F /* RowVisitor.h in Headers */, + 83E5FCF51FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h in Headers */, + 83E5FDFB1FFEFA8500659F0F /* PluginManager.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 83E5EFBC1FFEF7CC00659F0F /* OpenMPT */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83E5EFC51FFEF7CC00659F0F /* Build configuration list for PBXNativeTarget "OpenMPT" */; + buildPhases = ( + 83E5EFB81FFEF7CC00659F0F /* Sources */, + 83E5EFB91FFEF7CC00659F0F /* Frameworks */, + 83E5EFBA1FFEF7CC00659F0F /* Headers */, + 83E5EFBB1FFEF7CC00659F0F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OpenMPT; + productName = OpenMPT; + productReference = 83E5EFBD1FFEF7CC00659F0F /* OpenMPT.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83E5EFB41FFEF7CC00659F0F /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 83E5EFBC1FFEF7CC00659F0F = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83E5EFB71FFEF7CC00659F0F /* Build configuration list for PBXProject "OpenMPT" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83E5EFB31FFEF7CC00659F0F; + productRefGroup = 83E5EFBE1FFEF7CC00659F0F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 83E5EFBC1FFEF7CC00659F0F /* OpenMPT */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 83E5EFBB1FFEF7CC00659F0F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5F8821FFEF9E400659F0F /* OpenMPT.txt in Resources */, + 83E5F8831FFEF9E400659F0F /* LGPL.txt in Resources */, + 83E5EFD11FFEF9D200659F0F /* Info.plist in Resources */, + 83E5FE621FFEFEA600659F0F /* update_svn_version_vs_premake.cmd in Resources */, + 83E5F9841FFEF9E400659F0F /* OpenMPT.txt in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83E5EFB81FFEF7CC00659F0F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5FE3C1FFEFA8500659F0F /* Load_digi.cpp in Sources */, + 83E5FE581FFEFA8500659F0F /* Load_dtm.cpp in Sources */, + 83E5FE1B1FFEFA8500659F0F /* SampleFormatFLAC.cpp in Sources */, + 83E5FDEF1FFEFA8500659F0F /* Compressor.cpp in Sources */, + 83E5FE061FFEFA8500659F0F /* Sndfile.cpp in Sources */, + 83E5FE2E1FFEFA8500659F0F /* ContainerPP20.cpp in Sources */, + 83E5FE511FFEFA8500659F0F /* MixFuncTable.cpp in Sources */, + 83E5FE4C1FFEFA8500659F0F /* UMXTools.cpp in Sources */, + 83E5FE381FFEFA8500659F0F /* Load_xm.cpp in Sources */, + 83E5FDF51FFEFA8500659F0F /* Echo.cpp in Sources */, + 83E5FDB81FFEFA8500659F0F /* ITTools.cpp in Sources */, + 83E5FE471FFEFA8500659F0F /* SampleFormats.cpp in Sources */, + 83E5FDD71FFEFA8500659F0F /* Message.cpp in Sources */, + 83E5FDBC1FFEFA8500659F0F /* MixerLoops.cpp in Sources */, + 83E5FC7D1FFEFA0D00659F0F /* mptOS.cpp in Sources */, + 83E5FDE11FFEFA8500659F0F /* PlugInterface.cpp in Sources */, + 83E5FC871FFEFA0D00659F0F /* mptUUID.cpp in Sources */, + 83E5FE4A1FFEFA8500659F0F /* Load_it.cpp in Sources */, + 83E5FE3D1FFEFA8500659F0F /* Load_s3m.cpp in Sources */, + 83E5FDBF1FFEFA8500659F0F /* Load_gdm.cpp in Sources */, + 83E5FC6B1FFEFA0D00659F0F /* mptLibrary.cpp in Sources */, + 83E5FC8B1FFEFA0D00659F0F /* mptFileIO.cpp in Sources */, + 83E5FCEC1FFEFA1A00659F0F /* libopenmpt_impl.cpp in Sources */, + 83E5FDFF1FFEFA8500659F0F /* Load_ptm.cpp in Sources */, + 83E5FC821FFEFA0D00659F0F /* FileReader.cpp in Sources */, + 83E5FE3A1FFEFA8500659F0F /* MIDIEvents.cpp in Sources */, + 83E5FDD01FFEFA8500659F0F /* Load_med.cpp in Sources */, + 83E5FDC41FFEFA8500659F0F /* Load_mid.cpp in Sources */, + 83E5FE3E1FFEFA8500659F0F /* tuningCollection.cpp in Sources */, + 83E5FDE21FFEFA8500659F0F /* LFOPlugin.cpp in Sources */, + 83E5FDCE1FFEFA8500659F0F /* Load_far.cpp in Sources */, + 83E5FE2A1FFEFA8500659F0F /* MIDIMacros.cpp in Sources */, + 83E5FC761FFEFA0D00659F0F /* mptCPU.cpp in Sources */, + 83E5FE3B1FFEFA8500659F0F /* pattern.cpp in Sources */, + 83E5FE321FFEFA8500659F0F /* Load_dsm.cpp in Sources */, + 83E5FDD11FFEFA8500659F0F /* Load_dmf.cpp in Sources */, + 83E5FC6D1FFEFA0D00659F0F /* Logging.cpp in Sources */, + 83E5FE151FFEFA8500659F0F /* Sndmix.cpp in Sources */, + 83E5FDB71FFEFA8500659F0F /* WAVTools.cpp in Sources */, + 83E5FDEE1FFEFA8500659F0F /* I3DL2Reverb.cpp in Sources */, + 83E5FC9A1FFEFA0D00659F0F /* misc_util.cpp in Sources */, + 83E5FE0C1FFEFA8500659F0F /* Load_mtm.cpp in Sources */, + 83E5FC711FFEFA0D00659F0F /* version.cpp in Sources */, + 83E5FDE61FFEFA8500659F0F /* DigiBoosterEcho.cpp in Sources */, + 83E5FE4F1FFEFA8500659F0F /* Load_ult.cpp in Sources */, + 83E5FC811FFEFA0D00659F0F /* mptStringFormat.cpp in Sources */, + 83E5FC861FFEFA0D00659F0F /* mptPathString.cpp in Sources */, + 83E5FE561FFEFA8500659F0F /* Tagging.cpp in Sources */, + 83E5FDCB1FFEFA8500659F0F /* Load_psm.cpp in Sources */, + 83E5FCD31FFEFA1A00659F0F /* libopenmpt_c.cpp in Sources */, + 83E5FE0B1FFEFA8500659F0F /* modsmp_ctrl.cpp in Sources */, + 83E5FE5A1FFEFA8500659F0F /* XMTools.cpp in Sources */, + 83E5FE281FFEFA8500659F0F /* UpgradeModule.cpp in Sources */, + 83E5FDD91FFEFA8500659F0F /* SoundFilePlayConfig.cpp in Sources */, + 83E5FE131FFEFA8500659F0F /* Load_mod.cpp in Sources */, + 83E5FE4D1FFEFA8500659F0F /* Load_stp.cpp in Sources */, + 83E5FE0E1FFEFA8500659F0F /* OggStream.cpp in Sources */, + 83E5FE451FFEFA8500659F0F /* Load_mdl.cpp in Sources */, + 83E5FDBB1FFEFA8500659F0F /* Load_stm.cpp in Sources */, + 83E5FCD21FFEFA1A00659F0F /* libopenmpt_modplug.c in Sources */, + 83E5FC841FFEFA0D00659F0F /* mptWine.cpp in Sources */, + 83E5FE1D1FFEFA8500659F0F /* ModInstrument.cpp in Sources */, + 83E5FE461FFEFA8500659F0F /* WindowedFIR.cpp in Sources */, + 83E5FE1E1FFEFA8500659F0F /* Load_mo3.cpp in Sources */, + 83E5FE351FFEFA8500659F0F /* MixerSettings.cpp in Sources */, + 83E5FC891FFEFA0D00659F0F /* mptString.cpp in Sources */, + 83E5FDC61FFEFA8500659F0F /* mod_specifications.cpp in Sources */, + 83E5FDD61FFEFA8500659F0F /* modcommand.cpp in Sources */, + 83E5FD091FFEFA7D00659F0F /* DSP.cpp in Sources */, + 83E5FE081FFEFA8500659F0F /* Load_amf.cpp in Sources */, + 83E5FD081FFEFA7D00659F0F /* EQ.cpp in Sources */, + 83E5FDF61FFEFA8500659F0F /* Chorus.cpp in Sources */, + 83E5FE0F1FFEFA8500659F0F /* Load_plm.cpp in Sources */, + 83E5FE3F1FFEFA8500659F0F /* SampleIO.cpp in Sources */, + 83E5FDF21FFEFA8500659F0F /* WavesReverb.cpp in Sources */, + 83E5FE301FFEFA8500659F0F /* Load_imf.cpp in Sources */, + 83E5FCF31FFEFA1A00659F0F /* libopenmpt_cxx.cpp in Sources */, + 83E5FDFC1FFEFA8500659F0F /* Load_ams.cpp in Sources */, + 83E5FDBE1FFEFA8500659F0F /* ModChannel.cpp in Sources */, + 83E5FE041FFEFA8500659F0F /* SampleFormatMP3.cpp in Sources */, + 83E5FE361FFEFA8500659F0F /* S3MTools.cpp in Sources */, + 83E5FE5C1FFEFA8500659F0F /* InstrumentExtensions.cpp in Sources */, + 83E5FDE51FFEFA8500659F0F /* PluginManager.cpp in Sources */, + 83E5FC961FFEFA0D00659F0F /* mptIO.cpp in Sources */, + 83E5FDBD1FFEFA8500659F0F /* Load_dbm.cpp in Sources */, + 83E5FCDC1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp in Sources */, + 83E5FDE71FFEFA8500659F0F /* DMOPlugin.cpp in Sources */, + 83E5FCD71FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp in Sources */, + 83E5FE091FFEFA8500659F0F /* Load_669.cpp in Sources */, + 83E5FDCF1FFEFA8500659F0F /* patternContainer.cpp in Sources */, + 83E5FE141FFEFA8500659F0F /* Load_sfx.cpp in Sources */, + 83E5FE571FFEFA8500659F0F /* ITCompression.cpp in Sources */, + 83E5FE591FFEFA8500659F0F /* MPEGFrame.cpp in Sources */, + 83E5FE341FFEFA8500659F0F /* Load_mt2.cpp in Sources */, + 83E5FC941FFEFA0D00659F0F /* mptRandom.cpp in Sources */, + 83E5FC901FFEFA0D00659F0F /* stdafx.cpp in Sources */, + 83E5FDD31FFEFA8500659F0F /* Paula.cpp in Sources */, + 83E5FE251FFEFA8500659F0F /* Load_itp.cpp in Sources */, + 83E5FE401FFEFA8500659F0F /* Dither.cpp in Sources */, + 83E5FDDB1FFEFA8500659F0F /* Load_uax.cpp in Sources */, + 83E5F8801FFEF9E400659F0F /* minimp3.c in Sources */, + 83E5F9831FFEF9E400659F0F /* stb_vorbis.c in Sources */, + 83E5FC6F1FFEFA0D00659F0F /* Profiler.cpp in Sources */, + 83E5FDC81FFEFA8500659F0F /* Snd_flt.cpp in Sources */, + 83E5FDC11FFEFA8500659F0F /* Snd_fx.cpp in Sources */, + 83E5FE541FFEFA8500659F0F /* Fastmix.cpp in Sources */, + 83E5FE111FFEFA8500659F0F /* Tables.cpp in Sources */, + 83E5FE171FFEFA8500659F0F /* ModSequence.cpp in Sources */, + 83E5FE161FFEFA8500659F0F /* load_j2b.cpp in Sources */, + 83E5FDB91FFEFA8500659F0F /* AudioCriticalSection.cpp in Sources */, + 83E5FE1F1FFEFA8500659F0F /* ModSample.cpp in Sources */, + 83E5FE051FFEFA8500659F0F /* tuning.cpp in Sources */, + 83E5FD0B1FFEFA7D00659F0F /* Reverb.cpp in Sources */, + 83E5FE481FFEFA8500659F0F /* Load_wav.cpp in Sources */, + 83E5FDFD1FFEFA8500659F0F /* tuningbase.cpp in Sources */, + 83E5FE2F1FFEFA8500659F0F /* RowVisitor.cpp in Sources */, + 83E5FDE91FFEFA8500659F0F /* Distortion.cpp in Sources */, + 83E5FE201FFEFA8500659F0F /* Dlsbank.cpp in Sources */, + 83E5FD061FFEFA7D00659F0F /* AGC.cpp in Sources */, + 83E5FDFE1FFEFA8500659F0F /* ContainerUMX.cpp in Sources */, + 83E5FC671FFEFA0D00659F0F /* mptStringParse.cpp in Sources */, + 83E5FDE81FFEFA8500659F0F /* Flanger.cpp in Sources */, + 83E5FC621FFEFA0D00659F0F /* typedefs.cpp in Sources */, + 83E5FE4E1FFEFA8500659F0F /* Load_okt.cpp in Sources */, + 83E5FE521FFEFA8500659F0F /* SampleFormatOpus.cpp in Sources */, + 83E5FC7F1FFEFA0D00659F0F /* serialization_utils.cpp in Sources */, + 83E5FDEA1FFEFA8500659F0F /* ParamEq.cpp in Sources */, + 83E5FE071FFEFA8500659F0F /* ContainerMMCMP.cpp in Sources */, + 83E5FDEC1FFEFA8500659F0F /* Gargle.cpp in Sources */, + 83E5FE311FFEFA8500659F0F /* SampleFormatVorbis.cpp in Sources */, + 83E5FE031FFEFA8500659F0F /* ContainerXPK.cpp in Sources */, + 83E5FC881FFEFA0D00659F0F /* mptTime.cpp in Sources */, + 83E5FC7A1FFEFA0D00659F0F /* ComponentManager.cpp in Sources */, + 83E5FE5B1FFEFA8500659F0F /* SampleFormatMediaFoundation.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 83E5EFC31FFEF7CC00659F0F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "LIBOPENMPT_BUILD=1", + "HAVE_CONFIG_H=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + OpenMPT, + OpenMPT/include/modplug/include, + OpenMPT/include, + ); + MACOSX_DEPLOYMENT_TARGET = 10.7; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + USE_HEADERMAP = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 83E5EFC41FFEF7CC00659F0F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "LIBOPENMPT_BUILD=1", + "HAVE_CONFIG_H=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + OpenMPT, + OpenMPT/include/modplug/include, + OpenMPT/include, + ); + MACOSX_DEPLOYMENT_TARGET = 10.7; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + USE_HEADERMAP = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 83E5EFC61FFEF7CC00659F0F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/OpenMPT/include/foobar2000sdk/foobar2000/shared", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 83E5EFC71FFEF7CC00659F0F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/OpenMPT/include/foobar2000sdk/foobar2000/shared", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83E5EFB71FFEF7CC00659F0F /* Build configuration list for PBXProject "OpenMPT" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83E5EFC31FFEF7CC00659F0F /* Debug */, + 83E5EFC41FFEF7CC00659F0F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83E5EFC51FFEF7CC00659F0F /* Build configuration list for PBXNativeTarget "OpenMPT" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83E5EFC61FFEF7CC00659F0F /* Debug */, + 83E5EFC71FFEF7CC00659F0F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83E5EFB41FFEF7CC00659F0F /* Project object */; +} diff --git a/Frameworks/OpenMPT/config.h b/Frameworks/OpenMPT/config.h new file mode 100644 index 000000000..2a27ef21e --- /dev/null +++ b/Frameworks/OpenMPT/config.h @@ -0,0 +1,17 @@ +#ifndef _LIBOPENMPT_CONFIG_H_ +#define _LIBOPENMPT_CONFIG_H_ + +#if 0 +#define MPT_WITH_MPG123 1 +#define MPT_WITH_OGG 1 +#define MPT_WITH_VORBIS 1 +#define MPT_WITH_VORBISFILE 1 +#else +#define MPT_WITH_MINIMP3 1 +#define MPT_WITH_STBVORBIS 1 +#endif + +#define MPT_WITH_ZLIB 1 + +#endif + diff --git a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj new file mode 100644 index 000000000..819063a04 --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -0,0 +1,375 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 83E5FE671FFF001100659F0F /* OpenMPT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E5EFCD1FFEF7CE00659F0F /* OpenMPT.framework */; }; + 83E5FE691FFF002000659F0F /* OpenMPT.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E5EFCD1FFEF7CE00659F0F /* OpenMPT.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 83E5FE731FFF010C00659F0F /* OMPTDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FE6D1FFF010B00659F0F /* OMPTDecoder.mm */; }; + 83E5FE741FFF010C00659F0F /* OMPTContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FE6F1FFF010B00659F0F /* OMPTContainer.mm */; }; + 83E5FE751FFF010C00659F0F /* OMPTMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FE701FFF010C00659F0F /* OMPTMetadataReader.mm */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 83E5EFCC1FFEF7CE00659F0F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83E5EFC81FFEF7CC00659F0F /* OpenMPT.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83E5EFBD1FFEF7CC00659F0F; + remoteInfo = OpenMPT; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 83E5FE681FFF001800659F0F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 83E5FE691FFF002000659F0F /* OpenMPT.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 83E5EFA31FFEF78100659F0F /* OpenMPT.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenMPT.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 83E5EFA61FFEF78100659F0F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 83E5EFC81FFEF7CC00659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = ../../Frameworks/OpenMPT/OpenMPT.xcodeproj; sourceTree = ""; }; + 83E5FE6B1FFF004D00659F0F /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; + 83E5FE6C1FFF006400659F0F /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../Audio/Plugin.h; sourceTree = ""; }; + 83E5FE6D1FFF010B00659F0F /* OMPTDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OMPTDecoder.mm; path = OpenMPT/OMPTDecoder.mm; sourceTree = ""; }; + 83E5FE6E1FFF010B00659F0F /* OMPTContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OMPTContainer.h; path = OpenMPT/OMPTContainer.h; sourceTree = ""; }; + 83E5FE6F1FFF010B00659F0F /* OMPTContainer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OMPTContainer.mm; path = OpenMPT/OMPTContainer.mm; sourceTree = ""; }; + 83E5FE701FFF010C00659F0F /* OMPTMetadataReader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OMPTMetadataReader.mm; path = OpenMPT/OMPTMetadataReader.mm; sourceTree = ""; }; + 83E5FE711FFF010C00659F0F /* OMPTDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OMPTDecoder.h; path = OpenMPT/OMPTDecoder.h; sourceTree = ""; }; + 83E5FE721FFF010C00659F0F /* OMPTMetadataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OMPTMetadataReader.h; path = OpenMPT/OMPTMetadataReader.h; sourceTree = ""; }; + 83E5FE761FFF076F00659F0F /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../Playlist/PlaylistController.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83E5EFA01FFEF78100659F0F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5FE671FFF001100659F0F /* OpenMPT.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83E5EF9A1FFEF78100659F0F = { + isa = PBXGroup; + children = ( + 83E5FE6A1FFF003900659F0F /* Classes */, + 83E5EFB21FFEF79000659F0F /* Frameworks */, + 83E5EFA51FFEF78100659F0F /* OpenMPT */, + 83E5EFA41FFEF78100659F0F /* Products */, + ); + sourceTree = ""; + }; + 83E5EFA41FFEF78100659F0F /* Products */ = { + isa = PBXGroup; + children = ( + 83E5EFA31FFEF78100659F0F /* OpenMPT.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 83E5EFA51FFEF78100659F0F /* OpenMPT */ = { + isa = PBXGroup; + children = ( + 83E5EFA61FFEF78100659F0F /* Info.plist */, + ); + path = OpenMPT; + sourceTree = ""; + }; + 83E5EFB21FFEF79000659F0F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83E5EFC81FFEF7CC00659F0F /* OpenMPT.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83E5EFC91FFEF7CC00659F0F /* Products */ = { + isa = PBXGroup; + children = ( + 83E5EFCD1FFEF7CE00659F0F /* OpenMPT.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83E5FE6A1FFF003900659F0F /* Classes */ = { + isa = PBXGroup; + children = ( + 83E5FE6B1FFF004D00659F0F /* Logging.h */, + 83E5FE761FFF076F00659F0F /* PlaylistController.h */, + 83E5FE6C1FFF006400659F0F /* Plugin.h */, + 83E5FE6E1FFF010B00659F0F /* OMPTContainer.h */, + 83E5FE6F1FFF010B00659F0F /* OMPTContainer.mm */, + 83E5FE711FFF010C00659F0F /* OMPTDecoder.h */, + 83E5FE6D1FFF010B00659F0F /* OMPTDecoder.mm */, + 83E5FE721FFF010C00659F0F /* OMPTMetadataReader.h */, + 83E5FE701FFF010C00659F0F /* OMPTMetadataReader.mm */, + ); + name = Classes; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 83E5EFA21FFEF78100659F0F /* OpenMPT */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83E5EFA91FFEF78100659F0F /* Build configuration list for PBXNativeTarget "OpenMPT" */; + buildPhases = ( + 83E5EF9F1FFEF78100659F0F /* Sources */, + 83E5EFA01FFEF78100659F0F /* Frameworks */, + 83E5EFA11FFEF78100659F0F /* Resources */, + 83E5FE681FFF001800659F0F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OpenMPT; + productName = OpenMPT; + productReference = 83E5EFA31FFEF78100659F0F /* OpenMPT.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83E5EF9B1FFEF78100659F0F /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 83E5EFA21FFEF78100659F0F = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83E5EF9E1FFEF78100659F0F /* Build configuration list for PBXProject "OpenMPT" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83E5EF9A1FFEF78100659F0F; + productRefGroup = 83E5EFA41FFEF78100659F0F /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 83E5EFC91FFEF7CC00659F0F /* Products */; + ProjectRef = 83E5EFC81FFEF7CC00659F0F /* OpenMPT.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 83E5EFA21FFEF78100659F0F /* OpenMPT */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 83E5EFCD1FFEF7CE00659F0F /* OpenMPT.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = OpenMPT.framework; + remoteRef = 83E5EFCC1FFEF7CE00659F0F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 83E5EFA11FFEF78100659F0F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83E5EF9F1FFEF78100659F0F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83E5FE751FFF010C00659F0F /* OMPTMetadataReader.mm in Sources */, + 83E5FE731FFF010C00659F0F /* OMPTDecoder.mm in Sources */, + 83E5FE741FFF010C00659F0F /* OMPTContainer.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 83E5EFA71FFEF78100659F0F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 83E5EFA81FFEF78100659F0F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 83E5EFAA1FFEF78100659F0F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + INFOPLIST_FILE = OpenMPT/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 83E5EFAB1FFEF78100659F0F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + INFOPLIST_FILE = OpenMPT/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83E5EF9E1FFEF78100659F0F /* Build configuration list for PBXProject "OpenMPT" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83E5EFA71FFEF78100659F0F /* Debug */, + 83E5EFA81FFEF78100659F0F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83E5EFA91FFEF78100659F0F /* Build configuration list for PBXNativeTarget "OpenMPT" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83E5EFAA1FFEF78100659F0F /* Debug */, + 83E5EFAB1FFEF78100659F0F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83E5EF9B1FFEF78100659F0F /* Project object */; +} diff --git a/Plugins/OpenMPT/OpenMPT/Info.plist b/Plugins/OpenMPT/OpenMPT/Info.plist new file mode 100644 index 000000000..494824be3 --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2018 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Plugins/OpenMPT/OpenMPT/OMPTContainer.h b/Plugins/OpenMPT/OpenMPT/OMPTContainer.h new file mode 100755 index 000000000..436120d0d --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTContainer.h @@ -0,0 +1,17 @@ +// +// OMPTContainer.h +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface OMPTContainer : NSObject { + +} + +@end diff --git a/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm b/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm new file mode 100755 index 000000000..77454169f --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm @@ -0,0 +1,79 @@ +// +// OMPTContainer.m +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import "OMPTContainer.h" +#import "OMPTDecoder.h" + +#import "Logging.h" + +@implementation OMPTContainer + ++ (NSArray *)fileTypes +{ + return [OMPTDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return nil; +} + ++ (float)priority +{ + return 1.0f; +} + ++ (NSArray *)urlsForContainerURL:(NSURL *)url +{ + if ([url fragment]) { + // input url already has fragment defined - no need to expand further + return [NSMutableArray arrayWithObject:url]; + } + + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + [source seek:0 whence:SEEK_END]; + long size = [source tell]; + [source seek:0 whence:SEEK_SET]; + + std::vector data( static_cast( size ) ); + + [source read:data.data() amount:size]; + + try { + std::map< std::string, std::string > ctls; + openmpt::module * mod = new openmpt::module( data, std::clog, ctls ); + + NSMutableArray *tracks = [NSMutableArray array]; + + int i; + int subsongs = mod->get_num_subsongs(); + + delete mod; + + for (i = 0; i < subsongs; ++i) { + [tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]]; + } + + return tracks; + } catch ( std::exception & /*e*/ ) { + return 0; + } +} + + +@end diff --git a/Plugins/OpenMPT/OpenMPT/OMPTDecoder.h b/Plugins/OpenMPT/OpenMPT/OMPTDecoder.h new file mode 100755 index 000000000..a1cc674ae --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTDecoder.h @@ -0,0 +1,29 @@ +// +// OMPTDecoder.h +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import + +#include + +#import "Plugin.h" + +@interface OMPTDecoder : NSObject { + openmpt::module *mod; + std::vector left; + std::vector right; + + id source; + long length; +} + +- (void)setSource:(id)s; +- (id)source; +- (void)cleanUp; +@end diff --git a/Plugins/OpenMPT/OpenMPT/OMPTDecoder.mm b/Plugins/OpenMPT/OpenMPT/OMPTDecoder.mm new file mode 100755 index 000000000..37947ed0b --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTDecoder.mm @@ -0,0 +1,201 @@ +// +// OMPTDecoder.m +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import "OMPTDecoder.h" + +#import "Logging.h" + +#import "PlaylistController.h" + +static void g_push_archive_extensions(std::vector & list) +{ + static std::string archive_extensions[] = { + "mdz", "mdr", "s3z", "xmz", "itz", "mptmz" + }; + for (unsigned i = 0, j = 6; i < j; ++i) { + if (list.empty() || std::find(list.begin(), list.end(), archive_extensions[i]) == list.end()) + list.push_back(archive_extensions[i]); + } +} + +@implementation OMPTDecoder + +- (id)init +{ + self = [super init]; + if (self) { + mod = NULL; + } + return self; +} + +- (BOOL)open:(id)s +{ + [self setSource:s]; + + [source seek:0 whence:SEEK_END]; + long size = [source tell]; + [source seek:0 whence:SEEK_SET]; + + std::vector data( static_cast( size ) ); + + [source read:data.data() amount:size]; + + int track_num; + if ([[source.url fragment] length] == 0) + track_num = 0; + else + track_num = [[source.url fragment] intValue]; + + int interp = 8; + NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"]; + if ([resampling isEqualToString:@"zoh"]) + interp = 1; + else if ([resampling isEqualToString:@"blep"]) + interp = 1; + else if ([resampling isEqualToString:@"linear"]) + interp = 2; + else if ([resampling isEqualToString:@"blam"]) + interp = 2; + else if ([resampling isEqualToString:@"cubic"]) + interp = 4; + else if ([resampling isEqualToString:@"sinc"]) + interp = 8; + + try { + std::map< std::string, std::string > ctls; + ctls["seek.sync_samples"] = "1"; + mod = new openmpt::module( data, std::clog, ctls ); + + mod->select_subsong(track_num); + + length = mod->get_duration_seconds() * 44100.0; + + mod->set_repeat_count( IsRepeatOneSet() ? -1 : 0 ); + mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, 0 ); + mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, 100 ); + mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, interp ); + mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, -1 ); + mod->ctl_set( "render.resampler.emulate_amiga", "1" ); + + left.resize( 1024 ); + right.resize( 1024 ); + } catch (std::exception & /*e*/) { + return NO; + } + + [self willChangeValueForKey:@"properties"]; + [self didChangeValueForKey:@"properties"]; + + return YES; +} + +- (NSDictionary *)properties +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"bitrate", + [NSNumber numberWithFloat:44100], @"sampleRate", + [NSNumber numberWithDouble:length], @"totalFrames", + [NSNumber numberWithInt:32], @"bitsPerSample", //Samples are short + [NSNumber numberWithBool:YES], @"floatingPoint", + [NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo + [NSNumber numberWithBool:YES], @"seekable", + @"host", @"endian", + nil]; +} + +- (int)readAudio:(void *)buf frames:(UInt32)frames +{ + mod->set_repeat_count( IsRepeatOneSet() ? -1 : 0 ); + + int total = 0; + while ( total < frames ) { + int framesToRender = 1024; + if ( framesToRender > frames ) + framesToRender = frames; + + std::size_t count = mod->read(44100, framesToRender, left.data(), right.data()); + if ( count == 0 ) + break; + + for ( std::size_t frame = 0; frame < count; frame++ ) { + ((float *)buf)[(total + frame) * 2 + 0] = left[frame]; + ((float *)buf)[(total + frame) * 2 + 1] = right[frame]; + } + + total += count; + + if ( count < framesToRender ) + break; + } + + return total; +} + +- (long)seek:(long)frame +{ + mod->set_position_seconds( frame * (1.0 / 44100.0) ); + + return frame; +} + +- (void)cleanUp +{ + delete mod; + mod = NULL; +} + +- (void)close +{ + [self cleanUp]; + + if (source) { + [source close]; + [self setSource:nil]; + } +} + +- (void)dealloc +{ + [self close]; +} + +- (void)setSource:(id)s +{ + source = s; +} + +- (id)source +{ + return source; +} + ++ (NSArray *)fileTypes +{ + std::vector extensions = openmpt::get_supported_extensions(); + g_push_archive_extensions(extensions); + NSMutableArray * array = [NSMutableArray array]; + + for (std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext) { + [array addObject:[NSString stringWithUTF8String:ext->c_str()]]; + } + + return array; +} + ++ (NSArray *)mimeTypes +{ + return [NSArray arrayWithObjects:@"audio/x-it", @"audio/x-xm", @"audio/x-s3m", @"audio/x-mod", nil]; +} + ++ (float)priority +{ + return 1.0; +} + +@end diff --git a/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.h b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.h new file mode 100644 index 000000000..20acd258a --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.h @@ -0,0 +1,17 @@ +// +// OMPTMetadataReader.h +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface OMPTMetadataReader : NSObject { + +} + +@end diff --git a/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm new file mode 100644 index 000000000..0a3aa4ca3 --- /dev/null +++ b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm @@ -0,0 +1,97 @@ +// +// OMPTMetadataReader.m +// OpenMPT +// +// Created by Christopher Snowhill on 1/4/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import "OMPTMetadataReader.h" +#import "OMPTDecoder.h" + +#import + +#import "Logging.H" + +@implementation OMPTMetadataReader + ++ (NSArray *)fileTypes +{ + return [OMPTDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return [OMPTDecoder mimeTypes]; +} + ++ (float)priority +{ + return 1.0f; +} + ++ (NSDictionary *)metadataForURL:(NSURL *)url +{ + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + [source seek:0 whence:SEEK_END]; + long size = [source tell]; + [source seek:0 whence:SEEK_SET]; + + std::vector data( static_cast( size ) ); + + [source read:data.data() amount:size]; + + int track_num; + if ([[url fragment] length] == 0) + track_num = 0; + else + track_num = [[url fragment] intValue]; + + try { + std::map< std::string, std::string > ctls; + openmpt::module * mod = new openmpt::module( data, std::clog, ctls ); + + NSString * title = nil; + NSString * artist = nil; + //NSString * comment = nil; + NSString * date = nil; + + std::vector keys = mod->get_metadata_keys(); + + for ( std::vector::iterator key = keys.begin(); key != keys.end(); ++key ) { + if ( *key == "title" ) + title = [NSString stringWithUTF8String: mod->get_metadata( *key ).c_str()]; + else if ( *key == "artist" ) + artist = [NSString stringWithUTF8String: mod->get_metadata( *key ).c_str()]; + /*else if ( *key == "message" ) + comment = [NSString stringWithUTF8String: mod->get_metadata( *key ).c_str()];*/ + else if ( *key == "date" ) + date = [NSString stringWithUTF8String: mod->get_metadata( *key ).c_str()]; + } + + delete mod; + + if (title == nil) + title = @""; + if (artist == nil) + artist = @""; + /*if (comment == nil) + comment = @"";*/ + if (date == nil) + date = @""; + + return [NSDictionary dictionaryWithObjectsAndKeys:title, @"title", artist, @"artist", /*comment, @"comment",*/ date, @"year", nil]; + } catch (std::exception & /*e*/) { + return 0; + } +} + +@end From 5ca94b626d890f442bed798169ee59792847e588 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 4 Jan 2018 18:09:02 -0800 Subject: [PATCH 014/104] Fix libopenmpt Framework rpath so it loads. --- Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj index 69f6e4430..9adcf7f6b 100644 --- a/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -1429,7 +1429,6 @@ DEVELOPMENT_TEAM = N6E749HJ2X; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; @@ -1454,7 +1453,6 @@ DEVELOPMENT_TEAM = N6E749HJ2X; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; From ddc3da7576dfef6c27f00b8e10dc6972b712264e Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 8 Jan 2018 18:54:28 -0800 Subject: [PATCH 015/104] Updated VGMStream to r1050-925-g2b92a562, now with Atrac9 support. --- .../libatrac9.xcodeproj/project.pbxproj | 441 ++++++ Frameworks/libatrac9/libatrac9/Info.plist | 26 + .../libatrac9/LibAtrac9/.gitattributes | 63 + .../libatrac9/libatrac9/LibAtrac9/.gitignore | 261 ++++ .../libatrac9/LibAtrac9/C/band_extension.c | 191 +++ .../libatrac9/LibAtrac9/C/band_extension.h | 13 + .../libatrac9/LibAtrac9/C/bit_allocation.c | 119 ++ .../libatrac9/LibAtrac9/C/bit_allocation.h | 6 + .../libatrac9/LibAtrac9/C/bit_reader.c | 110 ++ .../libatrac9/LibAtrac9/C/bit_reader.h | 13 + .../libatrac9/libatrac9/LibAtrac9/C/decinit.c | 188 +++ .../libatrac9/libatrac9/LibAtrac9/C/decinit.h | 19 + .../libatrac9/libatrac9/LibAtrac9/C/decoder.c | 114 ++ .../libatrac9/libatrac9/LibAtrac9/C/decoder.h | 11 + .../libatrac9/LibAtrac9/C/error_codes.h | 33 + .../libatrac9/LibAtrac9/C/huffCodes.c | 152 ++ .../libatrac9/LibAtrac9/C/huffCodes.h | 1295 ++++++++++++++++ .../libatrac9/libatrac9/LibAtrac9/C/imdct.c | 80 + .../libatrac9/libatrac9/LibAtrac9/C/imdct.h | 6 + .../libatrac9/LibAtrac9/C/libatrac9.c | 33 + .../libatrac9/LibAtrac9/C/libatrac9.h | 32 + .../libatrac9/LibAtrac9/C/libatrac9.sln | 31 + .../libatrac9/LibAtrac9/C/libatrac9.vcxproj | 162 ++ .../LibAtrac9/C/libatrac9.vcxproj.filters | 105 ++ .../libatrac9/LibAtrac9/C/quantization.c | 54 + .../libatrac9/LibAtrac9/C/quantization.h | 8 + .../libatrac9/LibAtrac9/C/scale_factors.c | 146 ++ .../libatrac9/LibAtrac9/C/scale_factors.h | 8 + .../libatrac9/LibAtrac9/C/structures.h | 160 ++ .../libatrac9/libatrac9/LibAtrac9/C/tables.c | 7 + .../libatrac9/libatrac9/LibAtrac9/C/tables.h | 389 +++++ .../libatrac9/libatrac9/LibAtrac9/C/unpack.c | 423 ++++++ .../libatrac9/libatrac9/LibAtrac9/C/unpack.h | 23 + .../libatrac9/libatrac9/LibAtrac9/C/utility.c | 30 + .../libatrac9/libatrac9/LibAtrac9/C/utility.h | 15 + .../libatrac9/LibAtrac9/CSharp/LibAtrac9.sln | 42 + .../CSharp/LibAtrac9/Atrac9Config.cs | 119 ++ .../CSharp/LibAtrac9/Atrac9Decoder.cs | 127 ++ .../LibAtrac9/CSharp/LibAtrac9/Atrac9Rng.cs | 33 + .../CSharp/LibAtrac9/BandExtension.cs | 372 +++++ .../CSharp/LibAtrac9/BitAllocation.cs | 140 ++ .../LibAtrac9/CSharp/LibAtrac9/Block.cs | 91 ++ .../LibAtrac9/CSharp/LibAtrac9/Channel.cs | 48 + .../CSharp/LibAtrac9/ChannelConfig.cs | 33 + .../LibAtrac9/CSharp/LibAtrac9/Frame.cs | 20 + .../CSharp/LibAtrac9/HuffmanCodebook.cs | 62 + .../CSharp/LibAtrac9/HuffmanCodebooks.cs | 1352 +++++++++++++++++ .../CSharp/LibAtrac9/LibAtrac9.csproj | 8 + .../CSharp/LibAtrac9/Quantization.cs | 57 + .../CSharp/LibAtrac9/ScaleFactors.cs | 171 +++ .../LibAtrac9/CSharp/LibAtrac9/Stereo.cs | 33 + .../LibAtrac9/CSharp/LibAtrac9/Tables.cs | 115 ++ .../LibAtrac9/CSharp/LibAtrac9/Unpack.cs | 425 ++++++ .../CSharp/LibAtrac9/Utilities/Bit.cs | 22 + .../CSharp/LibAtrac9/Utilities/BitReader.cs | 132 ++ .../CSharp/LibAtrac9/Utilities/Helpers.cs | 50 + .../CSharp/LibAtrac9/Utilities/Mdct.cs | 177 +++ .../vgmstream.xcodeproj/project.pbxproj | 70 + .../vgmstream/src/coding/at3_decoder.c | 17 +- .../vgmstream/src/coding/atrac9_decoder.c | 219 +++ .../vgmstream/vgmstream/src/coding/coding.h | 29 +- .../src/coding/ffmpeg_decoder_utils_ea_xma.c | 13 +- .../vgmstream/src/coding/ima_decoder.c | 5 +- .../vgmstream/src/coding/mpeg_decoder.c | 4 +- .../vgmstream/src/coding/pcm_decoder.c | 114 +- .../vgmstream/src/coding/psx_decoder.c | 31 +- .../src/coding/vorbis_custom_decoder.c | 2 +- .../vgmstream/src/coding/xa_decoder.c | 4 - Frameworks/vgmstream/vgmstream/src/formats.c | 16 +- .../vgmstream/src/layout/blocked_ea_1snh.c | 63 +- .../vgmstream/src/layout/blocked_ea_schl.c | 62 +- .../vgmstream/src/layout/xa_blocked.c | 7 +- Frameworks/vgmstream/vgmstream/src/meta/ahx.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/awc.c | 2 +- .../vgmstream/vgmstream/src/meta/ea_1snh.c | 137 +- .../vgmstream/vgmstream/src/meta/ea_eaac.c | 22 +- .../vgmstream/vgmstream/src/meta/ea_schl.c | 99 +- Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 2 +- .../vgmstream/vgmstream/src/meta/fsb5.c | 87 +- .../vgmstream/vgmstream/src/meta/genh.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/gtd.c | 50 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 76 +- .../vgmstream/vgmstream/src/meta/ktss.c | 49 + .../vgmstream/vgmstream/src/meta/meta.h | 4 +- .../vgmstream/vgmstream/src/meta/ngc_vid1.c | 2 +- .../vgmstream/src/meta/ogg_vorbis_file.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/ogl.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/p3d.c | 2 +- .../vgmstream/vgmstream/src/meta/ps3_msf.c | 2 +- .../vgmstream/vgmstream/src/meta/riff.c | 861 ++++------- .../vgmstream/vgmstream/src/meta/sk_aud.c | 2 +- .../vgmstream/vgmstream/src/meta/sqex_scd.c | 2 +- Frameworks/vgmstream/vgmstream/src/meta/sxd.c | 98 +- .../vgmstream/vgmstream/src/meta/ta_aac.c | 47 + .../vgmstream/vgmstream/src/meta/txth.c | 2 +- .../vgmstream/vgmstream/src/meta/ubi_raki.c | 60 +- .../vgmstream/vgmstream/src/meta/wwise.c | 27 +- Frameworks/vgmstream/vgmstream/src/meta/xnb.c | 123 ++ .../vgmstream/vgmstream/src/meta/xvag.c | 122 +- .../vgmstream/vgmstream/src/vgmstream.c | 238 +-- .../vgmstream/vgmstream/src/vgmstream.h | 461 +++--- 101 files changed, 10387 insertions(+), 1250 deletions(-) create mode 100644 Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj create mode 100644 Frameworks/libatrac9/libatrac9/Info.plist create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/.gitattributes create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/.gitignore create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/error_codes.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.sln create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj.filters create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/structures.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.c create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.h create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9.sln create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Config.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Decoder.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Rng.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BandExtension.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BitAllocation.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Block.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Channel.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ChannelConfig.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Frame.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebook.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebooks.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/LibAtrac9.csproj create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Quantization.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ScaleFactors.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Stereo.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Tables.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Unpack.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Bit.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/BitReader.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Helpers.cs create mode 100644 Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Mdct.cs create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ktss.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/xnb.c diff --git a/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj b/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj new file mode 100644 index 000000000..bf4b58be3 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj @@ -0,0 +1,441 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 830EBDF020045FF80023AA10 /* tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDB920045FF80023AA10 /* tables.c */; }; + 830EBDF120045FF80023AA10 /* libatrac9.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBA20045FF80023AA10 /* libatrac9.c */; }; + 830EBDF220045FF80023AA10 /* huffCodes.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBB20045FF80023AA10 /* huffCodes.c */; }; + 830EBDF320045FF80023AA10 /* imdct.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBC20045FF80023AA10 /* imdct.c */; }; + 830EBDF420045FF80023AA10 /* scale_factors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDBD20045FF80023AA10 /* scale_factors.h */; }; + 830EBDF520045FF80023AA10 /* utility.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBE20045FF80023AA10 /* utility.c */; }; + 830EBDF620045FF80023AA10 /* quantization.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDBF20045FF80023AA10 /* quantization.h */; }; + 830EBDF720045FF80023AA10 /* band_extension.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC020045FF80023AA10 /* band_extension.c */; }; + 830EBDF820045FF80023AA10 /* unpack.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC120045FF80023AA10 /* unpack.c */; }; + 830EBDF920045FF80023AA10 /* bit_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC220045FF80023AA10 /* bit_reader.h */; }; + 830EBDFA20045FF80023AA10 /* decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC320045FF80023AA10 /* decoder.h */; }; + 830EBDFB20045FF80023AA10 /* decinit.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC420045FF80023AA10 /* decinit.c */; }; + 830EBDFC20045FF80023AA10 /* bit_allocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC520045FF80023AA10 /* bit_allocation.h */; }; + 830EBDFE20045FF80023AA10 /* libatrac9.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC720045FF80023AA10 /* libatrac9.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 830EBDFF20045FF80023AA10 /* tables.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC820045FF80023AA10 /* tables.h */; }; + 830EBE0020045FF80023AA10 /* scale_factors.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC920045FF80023AA10 /* scale_factors.c */; }; + 830EBE0120045FF80023AA10 /* error_codes.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCA20045FF80023AA10 /* error_codes.h */; }; + 830EBE0220045FF80023AA10 /* imdct.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCB20045FF80023AA10 /* imdct.h */; }; + 830EBE0420045FF80023AA10 /* huffCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCD20045FF80023AA10 /* huffCodes.h */; }; + 830EBE0520045FF80023AA10 /* decinit.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCE20045FF80023AA10 /* decinit.h */; }; + 830EBE0620045FF80023AA10 /* decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDCF20045FF80023AA10 /* decoder.c */; }; + 830EBE0720045FF80023AA10 /* bit_reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD020045FF80023AA10 /* bit_reader.c */; }; + 830EBE0820045FF80023AA10 /* unpack.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD120045FF80023AA10 /* unpack.h */; }; + 830EBE0A20045FF80023AA10 /* quantization.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD320045FF80023AA10 /* quantization.c */; }; + 830EBE0B20045FF80023AA10 /* utility.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD420045FF80023AA10 /* utility.h */; }; + 830EBE0C20045FF80023AA10 /* band_extension.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD520045FF80023AA10 /* band_extension.h */; }; + 830EBE0D20045FF80023AA10 /* bit_allocation.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD620045FF80023AA10 /* bit_allocation.c */; }; + 830EBE0E20045FF80023AA10 /* structures.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD720045FF80023AA10 /* structures.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 830EBD8720045F190023AA10 /* libatrac9.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libatrac9.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 830EBD8B20045F190023AA10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 830EBDB920045FF80023AA10 /* tables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tables.c; sourceTree = ""; }; + 830EBDBA20045FF80023AA10 /* libatrac9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libatrac9.c; sourceTree = ""; }; + 830EBDBB20045FF80023AA10 /* huffCodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = huffCodes.c; sourceTree = ""; }; + 830EBDBC20045FF80023AA10 /* imdct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imdct.c; sourceTree = ""; }; + 830EBDBD20045FF80023AA10 /* scale_factors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scale_factors.h; sourceTree = ""; }; + 830EBDBE20045FF80023AA10 /* utility.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utility.c; sourceTree = ""; }; + 830EBDBF20045FF80023AA10 /* quantization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quantization.h; sourceTree = ""; }; + 830EBDC020045FF80023AA10 /* band_extension.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = band_extension.c; sourceTree = ""; }; + 830EBDC120045FF80023AA10 /* unpack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = ""; }; + 830EBDC220045FF80023AA10 /* bit_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_reader.h; sourceTree = ""; }; + 830EBDC320045FF80023AA10 /* decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoder.h; sourceTree = ""; }; + 830EBDC420045FF80023AA10 /* decinit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decinit.c; sourceTree = ""; }; + 830EBDC520045FF80023AA10 /* bit_allocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_allocation.h; sourceTree = ""; }; + 830EBDC720045FF80023AA10 /* libatrac9.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libatrac9.h; sourceTree = ""; }; + 830EBDC820045FF80023AA10 /* tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tables.h; sourceTree = ""; }; + 830EBDC920045FF80023AA10 /* scale_factors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scale_factors.c; sourceTree = ""; }; + 830EBDCA20045FF80023AA10 /* error_codes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error_codes.h; sourceTree = ""; }; + 830EBDCB20045FF80023AA10 /* imdct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imdct.h; sourceTree = ""; }; + 830EBDCD20045FF80023AA10 /* huffCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huffCodes.h; sourceTree = ""; }; + 830EBDCE20045FF80023AA10 /* decinit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decinit.h; sourceTree = ""; }; + 830EBDCF20045FF80023AA10 /* decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decoder.c; sourceTree = ""; }; + 830EBDD020045FF80023AA10 /* bit_reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bit_reader.c; sourceTree = ""; }; + 830EBDD120045FF80023AA10 /* unpack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unpack.h; sourceTree = ""; }; + 830EBDD320045FF80023AA10 /* quantization.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = quantization.c; sourceTree = ""; }; + 830EBDD420045FF80023AA10 /* utility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utility.h; sourceTree = ""; }; + 830EBDD520045FF80023AA10 /* band_extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = band_extension.h; sourceTree = ""; }; + 830EBDD620045FF80023AA10 /* bit_allocation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bit_allocation.c; sourceTree = ""; }; + 830EBDD720045FF80023AA10 /* structures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = structures.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 830EBD8320045F190023AA10 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 830EBD7D20045F190023AA10 = { + isa = PBXGroup; + children = ( + 830EBD8920045F190023AA10 /* libatrac9 */, + 830EBD8820045F190023AA10 /* Products */, + ); + sourceTree = ""; + }; + 830EBD8820045F190023AA10 /* Products */ = { + isa = PBXGroup; + children = ( + 830EBD8720045F190023AA10 /* libatrac9.framework */, + ); + name = Products; + sourceTree = ""; + }; + 830EBD8920045F190023AA10 /* libatrac9 */ = { + isa = PBXGroup; + children = ( + 830EBD9C20045FF80023AA10 /* LibAtrac9 */, + 830EBD8B20045F190023AA10 /* Info.plist */, + ); + path = libatrac9; + sourceTree = ""; + }; + 830EBD9C20045FF80023AA10 /* LibAtrac9 */ = { + isa = PBXGroup; + children = ( + 830EBDB820045FF80023AA10 /* C */, + ); + path = LibAtrac9; + sourceTree = ""; + }; + 830EBDB820045FF80023AA10 /* C */ = { + isa = PBXGroup; + children = ( + 830EBDC020045FF80023AA10 /* band_extension.c */, + 830EBDD520045FF80023AA10 /* band_extension.h */, + 830EBDD620045FF80023AA10 /* bit_allocation.c */, + 830EBDC520045FF80023AA10 /* bit_allocation.h */, + 830EBDD020045FF80023AA10 /* bit_reader.c */, + 830EBDC220045FF80023AA10 /* bit_reader.h */, + 830EBDC420045FF80023AA10 /* decinit.c */, + 830EBDCE20045FF80023AA10 /* decinit.h */, + 830EBDCF20045FF80023AA10 /* decoder.c */, + 830EBDC320045FF80023AA10 /* decoder.h */, + 830EBDCA20045FF80023AA10 /* error_codes.h */, + 830EBDBB20045FF80023AA10 /* huffCodes.c */, + 830EBDCD20045FF80023AA10 /* huffCodes.h */, + 830EBDBC20045FF80023AA10 /* imdct.c */, + 830EBDCB20045FF80023AA10 /* imdct.h */, + 830EBDBA20045FF80023AA10 /* libatrac9.c */, + 830EBDC720045FF80023AA10 /* libatrac9.h */, + 830EBDD320045FF80023AA10 /* quantization.c */, + 830EBDBF20045FF80023AA10 /* quantization.h */, + 830EBDC920045FF80023AA10 /* scale_factors.c */, + 830EBDBD20045FF80023AA10 /* scale_factors.h */, + 830EBDD720045FF80023AA10 /* structures.h */, + 830EBDB920045FF80023AA10 /* tables.c */, + 830EBDC820045FF80023AA10 /* tables.h */, + 830EBDC120045FF80023AA10 /* unpack.c */, + 830EBDD120045FF80023AA10 /* unpack.h */, + 830EBDBE20045FF80023AA10 /* utility.c */, + 830EBDD420045FF80023AA10 /* utility.h */, + ); + path = C; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 830EBD8420045F190023AA10 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 830EBDFE20045FF80023AA10 /* libatrac9.h in Headers */, + 830EBE0220045FF80023AA10 /* imdct.h in Headers */, + 830EBE0520045FF80023AA10 /* decinit.h in Headers */, + 830EBE0420045FF80023AA10 /* huffCodes.h in Headers */, + 830EBE0B20045FF80023AA10 /* utility.h in Headers */, + 830EBE0820045FF80023AA10 /* unpack.h in Headers */, + 830EBDF420045FF80023AA10 /* scale_factors.h in Headers */, + 830EBE0C20045FF80023AA10 /* band_extension.h in Headers */, + 830EBE0120045FF80023AA10 /* error_codes.h in Headers */, + 830EBDFA20045FF80023AA10 /* decoder.h in Headers */, + 830EBDFF20045FF80023AA10 /* tables.h in Headers */, + 830EBDF920045FF80023AA10 /* bit_reader.h in Headers */, + 830EBE0E20045FF80023AA10 /* structures.h in Headers */, + 830EBDFC20045FF80023AA10 /* bit_allocation.h in Headers */, + 830EBDF620045FF80023AA10 /* quantization.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 830EBD8620045F190023AA10 /* libatrac9 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 830EBD8F20045F190023AA10 /* Build configuration list for PBXNativeTarget "libatrac9" */; + buildPhases = ( + 830EBD8220045F190023AA10 /* Sources */, + 830EBD8320045F190023AA10 /* Frameworks */, + 830EBD8420045F190023AA10 /* Headers */, + 830EBD8520045F190023AA10 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libatrac9; + productName = libatrac9; + productReference = 830EBD8720045F190023AA10 /* libatrac9.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 830EBD7E20045F190023AA10 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 830EBD8620045F190023AA10 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 830EBD8120045F190023AA10 /* Build configuration list for PBXProject "libatrac9" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 830EBD7D20045F190023AA10; + productRefGroup = 830EBD8820045F190023AA10 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 830EBD8620045F190023AA10 /* libatrac9 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 830EBD8520045F190023AA10 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 830EBD8220045F190023AA10 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 830EBE0620045FF80023AA10 /* decoder.c in Sources */, + 830EBDF720045FF80023AA10 /* band_extension.c in Sources */, + 830EBE0020045FF80023AA10 /* scale_factors.c in Sources */, + 830EBDF520045FF80023AA10 /* utility.c in Sources */, + 830EBDFB20045FF80023AA10 /* decinit.c in Sources */, + 830EBE0720045FF80023AA10 /* bit_reader.c in Sources */, + 830EBE0A20045FF80023AA10 /* quantization.c in Sources */, + 830EBDF820045FF80023AA10 /* unpack.c in Sources */, + 830EBDF020045FF80023AA10 /* tables.c in Sources */, + 830EBDF320045FF80023AA10 /* imdct.c in Sources */, + 830EBDF220045FF80023AA10 /* huffCodes.c in Sources */, + 830EBDF120045FF80023AA10 /* libatrac9.c in Sources */, + 830EBE0D20045FF80023AA10 /* bit_allocation.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 830EBD8D20045F190023AA10 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "@loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 830EBD8E20045F190023AA10 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "@loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 830EBD9020045F190023AA10 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + HEADER_SEARCH_PATHS = libatrac9/LibAtrac9/C; + INFOPLIST_FILE = libatrac9/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libatrac9; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 830EBD9120045F190023AA10 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + HEADER_SEARCH_PATHS = libatrac9/LibAtrac9/C; + INFOPLIST_FILE = libatrac9/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libatrac9; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 830EBD8120045F190023AA10 /* Build configuration list for PBXProject "libatrac9" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 830EBD8D20045F190023AA10 /* Debug */, + 830EBD8E20045F190023AA10 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 830EBD8F20045F190023AA10 /* Build configuration list for PBXNativeTarget "libatrac9" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 830EBD9020045F190023AA10 /* Debug */, + 830EBD9120045F190023AA10 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 830EBD7E20045F190023AA10 /* Project object */; +} diff --git a/Frameworks/libatrac9/libatrac9/Info.plist b/Frameworks/libatrac9/libatrac9/Info.plist new file mode 100644 index 000000000..a351796f3 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2018 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitattributes b/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitattributes new file mode 100644 index 000000000..1ff0c4230 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitignore b/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitignore new file mode 100644 index 000000000..3c4efe206 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.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 + +# 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 + +# 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 +# TODO: 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 +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable 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 + +# 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 +node_modules/ +orleans.codegen.cs + +# 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 + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# 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/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.c new file mode 100644 index 000000000..912c58848 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.c @@ -0,0 +1,191 @@ +#include "band_extension.h" +#include "tables.h" +#include "utility.h" +#include + +void ApplyBandExtension(block* block) +{ + if (!block->BandExtensionEnabled || !block->HasExtensionData) return; + + for (int i = 0; i < block->ChannelCount; i++) + { + ApplyBandExtensionChannel(&block->Channels[i]); + } +} + +void ApplyBandExtensionChannel(channel* channel) +{ + const int groupAUnit = channel->Block->QuantizationUnitCount; + int* scaleFactors = channel->ScaleFactors; + double* spectra = channel->Spectra; + double scales[6]; + int* values = channel->BexValues; + + const bex_group* bex_info = &BexGroupInfo[channel->Block->QuantizationUnitCount - 13]; + const int bandCount = bex_info->band_count; + const int groupBUnit = bex_info->group_b_unit; + const int groupCUnit = bex_info->group_c_unit; + + const int totalUnits = max(groupCUnit, 22); + const int bexQuantUnits = totalUnits - groupAUnit; + + const int groupABin = QuantUnitToCoeffIndex[groupAUnit]; + const int groupBBin = QuantUnitToCoeffIndex[groupBUnit]; + const int groupCBin = QuantUnitToCoeffIndex[groupCUnit]; + const int totalBins = QuantUnitToCoeffIndex[totalUnits]; + + FillHighFrequencies(spectra, groupABin, groupBBin, groupCBin, totalBins); + + double groupAScale, groupBScale, groupCScale; + double rate, scale, mult; + + switch (channel->BexMode) + { + case 0: + switch (bandCount) + { + case 3: + scales[0] = BexMode0Bands3[0][values[0]]; + scales[1] = BexMode0Bands3[1][values[0]]; + scales[2] = BexMode0Bands3[2][values[1]]; + scales[3] = BexMode0Bands3[3][values[2]]; + scales[4] = BexMode0Bands3[4][values[3]]; + break; + case 4: + scales[0] = BexMode0Bands4[0][values[0]]; + scales[1] = BexMode0Bands4[1][values[0]]; + scales[2] = BexMode0Bands4[2][values[1]]; + scales[3] = BexMode0Bands4[3][values[2]]; + scales[4] = BexMode0Bands4[4][values[3]]; + break; + case 5: + scales[0] = BexMode0Bands5[0][values[0]]; + scales[1] = BexMode0Bands5[1][values[1]]; + scales[2] = BexMode0Bands5[2][values[1]]; + break; + } + + scales[bexQuantUnits - 1] = SpectrumScale[scaleFactors[groupAUnit]]; + + AddNoiseToSpectrum(channel, QuantUnitToCoeffIndex[totalUnits - 1], + QuantUnitToCoeffCount[totalUnits - 1]); + ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits); + break; + case 1: + for (int i = groupAUnit; i < totalUnits; i++) + { + scales[i - groupAUnit] = SpectrumScale[scaleFactors[i]]; + } + + AddNoiseToSpectrum(channel, groupABin, totalBins - groupABin); + ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits); + break; + case 2: + groupAScale = BexMode2Scale[values[0]]; + groupBScale = BexMode2Scale[values[1]]; + + for (int i = groupABin; i < groupBBin; i++) + { + spectra[i] *= groupAScale; + } + + for (int i = groupBBin; i < groupCBin; i++) + { + spectra[i] *= groupBScale; + } + return; + case 3: + rate = pow(2, BexMode3Rate[values[1]]); + scale = BexMode3Initial[values[0]]; + for (int i = groupABin; i < totalBins; i++) + { + scale *= rate; + spectra[i] *= scale; + } + return; + case 4: + mult = BexMode4Multiplier[values[0]]; + groupAScale = 0.7079468 * mult; + groupBScale = 0.5011902 * mult; + groupCScale = 0.3548279 * mult; + + for (int i = groupABin; i < groupBBin; i++) + { + spectra[i] *= groupAScale; + } + + for (int i = groupBBin; i < groupCBin; i++) + { + spectra[i] *= groupBScale; + } + + for (int i = groupCBin; i < totalBins; i++) + { + spectra[i] *= groupCScale; + } + } +} + +void ScaleBexQuantUnits(double* spectra, double* scales, int startUnit, int totalUnits) +{ + for (int i = startUnit; i < totalUnits; i++) + { + for (int k = QuantUnitToCoeffIndex[i]; k < QuantUnitToCoeffIndex[i + 1]; k++) + { + spectra[k] *= scales[i - startUnit]; + } + } +} + +void FillHighFrequencies(double* spectra, int groupABin, int groupBBin, int groupCBin, int totalBins) +{ + for (int i = 0; i < groupBBin - groupABin; i++) + { + spectra[groupABin + i] = spectra[groupABin - i - 1]; + } + + for (int i = 0; i < groupCBin - groupBBin; i++) + { + spectra[groupBBin + i] = spectra[groupBBin - i - 1]; + } + + for (int i = 0; i < totalBins - groupCBin; i++) + { + spectra[groupCBin + i] = spectra[groupCBin - i - 1]; + } +} + +void AddNoiseToSpectrum(channel* channel, int index, int count) +{ + if (!channel->rng.initialized) + { + int* sf = channel->ScaleFactors; + const unsigned short seed = (unsigned short)(543 * (sf[8] + sf[12] + sf[15] + 1)); + rng_init(&channel->rng, seed); + } + for (int i = 0; i < count; i++) + { + channel->Spectra[i + index] = rng_next(&channel->rng) / 65535.0 * 2.0 - 1.0; + } +} + +void rng_init(rng_cxt* rng, unsigned short seed) +{ + const int startValue = 0x4D93 * (seed ^ (seed >> 14)); + + rng->stateA = (unsigned short)(3 - startValue); + rng->stateB = (unsigned short)(2 - startValue); + rng->stateC = (unsigned short)(1 - startValue); + rng->stateD = (unsigned short)(0 - startValue); + rng->initialized = TRUE; +} + +unsigned short rng_next(rng_cxt* rng) +{ + const unsigned short t = (unsigned short)(rng->stateD ^ (rng->stateD << 5)); + rng->stateD = rng->stateC; + rng->stateC = rng->stateB; + rng->stateB = rng->stateA; + rng->stateA = (unsigned short)(t ^ rng->stateA ^ ((t ^ (rng->stateA >> 5)) >> 4)); + return rng->stateA; +} \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.h new file mode 100644 index 000000000..c0b6a902e --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/band_extension.h @@ -0,0 +1,13 @@ +#pragma once + +#include "structures.h" + +void ApplyBandExtension(block* block); +void ApplyBandExtensionChannel(channel* channel); + +void ScaleBexQuantUnits(double* spectra, double* scales, int startUnit, int totalUnits); +void FillHighFrequencies(double* spectra, int groupABin, int groupBBin, int groupCBin, int totalBins); +void AddNoiseToSpectrum(channel* channel, int index, int count); + +void rng_init(rng_cxt* rng, unsigned short seed); +unsigned short rng_next(rng_cxt* rng); \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.c new file mode 100644 index 000000000..2977efc37 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.c @@ -0,0 +1,119 @@ +#include "bit_allocation.h" +#include "tables.h" +#include "utility.h" +#include + +at9_status CreateGradient(block* block) +{ + int valueCount = block->GradientEndValue - block->GradientStartValue; + int unitCount = block->GradientEndUnit - block->GradientStartUnit; + + for (int i = 0; i < block->GradientEndUnit; i++) + { + block->Gradient[i] = block->GradientStartValue; + } + + for (int i = block->GradientEndUnit; i <= block->QuantizationUnitCount; i++) + { + block->Gradient[i] = block->GradientEndValue; + } + if (unitCount <= 0) return ERR_SUCCESS; + if (valueCount == 0) return ERR_SUCCESS; + + const unsigned char* curve = GradientCurves[unitCount - 1]; + if (valueCount <= 0) + { + double scale = (-valueCount - 1) / 31.0; + int baseVal = block->GradientStartValue - 1; + for (int i = block->GradientStartUnit; i < block->GradientEndUnit; i++) + { + block->Gradient[i] = baseVal - (int)(curve[i - block->GradientStartUnit] * scale); + } + } + else + { + double scale = (valueCount - 1) / 31.0; + int baseVal = block->GradientStartValue + 1; + for (int i = block->GradientStartUnit; i < block->GradientEndUnit; i++) + { + block->Gradient[i] = baseVal + (int)(curve[i - block->GradientStartUnit] * scale); + } + } + + return ERR_SUCCESS; +} + +void CalculateMask(channel* channel) +{ + memset(channel->PrecisionMask, 0, sizeof(channel->PrecisionMask)); + for (int i = 1; i < channel->Block->QuantizationUnitCount; i++) + { + const int delta = channel->ScaleFactors[i] - channel->ScaleFactors[i - 1]; + if (delta > 1) + { + channel->PrecisionMask[i] += min(delta - 1, 5); + } + else if (delta < -1) + { + channel->PrecisionMask[i - 1] += min(delta * -1 - 1, 5); + } + } +} + +void CalculatePrecisions(channel* channel) +{ + block* block = channel->Block; + + if (block->GradientMode != 0) + { + for (int i = 0; i < block->QuantizationUnitCount; i++) + { + channel->Precisions[i] = channel->ScaleFactors[i] + channel->PrecisionMask[i] - block->Gradient[i]; + if (channel->Precisions[i] > 0) + { + switch (block->GradientMode) + { + case 1: + channel->Precisions[i] /= 2; + break; + case 2: + channel->Precisions[i] = 3 * channel->Precisions[i] / 8; + break; + case 3: + channel->Precisions[i] /= 4; + break; + } + } + } + } + else + { + for (int i = 0; i < block->QuantizationUnitCount; i++) + { + channel->Precisions[i] = channel->ScaleFactors[i] - block->Gradient[i]; + } + } + + for (int i = 0; i < block->QuantizationUnitCount; i++) + { + if (channel->Precisions[i] < 1) + { + channel->Precisions[i] = 1; + } + } + + for (int i = 0; i < block->GradientBoundary; i++) + { + channel->Precisions[i]++; + } + + for (int i = 0; i < block->QuantizationUnitCount; i++) + { + channel->PrecisionsFine[i] = 0; + if (channel->Precisions[i] > 15) + { + channel->PrecisionsFine[i] = channel->Precisions[i] - 15; + channel->Precisions[i] = 15; + } + } +} \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.h new file mode 100644 index 000000000..8ab8ba208 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_allocation.h @@ -0,0 +1,6 @@ +#pragma once +#include "unpack.h" + +at9_status CreateGradient(block* block); +void CalculateMask(channel* channel); +void CalculatePrecisions(channel* channel); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.c new file mode 100644 index 000000000..f38530562 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.c @@ -0,0 +1,110 @@ +#include "bit_reader.h" +#include "utility.h" + +static int peek_int_fallback(bit_reader_cxt* br, int bit_count); + +void init_bit_reader_cxt(bit_reader_cxt* br, const void * buffer) +{ + br->buffer = buffer; + br->position = 0; +} + +int read_int(bit_reader_cxt* br, const int bits) +{ + const int value = peek_int(br, bits); + br->position += bits; + return value; +} + +int read_signed_int(bit_reader_cxt* br, const int bits) +{ + const int value = peek_int(br, bits); + br->position += bits; + return SignExtend32(value, bits); +} + +int read_offset_binary(bit_reader_cxt* br, const int bits) +{ + const int offset = 1 << (bits - 1); + const int value = peek_int(br, bits) - offset; + br->position += bits; + return value; +} + +int peek_int(bit_reader_cxt* br, const int bits) +{ + const int byte_index = br->position / 8; + const int bit_index = br->position % 8; + const unsigned char* buffer = br->buffer; + + if (bits <= 9) + { + int value = buffer[byte_index] << 8 | buffer[byte_index + 1]; + value &= 0xFFFF >> bit_index; + value >>= 16 - bits - bit_index; + return value; + } + + if (bits <= 17) + { + int value = buffer[byte_index] << 16 | buffer[byte_index + 1] << 8 | buffer[byte_index + 2]; + value &= 0xFFFFFF >> bit_index; + value >>= 24 - bits - bit_index; + return value; + } + + if (bits <= 25) + { + int value = buffer[byte_index] << 24 + | buffer[byte_index + 1] << 16 + | buffer[byte_index + 2] << 8 + | buffer[byte_index + 3]; + + value &= (int)(0xFFFFFFFF >> bit_index); + value >>= 32 - bits - bit_index; + return value; + } + return peek_int_fallback(br, bits); +} + +void align_position(bit_reader_cxt* br, const unsigned int multiple) +{ + const int position = br->position; + if (position % multiple == 0) + { + return; + } + + br->position = position + multiple - position % multiple; +} + +static int peek_int_fallback(bit_reader_cxt* br, int bit_count) +{ + int value = 0; + int byte_index = br->position / 8; + int bit_index = br->position % 8; + const unsigned char* buffer = br->buffer; + + while (bit_count > 0) + { + if (bit_index >= 8) + { + bit_index = 0; + byte_index++; + } + + int bits_to_read = bit_count; + if (bits_to_read > 8 - bit_index) + { + bits_to_read = 8 - bit_index; + } + + const int mask = 0xFF >> bit_index; + const int current_byte = (mask & buffer[byte_index]) >> (8 - bit_index - bits_to_read); + + value = (value << bits_to_read) | current_byte; + bit_index += bits_to_read; + bit_count -= bits_to_read; + } + return value; +} \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.h new file mode 100644 index 000000000..bcec9d40f --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/bit_reader.h @@ -0,0 +1,13 @@ +#pragma once + +typedef struct { + const unsigned char * buffer; + int position; +} bit_reader_cxt; + +void init_bit_reader_cxt(bit_reader_cxt* br, const void * buffer); +int peek_int(bit_reader_cxt* br, const int bits); +int read_int(bit_reader_cxt* br, const int bits); +int read_signed_int(bit_reader_cxt* br, const int bits); +int read_offset_binary(bit_reader_cxt* br, const int bits); +void align_position(bit_reader_cxt* br, const unsigned int multiple); \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.c new file mode 100644 index 000000000..c8a1945e0 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.c @@ -0,0 +1,188 @@ +#include "bit_reader.h" +#include "decinit.h" +#include "error_codes.h" +#include "structures.h" +#include "tables.h" +#include +#include +#include "utility.h" + +static int BlockTypeToChannelCount(BlockType block_type); + +at9_status init_decoder(atrac9_handle* handle, unsigned char* config_data, int wlength) +{ + ERROR_CHECK(init_config_data(&handle->config, config_data)); + ERROR_CHECK(init_frame(handle)); + init_mdct_tables(handle->config.FrameSamplesPower); + init_huffman_codebooks(); + handle->wlength = wlength; + handle->initialized = 1; + return ERR_SUCCESS; +} + +at9_status init_config_data(ConfigData* config, unsigned char* config_data) +{ + memcpy(config->ConfigData, config_data, CONFIG_DATA_SIZE); + ERROR_CHECK(read_config_data(config)); + + config->FramesPerSuperframe = 1 << config->SuperframeIndex; + config->SuperframeBytes = config->FrameBytes << config->SuperframeIndex; + + config->ChannelConfig = ChannelConfigs[config->ChannelConfigIndex]; + config->ChannelCount = config->ChannelConfig.ChannelCount; + config->SampleRate = SampleRates[config->SampleRateIndex]; + config->HighSampleRate = config->SampleRateIndex > 7; + config->FrameSamplesPower = SamplingRateIndexToFrameSamplesPower[config->SampleRateIndex]; + config->FrameSamples = 1 << config->FrameSamplesPower; + config->SuperframeSamples = config->FrameSamples * config->FramesPerSuperframe; + + return ERR_SUCCESS; +} + +at9_status read_config_data(ConfigData* config) +{ + bit_reader_cxt br; + init_bit_reader_cxt(&br, &config->ConfigData); + + const int header = read_int(&br, 8); + config->SampleRateIndex = read_int(&br, 4); + config->ChannelConfigIndex = read_int(&br, 3); + const int validation_bit = read_int(&br, 1); + config->FrameBytes = read_int(&br, 11) + 1; + config->SuperframeIndex = read_int(&br, 2); + + if (header != 0xFE || validation_bit != 0) + { + return ERR_BAD_CONFIG_DATA; + } + + return ERR_SUCCESS; +} + +at9_status init_frame(atrac9_handle* handle) +{ + const int block_count = handle->config.ChannelConfig.BlockCount; + handle->frame.config = &handle->config; + + for (int i = 0; i < block_count; i++) + { + ERROR_CHECK(init_block(&handle->frame.Blocks[i], &handle->frame, i)); + } + + return ERR_SUCCESS; +} + +at9_status init_block(block* block, frame* parent_frame, int block_index) +{ + block->Frame = parent_frame; + block->BlockIndex = block_index; + block->config = parent_frame->config; + block->BlockType = block->config->ChannelConfig.Types[block_index]; + block->ChannelCount = BlockTypeToChannelCount(block->BlockType); + + for (int i = 0; i < block->ChannelCount; i++) + { + ERROR_CHECK(init_channel(&block->Channels[i], block, i)); + } + + return ERR_SUCCESS; +} + +at9_status init_channel(channel* channel, block* parent_block, int channel_index) +{ + channel->Block = parent_block; + channel->Frame = parent_block->Frame; + channel->config = parent_block->config; + channel->ChannelIndex = channel_index; + channel->mdct.bits = parent_block->config->FrameSamplesPower; + return ERR_SUCCESS; +} + +void init_huffman_codebooks() +{ + init_huffman_set(HuffmanScaleFactorsUnsigned, sizeof(HuffmanScaleFactorsUnsigned) / sizeof(HuffmanCodebook)); + init_huffman_set(HuffmanScaleFactorsSigned, sizeof(HuffmanScaleFactorsSigned) / sizeof(HuffmanCodebook)); + init_huffman_set(HuffmanSpectrum, sizeof(HuffmanSpectrum) / sizeof(HuffmanCodebook)); +} + +void init_huffman_set(const HuffmanCodebook* codebooks, int count) +{ + for (int i = 0; i < count; i++) + { + InitHuffmanCodebook(&codebooks[i]); + } +} + +void init_mdct_tables(int frameSizePower) +{ + for (int i = 0; i < 9; i++) + { + GenerateTrigTables(i); + GenerateShuffleTable(i); + } + GenerateMdctWindow(frameSizePower); + GenerateImdctWindow(frameSizePower); +} + +void GenerateTrigTables(int sizeBits) +{ + const int size = 1 << sizeBits; + double* sinTab = SinTables[sizeBits]; + double* cosTab = CosTables[sizeBits]; + + for (int i = 0; i < size; i++) + { + const double value = M_PI * (4 * i + 1) / (4 * size); + sinTab[i] = sin(value); + cosTab[i] = cos(value); + } +} + +void GenerateShuffleTable(int sizeBits) +{ + const int size = 1 << sizeBits; + int* table = ShuffleTables[sizeBits]; + + for (int i = 0; i < size; i++) + { + table[i] = BitReverse32(i ^ (i / 2), sizeBits); + } +} + +void GenerateMdctWindow(int frameSizePower) +{ + const int frameSize = 1 << frameSizePower; + double* mdct = MdctWindow[frameSizePower - 6]; + + for (int i = 0; i < frameSize; i++) + { + mdct[i] = (sin(((i + 0.5) / frameSize - 0.5) * M_PI) + 1.0) * 0.5; + } +} + +void GenerateImdctWindow(int frameSizePower) +{ + const int frameSize = 1 << frameSizePower; + double* imdct = ImdctWindow[frameSizePower - 6]; + double* mdct = MdctWindow[frameSizePower - 6]; + + for (int i = 0; i < frameSize; i++) + { + imdct[i] = mdct[i] / (mdct[frameSize - 1 - i] * mdct[frameSize - 1 - i] + mdct[i] * mdct[i]); + } +} + +static int BlockTypeToChannelCount(BlockType block_type) +{ + switch (block_type) + { + case Mono: + return 1; + case Stereo: + return 2; + case LFE: + return 1; + default: + return 0; + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.h new file mode 100644 index 000000000..3871e4fb2 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decinit.h @@ -0,0 +1,19 @@ +#pragma once + +#include "error_codes.h" +#include "structures.h" +#include "huffCodes.h" + +at9_status init_decoder(atrac9_handle* handle, unsigned char * config_data, int wlength); +at9_status init_config_data(ConfigData* config, unsigned char * config_data); +at9_status read_config_data(ConfigData* config); +at9_status init_frame(atrac9_handle* handle); +at9_status init_block(block* block, frame* parent_frame, int block_index); +at9_status init_channel(channel* channel, block* parent_block, int channel_index); +void init_huffman_codebooks(); +void init_huffman_set(const HuffmanCodebook* codebooks, int count); +void GenerateTrigTables(int sizeBits); +void GenerateShuffleTable(int sizeBits); +void init_mdct_tables(int frameSizePower); +void GenerateMdctWindow(int frameSizePower); +void GenerateImdctWindow(int frameSizePower); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.c new file mode 100644 index 000000000..4b63cc02d --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.c @@ -0,0 +1,114 @@ +#include +#include "decoder.h" +#include "unpack.h" +#include "quantization.h" +#include "tables.h" +#include "imdct.h" +#include +#include "utility.h" +#include "band_extension.h" + +at9_status Decode(atrac9_handle* handle, const unsigned char* audio, unsigned char* pcm, int* bytesUsed) +{ + handle->frame.frameNum++; + bit_reader_cxt br; + init_bit_reader_cxt(&br, audio); + ERROR_CHECK(DecodeFrame(&handle->frame, &br)); + + PcmFloatToShort(&handle->frame, (short*)pcm); + + *bytesUsed = br.position / 8; + return ERR_SUCCESS; +} + +at9_status DecodeFrame(frame* frame, bit_reader_cxt* br) +{ + ERROR_CHECK(UnpackFrame(frame, br)); + + for (int i = 0; i < frame->config->ChannelConfig.BlockCount; i++) + { + block* block = &frame->Blocks[i]; + + DequantizeSpectra(block); + ApplyIntensityStereo(block); + ScaleSpectrumBlock(block); + ApplyBandExtension(block); + ImdctBlock(block); + } + + return ERR_SUCCESS; +} + +void PcmFloatToShort(frame* frame, short* pcmOut) +{ + const int endSample = frame->config->FrameSamples; + short* dest = pcmOut; + for (int d = 0, s = 0; s < endSample; d++, s++) + { + for (int i = 0; i < frame->config->ChannelConfig.BlockCount; i++) + { + block* block = &frame->Blocks[i]; + + for (int j = 0; j < block->ChannelCount; j++) + { + channel* channel = &block->Channels[j]; + double* pcmSrc = channel->Pcm; + + const double sample = pcmSrc[d]; + const int roundedSample = (int)floor(sample + 0.5); + *dest++ = Clamp16(roundedSample); + } + } + } +} + +void ImdctBlock(block* block) +{ + for (int i = 0; i < block->ChannelCount; i++) + { + channel* channel = &block->Channels[i]; + + RunImdct(&channel->mdct, channel->Spectra, channel->Pcm); + } +} + +void ApplyIntensityStereo(block* block) +{ + if (block->BlockType != Stereo) return; + + const int totalUnits = block->QuantizationUnitCount; + const int stereoUnits = block->StereoQuantizationUnit; + if (stereoUnits >= totalUnits) return; + + channel* source = &block->Channels[block->PrimaryChannelIndex == 0 ? 0 : 1]; + channel* dest = &block->Channels[block->PrimaryChannelIndex == 0 ? 1 : 0]; + + for (int i = stereoUnits; i < totalUnits; i++) + { + const int sign = block->JointStereoSigns[i]; + for (int sb = QuantUnitToCoeffIndex[i]; sb < QuantUnitToCoeffIndex[i + 1]; sb++) + { + if (sign > 0) + { + dest->Spectra[sb] = -source->Spectra[sb]; + } + else + { + dest->Spectra[sb] = source->Spectra[sb]; + } + } + } +} + +int GetCodecInfo(atrac9_handle* handle, CodecInfo * pCodecInfo) +{ + pCodecInfo->channels = handle->config.ChannelCount; + pCodecInfo->channelConfigIndex = handle->config.ChannelConfigIndex; + pCodecInfo->samplingRate = handle->config.SampleRate; + pCodecInfo->superframeSize = handle->config.SuperframeBytes; + pCodecInfo->framesInSuperframe = handle->config.FramesPerSuperframe; + pCodecInfo->frameSamples = handle->config.FrameSamples; + pCodecInfo->wlength = handle->wlength; + memcpy(pCodecInfo->configData, handle->config.ConfigData, CONFIG_DATA_SIZE); + return ERR_SUCCESS; +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.h new file mode 100644 index 000000000..62bf7cb76 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/decoder.h @@ -0,0 +1,11 @@ +#pragma once +#include "bit_reader.h" +#include "error_codes.h" +#include "structures.h" + +at9_status Decode(atrac9_handle* handle, const unsigned char* audio, unsigned char* pcm, int* bytesUsed); +at9_status DecodeFrame(frame* frame, bit_reader_cxt* br); +void ImdctBlock(block* block); +void ApplyIntensityStereo(block* block); +void PcmFloatToShort(frame* frame, short* pcmOut); +int GetCodecInfo(atrac9_handle* handle, CodecInfo* pCodecInfo); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/error_codes.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/error_codes.h new file mode 100644 index 000000000..d40847d1c --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/error_codes.h @@ -0,0 +1,33 @@ +#pragma once + +typedef enum at9_status +{ + ERR_SUCCESS = 0, + + ERR_NOT_IMPLEMENTED = 0x80000000, + + ERR_BAD_CONFIG_DATA = 0x81000000, + + ERR_UNPACK_SUPERFRAME_FLAG_INVALID = 0x82000000, + ERR_UNPACK_REUSE_BAND_PARAMS_INVALID, + ERR_UNPACK_BAND_PARAMS_INVALID, + + ERR_UNPACK_GRAD_BOUNDARY_INVALID = 0x82100000, + ERR_UNPACK_GRAD_START_UNIT_OOB, + ERR_UNPACK_GRAD_END_UNIT_OOB, + ERR_UNPACK_GRAD_START_VALUE_OOB, + ERR_UNPACK_GRAD_END_VALUE_OOB, + ERR_UNPACK_GRAD_END_UNIT_INVALID, // start_unit > end_unit + + ERR_UNPACK_SCALE_FACTOR_MODE_INVALID, + ERR_UNPACK_SCALE_FACTOR_OOB, + + ERR_UNPACK_EXTENSION_DATA_INVALID +} at9_status; + +#define ERROR_CHECK(x) do { \ + at9_status status = (x); \ + if (status != ERR_SUCCESS) { \ + return status; \ + } \ +} while (0) \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.c new file mode 100644 index 000000000..2ee97e0d4 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.c @@ -0,0 +1,152 @@ +#include "huffCodes.h" +#include "utility.h" + +int ReadHuffmanValue(const HuffmanCodebook* huff, bit_reader_cxt* br, int is_signed) +{ + const int code = peek_int(br, huff->MaxBitSize); + const unsigned char value = huff->Lookup[code]; + const int bits = huff->Bits[value]; + br->position += bits; + return is_signed ? SignExtend32(value, huff->ValueBits) : value; +} + +void DecodeHuffmanValues(int* spectrum, int index, int bandCount, const HuffmanCodebook* huff, const int* values) +{ + const int valueCount = bandCount >> huff->ValueCountPower; + const int mask = (1 << huff->ValueBits) - 1; + + for (int i = 0; i < valueCount; i++) + { + int value = values[i]; + for (int j = 0; j < huff->ValueCount; j++) + { + spectrum[index++] = SignExtend32(value & mask, huff->ValueBits); + value >>= huff->ValueBits; + } + } +} + +void InitHuffmanCodebook(const HuffmanCodebook* codebook) +{ + const int huffLength = codebook->Length; + if (huffLength == 0) return; + + unsigned char* dest = codebook->Lookup; + + for (int i = 0; i < huffLength; i++) + { + if (codebook->Bits[i] == 0) continue; + const int unusedBits = codebook->MaxBitSize - codebook->Bits[i]; + + const int start = codebook->Codes[i] << unusedBits; + const int length = 1 << unusedBits; + const int end = start + length; + + for (int j = start; j < end; j++) + { + dest[j] = i; + } + } +} + +HuffmanCodebook HuffmanScaleFactorsUnsigned[7] = { + {0}, + {ScaleFactorsA1Bits, ScaleFactorsA1Codes, ScaleFactorsA1Lookup, 2, 1, 0, 1, 2, 1}, + {ScaleFactorsA2Bits, ScaleFactorsA2Codes, ScaleFactorsA2Lookup, 4, 1, 0, 2, 4, 3}, + {ScaleFactorsA3Bits, ScaleFactorsA3Codes, ScaleFactorsA3Lookup, 8, 1, 0, 3, 8, 6}, + {ScaleFactorsA4Bits, ScaleFactorsA4Codes, ScaleFactorsA4Lookup, 16, 1, 0, 4, 16, 8}, + {ScaleFactorsA5Bits, ScaleFactorsA5Codes, ScaleFactorsA5Lookup, 32, 1, 0, 5, 32, 8}, + {ScaleFactorsA6Bits, ScaleFactorsA6Codes, ScaleFactorsA6Lookup, 64, 1, 0, 6, 64, 8}, +}; + +HuffmanCodebook HuffmanScaleFactorsSigned[6] = { + {0}, + {0}, + {ScaleFactorsB2Bits, ScaleFactorsB2Codes, ScaleFactorsB2Lookup, 4, 1, 0, 2, 4, 2}, + {ScaleFactorsB3Bits, ScaleFactorsB3Codes, ScaleFactorsB3Lookup, 8, 1, 0, 3, 8, 6}, + {ScaleFactorsB4Bits, ScaleFactorsB4Codes, ScaleFactorsB4Lookup, 16, 1, 0, 4, 16, 8}, + {ScaleFactorsB5Bits, ScaleFactorsB5Codes, ScaleFactorsB5Lookup, 32, 1, 0, 5, 32, 8}, +}; + +HuffmanCodebook HuffmanSpectrum[2][8][4] = { + { + {0}, + {0}, + { + {SpectrumA21Bits, SpectrumA21Codes, SpectrumA21Lookup, 16, 2, 1, 2, 4, 3}, + {SpectrumA22Bits, SpectrumA22Codes, SpectrumA22Lookup, 256, 4, 2, 2, 4, 8}, + {SpectrumA23Bits, SpectrumA23Codes, SpectrumA23Lookup, 256, 4, 2, 2, 4, 9}, + {SpectrumA24Bits, SpectrumA24Codes, SpectrumA24Lookup, 256, 4, 2, 2, 4, 10} + }, + { + {SpectrumA31Bits, SpectrumA31Codes, SpectrumA31Lookup, 64, 2, 1, 3, 8, 7}, + {SpectrumA32Bits, SpectrumA32Codes, SpectrumA32Lookup, 64, 2, 1, 3, 8, 7}, + {SpectrumA33Bits, SpectrumA33Codes, SpectrumA33Lookup, 64, 2, 1, 3, 8, 8}, + {SpectrumA34Bits, SpectrumA34Codes, SpectrumA34Lookup, 64, 2, 1, 3, 8, 10} + }, + { + {SpectrumA41Bits, SpectrumA41Codes, SpectrumA41Lookup, 256, 2, 1, 4, 16, 9}, + {SpectrumA42Bits, SpectrumA42Codes, SpectrumA42Lookup, 256, 2, 1, 4, 16, 10}, + {SpectrumA43Bits, SpectrumA43Codes, SpectrumA43Lookup, 256, 2, 1, 4, 16, 10}, + {SpectrumA44Bits, SpectrumA44Codes, SpectrumA44Lookup, 256, 2, 1, 4, 16, 10} + }, + { + {SpectrumA51Bits, SpectrumA51Codes, SpectrumA51Lookup, 32, 1, 0, 5, 32, 6}, + {SpectrumA52Bits, SpectrumA52Codes, SpectrumA52Lookup, 32, 1, 0, 5, 32, 6}, + {SpectrumA53Bits, SpectrumA53Codes, SpectrumA53Lookup, 32, 1, 0, 5, 32, 7}, + {SpectrumA54Bits, SpectrumA54Codes, SpectrumA54Lookup, 32, 1, 0, 5, 32, 8} + }, + { + {SpectrumA61Bits, SpectrumA61Codes, SpectrumA61Lookup, 64, 1, 0, 6, 64, 7}, + {SpectrumA62Bits, SpectrumA62Codes, SpectrumA62Lookup, 64, 1, 0, 6, 64, 7}, + {SpectrumA63Bits, SpectrumA63Codes, SpectrumA63Lookup, 64, 1, 0, 6, 64, 8}, + {SpectrumA64Bits, SpectrumA64Codes, SpectrumA64Lookup, 64, 1, 0, 6, 64, 9} + }, + { + {SpectrumA71Bits, SpectrumA71Codes, SpectrumA71Lookup, 128, 1, 0, 7, 128, 8}, + {SpectrumA72Bits, SpectrumA72Codes, SpectrumA72Lookup, 128, 1, 0, 7, 128, 8}, + {SpectrumA73Bits, SpectrumA73Codes, SpectrumA73Lookup, 128, 1, 0, 7, 128, 9}, + {SpectrumA74Bits, SpectrumA74Codes, SpectrumA74Lookup, 128, 1, 0, 7, 128, 10} + } + }, + { + {0}, + {0}, + { + {0}, + {SpectrumB22Bits, SpectrumB22Codes, SpectrumB22Lookup, 256, 4, 2, 2, 4, 10}, + {SpectrumB23Bits, SpectrumB23Codes, SpectrumB23Lookup, 256, 4, 2, 2, 4, 10}, + {SpectrumB24Bits, SpectrumB24Codes, SpectrumB24Lookup, 256, 4, 2, 2, 4, 10} + }, + { + {0}, + {SpectrumB32Bits, SpectrumB32Codes, SpectrumB32Lookup, 64, 2, 1, 3, 8, 9}, + {SpectrumB33Bits, SpectrumB33Codes, SpectrumB33Lookup, 64, 2, 1, 3, 8, 10}, + {SpectrumB34Bits, SpectrumB34Codes, SpectrumB34Lookup, 64, 2, 1, 3, 8, 10} + }, + { + {0}, + {SpectrumB42Bits, SpectrumB42Codes, SpectrumB42Lookup, 256, 2, 1, 4, 16, 10}, + {SpectrumB43Bits, SpectrumB43Codes, SpectrumB43Lookup, 256, 2, 1, 4, 16, 10}, + {SpectrumB44Bits, SpectrumB44Codes, SpectrumB44Lookup, 256, 2, 1, 4, 16, 10} + }, + { + {0}, + {SpectrumB52Bits, SpectrumB52Codes, SpectrumB52Lookup, 32, 1, 0, 5, 32, 7}, + {SpectrumB53Bits, SpectrumB53Codes, SpectrumB53Lookup, 32, 1, 0, 5, 32, 8}, + {SpectrumB54Bits, SpectrumB54Codes, SpectrumB54Lookup, 32, 1, 0, 5, 32, 9} + }, + { + {0}, + {SpectrumB62Bits, SpectrumB62Codes, SpectrumB62Lookup, 64, 1, 0, 6, 64, 8}, + {SpectrumB63Bits, SpectrumB63Codes, SpectrumB63Lookup, 64, 1, 0, 6, 64, 9}, + {SpectrumB64Bits, SpectrumB64Codes, SpectrumB64Lookup, 64, 1, 0, 6, 64, 10} + }, + { + {0}, + {SpectrumB72Bits, SpectrumB72Codes, SpectrumB72Lookup, 128, 1, 0, 7, 128, 9}, + {SpectrumB73Bits, SpectrumB73Codes, SpectrumB73Lookup, 128, 1, 0, 7, 128, 10}, + {SpectrumB74Bits, SpectrumB74Codes, SpectrumB74Lookup, 128, 1, 0, 7, 128, 10} + } + } +}; diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.h new file mode 100644 index 000000000..a2fef16c2 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/huffCodes.h @@ -0,0 +1,1295 @@ +#pragma once +#include +#include "bit_reader.h" + +typedef struct +{ + const unsigned char* Bits; + const short* Codes; + unsigned char* Lookup; + const int Length; + const int ValueCount; + const int ValueCountPower; + const int ValueBits; + const int ValueMax; + const int MaxBitSize; +} HuffmanCodebook; + +int ReadHuffmanValue(const HuffmanCodebook* huff, bit_reader_cxt* br, int is_signed); +void DecodeHuffmanValues(int* spectrum, int index, int bandCount, const HuffmanCodebook* huff, const int* values); +void InitHuffmanCodebook(const HuffmanCodebook* codebook); + +extern HuffmanCodebook HuffmanScaleFactorsUnsigned[7]; +extern HuffmanCodebook HuffmanScaleFactorsSigned[6]; +extern HuffmanCodebook HuffmanSpectrum[2][8][4]; + +static const uint8_t ScaleFactorsA1Bits[2] = +{ + 1, 1 +}; + +static const uint16_t ScaleFactorsA1Codes[2] = +{ + 0x00, 0x01 +}; + +static const uint8_t ScaleFactorsA2Bits[4] = +{ + 1, 3, 3, 2 +}; + +static const uint16_t ScaleFactorsA2Codes[4] = +{ + 0x00, 0x06, 0x07, 0x02 +}; + +static const uint8_t ScaleFactorsA3Bits[8] = +{ + 2, 2, 4, 6, 6, 5, 3, 2 +}; + +static const uint16_t ScaleFactorsA3Codes[8] = +{ + 0x00, 0x01, 0x0E, 0x3E, 0x3F, 0x1E, 0x06, 0x02 +}; + +static const uint8_t ScaleFactorsA4Bits[16] = +{ + 2, 2, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 6, 5, 4, 2 +}; + +static const uint16_t ScaleFactorsA4Codes[16] = +{ + 0x01, 0x02, 0x00, 0x06, 0x0F, 0x13, 0x23, 0x24, 0x25, 0x22, 0x21, 0x20, 0x0E, 0x05, 0x01, 0x03 +}; + +static const uint8_t ScaleFactorsA5Bits[32] = +{ + 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 5, 5, 4, 3 +}; + +static const uint16_t ScaleFactorsA5Codes[32] = +{ + 0x02, 0x01, 0x07, 0x0D, 0x0C, 0x18, 0x1B, 0x21, 0x3F, 0x6A, 0x6B, 0x68, 0x73, 0x79, 0x7C, 0x7D, + 0x7A, 0x7B, 0x78, 0x72, 0x44, 0x45, 0x47, 0x46, 0x69, 0x38, 0x20, 0x1D, 0x19, 0x09, 0x05, 0x00 +}; + +static const uint8_t ScaleFactorsA6Bits[64] = +{ + 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 4 +}; + +static const uint16_t ScaleFactorsA6Codes[64] = +{ + 0x00, 0x01, 0x04, 0x05, 0x12, 0x13, 0x2E, 0x2F, 0x30, 0x66, 0x67, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, + 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x68, 0x69, 0x6A, 0x31, 0x32, 0x14, 0x15, 0x16, 0x06, 0x07, 0x08 +}; + +static const uint8_t ScaleFactorsB2Bits[4] = +{ + 1, 2, 0, 2 +}; + +static const uint16_t ScaleFactorsB2Codes[4] = +{ + 0x00, 0x03, 0x00, 0x02 +}; + +static const uint8_t ScaleFactorsB3Bits[8] = +{ + 1, 3, 5, 6, 0, 6, 4, 2 +}; + +static const uint16_t ScaleFactorsB3Codes[8] = +{ + 0x01, 0x00, 0x04, 0x0B, 0x00, 0x0A, 0x03, 0x01 +}; + +static const uint8_t ScaleFactorsB4Bits[16] = +{ + 1, 3, 4, 5, 5, 7, 8, 8, 0, 8, 8, 7, 6, 6, 4, 3 +}; + +static const uint16_t ScaleFactorsB4Codes[16] = +{ + 0x01, 0x01, 0x04, 0x0E, 0x0F, 0x2C, 0x5A, 0x5D, 0x00, 0x5C, 0x5B, 0x2F, 0x15, 0x14, 0x06, 0x00 +}; + +static const uint8_t ScaleFactorsB5Bits[32] = +{ + 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 3 +}; + +static const uint16_t ScaleFactorsB5Codes[32] = +{ + 0x00, 0x05, 0x07, 0x0C, 0x04, 0x02, 0x03, 0x05, 0x09, 0x10, 0x23, 0x33, 0x36, 0x6E, 0x60, 0x65, + 0x62, 0x61, 0x63, 0x64, 0x6F, 0x6D, 0x6C, 0x6B, 0x6A, 0x68, 0x69, 0x45, 0x44, 0x37, 0x1A, 0x07 +}; + +static const uint8_t SpectrumA21Bits[16] = +{ + 0, 3, 0, 3, 3, 3, 0, 3, 0, 0, 0, 0, 3, 3, 0, 3 +}; + +static const uint16_t SpectrumA21Codes[16] = +{ + 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x06 +}; + +static const uint8_t SpectrumA22Bits[256] = +{ + 0, 4, 0, 4, 5, 6, 0, 6, 0, 0, 0, 0, 5, 6, 0, 6, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 5, 6, 0, 6, 7, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 6, 7, 0, 7, 7, 8, 0, 8, 0, 0, 0, 0, 7, 8, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 7, 7, 8, 0, 8, 0, 0, 0, 0, 7, 7, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 7, 7, 0, 7, + 6, 7, 0, 7, 7, 8, 0, 7, 0, 0, 0, 0, 7, 8, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 7, 7, 7, 0, 8, 0, 0, 0, 0, 7, 8, 0, 8 +}; + +static const uint16_t SpectrumA22Codes[256] = +{ + 0x00, 0x02, 0x00, 0x03, 0x10, 0x3C, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x11, 0x3E, 0x00, 0x3D, + 0x0E, 0x00, 0x00, 0x39, 0x18, 0x26, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x24, 0x00, 0x6D, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x38, 0x00, 0x01, 0x1A, 0x6C, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x19, 0x74, 0x00, 0x27, + 0x16, 0x14, 0x00, 0x17, 0x76, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x35, 0x64, 0x00, 0x6F, + 0x26, 0x04, 0x00, 0x63, 0x22, 0xA2, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x67, 0xA0, 0x00, 0x0D, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x52, 0x00, 0x0B, 0x20, 0x92, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x95, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x16, 0x00, 0x15, 0x34, 0x6E, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x77, 0x08, 0x00, 0x07, + 0x2A, 0x0A, 0x00, 0x53, 0x60, 0x94, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x21, 0x90, 0x00, 0x93, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x62, 0x00, 0x05, 0x66, 0x0C, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x23, 0x96, 0x00, 0xA3 +}; + +static const uint8_t SpectrumA23Bits[256] = +{ + 3, 4, 0, 4, 5, 6, 0, 6, 0, 0, 0, 0, 5, 6, 0, 6, + 5, 7, 0, 6, 6, 8, 0, 7, 0, 0, 0, 0, 6, 8, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 7, 6, 7, 0, 8, 0, 0, 0, 0, 6, 7, 0, 8, + 5, 6, 0, 6, 7, 8, 0, 8, 0, 0, 0, 0, 6, 7, 0, 7, + 6, 8, 0, 7, 8, 9, 0, 9, 0, 0, 0, 0, 7, 9, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 8, 0, 8, 8, 9, 0, 9, 0, 0, 0, 0, 7, 8, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 7, 8, 0, 8, + 6, 8, 0, 8, 7, 9, 0, 8, 0, 0, 0, 0, 8, 9, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 8, 7, 8, 0, 9, 0, 0, 0, 0, 8, 9, 0, 9 +}; + +static const uint16_t SpectrumA23Codes[256] = +{ + 0x006, 0x002, 0x000, 0x003, 0x016, 0x01E, 0x000, 0x021, 0x000, 0x000, 0x000, 0x000, + 0x017, 0x020, 0x000, 0x01F, 0x01C, 0x054, 0x000, 0x027, 0x010, 0x0A6, 0x000, 0x027, + 0x000, 0x000, 0x000, 0x000, 0x015, 0x0A4, 0x000, 0x02D, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01D, 0x026, 0x000, 0x055, 0x014, 0x02C, 0x000, 0x0A5, 0x000, 0x000, 0x000, 0x000, + 0x011, 0x026, 0x000, 0x0A7, 0x01E, 0x000, 0x000, 0x003, 0x04A, 0x074, 0x000, 0x071, + 0x000, 0x000, 0x000, 0x000, 0x023, 0x00A, 0x000, 0x009, 0x018, 0x072, 0x000, 0x00D, + 0x0A2, 0x15A, 0x000, 0x123, 0x000, 0x000, 0x000, 0x000, 0x00F, 0x158, 0x000, 0x05D, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x01B, 0x0AE, 0x000, 0x077, 0x092, 0x140, 0x000, 0x121, + 0x000, 0x000, 0x000, 0x000, 0x025, 0x05E, 0x000, 0x143, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01F, 0x002, 0x000, 0x001, 0x022, 0x008, 0x000, 0x00B, 0x000, 0x000, 0x000, 0x000, + 0x04B, 0x070, 0x000, 0x075, 0x01A, 0x076, 0x000, 0x0AF, 0x024, 0x142, 0x000, 0x05F, + 0x000, 0x000, 0x000, 0x000, 0x093, 0x120, 0x000, 0x141, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x019, 0x00C, 0x000, 0x073, 0x00E, 0x05C, 0x000, 0x159, 0x000, 0x000, 0x000, 0x000, + 0x0A3, 0x122, 0x000, 0x15B +}; + +static const uint8_t SpectrumA24Bits[256] = +{ + 2, 4, 0, 4, 5, 6, 0, 6, 0, 0, 0, 0, 5, 6, 0, 6, + 5, 7, 0, 6, 6, 8, 0, 8, 0, 0, 0, 0, 6, 8, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 7, 6, 8, 0, 8, 0, 0, 0, 0, 6, 8, 0, 8, + 5, 7, 0, 7, 7, 9, 0, 9, 0, 0, 0, 0, 6, 8, 0, 8, + 6, 9, 0, 8, 8, 10, 0, 10, 0, 0, 0, 0, 8, 10, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 8, 0, 9, 9, 10, 0, 10, 0, 0, 0, 0, 8, 9, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 7, 0, 7, 6, 8, 0, 8, 0, 0, 0, 0, 7, 9, 0, 9, + 6, 9, 0, 8, 8, 10, 0, 9, 0, 0, 0, 0, 9, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 8, 0, 9, 8, 9, 0, 10, 0, 0, 0, 0, 8, 10, 0, 10 +}; + +static const uint16_t SpectrumA24Codes[256] = +{ + 0x002, 0x002, 0x000, 0x003, 0x01E, 0x010, 0x000, 0x013, 0x000, 0x000, 0x000, 0x000, + 0x01F, 0x012, 0x000, 0x011, 0x01A, 0x030, 0x000, 0x01B, 0x000, 0x064, 0x000, 0x0C1, + 0x000, 0x000, 0x000, 0x000, 0x003, 0x052, 0x000, 0x07D, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01B, 0x01A, 0x000, 0x031, 0x002, 0x07C, 0x000, 0x053, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x0C0, 0x000, 0x065, 0x01C, 0x062, 0x000, 0x065, 0x02A, 0x198, 0x000, 0x19B, + 0x000, 0x000, 0x000, 0x000, 0x017, 0x078, 0x000, 0x07B, 0x004, 0x0FE, 0x000, 0x077, + 0x050, 0x33A, 0x000, 0x1F9, 0x000, 0x000, 0x000, 0x000, 0x073, 0x338, 0x000, 0x0E1, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x007, 0x066, 0x000, 0x187, 0x19E, 0x308, 0x000, 0x30B, + 0x000, 0x000, 0x000, 0x000, 0x075, 0x0E2, 0x000, 0x1FB, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01D, 0x064, 0x000, 0x063, 0x016, 0x07A, 0x000, 0x079, 0x000, 0x000, 0x000, 0x000, + 0x02B, 0x19A, 0x000, 0x199, 0x006, 0x186, 0x000, 0x067, 0x074, 0x1FA, 0x000, 0x0E3, + 0x000, 0x000, 0x000, 0x000, 0x19F, 0x30A, 0x000, 0x309, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x005, 0x076, 0x000, 0x0FF, 0x072, 0x0E0, 0x000, 0x339, 0x000, 0x000, 0x000, 0x000, + 0x051, 0x1F8, 0x000, 0x33B +}; + +static const uint8_t SpectrumA31Bits[64] = +{ + 0, 0, 4, 5, 0, 5, 4, 0, 0, 0, 5, 5, 0, 5, 5, 0, + 5, 5, 6, 6, 0, 6, 5, 5, 5, 6, 6, 7, 0, 7, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 7, 0, 7, 6, 6, + 5, 5, 5, 6, 0, 6, 6, 5, 0, 0, 5, 5, 0, 5, 5, 0 +}; + +static const uint16_t SpectrumA31Codes[64] = +{ + 0x00, 0x00, 0x02, 0x18, 0x00, 0x19, 0x03, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x09, 0x15, 0x00, + 0x1A, 0x0A, 0x3E, 0x2C, 0x00, 0x2F, 0x01, 0x0D, 0x0E, 0x38, 0x20, 0x78, 0x00, 0x7B, 0x23, 0x3B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x3A, 0x22, 0x7A, 0x00, 0x79, 0x21, 0x39, + 0x1B, 0x0C, 0x00, 0x2E, 0x00, 0x2D, 0x3F, 0x0B, 0x00, 0x00, 0x14, 0x08, 0x00, 0x03, 0x13, 0x00 +}; + +static const uint8_t SpectrumA32Bits[64] = +{ + 4, 5, 5, 6, 0, 6, 5, 5, 5, 6, 5, 6, 0, 6, 5, 5, + 5, 5, 6, 7, 0, 7, 6, 5, 6, 6, 7, 7, 0, 7, 7, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 7, 0, 7, 7, 6, + 5, 5, 6, 7, 0, 7, 6, 5, 5, 5, 5, 6, 0, 6, 5, 6 +}; + +static const uint16_t SpectrumA32Codes[64] = +{ + 0x0D, 0x18, 0x16, 0x3A, 0x00, 0x3B, 0x17, 0x19, 0x12, 0x3E, 0x08, 0x1C, 0x00, 0x1B, 0x07, 0x01, + 0x10, 0x02, 0x28, 0x78, 0x00, 0x7B, 0x1F, 0x05, 0x2A, 0x16, 0x72, 0x2A, 0x00, 0x29, 0x71, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x18, 0x70, 0x28, 0x00, 0x2B, 0x73, 0x17, + 0x11, 0x04, 0x1E, 0x7A, 0x00, 0x79, 0x29, 0x03, 0x13, 0x00, 0x06, 0x1A, 0x00, 0x1D, 0x09, 0x3F +}; + +static const uint8_t SpectrumA33Bits[64] = +{ + 3, 4, 5, 6, 0, 6, 5, 4, 4, 5, 6, 7, 0, 7, 6, 5, + 5, 6, 6, 7, 0, 7, 6, 6, 6, 7, 8, 8, 0, 8, 8, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 8, 0, 8, 8, 7, + 5, 6, 6, 7, 0, 7, 6, 6, 4, 5, 6, 7, 0, 7, 6, 5 +}; + +static const uint16_t SpectrumA33Codes[64] = +{ + 0x05, 0x06, 0x10, 0x08, 0x00, 0x09, 0x11, 0x07, 0x04, 0x12, 0x3E, 0x6A, 0x00, 0x6D, 0x3D, 0x19, + 0x06, 0x3A, 0x06, 0x02, 0x00, 0x01, 0x05, 0x39, 0x02, 0x16, 0xDC, 0x2A, 0x00, 0x29, 0xDF, 0x69, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x68, 0xDE, 0x28, 0x00, 0x2B, 0xDD, 0x17, + 0x07, 0x38, 0x04, 0x00, 0x00, 0x03, 0x07, 0x3B, 0x05, 0x18, 0x3C, 0x6C, 0x00, 0x6B, 0x3F, 0x13 +}; + +static const uint8_t SpectrumA34Bits[64] = +{ + 2, 4, 5, 7, 0, 7, 5, 4, 4, 5, 6, 8, 0, 8, 6, 5, + 5, 6, 7, 8, 0, 8, 7, 6, 7, 8, 8, 10, 0, 10, 9, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 10, 0, 10, 8, 8, + 5, 6, 7, 8, 0, 8, 7, 6, 4, 5, 6, 8, 0, 8, 6, 5 +}; + +static const uint16_t SpectrumA34Codes[64] = +{ + 0x000, 0x00A, 0x00A, 0x034, 0x000, 0x035, 0x00B, 0x00B, 0x008, 0x01C, 0x032, 0x0DA, + 0x000, 0x0DD, 0x035, 0x01F, 0x008, 0x01E, 0x03A, 0x06C, 0x000, 0x063, 0x039, 0x031, + 0x032, 0x06E, 0x060, 0x37A, 0x000, 0x379, 0x1BF, 0x0D9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x033, 0x0D8, 0x1BE, 0x378, 0x000, 0x37B, 0x061, 0x06F, + 0x009, 0x030, 0x038, 0x062, 0x000, 0x06D, 0x03B, 0x01F, 0x009, 0x01E, 0x034, 0x0DC, + 0x000, 0x0DB, 0x033, 0x01D +}; + +static const uint8_t SpectrumA41Bits[256] = +{ + 0, 0, 0, 0, 6, 6, 7, 7, 0, 7, 7, 6, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 8, 0, 8, 7, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 8, 8, 0, 8, 8, 7, 7, 0, 0, 0, + 7, 7, 7, 8, 7, 8, 8, 8, 0, 8, 8, 8, 7, 8, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 9, 0, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 0, 9, 8, 8, 8, 8, 8, 7, + 8, 8, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 8, + 7, 7, 8, 8, 8, 8, 8, 9, 0, 9, 9, 8, 8, 8, 8, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 0, 9, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 7, 8, 8, 8, 0, 8, 8, 8, 7, 8, 7, 7, + 0, 0, 0, 0, 7, 7, 8, 8, 0, 8, 8, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 8, 0, 8, 7, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 7, 0, 7, 7, 7, 7, 0, 0, 0 +}; + +static const uint16_t SpectrumA41Codes[256] = +{ + 0x000, 0x000, 0x000, 0x000, 0x018, 0x00E, 0x05E, 0x028, 0x000, 0x029, 0x05F, 0x00F, + 0x019, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x076, 0x06E, 0x03E, 0x004, + 0x000, 0x017, 0x045, 0x07B, 0x013, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x04A, 0x048, 0x010, 0x0CE, 0x000, 0x0E1, 0x023, 0x055, 0x053, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x008, 0x018, 0x0D6, 0x09E, 0x000, 0x09D, 0x0E5, 0x02B, + 0x01B, 0x000, 0x000, 0x000, 0x07C, 0x05C, 0x038, 0x0FC, 0x002, 0x0D2, 0x09A, 0x05C, + 0x000, 0x06B, 0x0A3, 0x0D9, 0x00F, 0x0FF, 0x03D, 0x061, 0x074, 0x056, 0x036, 0x000, + 0x0CC, 0x08C, 0x058, 0x1E2, 0x000, 0x00F, 0x05F, 0x0A1, 0x0D5, 0x00D, 0x03B, 0x059, + 0x040, 0x014, 0x0DA, 0x0B6, 0x084, 0x040, 0x1E0, 0x196, 0x000, 0x1A1, 0x00D, 0x043, + 0x087, 0x0C7, 0x0E3, 0x00B, 0x0F2, 0x0C4, 0x08E, 0x05A, 0x024, 0x1CC, 0x194, 0x168, + 0x000, 0x16B, 0x1A3, 0x1CF, 0x027, 0x069, 0x099, 0x0C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0F3, 0x0C8, 0x098, 0x068, 0x026, 0x1CE, 0x1A2, 0x16A, 0x000, 0x169, 0x195, 0x1CD, + 0x025, 0x05B, 0x08F, 0x0C5, 0x041, 0x00A, 0x0E2, 0x0C6, 0x086, 0x042, 0x00C, 0x1A0, + 0x000, 0x197, 0x1E1, 0x041, 0x085, 0x0B7, 0x0DB, 0x015, 0x075, 0x058, 0x03A, 0x00C, + 0x0D4, 0x0A0, 0x05E, 0x00E, 0x000, 0x1E3, 0x059, 0x08D, 0x0CD, 0x001, 0x037, 0x057, + 0x07D, 0x060, 0x03C, 0x0FE, 0x00E, 0x0D8, 0x0A2, 0x06A, 0x000, 0x05D, 0x09B, 0x0D3, + 0x003, 0x0FD, 0x039, 0x05D, 0x000, 0x000, 0x000, 0x000, 0x01A, 0x02A, 0x0E4, 0x09C, + 0x000, 0x09F, 0x0D7, 0x019, 0x009, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x052, 0x054, 0x022, 0x0E0, 0x000, 0x0CF, 0x011, 0x049, 0x04B, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x012, 0x07A, 0x044, 0x016, 0x000, 0x005, 0x03F, 0x06F, + 0x077, 0x000, 0x000, 0x000 +}; + +static const uint8_t SpectrumA42Bits[256] = +{ + 5, 6, 7, 7, 7, 7, 8, 8, 0, 8, 8, 7, 7, 7, 7, 6, + 6, 7, 7, 8, 7, 7, 8, 8, 0, 8, 8, 7, 7, 8, 7, 7, + 7, 7, 8, 8, 7, 8, 8, 9, 0, 9, 8, 8, 7, 8, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 0, 9, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 8, 8, 8, 9, 9, 0, 9, 9, 8, 8, 8, 7, 7, + 7, 7, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 7, + 8, 8, 8, 8, 9, 9, 9, 10, 0, 10, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 10, 10, 0, 10, 10, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 9, 9, 9, 9, 10, 10, 0, 10, 10, 9, 9, 9, 9, 8, + 8, 8, 8, 8, 9, 9, 9, 10, 0, 10, 9, 9, 9, 8, 8, 8, + 7, 7, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 7, + 7, 7, 7, 8, 8, 8, 9, 9, 0, 9, 9, 8, 8, 8, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 0, 9, 8, 8, 8, 8, 8, 8, + 7, 7, 8, 8, 7, 8, 8, 9, 0, 9, 8, 8, 7, 8, 8, 7, + 6, 7, 7, 8, 7, 7, 8, 8, 0, 8, 8, 7, 7, 8, 7, 7 +}; + +static const uint16_t SpectrumA42Codes[256] = +{ + 0x003, 0x018, 0x058, 0x000, 0x066, 0x03C, 0x0D6, 0x07C, 0x000, 0x07D, 0x0D7, 0x03D, + 0x067, 0x001, 0x059, 0x019, 0x002, 0x064, 0x036, 0x0DA, 0x04C, 0x01C, 0x0BE, 0x02C, + 0x000, 0x037, 0x0C5, 0x029, 0x04B, 0x0E7, 0x03B, 0x069, 0x044, 0x02E, 0x0FA, 0x092, + 0x020, 0x0F8, 0x086, 0x1FC, 0x000, 0x1E7, 0x07F, 0x0F5, 0x023, 0x0AD, 0x0FD, 0x02D, + 0x0F6, 0x0DC, 0x09C, 0x03E, 0x0F0, 0x0B6, 0x026, 0x186, 0x000, 0x18D, 0x02F, 0x0B5, + 0x0E1, 0x03D, 0x0AF, 0x0D9, 0x054, 0x040, 0x014, 0x0EC, 0x0BC, 0x054, 0x1C6, 0x108, + 0x000, 0x10B, 0x1C5, 0x069, 0x0B9, 0x0DF, 0x019, 0x047, 0x026, 0x008, 0x0E4, 0x0A2, + 0x056, 0x1DC, 0x142, 0x06A, 0x000, 0x091, 0x123, 0x1DF, 0x04B, 0x0A7, 0x0EB, 0x00B, + 0x0C0, 0x09E, 0x06A, 0x022, 0x1AA, 0x140, 0x092, 0x3CA, 0x000, 0x3A7, 0x04B, 0x121, + 0x18F, 0x007, 0x071, 0x0A5, 0x020, 0x004, 0x1A8, 0x174, 0x0E4, 0x068, 0x3A4, 0x2EE, + 0x000, 0x2ED, 0x3C9, 0x049, 0x0E7, 0x185, 0x1D1, 0x1FF, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x021, 0x1FE, 0x1D0, 0x184, 0x0E6, 0x048, 0x3C8, 0x2EC, 0x000, 0x2EF, 0x3A5, 0x069, + 0x0E5, 0x175, 0x1A9, 0x005, 0x0C1, 0x0A4, 0x070, 0x006, 0x18E, 0x120, 0x04A, 0x3A6, + 0x000, 0x3CB, 0x093, 0x141, 0x1AB, 0x023, 0x06B, 0x09F, 0x027, 0x00A, 0x0EA, 0x0A6, + 0x04A, 0x1DE, 0x122, 0x090, 0x000, 0x06B, 0x143, 0x1DD, 0x057, 0x0A3, 0x0E5, 0x009, + 0x055, 0x046, 0x018, 0x0DE, 0x0B8, 0x068, 0x1C4, 0x10A, 0x000, 0x109, 0x1C7, 0x055, + 0x0BD, 0x0ED, 0x015, 0x041, 0x0F7, 0x0D8, 0x0AE, 0x03C, 0x0E0, 0x0B4, 0x02E, 0x18C, + 0x000, 0x187, 0x027, 0x0B7, 0x0F1, 0x03F, 0x09D, 0x0DD, 0x045, 0x02C, 0x0FC, 0x0AC, + 0x022, 0x0F4, 0x07E, 0x1E6, 0x000, 0x1FD, 0x087, 0x0F9, 0x021, 0x093, 0x0FB, 0x02F, + 0x003, 0x068, 0x03A, 0x0E6, 0x04A, 0x028, 0x0C4, 0x036, 0x000, 0x02D, 0x0BF, 0x01D, + 0x04D, 0x0DB, 0x037, 0x065 +}; + +static const uint8_t SpectrumA43Bits[256] = +{ + 4, 6, 6, 7, 7, 8, 8, 9, 0, 9, 8, 8, 7, 7, 6, 6, + 5, 6, 7, 7, 7, 8, 8, 9, 0, 9, 8, 8, 7, 7, 7, 6, + 6, 7, 7, 7, 8, 8, 9, 9, 0, 9, 9, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 9, 10, 0, 10, 9, 9, 8, 8, 7, 7, + 7, 7, 8, 8, 8, 9, 10, 10, 0, 10, 10, 9, 8, 8, 8, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 0, 10, 10, 9, 9, 9, 8, 8, + 8, 9, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 8, 9, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 9, + 8, 8, 8, 9, 9, 9, 10, 10, 0, 10, 10, 9, 9, 9, 8, 8, + 7, 7, 8, 8, 8, 9, 10, 10, 0, 10, 10, 9, 8, 8, 8, 7, + 7, 7, 7, 8, 8, 9, 9, 10, 0, 10, 9, 8, 8, 8, 7, 7, + 6, 7, 7, 7, 8, 8, 9, 9, 0, 9, 9, 8, 8, 7, 7, 7, + 5, 6, 7, 7, 7, 8, 8, 9, 0, 9, 8, 8, 7, 7, 7, 6 +}; + +static const uint16_t SpectrumA43Codes[256] = +{ + 0x002, 0x03E, 0x016, 0x060, 0x04E, 0x0DC, 0x04A, 0x130, 0x000, 0x131, 0x04B, 0x0DD, + 0x04F, 0x061, 0x017, 0x03F, 0x002, 0x02C, 0x076, 0x042, 0x034, 0x0CE, 0x002, 0x0E8, + 0x000, 0x0CF, 0x001, 0x0D1, 0x037, 0x045, 0x07B, 0x02F, 0x014, 0x072, 0x052, 0x01A, + 0x0E0, 0x080, 0x198, 0x01E, 0x000, 0x01D, 0x19B, 0x083, 0x0DF, 0x019, 0x055, 0x079, + 0x050, 0x03C, 0x004, 0x0C4, 0x096, 0x00C, 0x0EA, 0x34A, 0x000, 0x34F, 0x0ED, 0x1D7, + 0x095, 0x0AF, 0x003, 0x03F, 0x046, 0x026, 0x0D6, 0x092, 0x046, 0x15A, 0x3A8, 0x108, + 0x000, 0x10F, 0x3A3, 0x135, 0x039, 0x091, 0x0D9, 0x031, 0x0D4, 0x0CA, 0x072, 0x1C6, + 0x136, 0x090, 0x2B2, 0x104, 0x000, 0x103, 0x111, 0x08B, 0x133, 0x1D3, 0x071, 0x0C9, + 0x03E, 0x1B4, 0x18C, 0x0CC, 0x38A, 0x2B0, 0x106, 0x0F2, 0x000, 0x0EF, 0x101, 0x113, + 0x3A1, 0x0CB, 0x18F, 0x1B7, 0x0EE, 0x092, 0x388, 0x348, 0x10A, 0x0F4, 0x0F0, 0x0EA, + 0x000, 0x0E9, 0x0ED, 0x0F7, 0x10D, 0x34D, 0x3AB, 0x0C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0EF, 0x0C8, 0x3AA, 0x34C, 0x10C, 0x0F6, 0x0EC, 0x0E8, 0x000, 0x0EB, 0x0F1, 0x0F5, + 0x10B, 0x349, 0x389, 0x093, 0x03F, 0x1B6, 0x18E, 0x0CA, 0x3A0, 0x112, 0x100, 0x0EE, + 0x000, 0x0F3, 0x107, 0x2B1, 0x38B, 0x0CD, 0x18D, 0x1B5, 0x0D5, 0x0C8, 0x070, 0x1D2, + 0x132, 0x08A, 0x110, 0x102, 0x000, 0x105, 0x2B3, 0x091, 0x137, 0x1C7, 0x073, 0x0CB, + 0x047, 0x030, 0x0D8, 0x090, 0x038, 0x134, 0x3A2, 0x10E, 0x000, 0x109, 0x3A9, 0x15B, + 0x047, 0x093, 0x0D7, 0x027, 0x051, 0x03E, 0x002, 0x0AE, 0x094, 0x1D6, 0x0EC, 0x34E, + 0x000, 0x34B, 0x0EB, 0x00D, 0x097, 0x0C5, 0x005, 0x03D, 0x015, 0x078, 0x054, 0x018, + 0x0DE, 0x082, 0x19A, 0x01C, 0x000, 0x01F, 0x199, 0x081, 0x0E1, 0x01B, 0x053, 0x073, + 0x003, 0x02E, 0x07A, 0x044, 0x036, 0x0D0, 0x000, 0x0CE, 0x000, 0x0E9, 0x003, 0x0CF, + 0x035, 0x043, 0x077, 0x02D +}; + +static const uint8_t SpectrumA44Bits[256] = +{ + 4, 5, 6, 7, 7, 8, 9, 10, 0, 10, 9, 8, 7, 7, 6, 5, + 5, 6, 6, 7, 7, 8, 9, 10, 0, 10, 9, 8, 7, 7, 6, 6, + 6, 6, 7, 7, 8, 9, 10, 10, 0, 10, 10, 9, 8, 7, 7, 6, + 7, 7, 7, 8, 8, 9, 10, 10, 0, 10, 10, 9, 8, 8, 7, 7, + 7, 8, 8, 8, 9, 10, 10, 10, 0, 10, 10, 10, 9, 8, 8, 7, + 8, 8, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 8, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 8, 8, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 8, + 7, 7, 8, 8, 9, 10, 10, 10, 0, 10, 10, 10, 9, 8, 8, 8, + 7, 7, 7, 8, 8, 9, 10, 10, 0, 10, 10, 9, 8, 8, 7, 7, + 6, 6, 7, 7, 8, 9, 10, 10, 0, 10, 10, 9, 8, 7, 7, 6, + 5, 6, 6, 7, 7, 8, 9, 10, 0, 10, 9, 8, 7, 7, 6, 6 +}; + +static const uint16_t SpectrumA44Codes[256] = +{ + 0x00A, 0x012, 0x030, 0x06E, 0x024, 0x074, 0x0EC, 0x07E, 0x000, 0x07F, 0x0ED, 0x075, + 0x025, 0x06F, 0x031, 0x013, 0x010, 0x03C, 0x018, 0x05A, 0x002, 0x046, 0x09E, 0x07C, + 0x000, 0x079, 0x0E5, 0x04D, 0x007, 0x065, 0x01B, 0x03F, 0x02E, 0x016, 0x072, 0x01A, + 0x0D6, 0x1C6, 0x3B4, 0x066, 0x000, 0x06B, 0x3B7, 0x1D9, 0x0D5, 0x021, 0x075, 0x015, + 0x06C, 0x03E, 0x01E, 0x0CC, 0x044, 0x0F2, 0x082, 0x05C, 0x000, 0x05F, 0x087, 0x0F5, + 0x031, 0x0CF, 0x017, 0x059, 0x01C, 0x0EE, 0x0D0, 0x024, 0x1C0, 0x08E, 0x06E, 0x048, + 0x000, 0x04D, 0x06D, 0x089, 0x0F7, 0x033, 0x0D3, 0x001, 0x070, 0x028, 0x1C2, 0x0F0, + 0x08A, 0x074, 0x054, 0x040, 0x000, 0x043, 0x053, 0x073, 0x099, 0x0EF, 0x1C5, 0x02B, + 0x0E6, 0x04E, 0x08C, 0x080, 0x068, 0x058, 0x046, 0x02A, 0x000, 0x029, 0x045, 0x051, + 0x065, 0x085, 0x09B, 0x09D, 0x07A, 0x076, 0x060, 0x056, 0x04E, 0x02C, 0x024, 0x022, + 0x000, 0x021, 0x027, 0x02F, 0x04B, 0x05B, 0x063, 0x071, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x07B, 0x070, 0x062, 0x05A, 0x04A, 0x02E, 0x026, 0x020, 0x000, 0x023, 0x025, 0x02D, + 0x04F, 0x057, 0x061, 0x077, 0x0E7, 0x09C, 0x09A, 0x084, 0x064, 0x050, 0x044, 0x028, + 0x000, 0x02B, 0x047, 0x059, 0x069, 0x081, 0x08D, 0x04F, 0x071, 0x02A, 0x1C4, 0x0EE, + 0x098, 0x072, 0x052, 0x042, 0x000, 0x041, 0x055, 0x075, 0x08B, 0x0F1, 0x1C3, 0x029, + 0x01D, 0x000, 0x0D2, 0x032, 0x0F6, 0x088, 0x06C, 0x04C, 0x000, 0x049, 0x06F, 0x08F, + 0x1C1, 0x025, 0x0D1, 0x0EF, 0x06D, 0x058, 0x016, 0x0CE, 0x030, 0x0F4, 0x086, 0x05E, + 0x000, 0x05D, 0x083, 0x0F3, 0x045, 0x0CD, 0x01F, 0x03F, 0x02F, 0x014, 0x074, 0x020, + 0x0D4, 0x1D8, 0x3B6, 0x06A, 0x000, 0x067, 0x3B5, 0x1C7, 0x0D7, 0x01B, 0x073, 0x017, + 0x011, 0x03E, 0x01A, 0x064, 0x006, 0x04C, 0x0E4, 0x078, 0x000, 0x07D, 0x09F, 0x047, + 0x003, 0x05B, 0x019, 0x03D +}; + +static const uint8_t SpectrumA51Bits[32] = +{ + 5, 5, 5, 5, 5, 6, 6, 6, 4, 4, 5, 5, 5, 5, 5, 5, + 0, 5, 5, 5, 5, 5, 5, 4, 4, 6, 6, 6, 5, 5, 5, 5 +}; + +static const uint16_t SpectrumA51Codes[32] = +{ + 0x19, 0x16, 0x12, 0x0E, 0x06, 0x3A, 0x38, 0x30, 0x00, 0x04, 0x1E, 0x1A, 0x14, 0x10, 0x0C, 0x04, + 0x00, 0x05, 0x0D, 0x11, 0x15, 0x1B, 0x1F, 0x05, 0x01, 0x31, 0x39, 0x3B, 0x07, 0x0F, 0x13, 0x17 +}; + +static const uint8_t SpectrumA52Bits[32] = +{ + 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 0, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4 +}; + +static const uint16_t SpectrumA52Codes[32] = +{ + 0x09, 0x04, 0x00, 0x1E, 0x1A, 0x14, 0x0C, 0x06, 0x18, 0x16, 0x0E, 0x04, 0x3A, 0x38, 0x22, 0x20, + 0x00, 0x21, 0x23, 0x39, 0x3B, 0x05, 0x0F, 0x17, 0x19, 0x07, 0x0D, 0x15, 0x1B, 0x1F, 0x01, 0x05 +}; + +static const uint8_t SpectrumA53Bits[32] = +{ + 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4 +}; + +static const uint16_t SpectrumA53Codes[32] = +{ + 0x00, 0x0C, 0x08, 0x04, 0x1E, 0x16, 0x14, 0x06, 0x0C, 0x04, 0x38, 0x1E, 0x76, 0x74, 0x3A, 0x38, + 0x00, 0x39, 0x3B, 0x75, 0x77, 0x1F, 0x39, 0x05, 0x0D, 0x07, 0x15, 0x17, 0x1F, 0x05, 0x09, 0x0D +}; + +static const uint8_t SpectrumA54Bits[32] = +{ + 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, + 0, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4 +}; + +static const uint16_t SpectrumA54Codes[32] = +{ + 0x02, 0x0E, 0x0A, 0x08, 0x02, 0x1A, 0x0E, 0x02, 0x00, 0x30, 0x18, 0x66, 0x36, 0x34, 0xCA, 0xC8, + 0x00, 0xC9, 0xCB, 0x35, 0x37, 0x67, 0x19, 0x31, 0x01, 0x03, 0x0F, 0x1B, 0x03, 0x09, 0x0B, 0x0F +}; + +static const uint8_t SpectrumA61Bits[64] = +{ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, + 5, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6 +}; + +static const uint16_t SpectrumA61Codes[64] = +{ + 0x35, 0x30, 0x2A, 0x28, 0x24, 0x20, 0x18, 0x0E, 0x0C, 0x7E, 0x7C, 0x72, 0x70, 0x68, 0x5E, 0x5C, + 0x04, 0x0E, 0x08, 0x00, 0x3C, 0x3A, 0x36, 0x32, 0x2C, 0x26, 0x22, 0x1A, 0x16, 0x14, 0x06, 0x04, + 0x00, 0x05, 0x07, 0x15, 0x17, 0x1B, 0x23, 0x27, 0x2D, 0x33, 0x37, 0x3B, 0x3D, 0x01, 0x09, 0x0F, + 0x05, 0x5D, 0x5F, 0x69, 0x71, 0x73, 0x7D, 0x7F, 0x0D, 0x0F, 0x19, 0x21, 0x25, 0x29, 0x2B, 0x31 +}; + +static const uint8_t SpectrumA62Bits[64] = +{ + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 +}; + +static const uint16_t SpectrumA62Codes[64] = +{ + 0x14, 0x0E, 0x08, 0x04, 0x02, 0x3E, 0x3C, 0x38, 0x34, 0x30, 0x2A, 0x24, 0x1A, 0x18, 0x0E, 0x02, + 0x32, 0x36, 0x2C, 0x26, 0x20, 0x16, 0x0C, 0x00, 0x76, 0x74, 0x5E, 0x5C, 0x46, 0x44, 0x2A, 0x28, + 0x00, 0x29, 0x2B, 0x45, 0x47, 0x5D, 0x5F, 0x75, 0x77, 0x01, 0x0D, 0x17, 0x21, 0x27, 0x2D, 0x37, + 0x33, 0x03, 0x0F, 0x19, 0x1B, 0x25, 0x2B, 0x31, 0x35, 0x39, 0x3D, 0x3F, 0x03, 0x05, 0x09, 0x0F +}; + +static const uint8_t SpectrumA63Bits[64] = +{ + 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5 +}; + +static const uint16_t SpectrumA63Codes[64] = +{ + 0x00, 0x1C, 0x18, 0x14, 0x10, 0x0A, 0x08, 0x02, 0x3E, 0x36, 0x2E, 0x2C, 0x24, 0x1C, 0x0E, 0x08, + 0x1E, 0x1A, 0x0C, 0x7A, 0x6A, 0x68, 0x4C, 0x32, 0x16, 0x14, 0xF2, 0xF0, 0x9E, 0x9C, 0x62, 0x60, + 0x00, 0x61, 0x63, 0x9D, 0x9F, 0xF1, 0xF3, 0x15, 0x17, 0x33, 0x4D, 0x69, 0x6B, 0x7B, 0x0D, 0x1B, + 0x1F, 0x09, 0x0F, 0x1D, 0x25, 0x2D, 0x2F, 0x37, 0x3F, 0x03, 0x09, 0x0B, 0x11, 0x15, 0x19, 0x1D +}; + +static const uint8_t SpectrumA64Bits[64] = +{ + 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, + 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 6, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4 +}; + +static const uint16_t SpectrumA64Codes[64] = +{ + 0x006, 0x002, 0x01C, 0x01A, 0x016, 0x012, 0x00E, 0x00A, 0x002, 0x03E, 0x032, 0x02A, + 0x022, 0x020, 0x010, 0x07A, 0x000, 0x078, 0x060, 0x050, 0x024, 0x006, 0x0C6, 0x0C4, + 0x0A4, 0x04E, 0x00A, 0x008, 0x14E, 0x14C, 0x09A, 0x098, 0x000, 0x099, 0x09B, 0x14D, + 0x14F, 0x009, 0x00B, 0x04F, 0x0A5, 0x0C5, 0x0C7, 0x007, 0x025, 0x051, 0x061, 0x079, + 0x001, 0x07B, 0x011, 0x021, 0x023, 0x02B, 0x033, 0x03F, 0x003, 0x00B, 0x00F, 0x013, + 0x017, 0x01B, 0x01D, 0x003 +}; + +static const uint8_t SpectrumA71Bits[128] = +{ + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, + 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; + +static const uint16_t SpectrumA71Codes[128] = +{ + 0x6C, 0x66, 0x62, 0x5C, 0x56, 0x50, 0x52, 0x4E, 0x48, 0x3E, 0x36, 0x34, 0x2A, 0x26, 0x1E, 0x16, + 0x0E, 0x08, 0x00, 0xF6, 0xF4, 0xEE, 0xEC, 0xE2, 0xE0, 0xDA, 0xD2, 0xD0, 0xBE, 0xBC, 0xB2, 0xB0, + 0x0C, 0x20, 0x1C, 0x16, 0x10, 0x08, 0x02, 0x7E, 0x7C, 0x78, 0x74, 0x72, 0x6E, 0x6A, 0x64, 0x60, + 0x5A, 0x54, 0x4C, 0x4A, 0x46, 0x44, 0x3C, 0x32, 0x30, 0x28, 0x24, 0x1C, 0x14, 0x0C, 0x0A, 0x02, + 0x00, 0x03, 0x0B, 0x0D, 0x15, 0x1D, 0x25, 0x29, 0x31, 0x33, 0x3D, 0x45, 0x47, 0x4B, 0x4D, 0x55, + 0x5B, 0x61, 0x65, 0x6B, 0x6F, 0x73, 0x75, 0x79, 0x7D, 0x7F, 0x03, 0x09, 0x11, 0x17, 0x1D, 0x21, + 0x0D, 0xB1, 0xB3, 0xBD, 0xBF, 0xD1, 0xD3, 0xDB, 0xE1, 0xE3, 0xED, 0xEF, 0xF5, 0xF7, 0x01, 0x09, + 0x0F, 0x17, 0x1F, 0x27, 0x2B, 0x35, 0x37, 0x3F, 0x49, 0x4F, 0x53, 0x51, 0x57, 0x5D, 0x63, 0x67 +}; + +static const uint8_t SpectrumA72Bits[128] = +{ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6 +}; + +static const uint16_t SpectrumA72Codes[128] = +{ + 0x2A, 0x24, 0x1C, 0x18, 0x12, 0x0E, 0x0A, 0x06, 0x02, 0x7E, 0x7C, 0x7A, 0x76, 0x72, 0x70, 0x6A, + 0x68, 0x62, 0x5C, 0x5A, 0x52, 0x4E, 0x46, 0x42, 0x3C, 0x34, 0x2A, 0x28, 0x20, 0x12, 0x10, 0x08, + 0x66, 0x74, 0x6C, 0x64, 0x5E, 0x58, 0x50, 0x44, 0x40, 0x36, 0x2C, 0x22, 0x1A, 0x0A, 0x02, 0x00, + 0xF2, 0xF0, 0xDE, 0xDC, 0xC2, 0xC0, 0xAE, 0xAC, 0x9A, 0x98, 0x7E, 0x7C, 0x5E, 0x5C, 0x32, 0x30, + 0x00, 0x31, 0x33, 0x5D, 0x5F, 0x7D, 0x7F, 0x99, 0x9B, 0xAD, 0xAF, 0xC1, 0xC3, 0xDD, 0xDF, 0xF1, + 0xF3, 0x01, 0x03, 0x0B, 0x1B, 0x23, 0x2D, 0x37, 0x41, 0x45, 0x51, 0x59, 0x5F, 0x65, 0x6D, 0x75, + 0x67, 0x09, 0x11, 0x13, 0x21, 0x29, 0x2B, 0x35, 0x3D, 0x43, 0x47, 0x4F, 0x53, 0x5B, 0x5D, 0x63, + 0x69, 0x6B, 0x71, 0x73, 0x77, 0x7B, 0x7D, 0x7F, 0x03, 0x07, 0x0B, 0x0F, 0x13, 0x19, 0x1D, 0x25 +}; + +static const uint8_t SpectrumA73Bits[128] = +{ + 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 +}; + +static const uint16_t SpectrumA73Codes[128] = +{ + 0x003, 0x03E, 0x038, 0x034, 0x030, 0x02C, 0x028, 0x024, 0x020, 0x01C, 0x016, 0x014, + 0x00E, 0x00A, 0x004, 0x000, 0x07A, 0x076, 0x06E, 0x06C, 0x064, 0x05E, 0x056, 0x04E, + 0x04C, 0x044, 0x036, 0x030, 0x022, 0x018, 0x012, 0x004, 0x03C, 0x03E, 0x032, 0x024, + 0x020, 0x010, 0x0F2, 0x0F0, 0x0E8, 0x0CE, 0x0BA, 0x0B8, 0x0A8, 0x08C, 0x06A, 0x04E, + 0x04C, 0x034, 0x00E, 0x00C, 0x1D6, 0x1D4, 0x19A, 0x198, 0x156, 0x154, 0x11E, 0x11C, + 0x0D2, 0x0D0, 0x06E, 0x06C, 0x000, 0x06D, 0x06F, 0x0D1, 0x0D3, 0x11D, 0x11F, 0x155, + 0x157, 0x199, 0x19B, 0x1D5, 0x1D7, 0x00D, 0x00F, 0x035, 0x04D, 0x04F, 0x06B, 0x08D, + 0x0A9, 0x0B9, 0x0BB, 0x0CF, 0x0E9, 0x0F1, 0x0F3, 0x011, 0x021, 0x025, 0x033, 0x03F, + 0x03D, 0x005, 0x013, 0x019, 0x023, 0x031, 0x037, 0x045, 0x04D, 0x04F, 0x057, 0x05F, + 0x065, 0x06D, 0x06F, 0x077, 0x07B, 0x001, 0x005, 0x00B, 0x00F, 0x015, 0x017, 0x01D, + 0x021, 0x025, 0x029, 0x02D, 0x031, 0x035, 0x039, 0x03F +}; + +static const uint8_t SpectrumA74Bits[128] = +{ + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5 +}; + +static const uint16_t SpectrumA74Codes[128] = +{ + 0x00D, 0x00A, 0x004, 0x000, 0x03A, 0x036, 0x032, 0x030, 0x02C, 0x028, 0x026, 0x022, + 0x01E, 0x018, 0x012, 0x00E, 0x006, 0x07E, 0x07A, 0x070, 0x06A, 0x05E, 0x056, 0x054, + 0x048, 0x040, 0x038, 0x022, 0x01A, 0x00A, 0x0F8, 0x0E6, 0x008, 0x0FA, 0x0F0, 0x0D2, + 0x0BA, 0x0B8, 0x094, 0x084, 0x074, 0x042, 0x032, 0x1E6, 0x1CA, 0x1C8, 0x1A2, 0x12E, + 0x10E, 0x10C, 0x0EC, 0x082, 0x062, 0x060, 0x3CA, 0x3C8, 0x342, 0x340, 0x25A, 0x258, + 0x1DE, 0x1DC, 0x102, 0x100, 0x000, 0x101, 0x103, 0x1DD, 0x1DF, 0x259, 0x25B, 0x341, + 0x343, 0x3C9, 0x3CB, 0x061, 0x063, 0x083, 0x0ED, 0x10D, 0x10F, 0x12F, 0x1A3, 0x1C9, + 0x1CB, 0x1E7, 0x033, 0x043, 0x075, 0x085, 0x095, 0x0B9, 0x0BB, 0x0D3, 0x0F1, 0x0FB, + 0x009, 0x0E7, 0x0F9, 0x00B, 0x01B, 0x023, 0x039, 0x041, 0x049, 0x055, 0x057, 0x05F, + 0x06B, 0x071, 0x07B, 0x07F, 0x007, 0x00F, 0x013, 0x019, 0x01F, 0x023, 0x027, 0x029, + 0x02D, 0x031, 0x033, 0x037, 0x03B, 0x001, 0x005, 0x00B +}; + +static const uint8_t SpectrumB22Bits[256] = +{ + 0, 4, 0, 4, 4, 5, 0, 5, 0, 0, 0, 0, 4, 5, 0, 5, + 4, 7, 0, 6, 6, 9, 0, 7, 0, 0, 0, 0, 6, 9, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 6, 0, 7, 6, 7, 0, 9, 0, 0, 0, 0, 6, 7, 0, 9, + 4, 8, 0, 8, 8, 10, 0, 10, 0, 0, 0, 0, 6, 9, 0, 9, + 5, 10, 0, 9, 9, 10, 0, 10, 0, 0, 0, 0, 7, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 9, 0, 10, 9, 10, 0, 10, 0, 0, 0, 0, 7, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 8, 0, 8, 6, 9, 0, 9, 0, 0, 0, 0, 8, 10, 0, 10, + 6, 10, 0, 9, 7, 10, 0, 10, 0, 0, 0, 0, 9, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 9, 0, 10, 7, 10, 0, 10, 0, 0, 0, 0, 9, 10, 0, 10 +}; + +static const uint16_t SpectrumB22Codes[256] = +{ + 0x000, 0x00E, 0x000, 0x00F, 0x008, 0x006, 0x000, 0x00B, 0x000, 0x000, 0x000, 0x000, + 0x009, 0x00A, 0x000, 0x007, 0x006, 0x00A, 0x000, 0x029, 0x006, 0x158, 0x000, 0x023, + 0x000, 0x000, 0x000, 0x000, 0x013, 0x174, 0x000, 0x021, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x007, 0x028, 0x000, 0x00B, 0x012, 0x020, 0x000, 0x175, 0x000, 0x000, 0x000, 0x000, + 0x007, 0x022, 0x000, 0x159, 0x00C, 0x0BC, 0x000, 0x0BF, 0x022, 0x2B8, 0x000, 0x2BB, + 0x000, 0x000, 0x000, 0x000, 0x00B, 0x170, 0x000, 0x15B, 0x000, 0x04E, 0x000, 0x15F, + 0x042, 0x04A, 0x000, 0x041, 0x000, 0x000, 0x000, 0x000, 0x055, 0x044, 0x000, 0x04D, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x02D, 0x172, 0x000, 0x2ED, 0x040, 0x042, 0x000, 0x047, + 0x000, 0x000, 0x000, 0x000, 0x013, 0x2EE, 0x000, 0x049, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00D, 0x0BE, 0x000, 0x0BD, 0x00A, 0x15A, 0x000, 0x171, 0x000, 0x000, 0x000, 0x000, + 0x023, 0x2BA, 0x000, 0x2B9, 0x02C, 0x2EC, 0x000, 0x173, 0x012, 0x048, 0x000, 0x2EF, + 0x000, 0x000, 0x000, 0x000, 0x041, 0x046, 0x000, 0x043, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x15E, 0x000, 0x04F, 0x054, 0x04C, 0x000, 0x045, 0x000, 0x000, 0x000, 0x000, + 0x043, 0x040, 0x000, 0x04B +}; + +static const uint8_t SpectrumB23Bits[256] = +{ + 2, 4, 0, 4, 4, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 6, + 4, 9, 0, 7, 7, 9, 0, 8, 0, 0, 0, 0, 7, 9, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 7, 0, 9, 7, 8, 0, 9, 0, 0, 0, 0, 7, 8, 0, 9, + 4, 8, 0, 8, 9, 10, 0, 10, 0, 0, 0, 0, 7, 10, 0, 10, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 9, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 8, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 8, 0, 8, 7, 10, 0, 10, 0, 0, 0, 0, 9, 10, 0, 10, + 7, 10, 0, 10, 8, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 10, 0, 10, 9, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10 +}; + +static const uint16_t SpectrumB23Codes[256] = +{ + 0x003, 0x008, 0x000, 0x009, 0x002, 0x018, 0x000, 0x01B, 0x000, 0x000, 0x000, 0x000, + 0x003, 0x01A, 0x000, 0x019, 0x000, 0x17C, 0x000, 0x055, 0x056, 0x0E8, 0x000, 0x07D, + 0x000, 0x000, 0x000, 0x000, 0x059, 0x0F6, 0x000, 0x07F, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x054, 0x000, 0x17D, 0x058, 0x07E, 0x000, 0x0F7, 0x000, 0x000, 0x000, 0x000, + 0x057, 0x07C, 0x000, 0x0E9, 0x004, 0x0A2, 0x000, 0x0A1, 0x17A, 0x1DA, 0x000, 0x1D9, + 0x000, 0x000, 0x000, 0x000, 0x053, 0x1E8, 0x000, 0x2F3, 0x05C, 0x1D6, 0x000, 0x1E7, + 0x1EA, 0x1E2, 0x000, 0x1CF, 0x000, 0x000, 0x000, 0x000, 0x17F, 0x1CA, 0x000, 0x1DD, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x05B, 0x2F0, 0x000, 0x1DF, 0x1E4, 0x1CC, 0x000, 0x1D5, + 0x000, 0x000, 0x000, 0x000, 0x071, 0x1E0, 0x000, 0x1C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x005, 0x0A0, 0x000, 0x0A3, 0x052, 0x2F2, 0x000, 0x1E9, 0x000, 0x000, 0x000, 0x000, + 0x17B, 0x1D8, 0x000, 0x1DB, 0x05A, 0x1DE, 0x000, 0x2F1, 0x070, 0x1C8, 0x000, 0x1E1, + 0x000, 0x000, 0x000, 0x000, 0x1E5, 0x1D4, 0x000, 0x1CD, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x05D, 0x1E6, 0x000, 0x1D7, 0x17E, 0x1DC, 0x000, 0x1CB, 0x000, 0x000, 0x000, 0x000, + 0x1EB, 0x1CE, 0x000, 0x1E3 +}; + +static const uint8_t SpectrumB24Bits[256] = +{ + 1, 4, 0, 4, 5, 7, 0, 7, 0, 0, 0, 0, 5, 7, 0, 7, + 5, 9, 0, 7, 8, 10, 0, 9, 0, 0, 0, 0, 7, 10, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 7, 0, 9, 7, 9, 0, 10, 0, 0, 0, 0, 8, 9, 0, 10, + 5, 9, 0, 8, 9, 10, 0, 10, 0, 0, 0, 0, 7, 10, 0, 10, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 8, 0, 9, 7, 10, 0, 10, 0, 0, 0, 0, 9, 10, 0, 10, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 10, 0, 10, 10, 10, 0, 10, 0, 0, 0, 0, 10, 10, 0, 10 +}; + +static const uint16_t SpectrumB24Codes[256] = +{ + 0x001, 0x000, 0x000, 0x001, 0x00A, 0x01C, 0x000, 0x033, 0x000, 0x000, 0x000, 0x000, + 0x00B, 0x032, 0x000, 0x01D, 0x008, 0x0D8, 0x000, 0x031, 0x06E, 0x0FA, 0x000, 0x0D7, + 0x000, 0x000, 0x000, 0x000, 0x011, 0x0F4, 0x000, 0x0D5, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x009, 0x030, 0x000, 0x0D9, 0x010, 0x0D4, 0x000, 0x0F5, 0x000, 0x000, 0x000, 0x000, + 0x06F, 0x0D6, 0x000, 0x0FB, 0x00E, 0x0DA, 0x000, 0x025, 0x0D2, 0x0D4, 0x000, 0x0DB, + 0x000, 0x000, 0x000, 0x000, 0x017, 0x0FE, 0x000, 0x0FD, 0x014, 0x0DC, 0x000, 0x0F9, + 0x0F2, 0x0D6, 0x000, 0x09B, 0x000, 0x000, 0x000, 0x000, 0x1A3, 0x09C, 0x000, 0x0D3, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x019, 0x0F6, 0x000, 0x0D9, 0x0F0, 0x09E, 0x000, 0x0D1, + 0x000, 0x000, 0x000, 0x000, 0x1A1, 0x0DE, 0x000, 0x099, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00F, 0x024, 0x000, 0x0DB, 0x016, 0x0FC, 0x000, 0x0FF, 0x000, 0x000, 0x000, 0x000, + 0x0D3, 0x0DA, 0x000, 0x0D5, 0x018, 0x0D8, 0x000, 0x0F7, 0x1A0, 0x098, 0x000, 0x0DF, + 0x000, 0x000, 0x000, 0x000, 0x0F1, 0x0D0, 0x000, 0x09F, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x015, 0x0F8, 0x000, 0x0DD, 0x1A2, 0x0D2, 0x000, 0x09D, 0x000, 0x000, 0x000, 0x000, + 0x0F3, 0x09A, 0x000, 0x0D7 +}; + +static const uint8_t SpectrumB32Bits[64] = +{ + 2, 4, 5, 6, 0, 6, 5, 4, 5, 6, 6, 7, 0, 6, 5, 6, + 5, 6, 7, 7, 0, 8, 7, 6, 6, 7, 8, 9, 0, 9, 8, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 0, 9, 8, 7, + 5, 6, 7, 8, 0, 7, 7, 6, 5, 6, 5, 6, 0, 7, 6, 6 +}; + +static const uint16_t SpectrumB32Codes[64] = +{ + 0x001, 0x002, 0x01E, 0x02A, 0x000, 0x02B, 0x01F, 0x003, 0x016, 0x020, 0x03A, 0x064, + 0x000, 0x005, 0x001, 0x023, 0x01A, 0x026, 0x070, 0x00C, 0x000, 0x0CF, 0x073, 0x031, + 0x024, 0x00E, 0x0CC, 0x146, 0x000, 0x145, 0x0A1, 0x053, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x025, 0x052, 0x0A0, 0x144, 0x000, 0x147, 0x0CD, 0x00F, + 0x01B, 0x030, 0x072, 0x0CE, 0x000, 0x00D, 0x071, 0x027, 0x017, 0x022, 0x000, 0x004, + 0x000, 0x065, 0x03B, 0x021 +}; + +static const uint8_t SpectrumB33Bits[64] = +{ + 2, 4, 5, 7, 0, 7, 5, 4, 4, 5, 6, 8, 0, 7, 6, 5, + 5, 6, 7, 9, 0, 8, 7, 6, 7, 8, 9, 10, 0, 10, 9, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 10, 0, 10, 9, 8, + 5, 6, 7, 8, 0, 9, 7, 6, 4, 5, 6, 7, 0, 8, 6, 5 +}; + +static const uint16_t SpectrumB33Codes[64] = +{ + 0x003, 0x008, 0x014, 0x05E, 0x000, 0x05F, 0x015, 0x009, 0x004, 0x002, 0x01C, 0x0BA, + 0x000, 0x011, 0x01F, 0x001, 0x00C, 0x00C, 0x014, 0x166, 0x000, 0x02D, 0x013, 0x00F, + 0x05A, 0x0B0, 0x05E, 0x0B8, 0x000, 0x0BB, 0x165, 0x0B9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x05B, 0x0B8, 0x164, 0x0BA, 0x000, 0x0B9, 0x05F, 0x0B1, + 0x00D, 0x00E, 0x012, 0x02C, 0x000, 0x167, 0x015, 0x00D, 0x005, 0x000, 0x01E, 0x010, + 0x000, 0x0BB, 0x01D, 0x003 +}; + +static const uint8_t SpectrumB34Bits[64] = +{ + 1, 4, 6, 8, 0, 8, 6, 4, 4, 6, 7, 9, 0, 8, 7, 6, + 6, 7, 8, 10, 0, 10, 8, 7, 8, 9, 10, 10, 0, 10, 10, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10, 10, 0, 10, 10, 9, + 6, 7, 8, 10, 0, 10, 8, 7, 4, 6, 7, 8, 0, 9, 7, 6 +}; + +static const uint16_t SpectrumB34Codes[64] = +{ + 0x000, 0x00A, 0x038, 0x0EE, 0x000, 0x0EF, 0x039, 0x00B, 0x008, 0x03C, 0x06E, 0x1D8, + 0x000, 0x0C1, 0x075, 0x03F, 0x032, 0x068, 0x0C4, 0x358, 0x000, 0x30F, 0x0C7, 0x06D, + 0x0D4, 0x1AE, 0x30C, 0x308, 0x000, 0x30B, 0x35B, 0x1DB, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x0D5, 0x1DA, 0x35A, 0x30A, 0x000, 0x309, 0x30D, 0x1AF, + 0x033, 0x06C, 0x0C6, 0x30E, 0x000, 0x359, 0x0C5, 0x069, 0x009, 0x03E, 0x074, 0x0C0, + 0x000, 0x1D9, 0x06F, 0x03D +}; + +static const uint8_t SpectrumB42Bits[256] = +{ + 4, 5, 6, 8, 6, 7, 8, 8, 0, 8, 8, 7, 6, 8, 6, 5, + 5, 6, 7, 8, 7, 7, 8, 9, 0, 8, 8, 7, 7, 8, 7, 6, + 7, 7, 8, 9, 7, 8, 9, 9, 0, 9, 9, 8, 7, 9, 8, 7, + 8, 9, 9, 10, 8, 8, 9, 10, 0, 10, 9, 8, 8, 10, 9, 8, + 6, 7, 8, 8, 9, 9, 10, 10, 0, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 8, 9, 9, 10, 10, 10, 0, 10, 10, 10, 9, 9, 8, 7, + 8, 8, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 9, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 9, 9, + 8, 8, 9, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 9, 8, + 7, 7, 8, 9, 9, 10, 10, 10, 0, 10, 10, 10, 9, 9, 8, 7, + 6, 7, 8, 8, 9, 9, 10, 10, 0, 10, 10, 9, 9, 8, 8, 7, + 8, 8, 9, 10, 8, 8, 9, 10, 0, 10, 9, 8, 8, 10, 9, 9, + 7, 7, 8, 9, 7, 8, 9, 9, 0, 9, 9, 8, 7, 9, 8, 7, + 5, 6, 7, 8, 7, 7, 8, 8, 0, 9, 8, 7, 7, 8, 7, 6 +}; + +static const uint16_t SpectrumB42Codes[256] = +{ + 0x00E, 0x018, 0x010, 0x0F0, 0x024, 0x05A, 0x0F6, 0x078, 0x000, 0x079, 0x0F7, 0x05B, + 0x025, 0x0F1, 0x011, 0x019, 0x00C, 0x014, 0x01C, 0x036, 0x05C, 0x012, 0x09E, 0x1E4, + 0x000, 0x00B, 0x0A9, 0x03B, 0x05F, 0x071, 0x019, 0x017, 0x06E, 0x000, 0x03E, 0x114, + 0x002, 0x0B0, 0x1AA, 0x07A, 0x000, 0x099, 0x1E7, 0x0B3, 0x00B, 0x131, 0x07F, 0x00D, + 0x0D8, 0x1FE, 0x112, 0x22E, 0x086, 0x010, 0x134, 0x35C, 0x000, 0x35F, 0x133, 0x013, + 0x081, 0x22D, 0x119, 0x07B, 0x00A, 0x050, 0x0F8, 0x04E, 0x1B4, 0x154, 0x3EC, 0x0D2, + 0x000, 0x0D7, 0x3D7, 0x137, 0x1FD, 0x073, 0x0FD, 0x057, 0x052, 0x010, 0x08E, 0x1E8, + 0x11A, 0x3EE, 0x0F2, 0x03C, 0x000, 0x03F, 0x0F1, 0x3D5, 0x111, 0x1F5, 0x09D, 0x025, + 0x0D2, 0x082, 0x1A0, 0x0F8, 0x36E, 0x0D4, 0x072, 0x03A, 0x000, 0x027, 0x071, 0x07D, + 0x36D, 0x0FB, 0x1AD, 0x085, 0x00C, 0x1A8, 0x03C, 0x346, 0x0D0, 0x076, 0x024, 0x020, + 0x000, 0x023, 0x039, 0x075, 0x07F, 0x345, 0x09B, 0x157, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00D, 0x156, 0x09A, 0x344, 0x07E, 0x074, 0x038, 0x022, 0x000, 0x021, 0x025, 0x077, + 0x0D1, 0x347, 0x03D, 0x1A9, 0x0D3, 0x084, 0x1AC, 0x0FA, 0x36C, 0x07C, 0x070, 0x026, + 0x000, 0x03B, 0x073, 0x0D5, 0x36F, 0x0F9, 0x1A1, 0x083, 0x053, 0x024, 0x09C, 0x1F4, + 0x110, 0x3D4, 0x0F0, 0x03E, 0x000, 0x03D, 0x0F3, 0x3EF, 0x11B, 0x1E9, 0x08F, 0x011, + 0x00B, 0x056, 0x0FC, 0x072, 0x1FC, 0x136, 0x3D6, 0x0D6, 0x000, 0x0D3, 0x3ED, 0x155, + 0x1B5, 0x04F, 0x0F9, 0x051, 0x0D9, 0x07A, 0x118, 0x22C, 0x080, 0x012, 0x132, 0x35E, + 0x000, 0x35D, 0x135, 0x011, 0x087, 0x22F, 0x113, 0x1FF, 0x06F, 0x00C, 0x07E, 0x130, + 0x00A, 0x0B2, 0x1E6, 0x098, 0x000, 0x07B, 0x1AB, 0x0B1, 0x003, 0x115, 0x03F, 0x001, + 0x00D, 0x016, 0x018, 0x070, 0x05E, 0x03A, 0x0A8, 0x00A, 0x000, 0x1E5, 0x09F, 0x013, + 0x05D, 0x037, 0x01D, 0x015 +}; + +static const uint8_t SpectrumB43Bits[256] = +{ + 2, 5, 6, 7, 7, 8, 8, 9, 0, 9, 8, 8, 7, 7, 6, 5, + 5, 6, 7, 8, 7, 8, 9, 10, 0, 10, 9, 8, 7, 8, 7, 6, + 6, 7, 8, 9, 8, 9, 10, 10, 0, 10, 10, 9, 8, 9, 8, 7, + 7, 8, 9, 10, 9, 9, 10, 10, 0, 10, 10, 10, 9, 10, 9, 8, + 7, 8, 8, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 8, 7, + 8, 8, 9, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 9, 8, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 9, + 8, 8, 9, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 9, 8, + 7, 7, 8, 9, 10, 10, 10, 10, 0, 10, 10, 10, 10, 9, 8, 8, + 7, 8, 9, 10, 9, 10, 10, 10, 0, 10, 10, 9, 9, 10, 9, 8, + 6, 7, 8, 9, 8, 9, 10, 10, 0, 10, 10, 9, 8, 9, 8, 7, + 5, 6, 7, 8, 7, 8, 9, 10, 0, 10, 9, 8, 7, 8, 7, 6 +}; + +static const uint16_t SpectrumB43Codes[256] = +{ + 0x001, 0x01E, 0x022, 0x018, 0x064, 0x0EC, 0x008, 0x100, 0x000, 0x101, 0x009, 0x0ED, + 0x065, 0x019, 0x023, 0x01F, 0x01A, 0x030, 0x056, 0x09A, 0x00A, 0x090, 0x12C, 0x0A6, + 0x000, 0x0A9, 0x12F, 0x093, 0x00F, 0x09F, 0x059, 0x039, 0x00E, 0x054, 0x0BC, 0x19E, + 0x082, 0x176, 0x0AC, 0x088, 0x000, 0x08B, 0x0AF, 0x19D, 0x095, 0x1D1, 0x0BF, 0x051, + 0x002, 0x098, 0x1D4, 0x0B8, 0x170, 0x046, 0x090, 0x060, 0x000, 0x067, 0x095, 0x0BD, + 0x173, 0x0B5, 0x1D3, 0x09D, 0x052, 0x0EE, 0x034, 0x174, 0x0BA, 0x09C, 0x080, 0x044, + 0x000, 0x047, 0x06D, 0x099, 0x0BF, 0x16F, 0x085, 0x001, 0x0CC, 0x036, 0x16C, 0x0B0, + 0x09A, 0x084, 0x04E, 0x03E, 0x000, 0x037, 0x04B, 0x06B, 0x0A1, 0x0B3, 0x16B, 0x087, + 0x1D6, 0x102, 0x0A4, 0x092, 0x068, 0x04C, 0x034, 0x030, 0x000, 0x02D, 0x03D, 0x049, + 0x083, 0x097, 0x0AB, 0x169, 0x0B6, 0x09E, 0x06E, 0x064, 0x040, 0x038, 0x02E, 0x02A, + 0x000, 0x029, 0x033, 0x03B, 0x043, 0x063, 0x087, 0x0A3, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0B7, 0x0A2, 0x086, 0x062, 0x042, 0x03A, 0x032, 0x028, 0x000, 0x02B, 0x02F, 0x039, + 0x041, 0x065, 0x06F, 0x09F, 0x1D7, 0x168, 0x0AA, 0x096, 0x082, 0x048, 0x03C, 0x02C, + 0x000, 0x031, 0x035, 0x04D, 0x069, 0x093, 0x0A5, 0x103, 0x0CD, 0x086, 0x16A, 0x0B2, + 0x0A0, 0x06A, 0x04A, 0x036, 0x000, 0x03F, 0x04F, 0x085, 0x09B, 0x0B1, 0x16D, 0x037, + 0x053, 0x000, 0x084, 0x16E, 0x0BE, 0x098, 0x06C, 0x046, 0x000, 0x045, 0x081, 0x09D, + 0x0BB, 0x175, 0x035, 0x0EF, 0x003, 0x09C, 0x1D2, 0x0B4, 0x172, 0x0BC, 0x094, 0x066, + 0x000, 0x061, 0x091, 0x047, 0x171, 0x0B9, 0x1D5, 0x099, 0x00F, 0x050, 0x0BE, 0x1D0, + 0x094, 0x19C, 0x0AE, 0x08A, 0x000, 0x089, 0x0AD, 0x177, 0x083, 0x19F, 0x0BD, 0x055, + 0x01B, 0x038, 0x058, 0x09E, 0x00E, 0x092, 0x12E, 0x0A8, 0x000, 0x0A7, 0x12D, 0x091, + 0x00B, 0x09B, 0x057, 0x031 +}; + +static const uint8_t SpectrumB44Bits[256] = +{ + 2, 4, 6, 7, 7, 8, 10, 10, 0, 10, 10, 8, 7, 7, 6, 4, + 5, 5, 7, 8, 8, 10, 10, 10, 0, 10, 10, 10, 8, 8, 7, 5, + 6, 7, 8, 9, 9, 10, 10, 10, 0, 10, 10, 10, 10, 9, 8, 7, + 8, 8, 9, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 8, + 8, 8, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 8, + 9, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 9, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 8, + 8, 8, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 9, 8, + 6, 7, 8, 9, 10, 10, 10, 10, 0, 10, 10, 10, 9, 9, 8, 7, + 5, 5, 7, 8, 8, 10, 10, 10, 0, 10, 10, 10, 8, 8, 7, 5 +}; + +static const uint16_t SpectrumB44Codes[256] = +{ + 0x002, 0x002, 0x030, 0x000, 0x002, 0x00C, 0x1D2, 0x1AE, 0x000, 0x1AF, 0x1D3, 0x00D, + 0x003, 0x001, 0x031, 0x003, 0x01E, 0x002, 0x070, 0x0C8, 0x07E, 0x1E8, 0x1C0, 0x176, + 0x000, 0x17F, 0x1C3, 0x1EB, 0x0CF, 0x0D3, 0x073, 0x009, 0x018, 0x06A, 0x0EC, 0x1DE, + 0x1A2, 0x1CA, 0x1AA, 0x164, 0x000, 0x16D, 0x1AD, 0x1D1, 0x1EF, 0x1DD, 0x0EB, 0x06D, + 0x0E8, 0x0CA, 0x1BE, 0x1CE, 0x1DA, 0x1B6, 0x170, 0x154, 0x000, 0x153, 0x173, 0x1B1, + 0x1D7, 0x1D5, 0x343, 0x0CD, 0x0DC, 0x078, 0x340, 0x1CC, 0x1BA, 0x1A8, 0x156, 0x148, + 0x000, 0x145, 0x15F, 0x1A1, 0x1BD, 0x1D9, 0x1ED, 0x07D, 0x1BC, 0x1DC, 0x1C4, 0x1B2, + 0x17C, 0x15A, 0x14A, 0x03A, 0x000, 0x039, 0x147, 0x16B, 0x17B, 0x1B5, 0x1C9, 0x1DF, + 0x1C6, 0x1B8, 0x1A2, 0x168, 0x160, 0x14C, 0x02E, 0x024, 0x000, 0x027, 0x03D, 0x151, + 0x15D, 0x16F, 0x1A7, 0x1BF, 0x1A4, 0x174, 0x162, 0x14E, 0x140, 0x02C, 0x02A, 0x022, + 0x000, 0x021, 0x029, 0x03F, 0x143, 0x159, 0x167, 0x179, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x1A5, 0x178, 0x166, 0x158, 0x142, 0x03E, 0x028, 0x020, 0x000, 0x023, 0x02B, 0x02D, + 0x141, 0x14F, 0x163, 0x175, 0x1C7, 0x1BE, 0x1A6, 0x16E, 0x15C, 0x150, 0x03C, 0x026, + 0x000, 0x025, 0x02F, 0x14D, 0x161, 0x169, 0x1A3, 0x1B9, 0x1BD, 0x1DE, 0x1C8, 0x1B4, + 0x17A, 0x16A, 0x146, 0x038, 0x000, 0x03B, 0x14B, 0x15B, 0x17D, 0x1B3, 0x1C5, 0x1DD, + 0x0DD, 0x07C, 0x1EC, 0x1D8, 0x1BC, 0x1A0, 0x15E, 0x144, 0x000, 0x149, 0x157, 0x1A9, + 0x1BB, 0x1CD, 0x341, 0x079, 0x0E9, 0x0CC, 0x342, 0x1D4, 0x1D6, 0x1B0, 0x172, 0x152, + 0x000, 0x155, 0x171, 0x1B7, 0x1DB, 0x1CF, 0x1BF, 0x0CB, 0x019, 0x06C, 0x0EA, 0x1DC, + 0x1EE, 0x1D0, 0x1AC, 0x16C, 0x000, 0x165, 0x1AB, 0x1CB, 0x1A3, 0x1DF, 0x0ED, 0x06B, + 0x01F, 0x008, 0x072, 0x0D2, 0x0CE, 0x1EA, 0x1C2, 0x17E, 0x000, 0x177, 0x1C1, 0x1E9, + 0x07F, 0x0C9, 0x071, 0x003 +}; + +static const uint8_t SpectrumB52Bits[32] = +{ + 3, 4, 4, 4, 5, 5, 6, 6, 5, 5, 5, 6, 6, 6, 7, 7, + 0, 7, 7, 6, 6, 6, 5, 5, 5, 6, 6, 5, 5, 4, 4, 4 +}; + +static const uint16_t SpectrumB52Codes[32] = +{ + 0x06, 0x0E, 0x06, 0x00, 0x0A, 0x04, 0x2C, 0x12, 0x14, 0x10, 0x06, 0x2E, 0x24, 0x10, 0x4E, 0x4C, + 0x00, 0x4D, 0x4F, 0x11, 0x25, 0x2F, 0x07, 0x11, 0x15, 0x13, 0x2D, 0x05, 0x0B, 0x01, 0x07, 0x0F +}; + +static const uint8_t SpectrumB53Bits[32] = +{ + 2, 3, 4, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, + 0, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 4, 3 +}; + +static const uint16_t SpectrumB53Codes[32] = +{ + 0x02, 0x00, 0x06, 0x1C, 0x18, 0x3E, 0x16, 0x10, 0x3C, 0x36, 0x14, 0x6A, 0x26, 0x24, 0xD2, 0xD0, + 0x00, 0xD1, 0xD3, 0x25, 0x27, 0x6B, 0x15, 0x37, 0x3D, 0x11, 0x17, 0x3F, 0x19, 0x1D, 0x07, 0x01 +}; + +static const uint8_t SpectrumB54Bits[32] = +{ + 2, 3, 4, 4, 5, 6, 6, 7, 6, 6, 7, 8, 8, 8, 9, 9, + 0, 9, 9, 8, 8, 8, 7, 6, 6, 7, 6, 6, 5, 4, 4, 3 +}; + +static const uint16_t SpectrumB54Codes[32] = +{ + 0x003, 0x002, 0x008, 0x000, 0x014, 0x02E, 0x00E, 0x05A, 0x00A, 0x008, 0x01A, 0x0B2, + 0x032, 0x030, 0x162, 0x160, 0x000, 0x161, 0x163, 0x031, 0x033, 0x0B3, 0x01B, 0x009, + 0x00B, 0x05B, 0x00F, 0x02F, 0x015, 0x001, 0x009, 0x003 +}; + +static const uint8_t SpectrumB62Bits[64] = +{ + 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 4 +}; + +static const uint16_t SpectrumB62Codes[64] = +{ + 0x0D, 0x06, 0x1C, 0x14, 0x0A, 0x04, 0x3E, 0x2E, 0x22, 0x0E, 0x06, 0x00, 0x5A, 0x4E, 0x40, 0x20, + 0x30, 0x32, 0x24, 0x12, 0x0C, 0x02, 0x78, 0x58, 0x42, 0x22, 0x0A, 0x08, 0xF6, 0xF4, 0x9A, 0x98, + 0x00, 0x99, 0x9B, 0xF5, 0xF7, 0x09, 0x0B, 0x23, 0x43, 0x59, 0x79, 0x03, 0x0D, 0x13, 0x25, 0x33, + 0x31, 0x21, 0x41, 0x4F, 0x5B, 0x01, 0x07, 0x0F, 0x23, 0x2F, 0x3F, 0x05, 0x0B, 0x15, 0x1D, 0x07 +}; + +static const uint8_t SpectrumB63Bits[64] = +{ + 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, + 6, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4 +}; + +static const uint16_t SpectrumB63Codes[64] = +{ + 0x006, 0x00E, 0x004, 0x014, 0x010, 0x006, 0x000, 0x026, 0x01C, 0x018, 0x004, 0x05C, + 0x04A, 0x03C, 0x016, 0x0BC, 0x006, 0x008, 0x058, 0x03E, 0x036, 0x014, 0x0B6, 0x0B4, + 0x090, 0x068, 0x17E, 0x17C, 0x126, 0x124, 0x0D6, 0x0D4, 0x000, 0x0D5, 0x0D7, 0x125, + 0x127, 0x17D, 0x17F, 0x069, 0x091, 0x0B5, 0x0B7, 0x015, 0x037, 0x03F, 0x059, 0x009, + 0x007, 0x0BD, 0x017, 0x03D, 0x04B, 0x05D, 0x005, 0x019, 0x01D, 0x027, 0x001, 0x007, + 0x011, 0x015, 0x005, 0x00F +}; + +static const uint8_t SpectrumB64Bits[64] = +{ + 3, 3, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, + 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 0, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 7, 7, + 7, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 3 +}; + +static const uint16_t SpectrumB64Codes[64] = +{ + 0x007, 0x000, 0x008, 0x01A, 0x014, 0x00C, 0x032, 0x02E, 0x01E, 0x014, 0x062, 0x05A, + 0x03A, 0x026, 0x020, 0x0B2, 0x038, 0x02C, 0x022, 0x0C0, 0x05E, 0x04A, 0x186, 0x184, + 0x160, 0x0BA, 0x092, 0x090, 0x2C6, 0x2C4, 0x172, 0x170, 0x000, 0x171, 0x173, 0x2C5, + 0x2C7, 0x091, 0x093, 0x0BB, 0x161, 0x185, 0x187, 0x04B, 0x05F, 0x0C1, 0x023, 0x02D, + 0x039, 0x0B3, 0x021, 0x027, 0x03B, 0x05B, 0x063, 0x015, 0x01F, 0x02F, 0x033, 0x00D, + 0x015, 0x01B, 0x009, 0x001 +}; + +static const uint8_t SpectrumB72Bits[128] = +{ + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5 +}; + +static const uint16_t SpectrumB72Codes[128] = +{ + 0x01E, 0x016, 0x00C, 0x000, 0x038, 0x032, 0x028, 0x022, 0x01C, 0x012, 0x00E, 0x006, + 0x076, 0x06C, 0x060, 0x04E, 0x03E, 0x02A, 0x022, 0x01A, 0x012, 0x00A, 0x0FC, 0x0DC, + 0x0C6, 0x0A8, 0x094, 0x086, 0x058, 0x042, 0x040, 0x02A, 0x068, 0x07C, 0x06A, 0x056, + 0x048, 0x040, 0x02E, 0x028, 0x016, 0x010, 0x008, 0x0EA, 0x0DE, 0x0AA, 0x09A, 0x096, + 0x07A, 0x078, 0x05A, 0x032, 0x030, 0x028, 0x1FE, 0x1FC, 0x1D2, 0x1D0, 0x18A, 0x188, + 0x132, 0x130, 0x10A, 0x108, 0x000, 0x109, 0x10B, 0x131, 0x133, 0x189, 0x18B, 0x1D1, + 0x1D3, 0x1FD, 0x1FF, 0x029, 0x031, 0x033, 0x05B, 0x079, 0x07B, 0x097, 0x09B, 0x0AB, + 0x0DF, 0x0EB, 0x009, 0x011, 0x017, 0x029, 0x02F, 0x041, 0x049, 0x057, 0x06B, 0x07D, + 0x069, 0x02B, 0x041, 0x043, 0x059, 0x087, 0x095, 0x0A9, 0x0C7, 0x0DD, 0x0FD, 0x00B, + 0x013, 0x01B, 0x023, 0x02B, 0x03F, 0x04F, 0x061, 0x06D, 0x077, 0x007, 0x00F, 0x013, + 0x01D, 0x023, 0x029, 0x033, 0x039, 0x001, 0x00D, 0x017 +}; + +static const uint8_t SpectrumB73Bits[128] = +{ + 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 7, + 8, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 4 +}; + +static const uint16_t SpectrumB73Codes[128] = +{ + 0x000, 0x006, 0x018, 0x010, 0x004, 0x03A, 0x034, 0x02A, 0x026, 0x014, 0x010, 0x07E, + 0x072, 0x06E, 0x05C, 0x052, 0x04A, 0x02C, 0x024, 0x018, 0x0F4, 0x0E0, 0x0DA, 0x0B6, + 0x0B2, 0x0A0, 0x05E, 0x04E, 0x038, 0x034, 0x1E6, 0x1B2, 0x0FA, 0x01E, 0x0F8, 0x0F0, + 0x0BE, 0x0B4, 0x0A2, 0x090, 0x04C, 0x03A, 0x1EE, 0x1E4, 0x1C6, 0x1B0, 0x178, 0x162, + 0x126, 0x124, 0x0B8, 0x06C, 0x3DA, 0x3D8, 0x38A, 0x388, 0x2F6, 0x2F4, 0x2C2, 0x2C0, + 0x176, 0x174, 0x0DC, 0x0DE, 0x000, 0x0DF, 0x0DD, 0x175, 0x177, 0x2C1, 0x2C3, 0x2F5, + 0x2F7, 0x389, 0x38B, 0x3D9, 0x3DB, 0x06D, 0x0B9, 0x125, 0x127, 0x163, 0x179, 0x1B1, + 0x1C7, 0x1E5, 0x1EF, 0x03B, 0x04D, 0x091, 0x0A3, 0x0B5, 0x0BF, 0x0F1, 0x0F9, 0x01F, + 0x0FB, 0x1B3, 0x1E7, 0x035, 0x039, 0x04F, 0x05F, 0x0A1, 0x0B3, 0x0B7, 0x0DB, 0x0E1, + 0x0F5, 0x019, 0x025, 0x02D, 0x04B, 0x053, 0x05D, 0x06F, 0x073, 0x07F, 0x011, 0x015, + 0x027, 0x02B, 0x035, 0x03B, 0x005, 0x011, 0x019, 0x007 +}; + +static const uint8_t SpectrumB74Bits[128] = +{ + 3, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, + 8, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 4 +}; + +static const uint16_t SpectrumB74Codes[128] = +{ + 0x001, 0x008, 0x01E, 0x018, 0x00C, 0x002, 0x03A, 0x034, 0x02C, 0x01E, 0x016, 0x012, + 0x072, 0x06E, 0x05E, 0x056, 0x050, 0x038, 0x022, 0x004, 0x0E2, 0x0DA, 0x0BA, 0x0A8, + 0x076, 0x054, 0x050, 0x002, 0x000, 0x1C0, 0x1B0, 0x156, 0x0A4, 0x0A6, 0x074, 0x052, + 0x004, 0x1C2, 0x1B2, 0x170, 0x154, 0x0AE, 0x0AC, 0x086, 0x2E6, 0x2E4, 0x10A, 0x108, + 0x106, 0x104, 0x102, 0x100, 0x03E, 0x03A, 0x03C, 0x038, 0x036, 0x034, 0x032, 0x030, + 0x01E, 0x01A, 0x01C, 0x018, 0x000, 0x019, 0x01D, 0x01B, 0x01F, 0x031, 0x033, 0x035, + 0x037, 0x039, 0x03D, 0x03B, 0x03F, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x2E5, + 0x2E7, 0x087, 0x0AD, 0x0AF, 0x155, 0x171, 0x1B3, 0x1C3, 0x005, 0x053, 0x075, 0x0A7, + 0x0A5, 0x157, 0x1B1, 0x1C1, 0x001, 0x003, 0x051, 0x055, 0x077, 0x0A9, 0x0BB, 0x0DB, + 0x0E3, 0x005, 0x023, 0x039, 0x051, 0x057, 0x05F, 0x06F, 0x073, 0x013, 0x017, 0x01F, + 0x02D, 0x035, 0x03B, 0x003, 0x00D, 0x019, 0x01F, 0x009 +}; + +static uint8_t ScaleFactorsA1Lookup[2]; +static uint8_t ScaleFactorsA2Lookup[8]; +static uint8_t ScaleFactorsA3Lookup[64]; +static uint8_t ScaleFactorsA4Lookup[256]; +static uint8_t ScaleFactorsA5Lookup[256]; +static uint8_t ScaleFactorsA6Lookup[256]; +static uint8_t ScaleFactorsB2Lookup[4]; +static uint8_t ScaleFactorsB3Lookup[64]; +static uint8_t ScaleFactorsB4Lookup[256]; +static uint8_t ScaleFactorsB5Lookup[256]; +static uint8_t SpectrumA21Lookup[8]; +static uint8_t SpectrumA22Lookup[256]; +static uint8_t SpectrumA23Lookup[512]; +static uint8_t SpectrumA24Lookup[1024]; +static uint8_t SpectrumA31Lookup[128]; +static uint8_t SpectrumA32Lookup[128]; +static uint8_t SpectrumA33Lookup[256]; +static uint8_t SpectrumA34Lookup[1024]; +static uint8_t SpectrumA41Lookup[512]; +static uint8_t SpectrumA42Lookup[1024]; +static uint8_t SpectrumA43Lookup[1024]; +static uint8_t SpectrumA44Lookup[1024]; +static uint8_t SpectrumA51Lookup[64]; +static uint8_t SpectrumA52Lookup[64]; +static uint8_t SpectrumA53Lookup[128]; +static uint8_t SpectrumA54Lookup[256]; +static uint8_t SpectrumA61Lookup[128]; +static uint8_t SpectrumA62Lookup[128]; +static uint8_t SpectrumA63Lookup[256]; +static uint8_t SpectrumA64Lookup[512]; +static uint8_t SpectrumA71Lookup[256]; +static uint8_t SpectrumA72Lookup[256]; +static uint8_t SpectrumA73Lookup[512]; +static uint8_t SpectrumA74Lookup[1024]; +static uint8_t SpectrumB22Lookup[1024]; +static uint8_t SpectrumB23Lookup[1024]; +static uint8_t SpectrumB24Lookup[1024]; +static uint8_t SpectrumB32Lookup[512]; +static uint8_t SpectrumB33Lookup[1024]; +static uint8_t SpectrumB34Lookup[1024]; +static uint8_t SpectrumB42Lookup[1024]; +static uint8_t SpectrumB43Lookup[1024]; +static uint8_t SpectrumB44Lookup[1024]; +static uint8_t SpectrumB52Lookup[128]; +static uint8_t SpectrumB53Lookup[256]; +static uint8_t SpectrumB54Lookup[512]; +static uint8_t SpectrumB62Lookup[256]; +static uint8_t SpectrumB63Lookup[512]; +static uint8_t SpectrumB64Lookup[1024]; +static uint8_t SpectrumB72Lookup[512]; +static uint8_t SpectrumB73Lookup[1024]; +static uint8_t SpectrumB74Lookup[1024]; diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.c new file mode 100644 index 000000000..29bf3dfd7 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.c @@ -0,0 +1,80 @@ +#include "imdct.h" +#include "tables.h" + +void RunImdct(mdct* mdct, double* input, double* output) +{ + const int size = 1 << mdct->bits; + const int half = size / 2; + double dctOut[MAX_FRAME_SAMPLES]; + const double* window = ImdctWindow[mdct->bits - 6]; + double* previous = mdct->_imdctPrevious; + + Dct4(mdct, input, dctOut); + + for (int i = 0; i < half; i++) + { + output[i] = window[i] * dctOut[i + half] + previous[i]; + output[i + half] = window[i + half] * -dctOut[size - 1 - i] - previous[i + half]; + previous[i] = window[size - 1 - i] * -dctOut[half - i - 1]; + previous[i + half] = window[half - i - 1] * dctOut[i]; + } +} + +void Dct4(mdct* mdct, double* input, double* output) +{ + int MdctBits = mdct->bits; + int MdctSize = 1 << MdctBits; + const int* shuffleTable = ShuffleTables[MdctBits]; + const double* sinTable = SinTables[MdctBits]; + const double* cosTable = CosTables[MdctBits]; + double dctTemp[MAX_FRAME_SAMPLES]; + + int size = MdctSize; + int lastIndex = size - 1; + int halfSize = size / 2; + + for (int i = 0; i < halfSize; i++) + { + int i2 = i * 2; + double a = input[i2]; + double b = input[lastIndex - i2]; + double sin = sinTable[i]; + double cos = cosTable[i]; + dctTemp[i2] = a * cos + b * sin; + dctTemp[i2 + 1] = a * sin - b * cos; + } + int stageCount = MdctBits - 1; + + for (int stage = 0; stage < stageCount; stage++) + { + int blockCount = 1 << stage; + int blockSizeBits = stageCount - stage; + int blockHalfSizeBits = blockSizeBits - 1; + int blockSize = 1 << blockSizeBits; + int blockHalfSize = 1 << blockHalfSizeBits; + sinTable = SinTables[blockHalfSizeBits]; + cosTable = CosTables[blockHalfSizeBits]; + + for (int block = 0; block < blockCount; block++) + { + for (int i = 0; i < blockHalfSize; i++) + { + int frontPos = (block * blockSize + i) * 2; + int backPos = frontPos + blockSize; + double a = dctTemp[frontPos] - dctTemp[backPos]; + double b = dctTemp[frontPos + 1] - dctTemp[backPos + 1]; + double sin = sinTable[i]; + double cos = cosTable[i]; + dctTemp[frontPos] += dctTemp[backPos]; + dctTemp[frontPos + 1] += dctTemp[backPos + 1]; + dctTemp[backPos] = a * cos + b * sin; + dctTemp[backPos + 1] = a * sin - b * cos; + } + } + } + + for (int i = 0; i < MdctSize; i++) + { + output[i] = dctTemp[shuffleTable[i]]; + } +} \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.h new file mode 100644 index 000000000..e8c3bf3c6 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/imdct.h @@ -0,0 +1,6 @@ +#pragma once + +#include "structures.h" + +void RunImdct(mdct* mdct, double* input, double* output); +void Dct4(mdct* mdct, double* input, double* output); \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.c new file mode 100644 index 000000000..2dbe1cd7e --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.c @@ -0,0 +1,33 @@ +#include "libatrac9.h" +#include "structures.h" +#include +#include +#include "decinit.h" +#include "decoder.h" + +void* Atrac9GetHandle() +{ + struct atrac9_handle* handle = malloc(sizeof(atrac9_handle)); + memset(handle, 0, sizeof(atrac9_handle)); + return handle; +} + +void Atrac9ReleaseHandle(void* handle) +{ + free(handle); +} + +int Atrac9InitDecoder(void* handle, unsigned char * pConfigData) +{ + return init_decoder(handle, pConfigData, 16); +} + +int Atrac9Decode(void* handle, const unsigned char *pAtrac9Buffer, short *pPcmBuffer, int *pNBytesUsed) +{ + return Decode(handle, pAtrac9Buffer, (unsigned char*)pPcmBuffer, pNBytesUsed); +} + +int Atrac9GetCodecInfo(void* handle, Atrac9CodecInfo * pCodecInfo) +{ + return GetCodecInfo(handle, (CodecInfo*)pCodecInfo); +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.h new file mode 100644 index 000000000..977f80444 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.h @@ -0,0 +1,32 @@ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +#define ATRAC9_CONFIG_DATA_SIZE 4 + +typedef struct { + int channels; + int channelConfigIndex; + int samplingRate; + int superframeSize; + int framesInSuperframe; + int frameSamples; + int wlength; + unsigned char configData[ATRAC9_CONFIG_DATA_SIZE]; + + double MdctWindow[3][256]; + double ImdctWindow[3][256]; +} Atrac9CodecInfo; + +void* Atrac9GetHandle(void); +void Atrac9ReleaseHandle(void* handle); + +int Atrac9InitDecoder(void* handle, unsigned char *pConfigData); +int Atrac9Decode(void* handle, const unsigned char *pAtrac9Buffer, short *pPcmBuffer, int *pNBytesUsed); + +int Atrac9GetCodecInfo(void* handle, Atrac9CodecInfo *pCodecInfo); + +#ifdef __cplusplus +} +#endif diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.sln b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.sln new file mode 100644 index 000000000..8c7c6bfdf --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libatrac9", "libatrac9.vcxproj", "{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}" +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 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x64.ActiveCfg = Debug|x64 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x64.Build.0 = Debug|x64 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x86.ActiveCfg = Debug|Win32 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x86.Build.0 = Debug|Win32 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x64.ActiveCfg = Release|x64 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x64.Build.0 = Release|x64 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x86.ActiveCfg = Release|Win32 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BB003D83-77D8-4E7B-896D-7C9ADA458F73} + EndGlobalSection +EndGlobal diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj new file mode 100644 index 000000000..8830bda95 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {2425F2CC-BB1B-4069-BC10-1C7F535EF8E8} + libatrac9 + 10.0.16299.0 + libatrac9 + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + _WINDLL;COMPILING_DLL;%(PreprocessorDefinitions) + CompileAsC + + + + + Level3 + Disabled + true + true + CompileAsC + _WINDLL;COMPILING_DLL;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + true + CompileAsC + _WINDLL;COMPILING_DLL;%(PreprocessorDefinitions) + Sync + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + CompileAsC + _WINDLL;COMPILING_DLL;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj.filters b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj.filters new file mode 100644 index 000000000..24c4233a8 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/libatrac9.vcxproj.filters @@ -0,0 +1,105 @@ + + + + + {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;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 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.c new file mode 100644 index 000000000..6d95e62ab --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.c @@ -0,0 +1,54 @@ +#include "quantization.h" +#include +#include "tables.h" + +void DequantizeSpectra(block* block) +{ + for (int i = 0; i < block->ChannelCount; i++) + { + channel* channel = &block->Channels[i]; + memset(channel->Spectra, 0, sizeof(channel->Spectra)); + + for (int j = 0; j < channel->CodedQuantUnits; j++) + { + DequantizeQuantUnit(channel, j); + } + } +} + + void DequantizeQuantUnit(channel* channel, int band) +{ + const int subBandIndex = QuantUnitToCoeffIndex[band]; + const int subBandCount = QuantUnitToCoeffCount[band]; + const double stepSize = QuantizerStepSize[channel->Precisions[band]]; + const double stepSizeFine = QuantizerFineStepSize[channel->PrecisionsFine[band]]; + + for (int sb = 0; sb < subBandCount; sb++) + { + const double coarse = channel->QuantizedSpectra[subBandIndex + sb] * stepSize; + const double fine = channel->QuantizedSpectraFine[subBandIndex + sb] * stepSizeFine; + channel->Spectra[subBandIndex + sb] = coarse + fine; + } +} + +void ScaleSpectrumBlock(block* block) + { + for (int i = 0; i < block->ChannelCount; i++) + { + ScaleSpectrumChannel(&block->Channels[i]); + } + } + +void ScaleSpectrumChannel(channel* channel) +{ + const int quantUnitCount = channel->Block->QuantizationUnitCount; + double* spectra = channel->Spectra; + + for (int i = 0; i < quantUnitCount; i++) + { + for (int sb = QuantUnitToCoeffIndex[i]; sb < QuantUnitToCoeffIndex[i + 1]; sb++) + { + spectra[sb] *= SpectrumScale[channel->ScaleFactors[i]]; + } + } + } \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.h new file mode 100644 index 000000000..d9de74a3e --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/quantization.h @@ -0,0 +1,8 @@ +#pragma once + +#include "structures.h" + +void DequantizeSpectra(block* block); +void DequantizeQuantUnit(channel* channel, int band); +void ScaleSpectrumBlock(block* block); +void ScaleSpectrumChannel(channel* channel); \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.c new file mode 100644 index 000000000..ee812414a --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.c @@ -0,0 +1,146 @@ +#include +#include "huffCodes.h" +#include "scale_factors.h" +#include "tables.h" +#include "utility.h" + +at9_status read_scale_factors(channel * channel, bit_reader_cxt * br) +{ + memset(channel->ScaleFactors, 0, sizeof(channel->ScaleFactors)); + + channel->ScaleFactorCodingMode = read_int(br, 2); + if (channel->ChannelIndex == 0) + { + switch (channel->ScaleFactorCodingMode) + { + case 0: + ReadVlcDeltaOffset(channel, br); + break; + case 1: + ReadClcOffset(channel, br); + break; + case 2: + if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID; + ReadVlcDistanceToBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev); + break; + case 3: + if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID; + ReadVlcDeltaOffsetWithBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev); + break; + } + } + else + { + switch (channel->ScaleFactorCodingMode) + { + case 0: + ReadVlcDeltaOffset(channel, br); + break; + case 1: + ReadVlcDistanceToBaseline(channel, br, channel->Block->Channels[0].ScaleFactors, channel->Block->ExtensionUnit); + break; + case 2: + ReadVlcDeltaOffsetWithBaseline(channel, br, channel->Block->Channels[0].ScaleFactors, channel->Block->ExtensionUnit); + break; + case 3: + if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID; + ReadVlcDistanceToBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev); + break; + } + } + + for (int i = 0; i < channel->Block->ExtensionUnit; i++) + { + if (channel->ScaleFactors[i] < 0 || channel->ScaleFactors[i] > 31) + { + return ERR_UNPACK_SCALE_FACTOR_OOB; + } + } + + memcpy(channel->ScaleFactorsPrev, channel->ScaleFactors, sizeof(channel->ScaleFactors)); + + return ERR_SUCCESS; +} + +void ReadClcOffset(channel* channel, bit_reader_cxt* br) +{ + const int maxBits = 5; + int* sf = channel->ScaleFactors; + const int bitLength = read_int(br, 2) + 2; + const int baseValue = bitLength < maxBits ? read_int(br, maxBits) : 0; + + for (int i = 0; i < channel->Block->ExtensionUnit; i++) + { + sf[i] = read_int(br, bitLength) + baseValue; + } +} + +void ReadVlcDeltaOffset(channel* channel, bit_reader_cxt* br) +{ + const int weightIndex = read_int(br, 3); + const unsigned char* weights = ScaleFactorWeights[weightIndex]; + + int* sf = channel->ScaleFactors; + const int baseValue = read_int(br, 5); + const int bitLength = read_int(br, 2) + 3; + const HuffmanCodebook* codebook = &HuffmanScaleFactorsUnsigned[bitLength]; + + sf[0] = read_int(br, bitLength); + + for (int i = 1; i < channel->Block->ExtensionUnit; i++) + { + const int delta = ReadHuffmanValue(codebook, br, 0); + sf[i] = (sf[i - 1] + delta) & (codebook->ValueMax - 1); + } + + for (int i = 0; i < channel->Block->ExtensionUnit; i++) + { + sf[i] += baseValue - weights[i]; + } +} + +void ReadVlcDistanceToBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength) +{ + int* sf = channel->ScaleFactors; + const int bit_length = read_int(br, 2) + 2; + const HuffmanCodebook* codebook = &HuffmanScaleFactorsSigned[bit_length]; + const int unitCount = min(channel->Block->ExtensionUnit, baselineLength); + + for (int i = 0; i < unitCount; i++) + { + const int distance = ReadHuffmanValue(codebook, br, TRUE); + sf[i] = (baseline[i] + distance) & 31; + } + + for (int i = unitCount; i < channel->Block->ExtensionUnit; i++) + { + sf[i] = read_int(br, 5); + } +} + +void ReadVlcDeltaOffsetWithBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength) +{ + int* sf = channel->ScaleFactors; + const int baseValue = read_offset_binary(br, 5); + const int bitLength = read_int(br, 2) + 1; + const HuffmanCodebook* codebook = &HuffmanScaleFactorsUnsigned[bitLength]; + const int unitCount = min(channel->Block->ExtensionUnit, baselineLength); + + sf[0] = read_int(br, bitLength); + + for (int i = 1; i < unitCount; i++) + { + const int delta = ReadHuffmanValue(codebook, br, FALSE); + sf[i] = (sf[i - 1] + delta) & (codebook->ValueMax - 1); + } + + for (int i = 0; i < unitCount; i++) + { + sf[i] += baseValue + baseline[i]; + } + + for (int i = unitCount; i < channel->Block->ExtensionUnit; i++) + { + sf[i] = read_int(br, 5); + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.h new file mode 100644 index 000000000..557c83436 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/scale_factors.h @@ -0,0 +1,8 @@ +#pragma once +#include "bit_allocation.h" + +at9_status read_scale_factors(channel* channel, bit_reader_cxt* br); +void ReadClcOffset(channel* channel, bit_reader_cxt* br); +void ReadVlcDeltaOffset(channel* channel, bit_reader_cxt* br); +void ReadVlcDistanceToBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength); +void ReadVlcDeltaOffsetWithBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/structures.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/structures.h new file mode 100644 index 000000000..05a9b3049 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/structures.h @@ -0,0 +1,160 @@ +#pragma once + +#define CONFIG_DATA_SIZE 4 +#define MAX_BLOCK_COUNT 5 +#define MAX_BLOCK_CHANNEL_COUNT 2 +#define MAX_FRAME_SAMPLES 256 +#define MAX_BEX_VALUES 4 + +#define MAX_QUANT_UNITS 30 + +typedef struct frame frame; +typedef struct block block; + +typedef enum BlockType { + Mono = 0, + Stereo = 1, + LFE = 2 +} BlockType; + +typedef struct { + int BlockCount; + int ChannelCount; + enum BlockType Types[MAX_BLOCK_COUNT]; +} ChannelConfig; + +typedef struct { + unsigned char ConfigData[CONFIG_DATA_SIZE]; + int SampleRateIndex; + int ChannelConfigIndex; + int FrameBytes; + int SuperframeIndex; + + ChannelConfig ChannelConfig; + int ChannelCount; + int SampleRate; + int HighSampleRate; + int FramesPerSuperframe; + int FrameSamplesPower; + int FrameSamples; + int SuperframeBytes; + int SuperframeSamples; +} ConfigData; + +typedef struct { + int initialized; + unsigned short stateA; + unsigned short stateB; + unsigned short stateC; + unsigned short stateD; +} rng_cxt; + +typedef struct { + int bits; + int size; + double scale; + double _imdctPrevious[MAX_FRAME_SAMPLES]; + double* window; + double* sinTable; + double* cosTable; +} mdct; + +typedef struct { + frame* Frame; + block* Block; + ConfigData* config; + int ChannelIndex; + + mdct mdct; + + double Pcm[MAX_FRAME_SAMPLES]; + double Spectra[MAX_FRAME_SAMPLES]; + + int CodedQuantUnits; + int ScaleFactorCodingMode; + + int ScaleFactors[31]; + int ScaleFactorsPrev[31]; + + int Precisions[MAX_QUANT_UNITS]; + int PrecisionsFine[MAX_QUANT_UNITS]; + int PrecisionMask[MAX_QUANT_UNITS]; + + int CodebookSet[MAX_QUANT_UNITS]; + + int QuantizedSpectra[MAX_FRAME_SAMPLES]; + int QuantizedSpectraFine[MAX_FRAME_SAMPLES]; + + int BexMode; + int BexValueCount; + int BexValues[MAX_BEX_VALUES]; + + rng_cxt rng; +} channel; + +struct block { + frame* Frame; + ConfigData* config; + enum BlockType BlockType; + int BlockIndex; + channel Channels[MAX_BLOCK_CHANNEL_COUNT]; + int ChannelCount; + int FirstInSuperframe; + int ReuseBandParams; + + int BandCount; + int StereoBand; + int ExtensionBand; + int QuantizationUnitCount; + int StereoQuantizationUnit; + int ExtensionUnit; + int QuantizationUnitsPrev; + + int Gradient[31]; + int GradientMode; + int GradientStartUnit; + int GradientStartValue; + int GradientEndUnit; + int GradientEndValue; + int GradientBoundary; + + int PrimaryChannelIndex; + int HasJointStereoSigns; + int JointStereoSigns[MAX_QUANT_UNITS]; + + int BandExtensionEnabled; + int HasExtensionData; + int BexDataLength; + int BexMode; +}; + +struct frame { + ConfigData* config; + int FrameIndex; + block Blocks[MAX_BLOCK_COUNT]; + int frameNum; +}; + +typedef struct { + int initialized; + int wlength; + ConfigData config; + frame frame; +} atrac9_handle; + +typedef struct { + int group_b_unit; + int group_c_unit; + int band_count; +} bex_group; + +typedef struct { + int channels; + int channelConfigIndex; + int samplingRate; + int superframeSize; + int framesInSuperframe; + int frameSamples; + int wlength; + unsigned char configData[CONFIG_DATA_SIZE]; +} CodecInfo; diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.c new file mode 100644 index 000000000..35d9ad6ee --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.c @@ -0,0 +1,7 @@ +#include "tables.h" + +double MdctWindow[3][256]; +double ImdctWindow[3][256]; +double SinTables[9][256]; +double CosTables[9][256]; +int ShuffleTables[9][256]; diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.h new file mode 100644 index 000000000..7be2ebf05 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/tables.h @@ -0,0 +1,389 @@ +#pragma once + +#include "structures.h" + +static const ChannelConfig ChannelConfigs[6] = +{ + {1, 1, Mono}, + {2, 2, Mono, Mono}, + {1, 2, Stereo}, + {4, 6, Stereo, Mono, LFE, Stereo}, + {5, 8, Stereo, Mono, LFE, Stereo, Stereo}, + {2, 4, Stereo, Stereo}, +}; + +static const int MaxHuffPrecision[2] = { 7, 1 }; +static const int MinBandCount[2] = { 3, 1 }; +static const int MaxExtensionBand[2] = { 18, 16 }; + +static const int SamplingRateIndexToFrameSamplesPower[16] = +{ 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 7, 7, 7, 8, 8, 8 }; + +static const int MaxBandCount[16] = +{ 8, 8, 12, 12, 12, 18, 18, 18, 8, 8, 12, 12, 12, 16, 16, 16 }; + +static const int BandToQuantUnitCount[19] = +{ 0, 4, 8, 10, 12, 13, 14, 15, 16, 18, 20, 21, 22, 23, 24, 25, 26, 28, 30 }; + +static const int QuantUnitToCoeffCount[30] = +{ + 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 8, 8, 8, + 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 +}; + +static const int QuantUnitToCoeffIndex[31] = +{ + 0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 72, 80, 88, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256 +}; + +static const int QuantUnitToCodebookIndex[30] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 +}; + +static const int SampleRates[16] = +{ + 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 +}; + +static const unsigned char GradientCurves[48][48] = { + { 1 }, + { 1, 16 }, + { 1, 7, 25 }, + { 1, 4, 16, 27 }, + { 1, 3, 10, 21, 28 }, + { 1, 3, 7, 16, 25, 29 }, + { 1, 2, 5, 11, 20, 26, 29 }, + { 1, 2, 4, 9, 16, 23, 27, 29 }, + { 1, 2, 3, 7, 12, 19, 25, 28, 29 }, + { 1, 2, 3, 5, 10, 16, 21, 26, 28, 29 }, + { 1, 2, 3, 5, 8, 12, 19, 23, 26, 28, 29 }, + { 1, 2, 3, 4, 7, 11, 16, 21, 25, 27, 29, 30 }, + { 1, 1, 2, 4, 5, 9, 13, 18, 22, 26, 27, 29, 30 }, + { 1, 1, 2, 3, 5, 8, 11, 16, 20, 23, 26, 28, 29, 30 }, + { 1, 1, 2, 3, 4, 7, 10, 13, 18, 21, 25, 27, 28, 29, 30 }, + { 1, 1, 2, 3, 4, 6, 9, 12, 16, 20, 23, 26, 27, 28, 29, 30 }, + { 1, 1, 2, 3, 4, 5, 7, 10, 13, 18, 21, 24, 26, 27, 28, 29, 30 }, + { 1, 1, 2, 3, 3, 5, 7, 9, 12, 16, 19, 22, 25, 26, 28, 29, 29, 30 }, + { 1, 1, 2, 2, 3, 4, 6, 8, 11, 13, 18, 20, 23, 25, 27, 28, 29, 29, 30 }, + { 1, 1, 2, 2, 3, 4, 5, 7, 10, 12, 16, 19, 21, 24, 26, 27, 28, 29, 29, 30 }, + { 1, 1, 2, 2, 3, 4, 5, 7, 9, 11, 13, 18, 20, 22, 25, 26, 27, 28, 29, 29, 30 }, + { 1, 1, 2, 2, 3, 3, 5, 6, 8, 10, 12, 16, 19, 21, 23, 25, 26, 28, 28, 29, 29, + 30 }, + { 1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 13, 18, 20, 22, 24, 26, 27, 28, 28, 29, + 29, 30 }, + { 1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 13, 16, 19, 21, 23, 25, 26, 27, 28, 29, + 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 15, 16, 19, 21, 23, 25, 26, 27, 28, + 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 4, 4, 5, 7, 9, 11, 13, 16, 18, 20, 22, 24, 26, 27, 27, + 28, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 3, 4, 5, 7, 8, 10, 12, 15, 16, 19, 21, 23, 25, 26, 27, + 28, 28, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 8, 9, 11, 13, 16, 18, 20, 22, 23, 25, 26, 27, + 28, 28, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 7, 9, 10, 12, 15, 16, 19, 21, 22, 24, 26, 26, + 27, 28, 28, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 10, 11, 13, 16, 18, 20, 21, 23, 25, 26, + 27, 27, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 8, 9, 11, 12, 15, 16, 19, 20, 22, 23, 25, + 26, 27, 28, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 13, 16, 18, 20, 21, 23, 24, + 26, 26, 27, 28, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 7, 8, 9, 11, 12, 15, 16, 19, 20, 22, 23, + 25, 26, 26, 27, 28, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 9, 10, 12, 13, 16, 18, 19, 21, 22, + 24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 19, 20, 21, + 23, 24, 25, 26, 27, 28, 28, 28, 29, 29, 29, 30, 30 }, + { 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 7, 8, 9, 11, 12, 13, 16, 18, 19, 21, + 22, 23, 25, 26, 26, 27, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 9, 10, 11, 13, 15, 16, 18, 20, + 21, 22, 24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 11, 12, 13, 16, 18, 19, 20, + 22, 23, 24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 13, 15, 16, 18, 20, + 21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 13, 16, 18, 19, + 20, 21, 23, 24, 25, 26, 26, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18, + 19, 21, 22, 23, 24, 25, 26, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 12, 13, 16, 18, + 19, 20, 21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 29, 30, 30, + 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, + 18, 19, 20, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 29, 30, 30, + 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 29, 29, 29, 29, 30, + 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 12, 13, 15, + 16, 18, 19, 20, 21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29, 29, + 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29, + 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, + 29, 29, 30, 30, 30 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, + 29, 29, 30, 30, 30, 30 } +}; + +static const unsigned char ScaleFactorWeights[8][32] = { + { 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 3, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 10, 12, 12, 12 }, + { 3, 2, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 3, + 3, 4, 5, 7, 10, 10, 10 }, + { 0, 2, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, + 7, 7, 8, 9, 12, 12, 12 }, + { 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 8, 8, 10, + 11, 11, 12, 13, 13, 13, 13 }, + { 0, 2, 2, 3, 3, 4, 4, 5, 4, 5, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, + 11, 12, 12, 13, 13, 14, 14, 14 }, + { 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, + 6, 7, 7, 9, 11, 11, 11 }, + { 0, 5, 8, 10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 12, 12, 12, 12, 13, 15, 15, 15 }, + { 0, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 15, 15, 15 } +}; + +static const double SpectrumScale[32] = +{ + 3.0517578125e-5, 6.1035156250e-5, 1.2207031250e-4, 2.4414062500e-4, + 4.8828125000e-4, 9.7656250000e-4, 1.9531250000e-3, 3.9062500000e-3, + 7.8125000000e-3, 1.5625000000e-2, 3.1250000000e-2, 6.2500000000e-2, + 1.2500000000e-1, 2.5000000000e-1, 5.0000000000e-1, 1.0000000000e+0, + 2.0000000000e+0, 4.0000000000e+0, 8.0000000000e+0, 1.6000000000e+1, + 3.2000000000e+1, 6.4000000000e+1, 1.2800000000e+2, 2.5600000000e+2, + 5.1200000000e+2, 1.0240000000e+3, 2.0480000000e+3, 4.0960000000e+3, + 8.1920000000e+3, 1.6384000000e+4, 3.2768000000e+4, 6.5536000000e+4 +}; + +static const double QuantizerInverseStepSize[16] = +{ + 0.5, 1.5, 3.5, 7.5, 15.5, 31.5, 63.5, 127.5, + 255.5, 511.5, 1023.5, 2047.5, 4095.5, 8191.5, 16383.5, 32767.5 +}; + +static const double QuantizerStepSize[16] = +{ + 2.0000000000000000e+0, 6.6666666666666663e-1, 2.8571428571428570e-1, 1.3333333333333333e-1, + 6.4516129032258063e-2, 3.1746031746031744e-2, 1.5748031496062992e-2, 7.8431372549019607e-3, + 3.9138943248532287e-3, 1.9550342130987292e-3, 9.7703957010258913e-4, 4.8840048840048840e-4, + 2.4417043096081065e-4, 1.2207776353537203e-4, 6.1037018951994385e-5, 3.0518043793392844e-5 +}; + +static const double QuantizerFineStepSize[16] = +{ + 3.0518043793392844e-05, 1.0172681264464281e-05, 4.3597205419132631e-06, 2.0345362528928561e-06, + 9.8445302559331759e-07, 4.8441339354591809e-07, 2.4029955742829012e-07, 1.1967860311134448e-07, + 5.9722199204291275e-08, 2.9831909866464167e-08, 1.4908668194134265e-08, 7.4525137468602791e-09, + 3.7258019525568114e-09, 1.8627872668859698e-09, 9.3136520869755679e-10, 4.6567549848772173e-10 +}; + +static const bex_group BexGroupInfo[8] = +{ + { 16, 21, 0 }, + { 18, 22, 1 }, + { 20, 22, 2 }, + { 21, 22, 3 }, + { 21, 22, 3 }, + { 23, 24, 4 }, + { 23, 24, 4 }, + { 24, 24, 5 } +}; + +static const int BexEncodedValueCounts[5][6] = +{ + {0, 0, 0, 4, 4, 2}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 2, 2, 1}, + {0, 0, 0, 2, 2, 2}, + {1, 1, 1, 0, 0, 0} +}; + +// [mode][bands][valueIndex] +static const int BexDataLengths[5][6][4] = +{ + { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {5, 4, 3, 3}, + {4, 4, 3, 4}, + {4, 5, 0, 0} + }, { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }, { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {6, 6, 0, 0}, + {6, 6, 0, 0}, + {6, 0, 0, 0} + }, { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {4, 4, 0, 0}, + {4, 4, 0, 0}, + {4, 4, 0, 0} + }, { + {3, 0, 0, 0}, + {3, 0, 0, 0}, + {3, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + } +}; + +static const double BexMode0Bands3[5][32] = +{ + { + 0.000000e+0, 1.988220e-1, 2.514343e-1, 2.960510e-1, + 3.263550e-1, 3.771362e-1, 3.786926e-1, 4.540405e-1, + 4.877625e-1, 5.262451e-1, 5.447083e-1, 5.737000e-1, + 6.212158e-1, 6.222839e-1, 6.560974e-1, 6.896667e-1, + 7.555542e-1, 7.677917e-1, 7.918091e-1, 7.971497e-1, + 8.188171e-1, 8.446045e-1, 9.790649e-1, 9.822083e-1, + 9.846191e-1, 9.859314e-1, 9.863586e-1, 9.863892e-1, + 9.873352e-1, 9.881287e-1, 9.898682e-1, 9.913330e-1 + }, { + 0.000000e+0, 9.982910e-1, 7.592773e-2, 7.179565e-1, + 9.851379e-1, 5.340271e-1, 9.013672e-1, 6.349182e-1, + 7.226257e-1, 1.948547e-1, 7.628174e-1, 9.873657e-1, + 8.112183e-1, 2.715454e-1, 9.734192e-1, 1.443787e-1, + 4.640198e-1, 3.249207e-1, 3.790894e-1, 8.276367e-2, + 5.954590e-1, 2.864380e-1, 9.806824e-1, 7.929077e-1, + 6.292114e-1, 4.887085e-1, 2.905273e-1, 1.301880e-1, + 3.140869e-1, 5.482483e-1, 4.210815e-1, 1.182861e-1 + }, { + 0.000000e+0, 3.155518e-2, 8.581543e-2, 1.364746e-1, + 1.858826e-1, 2.368469e-1, 2.888184e-1, 3.432617e-1, + 4.012451e-1, 4.623108e-1, 5.271301e-1, 5.954895e-1, + 6.681213e-1, 7.448425e-1, 8.245239e-1, 9.097290e-1 + }, { + 0.000000e+0, 4.418945e-2, 1.303711e-1, 2.273560e-1, + 3.395996e-1, 4.735718e-1, 6.267090e-1, 8.003845e-1 + }, { + 0.000000e+0, 2.804565e-2, 9.683228e-2, 1.849976e-1, + 3.005981e-1, 4.470520e-1, 6.168518e-1, 8.007813e-1 + } +}; + +static const double BexMode0Bands4[5][16] = +{ + { + 0.000000e+0, 2.708740e-1, 3.479614e-1, 3.578186e-1, + 5.083618e-1, 5.299072e-1, 5.819092e-1, 6.381836e-1, + 7.276917e-1, 7.595520e-1, 7.878723e-1, 9.707336e-1, + 9.713135e-1, 9.736023e-1, 9.759827e-1, 9.832458e-1 + }, { + 0.000000e+0, 2.330627e-1, 5.891418e-1, 7.170410e-1, + 2.036438e-1, 1.613464e-1, 6.668701e-1, 9.481201e-1, + 9.769897e-1, 5.111694e-1, 3.522644e-1, 8.209534e-1, + 2.933960e-1, 9.757690e-1, 5.289917e-1, 4.372253e-1 + }, { + 0.000000e+0, 4.360962e-2, 1.056519e-1, 1.590576e-1, + 2.078857e-1, 2.572937e-1, 3.082581e-1, 3.616028e-1, + 4.191589e-1, 4.792175e-1, 5.438538e-1, 6.125183e-1, + 6.841125e-1, 7.589417e-1, 8.365173e-1, 9.148254e-1 + }, { + 0.000000e+0, 4.074097e-2, 1.164551e-1, 2.077026e-1, + 3.184509e-1, 4.532166e-1, 6.124268e-1, 7.932129e-1 + }, { + 0.000000e+0, 8.880615e-3, 2.932739e-2, 5.593872e-2, + 8.825684e-2, 1.259155e-1, 1.721497e-1, 2.270813e-1, + 2.901611e-1, 3.579712e-1, 4.334106e-1, 5.147095e-1, + 6.023254e-1, 6.956177e-1, 7.952881e-1, 8.977356e-1 + } +}; + +static const double BexMode0Bands5[3][32] = +{ + { + 0.000000e+0, 7.379150e-2, 1.806335e-1, 2.687073e-1, + 3.407898e-1, 4.047546e-1, 4.621887e-1, 5.168762e-1, + 5.703125e-1, 6.237488e-1, 6.763611e-1, 7.288208e-1, + 7.808533e-1, 8.337708e-1, 8.874512e-1, 9.418030e-1 + }, { + 0.000000e+0, 7.980347e-2, 1.615295e-1, 1.665649e-1, + 1.822205e-1, 2.185669e-1, 2.292175e-1, 2.456665e-1, + 2.666321e-1, 3.306580e-1, 3.330688e-1, 3.765259e-1, + 4.085083e-1, 4.400024e-1, 4.407654e-1, 4.817505e-1, + 4.924011e-1, 5.320740e-1, 5.893860e-1, 6.131287e-1, + 6.212463e-1, 6.278076e-1, 6.308899e-1, 7.660828e-1, + 7.850647e-1, 7.910461e-1, 7.929382e-1, 8.038330e-1, + 9.834900e-1, 9.846191e-1, 9.852295e-1, 9.862671e-1 + }, { + 0.000000e+0, 6.084290e-1, 3.672791e-1, 3.151855e-1, + 1.488953e-1, 2.571716e-1, 5.103455e-1, 3.311157e-1, + 5.426025e-2, 4.254456e-1, 7.998352e-1, 7.873230e-1, + 5.418701e-1, 2.925110e-1, 8.468628e-2, 1.410522e-1, + 9.819641e-1, 9.609070e-1, 3.530884e-2, 9.729004e-2, + 5.758362e-1, 9.941711e-1, 7.215576e-1, 7.183228e-1, + 2.028809e-1, 9.588623e-2, 2.032166e-1, 1.338806e-1, + 5.003357e-1, 1.874390e-1, 9.804993e-1, 1.107788e-1 + } +}; + +static const double BexMode2Scale[64] = +{ + 4.272461e-4, 1.312256e-3, 2.441406e-3, 3.692627e-3, + 4.913330e-3, 6.134033e-3, 7.507324e-3, 8.972168e-3, + 1.049805e-2, 1.223755e-2, 1.406860e-2, 1.599121e-2, + 1.800537e-2, 2.026367e-2, 2.264404e-2, 2.517700e-2, + 2.792358e-2, 3.073120e-2, 3.344727e-2, 3.631592e-2, + 3.952026e-2, 4.275513e-2, 4.608154e-2, 4.968262e-2, + 5.355835e-2, 5.783081e-2, 6.195068e-2, 6.677246e-2, + 7.196045e-2, 7.745361e-2, 8.319092e-2, 8.993530e-2, + 9.759521e-2, 1.056213e-1, 1.138916e-1, 1.236267e-1, + 1.348267e-1, 1.470337e-1, 1.603394e-1, 1.755676e-1, + 1.905823e-1, 2.071228e-1, 2.245178e-1, 2.444153e-1, + 2.658997e-1, 2.897644e-1, 3.146057e-1, 3.450012e-1, + 3.766174e-1, 4.122620e-1, 4.505615e-1, 4.893799e-1, + 5.305481e-1, 5.731201e-1, 6.157837e-1, 6.580811e-1, + 6.985168e-1, 7.435303e-1, 7.865906e-1, 8.302612e-1, + 8.718567e-1, 9.125671e-1, 9.575806e-1, 9.996643e-1 +}; + +static const double BexMode3Initial[16] = +{ + 3.491211e-1, 5.371094e-1, 6.782227e-1, 7.910156e-1, + 9.057617e-1, 1.024902e+0, 1.156250e+0, 1.290527e+0, + 1.458984e+0, 1.664551e+0, 1.929688e+0, 2.278320e+0, + 2.831543e+0, 3.659180e+0, 5.257813e+0, 8.373047e+0 +}; + +static const double BexMode3Rate[16] = +{ + -2.913818e-1, -2.541504e-1, -1.664429e-1, -1.476440e-1, + -1.342163e-1, -1.220703e-1, -1.117554e-1, -1.026611e-1, + -9.436035e-2, -8.483887e-2, -7.476807e-2, -6.304932e-2, + -4.492188e-2, -2.447510e-2, +1.831055e-4, +4.174805e-2 +}; + +static const double BexMode4Multiplier[8] = +{ + 3.610229e-2, 1.260681e-1, 2.227478e-1, 3.338318e-1, + 4.662170e-1, 6.221313e-1, 7.989197e-1, 9.939575e-1 +}; + +extern double MdctWindow[3][256]; +extern double ImdctWindow[3][256]; +extern double SinTables[9][256]; +extern double CosTables[9][256]; +extern int ShuffleTables[9][256]; + diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.c new file mode 100644 index 000000000..d65cab9c2 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.c @@ -0,0 +1,423 @@ +#include "tables.h" +#include "unpack.h" +#include "bit_allocation.h" +#include +#include "scale_factors.h" +#include "utility.h" +#include "huffCodes.h" + +at9_status UnpackFrame(frame* frame, bit_reader_cxt* br) +{ + const int block_count = frame->config->ChannelConfig.BlockCount; + + for (int i = 0; i < block_count; i++) + { + ERROR_CHECK(UnpackBlock(&frame->Blocks[i], br)); + } + return ERR_SUCCESS; +} + +at9_status UnpackBlock(block* block, bit_reader_cxt* br) +{ + ERROR_CHECK(ReadBlockHeader(block, br)); + + if (block->BlockType == LFE) + { + ERROR_CHECK(UnpackLfeBlock(block, br)); + } + else + { + ERROR_CHECK(UnpackStandardBlock(block, br)); + } + + align_position(br, 8); + return ERR_SUCCESS; +} + +at9_status ReadBlockHeader(block* block, bit_reader_cxt* br) +{ + int firstInSuperframe = block->Frame->FrameIndex == 0; + block->FirstInSuperframe = !read_int(br, 1); + block->ReuseBandParams = read_int(br, 1); + + if (block->FirstInSuperframe && block->ReuseBandParams && block->BlockType != LFE) + { + return ERR_UNPACK_REUSE_BAND_PARAMS_INVALID; + } + + return ERR_SUCCESS; +} + +at9_status UnpackStandardBlock(block* block, bit_reader_cxt* br) +{ + if (!block->ReuseBandParams) + { + ERROR_CHECK(ReadBandParams(block, br)); + } + + ERROR_CHECK(ReadGradientParams(block, br)); + ERROR_CHECK(CreateGradient(block)); + ERROR_CHECK(ReadStereoParams(block, br)); + ERROR_CHECK(ReadExtensionParams(block, br)); + + for (int i = 0; i < block->ChannelCount; i++) + { + channel* channel = &block->Channels[i]; + UpdateCodedUnits(channel); + + ERROR_CHECK(read_scale_factors(channel, br)); + CalculateMask(channel); + CalculatePrecisions(channel); + CalculateSpectrumCodebookIndex(channel); + + ERROR_CHECK(ReadSpectra(channel, br)); + ERROR_CHECK(ReadSpectraFine(channel, br)); + } + + block->QuantizationUnitsPrev = block->BandExtensionEnabled ? block->ExtensionUnit : block->QuantizationUnitCount; + return ERR_SUCCESS; +} + +at9_status ReadBandParams(block* block, bit_reader_cxt* br) +{ + const int minBandCount = MinBandCount[block->config->HighSampleRate]; + const int maxExtensionBand = MaxExtensionBand[block->config->HighSampleRate]; + block->BandCount = read_int(br, 4) + minBandCount; + block->QuantizationUnitCount = BandToQuantUnitCount[block->BandCount]; + + if (block->BandCount < minBandCount || block->BandCount > + MaxBandCount[block->config->SampleRateIndex]) + { + return ERR_SUCCESS; + } + + if (block->BlockType == Stereo) + { + block->StereoBand = read_int(br, 4); + block->StereoBand += minBandCount; + block->StereoQuantizationUnit = BandToQuantUnitCount[block->StereoBand]; + } + else + { + block->StereoBand = block->BandCount; + } + + block->BandExtensionEnabled = read_int(br, 1); + if (block->BandExtensionEnabled) + { + block->ExtensionBand = read_int(br, 4); + block->ExtensionBand += minBandCount; + + if (block->ExtensionBand < block->BandCount || block->ExtensionBand > maxExtensionBand) + { + return ERR_UNPACK_BAND_PARAMS_INVALID; + } + + block->ExtensionUnit = BandToQuantUnitCount[block->ExtensionBand]; + } + else + { + block->ExtensionBand = block->BandCount; + block->ExtensionUnit = block->QuantizationUnitCount; + } + + return ERR_SUCCESS; +} + +at9_status ReadGradientParams(block* block, bit_reader_cxt* br) +{ + block->GradientMode = read_int(br, 2); + if (block->GradientMode > 0) + { + block->GradientEndUnit = 31; + block->GradientEndValue = 31; + block->GradientStartUnit = read_int(br, 5); + block->GradientStartValue = read_int(br, 5); + } + else + { + block->GradientStartUnit = read_int(br, 6); + block->GradientEndUnit = read_int(br, 6) + 1; + block->GradientStartValue = read_int(br, 5); + block->GradientEndValue = read_int(br, 5); + } + block->GradientBoundary = read_int(br, 4); + + if (block->GradientBoundary > block->QuantizationUnitCount) + { + return ERR_UNPACK_GRAD_BOUNDARY_INVALID; + } + if (block->GradientStartUnit < 1 || block->GradientStartUnit >= 48) + { + return ERR_UNPACK_GRAD_START_UNIT_OOB; + } + if (block->GradientEndUnit < 1 || block->GradientEndUnit >= 48) + { + return ERR_UNPACK_GRAD_END_UNIT_OOB; + } + if (block->GradientStartUnit > block->GradientEndUnit) + { + return ERR_UNPACK_GRAD_END_UNIT_INVALID; + } + if (block->GradientStartValue < 0 || block->GradientStartValue >= 32) + { + return ERR_UNPACK_GRAD_START_VALUE_OOB; + } + if (block->GradientEndValue < 0 || block->GradientEndValue >= 32) + { + return ERR_UNPACK_GRAD_END_VALUE_OOB; + } + + return ERR_SUCCESS; +} + +at9_status ReadStereoParams(block* block, bit_reader_cxt* br) +{ + if (block->BlockType != Stereo) return ERR_SUCCESS; + + block->PrimaryChannelIndex = read_int(br, 1); + block->HasJointStereoSigns = read_int(br, 1); + if (block->HasJointStereoSigns) + { + for (int i = block->StereoQuantizationUnit; i < block->QuantizationUnitCount; i++) + { + block->JointStereoSigns[i] = read_int(br, 1); + } + } + else + { + memset(block->JointStereoSigns, 0, sizeof(block->JointStereoSigns)); + } + + return ERR_SUCCESS; +} + +void BexReadHeader(channel* channel, bit_reader_cxt* br, int bexBand) +{ + const int bexMode = read_int(br, 2); + channel->BexMode = bexBand > 2 ? bexMode : 4; + channel->BexValueCount = BexEncodedValueCounts[channel->BexMode][bexBand]; +} + +void BexReadData(channel* channel, bit_reader_cxt* br, int bexBand) +{ + for (int i = 0; i < channel->BexValueCount; i++) + { + const int dataLength = BexDataLengths[channel->BexMode][bexBand][i]; + channel->BexValues[i] = read_int(br, dataLength); + } +} + +at9_status ReadExtensionParams(block* block, bit_reader_cxt* br) +{ + int bexBand = 0; + if (block->BandExtensionEnabled) + { + bexBand = BexGroupInfo[block->QuantizationUnitCount - 13].band_count; + if (block->BlockType == Stereo) + { + BexReadHeader(&block->Channels[1], br, bexBand); + } + else + { + br->position += 1; + } + } + block->HasExtensionData = read_int(br, 1); + + if (!block->HasExtensionData) return ERR_SUCCESS; + if (!block->BandExtensionEnabled) + { + block->BexMode = read_int(br, 2); + block->BexDataLength = read_int(br, 5); + br->position += block->BexDataLength; + return ERR_SUCCESS; + } + + BexReadHeader(&block->Channels[0], br, bexBand); + + block->BexDataLength = read_int(br, 5); + if (block->BexDataLength <= 0) return ERR_SUCCESS; + const int bexDataEnd = br->position + block->BexDataLength; + + BexReadData(&block->Channels[0], br, bexBand); + + if (block->BlockType == Stereo) + { + BexReadData(&block->Channels[1], br, bexBand); + } + + // Make sure we didn't read too many bits + if (br->position > bexDataEnd) + { + return ERR_UNPACK_EXTENSION_DATA_INVALID; + } + + return ERR_SUCCESS; +} + +void UpdateCodedUnits(channel* channel) +{ + if (channel->Block->PrimaryChannelIndex == channel->ChannelIndex) + { + channel->CodedQuantUnits = channel->Block->QuantizationUnitCount; + } + else + { + channel->CodedQuantUnits = channel->Block->StereoQuantizationUnit; + } +} + +void CalculateSpectrumCodebookIndex(channel* channel) +{ + memset(channel->CodebookSet, 0, sizeof(channel->CodebookSet)); + const int quantUnits = channel->CodedQuantUnits; + int* sf = channel->ScaleFactors; + + if (quantUnits <= 1) return; + if (channel->config->HighSampleRate) return; + + // Temporarily setting this value allows for simpler code by + // making the last value a non-special case. + const int originalScaleTmp = sf[quantUnits]; + sf[quantUnits] = sf[quantUnits - 1]; + + int avg = 0; + if (quantUnits > 12) + { + for (int i = 0; i < 12; i++) + { + avg += sf[i]; + } + avg = (avg + 6) / 12; + } + + for (int i = 8; i < quantUnits; i++) + { + const int prevSf = sf[i - 1]; + const int nextSf = sf[i + 1]; + const int minSf = min(prevSf, nextSf); + if (sf[i] - minSf >= 3 || sf[i] - prevSf + sf[i] - nextSf >= 3) + { + channel->CodebookSet[i] = 1; + } + } + + for (int i = 12; i < quantUnits; i++) + { + if (channel->CodebookSet[i] == 0) + { + const int minSf = min(sf[i - 1], sf[i + 1]); + if (sf[i] - minSf >= 2 && sf[i] >= avg - (QuantUnitToCoeffCount[i] == 16 ? 1 : 0)) + { + channel->CodebookSet[i] = 1; + } + } + } + + sf[quantUnits] = originalScaleTmp; +} + +at9_status ReadSpectra(channel* channel, bit_reader_cxt* br) +{ + int values[16]; + memset(channel->QuantizedSpectra, 0, sizeof(channel->QuantizedSpectra)); + const int maxHuffPrecision = MaxHuffPrecision[channel->config->HighSampleRate]; + + for (int i = 0; i < channel->CodedQuantUnits; i++) + { + const int subbandCount = QuantUnitToCoeffCount[i]; + const int precision = channel->Precisions[i] + 1; + if (precision <= maxHuffPrecision) + { + const HuffmanCodebook* huff = &HuffmanSpectrum[channel->CodebookSet[i]][precision][QuantUnitToCodebookIndex[i]]; + const int groupCount = subbandCount >> huff->ValueCountPower; + for (int j = 0; j < groupCount; j++) + { + values[j] = ReadHuffmanValue(huff, br, FALSE); + } + + DecodeHuffmanValues(channel->QuantizedSpectra, QuantUnitToCoeffIndex[i], subbandCount, huff, values); + } + else + { + const int subbandIndex = QuantUnitToCoeffIndex[i]; + for (int j = subbandIndex; j < QuantUnitToCoeffIndex[i + 1]; j++) + { + channel->QuantizedSpectra[j] = read_signed_int(br, precision); + } + } + } + + return ERR_SUCCESS; +} + +at9_status ReadSpectraFine(channel* channel, bit_reader_cxt* br) +{ + memset(channel->QuantizedSpectraFine, 0, sizeof(channel->QuantizedSpectraFine)); + + for (int i = 0; i < channel->CodedQuantUnits; i++) + { + if (channel->PrecisionsFine[i] > 0) + { + const int overflowBits = channel->PrecisionsFine[i] + 1; + const int startSubband = QuantUnitToCoeffIndex[i]; + const int endSubband = QuantUnitToCoeffIndex[i + 1]; + + for (int j = startSubband; j < endSubband; j++) + { + channel->QuantizedSpectraFine[j] = read_signed_int(br, overflowBits); + } + } + } + return ERR_SUCCESS; +} + +at9_status UnpackLfeBlock(block* block, bit_reader_cxt* br) +{ + channel* channel = &block->Channels[0]; + block->QuantizationUnitCount = 2; + + DecodeLfeScaleFactors(channel, br); + CalculateLfePrecision(channel); + channel->CodedQuantUnits = block->QuantizationUnitCount; + ReadLfeSpectra(channel, br); + + return ERR_SUCCESS; +} + +void DecodeLfeScaleFactors(channel* channel, bit_reader_cxt* br) +{ + memset(channel->ScaleFactors, 0, sizeof(channel->ScaleFactors)); + for (int i = 0; i < channel->Block->QuantizationUnitCount; i++) + { + channel->ScaleFactors[i] = read_int(br, 5); + } +} + +void CalculateLfePrecision(channel* channel) +{ + block* block = channel->Block; + const int precision = block->ReuseBandParams ? 8 : 4; + for (int i = 0; i < block->QuantizationUnitCount; i++) + { + channel->Precisions[i] = precision; + channel->PrecisionsFine[i] = 0; + } +} + +void ReadLfeSpectra(channel* channel, bit_reader_cxt* br) +{ + memset(channel->QuantizedSpectra, 0, sizeof(channel->QuantizedSpectra)); + + for (int i = 0; i < channel->CodedQuantUnits; i++) + { + if (channel->Precisions[i] <= 0) continue; + + const int precision = channel->Precisions[i] + 1; + for (int j = QuantUnitToCoeffIndex[i]; j < QuantUnitToCoeffIndex[i + 1]; j++) + { + channel->QuantizedSpectra[j] = read_signed_int(br, precision); + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.h new file mode 100644 index 000000000..53a4c2b3d --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/unpack.h @@ -0,0 +1,23 @@ +#pragma once +#include "bit_reader.h" +#include "error_codes.h" +#include "structures.h" + +at9_status UnpackFrame(frame* frame, bit_reader_cxt* br); +at9_status UnpackBlock(block* block, bit_reader_cxt* br); +at9_status ReadBlockHeader(block* block, bit_reader_cxt* br); +at9_status UnpackStandardBlock(block* block, bit_reader_cxt* br); +at9_status ReadBandParams(block* block, bit_reader_cxt* br); +at9_status ReadGradientParams(block* block, bit_reader_cxt* br); +at9_status ReadStereoParams(block* block, bit_reader_cxt* br); +at9_status ReadExtensionParams(block* block, bit_reader_cxt* br); +void UpdateCodedUnits(channel* channel); +void CalculateSpectrumCodebookIndex(channel* channel); + +at9_status ReadSpectra(channel* channel, bit_reader_cxt* br); +at9_status ReadSpectraFine(channel* channel, bit_reader_cxt* br); + +at9_status UnpackLfeBlock(block* block, bit_reader_cxt* br); +void DecodeLfeScaleFactors(channel* channel, bit_reader_cxt* br); +void CalculateLfePrecision(channel* channel); +void ReadLfeSpectra(channel* channel, bit_reader_cxt* br); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.c b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.c new file mode 100644 index 000000000..b440ce126 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.c @@ -0,0 +1,30 @@ +#include "utility.h" +#include + +int max(int a, int b) { return a > b ? a : b; } +int min(int a, int b) { return a > b ? b : a; } + +unsigned int BitReverse32(unsigned int value, int bitCount) +{ + value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1); + value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2); + value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4); + value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8); + value = (value >> 16) | (value << 16); + return value >> (32 - bitCount); +} + +int SignExtend32(int value, int bits) +{ + const int shift = 8 * sizeof(int) - bits; + return (value << shift) >> shift; +} + +short Clamp16(int value) +{ + if (value > SHRT_MAX) + return SHRT_MAX; + if (value < SHRT_MIN) + return SHRT_MIN; + return (short)value; +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.h b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.h new file mode 100644 index 000000000..489b54da4 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/C/utility.h @@ -0,0 +1,15 @@ +#pragma once + +#define FALSE 0 +#define TRUE 1 +#define NULL 0 + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +int max(int a, int b); +int min(int a, int b); +unsigned int BitReverse32(unsigned int value, int bitCount); +int SignExtend32(int value, int bits); +short Clamp16(int value); diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9.sln b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9.sln new file mode 100644 index 000000000..5310f9132 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9.sln @@ -0,0 +1,42 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2003 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibAtrac9", "LibAtrac9\LibAtrac9.csproj", "{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.Build.0 = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.ActiveCfg = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.Build.0 = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.ActiveCfg = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.Build.0 = Debug|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.Build.0 = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.ActiveCfg = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.Build.0 = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.ActiveCfg = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.Build.0 = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.ActiveCfg = Release|Any CPU + {2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2AFD09F0-D995-47ED-AA86-FFA93824A514} + EndGlobalSection +EndGlobal diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Config.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Config.cs new file mode 100644 index 000000000..8d1e4357d --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Config.cs @@ -0,0 +1,119 @@ +using System.IO; +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + /// + /// Stores the configuration data needed to decode or encode an ATRAC9 stream. + /// + public class Atrac9Config + { + /// + /// The 4-byte ATRAC9 configuration data. + /// + public byte[] ConfigData { get; } + + /// + /// A 4-bit value specifying one of 16 sample rates. + /// + public int SampleRateIndex { get; } + /// + /// A 3-bit value specifying one of 6 substream channel mappings. + /// + public int ChannelConfigIndex { get; } + /// + /// An 11-bit value containing the average size of a single frame. + /// + public int FrameBytes { get; } + /// + /// A 2-bit value indicating how many frames are in each superframe. + /// + public int SuperframeIndex { get; } + + /// + /// The channel mapping used by the ATRAC9 stream. + /// + public ChannelConfig ChannelConfig { get; } + /// + /// The total number of channels in the ATRAC9 stream. + /// + public int ChannelCount { get; } + /// + /// The sample rate of the ATRAC9 stream. + /// + public int SampleRate { get; } + /// + /// Indicates whether the ATRAC9 stream has a of 8 or above. + /// + public bool HighSampleRate { get; } + + /// + /// The number of frames in each superframe. + /// + public int FramesPerSuperframe { get; } + /// + /// The number of samples in one frame as an exponent of 2. + /// = 2^. + /// + public int FrameSamplesPower { get; } + /// + /// The number of samples in one frame. + /// + public int FrameSamples { get; } + /// + /// The number of bytes in one superframe. + /// + public int SuperframeBytes { get; } + /// + /// The number of samples in one superframe. + /// + public int SuperframeSamples { get; } + + /// + /// Reads ATRAC9 configuration data and calculates the stream parameters from it. + /// + /// The processed ATRAC9 configuration. + public Atrac9Config(byte[] configData) + { + if (configData == null || configData.Length != 4) + { + throw new InvalidDataException("Config data must be 4 bytes long"); + } + + ReadConfigData(configData, out int a, out int b, out int c, out int d); + SampleRateIndex = a; + ChannelConfigIndex = b; + FrameBytes = c; + SuperframeIndex = d; + ConfigData = configData; + + FramesPerSuperframe = 1 << SuperframeIndex; + SuperframeBytes = FrameBytes << SuperframeIndex; + ChannelConfig = Tables.ChannelConfig[ChannelConfigIndex]; + + ChannelCount = ChannelConfig.ChannelCount; + SampleRate = Tables.SampleRates[SampleRateIndex]; + HighSampleRate = SampleRateIndex > 7; + FrameSamplesPower = Tables.SamplingRateIndexToFrameSamplesPower[SampleRateIndex]; + FrameSamples = 1 << FrameSamplesPower; + SuperframeSamples = FrameSamples * FramesPerSuperframe; + } + + private static void ReadConfigData(byte[] configData, out int sampleRateIndex, out int channelConfigIndex, out int frameBytes, out int superframeIndex) + { + var reader = new BitReader(configData); + + int header = reader.ReadInt(8); + sampleRateIndex = reader.ReadInt(4); + channelConfigIndex = reader.ReadInt(3); + int validationBit = reader.ReadInt(1); + frameBytes = reader.ReadInt(11) + 1; + superframeIndex = reader.ReadInt(2); + + if (header != 0xFE || validationBit != 0) + { + throw new InvalidDataException("ATRAC9 Config Data is invalid"); + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Decoder.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Decoder.cs new file mode 100644 index 000000000..fb2fb344b --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Decoder.cs @@ -0,0 +1,127 @@ +using System; +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + /// + /// Decodes an ATRAC9 stream into 16-bit PCM. + /// + public class Atrac9Decoder + { + /// + /// The config data for the current ATRAC9 stream. + /// + public Atrac9Config Config { get; private set; } + + private Frame Frame { get; set; } + private BitReader Reader { get; set; } + private bool _initialized; + + /// + /// Sets up the decoder to decode an ATRAC9 stream based on the information in . + /// + /// A 4-byte value containing information about the ATRAC9 stream. + public void Initialize(byte[] configData) + { + Config = new Atrac9Config(configData); + Frame = new Frame(Config); + Reader = new BitReader(null); + _initialized = true; + } + + /// + /// Decodes one superframe of ATRAC9 data. + /// + /// The ATRAC9 data to decode. The array must be at least + /// . bytes long. + /// A buffer that the decoded PCM data will be placed in. + /// The array must have dimensions of at least [.] + /// [.]. + public void Decode(byte[] atrac9Data, short[][] pcmOut) + { + if (!_initialized) throw new InvalidOperationException("Decoder must be initialized before decoding."); + + ValidateDecodeBuffers(atrac9Data, pcmOut); + Reader.SetBuffer(atrac9Data); + DecodeSuperFrame(pcmOut); + } + + private void ValidateDecodeBuffers(byte[] atrac9Buffer, short[][] pcmBuffer) + { + if (atrac9Buffer == null) throw new ArgumentNullException(nameof(atrac9Buffer)); + if (pcmBuffer == null) throw new ArgumentNullException(nameof(pcmBuffer)); + + if (atrac9Buffer.Length < Config.SuperframeBytes) + { + throw new ArgumentException("ATRAC9 buffer is too small"); + } + + if (pcmBuffer.Length < Config.ChannelCount) + { + throw new ArgumentException("PCM buffer is too small"); + } + + for (int i = 0; i < Config.ChannelCount; i++) + { + if (pcmBuffer[i]?.Length < Config.SuperframeSamples) + { + throw new ArgumentException("PCM buffer is too small"); + } + } + } + + private void DecodeSuperFrame(short[][] pcmOut) + { + for (int i = 0; i < Config.FramesPerSuperframe; i++) + { + Frame.FrameIndex = i; + DecodeFrame(Reader, Frame); + PcmFloatToShort(pcmOut, i * Config.FrameSamples); + Reader.AlignPosition(8); + } + } + + private void PcmFloatToShort(short[][] pcmOut, int start) + { + int endSample = start + Config.FrameSamples; + int channelNum = 0; + foreach (Block block in Frame.Blocks) + { + foreach (Channel channel in block.Channels) + { + double[] pcmSrc = channel.Pcm; + short[] pcmDest = pcmOut[channelNum++]; + for (int d = 0, s = start; s < endSample; d++, s++) + { + double sample = pcmSrc[d]; + // Not using Math.Round because it's ~20x slower on 64-bit + int roundedSample = (int)Math.Floor(sample + 0.5); + pcmDest[s] = Helpers.Clamp16(roundedSample); + } + } + } + } + + private static void DecodeFrame(BitReader reader, Frame frame) + { + Unpack.UnpackFrame(reader, frame); + + foreach (Block block in frame.Blocks) + { + Quantization.DequantizeSpectra(block); + Stereo.ApplyIntensityStereo(block); + Quantization.ScaleSpectrum(block); + BandExtension.ApplyBandExtension(block); + ImdctBlock(block); + } + } + + private static void ImdctBlock(Block block) + { + foreach (Channel channel in block.Channels) + { + channel.Mdct.RunImdct(channel.Spectra, channel.Pcm); + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Rng.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Rng.cs new file mode 100644 index 000000000..072a61391 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Atrac9Rng.cs @@ -0,0 +1,33 @@ +namespace LibAtrac9 +{ + /// + /// An Xorshift RNG used by the ATRAC9 codec + /// + internal class Atrac9Rng + { + private ushort _stateA; + private ushort _stateB; + private ushort _stateC; + private ushort _stateD; + + public Atrac9Rng(ushort seed) + { + int startValue = 0x4D93 * (seed ^ (seed >> 14)); + + _stateA = (ushort)(3 - startValue); + _stateB = (ushort)(2 - startValue); + _stateC = (ushort)(1 - startValue); + _stateD = (ushort)(0 - startValue); + } + + public ushort Next() + { + ushort t = (ushort)(_stateD ^ (_stateD << 5)); + _stateD = _stateC; + _stateC = _stateB; + _stateB = _stateA; + _stateA = (ushort)(t ^ _stateA ^ ((t ^ (_stateA >> 5)) >> 4)); + return _stateA; + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BandExtension.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BandExtension.cs new file mode 100644 index 000000000..297c79bfa --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BandExtension.cs @@ -0,0 +1,372 @@ +using System; + +namespace LibAtrac9 +{ + internal static class BandExtension + { + public static void ApplyBandExtension(Block block) + { + if (!block.BandExtensionEnabled || !block.HasExtensionData) return; + + foreach (Channel channel in block.Channels) + { + ApplyBandExtensionChannel(channel); + } + } + + private static void ApplyBandExtensionChannel(Channel channel) + { + int groupAUnit = channel.Block.QuantizationUnitCount; + int[] scaleFactors = channel.ScaleFactors; + double[] spectra = channel.Spectra; + double[] scales = channel.BexScales; + int[] values = channel.BexValues; + + GetBexBandInfo(out int bandCount, out int groupBUnit, out int groupCUnit, groupAUnit); + int totalUnits = Math.Max(groupCUnit, 22); + + int groupABin = Tables.QuantUnitToCoeffIndex[groupAUnit]; + int groupBBin = Tables.QuantUnitToCoeffIndex[groupBUnit]; + int groupCBin = Tables.QuantUnitToCoeffIndex[groupCUnit]; + int totalBins = Tables.QuantUnitToCoeffIndex[totalUnits]; + + FillHighFrequencies(spectra, groupABin, groupBBin, groupCBin, totalBins); + + switch (channel.BexMode) + { + case 0: + int bexQuantUnits = totalUnits - groupAUnit; + + switch (bandCount) + { + case 3: + scales[0] = BexMode0Bands3[0][values[0]]; + scales[1] = BexMode0Bands3[1][values[0]]; + scales[2] = BexMode0Bands3[2][values[1]]; + scales[3] = BexMode0Bands3[3][values[2]]; + scales[4] = BexMode0Bands3[4][values[3]]; + break; + case 4: + scales[0] = BexMode0Bands4[0][values[0]]; + scales[1] = BexMode0Bands4[1][values[0]]; + scales[2] = BexMode0Bands4[2][values[1]]; + scales[3] = BexMode0Bands4[3][values[2]]; + scales[4] = BexMode0Bands4[4][values[3]]; + break; + case 5: + scales[0] = BexMode0Bands5[0][values[0]]; + scales[1] = BexMode0Bands5[1][values[1]]; + scales[2] = BexMode0Bands5[2][values[1]]; + break; + } + + scales[bexQuantUnits - 1] = Tables.SpectrumScale[scaleFactors[groupAUnit]]; + + AddNoiseToSpectrum(channel, Tables.QuantUnitToCoeffIndex[totalUnits - 1], + Tables.QuantUnitToCoeffCount[totalUnits - 1]); + ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits); + break; + case 1: + for (int i = groupAUnit; i < totalUnits; i++) + { + scales[i - groupAUnit] = Tables.SpectrumScale[scaleFactors[i]]; + } + + AddNoiseToSpectrum(channel, groupABin, totalBins - groupABin); + ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits); + break; + case 2: + double groupAScale2 = BexMode2Scale[values[0]]; + double groupBScale2 = BexMode2Scale[values[1]]; + + for (int i = groupABin; i < groupBBin; i++) + { + spectra[i] *= groupAScale2; + } + + for (int i = groupBBin; i < groupCBin; i++) + { + spectra[i] *= groupBScale2; + } + return; + case 3: + double rate = Math.Pow(2, BexMode3Rate[values[1]]); + double scale = BexMode3Initial[values[0]]; + for (int i = groupABin; i < totalBins; i++) + { + scale *= rate; + spectra[i] *= scale; + } + return; + case 4: + double mult = BexMode4Multiplier[values[0]]; + double groupAScale4 = 0.7079468 * mult; + double groupBScale4 = 0.5011902 * mult; + double groupCScale4 = 0.3548279 * mult; + + for (int i = groupABin; i < groupBBin; i++) + { + spectra[i] *= groupAScale4; + } + + for (int i = groupBBin; i < groupCBin; i++) + { + spectra[i] *= groupBScale4; + } + + for (int i = groupCBin; i < totalBins; i++) + { + spectra[i] *= groupCScale4; + } + return; + } + } + + private static void ScaleBexQuantUnits(double[] spectra, double[] scales, int startUnit, int totalUnits) + { + for (int i = startUnit; i < totalUnits; i++) + { + for (int k = Tables.QuantUnitToCoeffIndex[i]; k < Tables.QuantUnitToCoeffIndex[i + 1]; k++) + { + spectra[k] *= scales[i - startUnit]; + } + } + } + + private static void FillHighFrequencies(double[] spectra, int groupABin, int groupBBin, int groupCBin, int totalBins) + { + for (int i = 0; i < groupBBin - groupABin; i++) + { + spectra[groupABin + i] = spectra[groupABin - i - 1]; + } + + for (int i = 0; i < groupCBin - groupBBin; i++) + { + spectra[groupBBin + i] = spectra[groupBBin - i - 1]; + } + + for (int i = 0; i < totalBins - groupCBin; i++) + { + spectra[groupCBin + i] = spectra[groupCBin - i - 1]; + } + } + + private static void AddNoiseToSpectrum(Channel channel, int index, int count) + { + if (channel.Rng == null) + { + int[] sf = channel.ScaleFactors; + ushort seed = (ushort)(543 * (sf[8] + sf[12] + sf[15] + 1)); + channel.Rng = new Atrac9Rng(seed); + } + for (int i = 0; i < count; i++) + { + channel.Spectra[i + index] = channel.Rng.Next() / 65535.0 * 2.0 - 1.0; + } + } + + public static void GetBexBandInfo(out int bandCount, out int groupAUnit, out int groupBUnit, int quantUnits) + { + groupAUnit = BexGroupInfo[quantUnits - 13][0]; + groupBUnit = BexGroupInfo[quantUnits - 13][1]; + bandCount = BexGroupInfo[quantUnits - 13][2]; + } + + public static readonly byte[][] BexGroupInfo = + { + new byte[] {16, 21, 0}, + new byte[] {18, 22, 1}, + new byte[] {20, 22, 2}, + new byte[] {21, 22, 3}, + new byte[] {21, 22, 3}, + new byte[] {23, 24, 4}, + new byte[] {23, 24, 4}, + new byte[] {24, 24, 5} + }; + + // [mode][bands] + + public static readonly byte[][] BexEncodedValueCounts = + { + new byte[] {0, 0, 0, 4, 4, 2}, + new byte[] {0, 0, 0, 0, 0, 0}, + new byte[] {0, 0, 0, 2, 2, 1}, + new byte[] {0, 0, 0, 2, 2, 2}, + new byte[] {1, 1, 1, 0, 0, 0} + }; + + // [mode][bands][valueIndex] + + public static readonly byte[][][] BexDataLengths = + { + new[] { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {5, 4, 3, 3}, + new byte[] {4, 4, 3, 4}, + new byte[] {4, 5, 0, 0} + }, new[] { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0} + }, new[] { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {6, 6, 0, 0}, + new byte[] {6, 6, 0, 0}, + new byte[] {6, 0, 0, 0} + }, new[] { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {4, 4, 0, 0}, + new byte[] {4, 4, 0, 0}, + new byte[] {4, 4, 0, 0} + }, new[] { + new byte[] {3, 0, 0, 0}, + new byte[] {3, 0, 0, 0}, + new byte[] {3, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0} + } + }; + + public static readonly double[][] BexMode0Bands3 = + { + new[] { + 0.000000e+0, 1.988220e-1, 2.514343e-1, 2.960510e-1, + 3.263550e-1, 3.771362e-1, 3.786926e-1, 4.540405e-1, + 4.877625e-1, 5.262451e-1, 5.447083e-1, 5.737000e-1, + 6.212158e-1, 6.222839e-1, 6.560974e-1, 6.896667e-1, + 7.555542e-1, 7.677917e-1, 7.918091e-1, 7.971497e-1, + 8.188171e-1, 8.446045e-1, 9.790649e-1, 9.822083e-1, + 9.846191e-1, 9.859314e-1, 9.863586e-1, 9.863892e-1, + 9.873352e-1, 9.881287e-1, 9.898682e-1, 9.913330e-1 + }, new[] { + 0.000000e+0, 9.982910e-1, 7.592773e-2, 7.179565e-1, + 9.851379e-1, 5.340271e-1, 9.013672e-1, 6.349182e-1, + 7.226257e-1, 1.948547e-1, 7.628174e-1, 9.873657e-1, + 8.112183e-1, 2.715454e-1, 9.734192e-1, 1.443787e-1, + 4.640198e-1, 3.249207e-1, 3.790894e-1, 8.276367e-2, + 5.954590e-1, 2.864380e-1, 9.806824e-1, 7.929077e-1, + 6.292114e-1, 4.887085e-1, 2.905273e-1, 1.301880e-1, + 3.140869e-1, 5.482483e-1, 4.210815e-1, 1.182861e-1 + }, new[] { + 0.000000e+0, 3.155518e-2, 8.581543e-2, 1.364746e-1, + 1.858826e-1, 2.368469e-1, 2.888184e-1, 3.432617e-1, + 4.012451e-1, 4.623108e-1, 5.271301e-1, 5.954895e-1, + 6.681213e-1, 7.448425e-1, 8.245239e-1, 9.097290e-1 + }, new[] { + 0.000000e+0, 4.418945e-2, 1.303711e-1, 2.273560e-1, + 3.395996e-1, 4.735718e-1, 6.267090e-1, 8.003845e-1 + }, new[] { + 0.000000e+0, 2.804565e-2, 9.683228e-2, 1.849976e-1, + 3.005981e-1, 4.470520e-1, 6.168518e-1, 8.007813e-1 + } + }; + + public static readonly double[][] BexMode0Bands4 = + { + new[] { + 0.000000e+0, 2.708740e-1, 3.479614e-1, 3.578186e-1, + 5.083618e-1, 5.299072e-1, 5.819092e-1, 6.381836e-1, + 7.276917e-1, 7.595520e-1, 7.878723e-1, 9.707336e-1, + 9.713135e-1, 9.736023e-1, 9.759827e-1, 9.832458e-1 + }, new[] { + 0.000000e+0, 2.330627e-1, 5.891418e-1, 7.170410e-1, + 2.036438e-1, 1.613464e-1, 6.668701e-1, 9.481201e-1, + 9.769897e-1, 5.111694e-1, 3.522644e-1, 8.209534e-1, + 2.933960e-1, 9.757690e-1, 5.289917e-1, 4.372253e-1 + }, new[] { + 0.000000e+0, 4.360962e-2, 1.056519e-1, 1.590576e-1, + 2.078857e-1, 2.572937e-1, 3.082581e-1, 3.616028e-1, + 4.191589e-1, 4.792175e-1, 5.438538e-1, 6.125183e-1, + 6.841125e-1, 7.589417e-1, 8.365173e-1, 9.148254e-1 + }, new[] { + 0.000000e+0, 4.074097e-2, 1.164551e-1, 2.077026e-1, + 3.184509e-1, 4.532166e-1, 6.124268e-1, 7.932129e-1 + }, new[] { + 0.000000e+0, 8.880615e-3, 2.932739e-2, 5.593872e-2, + 8.825684e-2, 1.259155e-1, 1.721497e-1, 2.270813e-1, + 2.901611e-1, 3.579712e-1, 4.334106e-1, 5.147095e-1, + 6.023254e-1, 6.956177e-1, 7.952881e-1, 8.977356e-1 + } + }; + + public static readonly double[][] BexMode0Bands5 = + { + new[] { + 0.000000e+0, 7.379150e-2, 1.806335e-1, 2.687073e-1, + 3.407898e-1, 4.047546e-1, 4.621887e-1, 5.168762e-1, + 5.703125e-1, 6.237488e-1, 6.763611e-1, 7.288208e-1, + 7.808533e-1, 8.337708e-1, 8.874512e-1, 9.418030e-1 + }, new[] { + 0.000000e+0, 7.980347e-2, 1.615295e-1, 1.665649e-1, + 1.822205e-1, 2.185669e-1, 2.292175e-1, 2.456665e-1, + 2.666321e-1, 3.306580e-1, 3.330688e-1, 3.765259e-1, + 4.085083e-1, 4.400024e-1, 4.407654e-1, 4.817505e-1, + 4.924011e-1, 5.320740e-1, 5.893860e-1, 6.131287e-1, + 6.212463e-1, 6.278076e-1, 6.308899e-1, 7.660828e-1, + 7.850647e-1, 7.910461e-1, 7.929382e-1, 8.038330e-1, + 9.834900e-1, 9.846191e-1, 9.852295e-1, 9.862671e-1 + }, new[] { + 0.000000e+0, 6.084290e-1, 3.672791e-1, 3.151855e-1, + 1.488953e-1, 2.571716e-1, 5.103455e-1, 3.311157e-1, + 5.426025e-2, 4.254456e-1, 7.998352e-1, 7.873230e-1, + 5.418701e-1, 2.925110e-1, 8.468628e-2, 1.410522e-1, + 9.819641e-1, 9.609070e-1, 3.530884e-2, 9.729004e-2, + 5.758362e-1, 9.941711e-1, 7.215576e-1, 7.183228e-1, + 2.028809e-1, 9.588623e-2, 2.032166e-1, 1.338806e-1, + 5.003357e-1, 1.874390e-1, 9.804993e-1, 1.107788e-1 + } + }; + + public static readonly double[] BexMode2Scale = + { + 4.272461e-4, 1.312256e-3, 2.441406e-3, 3.692627e-3, + 4.913330e-3, 6.134033e-3, 7.507324e-3, 8.972168e-3, + 1.049805e-2, 1.223755e-2, 1.406860e-2, 1.599121e-2, + 1.800537e-2, 2.026367e-2, 2.264404e-2, 2.517700e-2, + 2.792358e-2, 3.073120e-2, 3.344727e-2, 3.631592e-2, + 3.952026e-2, 4.275513e-2, 4.608154e-2, 4.968262e-2, + 5.355835e-2, 5.783081e-2, 6.195068e-2, 6.677246e-2, + 7.196045e-2, 7.745361e-2, 8.319092e-2, 8.993530e-2, + 9.759521e-2, 1.056213e-1, 1.138916e-1, 1.236267e-1, + 1.348267e-1, 1.470337e-1, 1.603394e-1, 1.755676e-1, + 1.905823e-1, 2.071228e-1, 2.245178e-1, 2.444153e-1, + 2.658997e-1, 2.897644e-1, 3.146057e-1, 3.450012e-1, + 3.766174e-1, 4.122620e-1, 4.505615e-1, 4.893799e-1, + 5.305481e-1, 5.731201e-1, 6.157837e-1, 6.580811e-1, + 6.985168e-1, 7.435303e-1, 7.865906e-1, 8.302612e-1, + 8.718567e-1, 9.125671e-1, 9.575806e-1, 9.996643e-1 + }; + + public static readonly double[] BexMode3Initial = + { + 3.491211e-1, 5.371094e-1, 6.782227e-1, 7.910156e-1, + 9.057617e-1, 1.024902e+0, 1.156250e+0, 1.290527e+0, + 1.458984e+0, 1.664551e+0, 1.929688e+0, 2.278320e+0, + 2.831543e+0, 3.659180e+0, 5.257813e+0, 8.373047e+0 + }; + + public static readonly double[] BexMode3Rate = + { + -2.913818e-1, -2.541504e-1, -1.664429e-1, -1.476440e-1, + -1.342163e-1, -1.220703e-1, -1.117554e-1, -1.026611e-1, + -9.436035e-2, -8.483887e-2, -7.476807e-2, -6.304932e-2, + -4.492188e-2, -2.447510e-2, +1.831055e-4, +4.174805e-2 + }; + + public static readonly double[] BexMode4Multiplier = + { + 3.610229e-2, 1.260681e-1, 2.227478e-1, 3.338318e-1, + 4.662170e-1, 6.221313e-1, 7.989197e-1, 9.939575e-1 + }; + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BitAllocation.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BitAllocation.cs new file mode 100644 index 000000000..1775b32d5 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/BitAllocation.cs @@ -0,0 +1,140 @@ +using System; + +namespace LibAtrac9 +{ + internal static class BitAllocation + { + public static void CreateGradient(Block block) + { + int valueCount = block.GradientEndValue - block.GradientStartValue; + int unitCount = block.GradientEndUnit - block.GradientStartUnit; + + for (int i = 0; i < block.GradientEndUnit; i++) + { + block.Gradient[i] = block.GradientStartValue; + } + + for (int i = block.GradientEndUnit; i <= block.QuantizationUnitCount; i++) + { + block.Gradient[i] = block.GradientEndValue; + } + if (unitCount <= 0) return; + if (valueCount == 0) return; + + byte[] curve = Tables.GradientCurves[unitCount - 1]; + if (valueCount <= 0) + { + double scale = (-valueCount - 1) / 31.0; + int baseVal = block.GradientStartValue - 1; + for (int i = block.GradientStartUnit; i < block.GradientEndUnit; i++) + { + block.Gradient[i] = baseVal - (int)(curve[i - block.GradientStartUnit] * scale); + } + } + else + { + double scale = (valueCount - 1) / 31.0; + int baseVal = block.GradientStartValue + 1; + for (int i = block.GradientStartUnit; i < block.GradientEndUnit; i++) + { + block.Gradient[i] = baseVal + (int)(curve[i - block.GradientStartUnit] * scale); + } + } + } + + public static void CalculateMask(Channel channel) + { + Array.Clear(channel.PrecisionMask, 0, channel.PrecisionMask.Length); + for (int i = 1; i < channel.Block.QuantizationUnitCount; i++) + { + int delta = channel.ScaleFactors[i] - channel.ScaleFactors[i - 1]; + if (delta > 1) + { + channel.PrecisionMask[i] += Math.Min(delta - 1, 5); + } + else if (delta < -1) + { + channel.PrecisionMask[i - 1] += Math.Min(delta * -1 - 1, 5); + } + } + } + + public static void CalculatePrecisions(Channel channel) + { + Block block = channel.Block; + + if (block.GradientMode != 0) + { + for (int i = 0; i < block.QuantizationUnitCount; i++) + { + channel.Precisions[i] = channel.ScaleFactors[i] + channel.PrecisionMask[i] - block.Gradient[i]; + if (channel.Precisions[i] > 0) + { + switch (block.GradientMode) + { + case 1: + channel.Precisions[i] /= 2; + break; + case 2: + channel.Precisions[i] = 3 * channel.Precisions[i] / 8; + break; + case 3: + channel.Precisions[i] /= 4; + break; + } + } + } + } + else + { + for (int i = 0; i < block.QuantizationUnitCount; i++) + { + channel.Precisions[i] = channel.ScaleFactors[i] - block.Gradient[i]; + } + } + + for (int i = 0; i < block.QuantizationUnitCount; i++) + { + if (channel.Precisions[i] < 1) + { + channel.Precisions[i] = 1; + } + } + + for (int i = 0; i < block.GradientBoundary; i++) + { + channel.Precisions[i]++; + } + + for (int i = 0; i < block.QuantizationUnitCount; i++) + { + channel.PrecisionsFine[i] = 0; + if (channel.Precisions[i] > 15) + { + channel.PrecisionsFine[i] = channel.Precisions[i] - 15; + channel.Precisions[i] = 15; + } + } + } + + public static byte[][] GenerateGradientCurves() + { + byte[] main = + { + 01, 01, 01, 01, 02, 02, 02, 02, 03, 03, 03, 04, 04, 05, 05, 06, 07, 08, 09, 10, 11, 12, 13, 15, + 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30 + }; + var curves = new byte[main.Length][]; + + for (int length = 1; length <= main.Length; length++) + { + curves[length - 1] = new byte[length]; + for (int i = 0; i < length; i++) + { + curves[length - 1][i] = main[i * main.Length / length]; + } + } + return curves; + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Block.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Block.cs new file mode 100644 index 000000000..fc5d0db24 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Block.cs @@ -0,0 +1,91 @@ +namespace LibAtrac9 +{ + internal class Block + { + public Atrac9Config Config { get; } + public BlockType BlockType { get; } + public int BlockIndex { get; } + public Frame Frame { get; } + + public Channel[] Channels { get; } + public int ChannelCount { get; } + + public bool FirstInSuperframe { get; set; } + public bool ReuseBandParams { get; set; } + + public int BandCount { get; set; } + public int StereoBand { get; set; } + public int ExtensionBand { get; set; } + public int QuantizationUnitCount { get; set; } + public int StereoQuantizationUnit { get; set; } + public int ExtensionUnit { get; set; } + public int QuantizationUnitsPrev { get; set; } + + public int[] Gradient { get; } = new int[31]; + public int GradientMode { get; set; } + public int GradientStartUnit { get; set; } + public int GradientStartValue { get; set; } + public int GradientEndUnit { get; set; } + public int GradientEndValue { get; set; } + public int GradientBoundary { get; set; } + + public int PrimaryChannelIndex { get; set; } + public int[] JointStereoSigns { get; } = new int[30]; + public bool HasJointStereoSigns { get; set; } + public Channel PrimaryChannel => Channels[PrimaryChannelIndex == 0 ? 0 : 1]; + public Channel SecondaryChannel => Channels[PrimaryChannelIndex == 0 ? 1 : 0]; + + public bool BandExtensionEnabled { get; set; } + public bool HasExtensionData { get; set; } + public int BexDataLength { get; set; } + public int BexMode { get; set; } + + public Block(Frame parentFrame, int blockIndex) + { + Frame = parentFrame; + BlockIndex = blockIndex; + Config = parentFrame.Config; + BlockType = Config.ChannelConfig.BlockTypes[blockIndex]; + ChannelCount = BlockTypeToChannelCount(BlockType); + Channels = new Channel[ChannelCount]; + for (int i = 0; i < ChannelCount; i++) + { + Channels[i] = new Channel(this, i); + } + } + + public static int BlockTypeToChannelCount(BlockType blockType) + { + switch (blockType) + { + case BlockType.Mono: + return 1; + case BlockType.Stereo: + return 2; + case BlockType.LFE: + return 1; + default: + return 0; + } + } + } + + /// + /// An ATRAC9 block (substream) type + /// + public enum BlockType + { + /// + /// Mono ATRAC9 block + /// + Mono = 0, + /// + /// Stereo ATRAC9 block + /// + Stereo = 1, + /// + /// Low-frequency effects ATRAC9 block + /// + LFE = 2 + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Channel.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Channel.cs new file mode 100644 index 000000000..4b77f6a98 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Channel.cs @@ -0,0 +1,48 @@ +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + internal class Channel + { + public Atrac9Config Config { get; } + public int ChannelIndex { get; } + public bool IsPrimary => Block.PrimaryChannelIndex == ChannelIndex; + public Block Block { get; } + public Mdct Mdct { get; } + + public double[] Pcm { get; } = new double[256]; + public double[] Spectra { get; } = new double[256]; + + public int CodedQuantUnits { get; set; } + public int ScaleFactorCodingMode { get; set; } + public int[] ScaleFactors { get; } = new int[31]; + public int[] ScaleFactorsPrev { get; } = new int[31]; + + public int[] Precisions { get; } = new int[30]; + public int[] PrecisionsFine { get; } = new int[30]; + public int[] PrecisionMask { get; } = new int[30]; + + public int[] SpectraValuesBuffer { get; } = new int[16]; + public int[] CodebookSet { get; } = new int[30]; + + public int[] QuantizedSpectra { get; } = new int[256]; + public int[] QuantizedSpectraFine { get; } = new int[256]; + + public int BexMode { get; set; } + public int BexValueCount { get; set; } + public int[] BexValues { get; } = new int[4]; + public double[] BexScales { get; } = new double[6]; + public Atrac9Rng Rng { get; set; } + + public Channel(Block parentBlock, int channelIndex) + { + Block = parentBlock; + ChannelIndex = channelIndex; + Config = parentBlock.Config; + Mdct = new Mdct(Config.FrameSamplesPower, Tables.ImdctWindow[Config.FrameSamplesPower - 6]); + } + + public void UpdateCodedUnits() => + CodedQuantUnits = IsPrimary ? Block.QuantizationUnitCount : Block.StereoQuantizationUnit; + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ChannelConfig.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ChannelConfig.cs new file mode 100644 index 000000000..6854c7fee --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ChannelConfig.cs @@ -0,0 +1,33 @@ +namespace LibAtrac9 +{ + /// + /// Describes the channel mapping for an ATRAC9 stream + /// + public class ChannelConfig + { + internal ChannelConfig(params BlockType[] blockTypes) + { + BlockCount = blockTypes.Length; + BlockTypes = blockTypes; + foreach (BlockType type in blockTypes) + { + ChannelCount += Block.BlockTypeToChannelCount(type); + } + } + + /// + /// The number of blocks or substreams in the ATRAC9 stream + /// + public int BlockCount { get; } + + /// + /// The type of each block or substream in the ATRAC9 stream + /// + public BlockType[] BlockTypes { get; } + + /// + /// The number of channels in the ATRAC9 stream + /// + public int ChannelCount { get; } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Frame.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Frame.cs new file mode 100644 index 000000000..46feb1bbb --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Frame.cs @@ -0,0 +1,20 @@ +namespace LibAtrac9 +{ + internal class Frame + { + public Atrac9Config Config { get; } + public int FrameIndex { get; set; } + public Block[] Blocks { get; } + + public Frame(Atrac9Config config) + { + Config = config; + Blocks = new Block[config.ChannelConfig.BlockCount]; + + for (int i = 0; i < config.ChannelConfig.BlockCount; i++) + { + Blocks[i] = new Block(this, i); + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebook.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebook.cs new file mode 100644 index 000000000..8a900b54c --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebook.cs @@ -0,0 +1,62 @@ +using System; +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + internal class HuffmanCodebook + { + public HuffmanCodebook(short[] codes, byte[] bits, byte valueCountPower) + { + Codes = codes; + Bits = bits; + if (Codes == null || Bits == null) return; + + ValueCount = 1 << valueCountPower; + ValueCountPower = valueCountPower; + ValueBits = Helpers.Log2(codes.Length) >> valueCountPower; + ValueMax = 1 << ValueBits; + + int max = 0; + foreach (byte bitSize in bits) + { + max = Math.Max(max, bitSize); + } + + MaxBitSize = max; + Lookup = CreateLookupTable(); + } + + private byte[] CreateLookupTable() + { + if (Codes == null || Bits == null) return null; + + int tableSize = 1 << MaxBitSize; + var dest = new byte[tableSize]; + + for (int i = 0; i < Bits.Length; i++) + { + if (Bits[i] == 0) continue; + int unusedBits = MaxBitSize - Bits[i]; + + int start = Codes[i] << unusedBits; + int length = 1 << unusedBits; + int end = start + length; + + for (int j = start; j < end; j++) + { + dest[j] = (byte)i; + } + } + return dest; + } + + public short[] Codes { get; } + public byte[] Bits { get; } + public byte[] Lookup { get; } + public int ValueCount { get; } + public int ValueCountPower { get; } + public int ValueBits { get; } + public int ValueMax { get; } + public int MaxBitSize { get; } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebooks.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebooks.cs new file mode 100644 index 000000000..8c350d098 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/HuffmanCodebooks.cs @@ -0,0 +1,1352 @@ +namespace LibAtrac9 +{ + internal static class HuffmanCodebooks + { + public static HuffmanCodebook[][] GenerateHuffmanCodebooks(short[][][] codes, byte[][][] bits, byte[][] groupCounts) + { + var tables = new HuffmanCodebook[bits.Length][]; + for (int i = 0; i < tables.Length; i++) + { + if (codes[i] != null) + { + tables[i] = GenerateHuffmanCodebooks(codes[i], bits[i], groupCounts[i]); + } + } + return tables; + } + + public static HuffmanCodebook[] GenerateHuffmanCodebooks(short[][] codes, byte[][] bits, byte[] groupCounts) + { + var tables = new HuffmanCodebook[bits.Length]; + for (int i = 0; i < tables.Length; i++) + { + if (codes[i] != null) + { + tables[i] = new HuffmanCodebook(codes[i], bits[i], groupCounts[i]); + } + } + return tables; + } + + // For scale factor table names, {letter}{number} correspond to the signedness and word length + private static readonly byte[] ScaleFactorsA1Bits = + { + 1, 1 + }; + + private static readonly short[] ScaleFactorsA1Codes = + { + 0x00, 0x01 + }; + + private static readonly byte[] ScaleFactorsA2Bits = + { + 1, 3, 3, 2 + }; + + private static readonly short[] ScaleFactorsA2Codes = + { + 0x00, 0x06, 0x07, 0x02 + }; + + private static readonly byte[] ScaleFactorsA3Bits = + { + 2, 2, 4, 6, 6, 5, 3, 2 + }; + + private static readonly short[] ScaleFactorsA3Codes = + { + 0x00, 0x01, 0x0E, 0x3E, 0x3F, 0x1E, 0x06, 0x02 + }; + + private static readonly byte[] ScaleFactorsA4Bits = + { + 2, 2, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 6, 5, 4, 2 + }; + + private static readonly short[] ScaleFactorsA4Codes = + { + 0x01, 0x02, 0x00, 0x06, 0x0F, 0x13, 0x23, 0x24, 0x25, 0x22, 0x21, 0x20, 0x0E, 0x05, 0x01, 0x03 + }; + + private static readonly byte[] ScaleFactorsA5Bits = + { + 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 5, 5, 4, 3 + }; + + private static readonly short[] ScaleFactorsA5Codes = + { + 0x02, 0x01, 0x07, 0x0D, 0x0C, 0x18, 0x1B, 0x21, 0x3F, 0x6A, 0x6B, 0x68, 0x73, 0x79, 0x7C, 0x7D, + 0x7A, 0x7B, 0x78, 0x72, 0x44, 0x45, 0x47, 0x46, 0x69, 0x38, 0x20, 0x1D, 0x19, 0x09, 0x05, 0x00 + }; + + private static readonly byte[] ScaleFactorsA6Bits = + { + 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 4 + }; + + private static readonly short[] ScaleFactorsA6Codes = + { + 0x00, 0x01, 0x04, 0x05, 0x12, 0x13, 0x2E, 0x2F, 0x30, 0x66, 0x67, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, + 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x68, 0x69, 0x6A, 0x31, 0x32, 0x14, 0x15, 0x16, 0x06, 0x07, 0x08 + }; + + private static readonly byte[] ScaleFactorsB2Bits = + { + 1, 2, 0, 2 + }; + + private static readonly short[] ScaleFactorsB2Codes = + { + 0x00, 0x03, 0x00, 0x02 + }; + + private static readonly byte[] ScaleFactorsB3Bits = + { + 1, 3, 5, 6, 0, 6, 4, 2 + }; + + private static readonly short[] ScaleFactorsB3Codes = + { + 0x01, 0x00, 0x04, 0x0B, 0x00, 0x0A, 0x03, 0x01 + }; + + private static readonly byte[] ScaleFactorsB4Bits = + { + 1, 3, 4, 5, 5, 7, 8, 8, 0, 8, 8, 7, 6, 6, 4, 3 + }; + + private static readonly short[] ScaleFactorsB4Codes = + { + 0x01, 0x01, 0x04, 0x0E, 0x0F, 0x2C, 0x5A, 0x5D, 0x00, 0x5C, 0x5B, 0x2F, 0x15, 0x14, 0x06, 0x00 + }; + + private static readonly byte[] ScaleFactorsB5Bits = + { + 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 3 + }; + + private static readonly short[] ScaleFactorsB5Codes = + { + 0x00, 0x05, 0x07, 0x0C, 0x04, 0x02, 0x03, 0x05, 0x09, 0x10, 0x23, 0x33, 0x36, 0x6E, 0x60, 0x65, + 0x62, 0x61, 0x63, 0x64, 0x6F, 0x6D, 0x6C, 0x6B, 0x6A, 0x68, 0x69, 0x45, 0x44, 0x37, 0x1A, 0x07 + }; + + // For spectrum table names, {letter}{number}{number} correspond to the + // codebook set, word length, and band group + private static readonly byte[] SpectrumA21Bits = + { + 0, 3, 0, 3, 3, 3, 0, 3, 0, 0, 0, 0, 3, 3, 0, 3 + }; + + private static readonly short[] SpectrumA21Codes = + { + 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x06 + }; + + private static readonly byte[] SpectrumA22Bits = + { + 0, 4, 0, 4, 5, 6, 0, 6, 0, 0, 0, 0, 5, 6, 0, 6, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 5, 6, 0, 6, 7, 7, 0, 7, 0, 0, 0, 0, 6, 7, 0, 7, + 6, 7, 0, 7, 7, 8, 0, 8, 0, 0, 0, 0, 7, 8, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 7, 7, 8, 0, 8, 0, 0, 0, 0, 7, 7, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 7, 7, 0, 7, + 6, 7, 0, 7, 7, 8, 0, 7, 0, 0, 0, 0, 7, 8, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 7, 7, 7, 0, 8, 0, 0, 0, 0, 7, 8, 0, 8 + }; + + private static readonly short[] SpectrumA22Codes = + { + 0x00, 0x02, 0x00, 0x03, 0x10, 0x3C, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x11, 0x3E, 0x00, 0x3D, + 0x0E, 0x00, 0x00, 0x39, 0x18, 0x26, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x24, 0x00, 0x6D, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x38, 0x00, 0x01, 0x1A, 0x6C, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x19, 0x74, 0x00, 0x27, + 0x16, 0x14, 0x00, 0x17, 0x76, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x35, 0x64, 0x00, 0x6F, + 0x26, 0x04, 0x00, 0x63, 0x22, 0xA2, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x67, 0xA0, 0x00, 0x0D, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x52, 0x00, 0x0B, 0x20, 0x92, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x95, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x16, 0x00, 0x15, 0x34, 0x6E, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x77, 0x08, 0x00, 0x07, + 0x2A, 0x0A, 0x00, 0x53, 0x60, 0x94, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x21, 0x90, 0x00, 0x93, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x62, 0x00, 0x05, 0x66, 0x0C, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x23, 0x96, 0x00, 0xA3 + }; + + private static readonly byte[] SpectrumA23Bits = + { + 3, 4, 0, 4, 5, 6, 0, 6, 0, 0, 0, 0, 5, 6, 0, 6, + 5, 7, 0, 6, 6, 8, 0, 7, 0, 0, 0, 0, 6, 8, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 7, 6, 7, 0, 8, 0, 0, 0, 0, 6, 7, 0, 8, + 5, 6, 0, 6, 7, 8, 0, 8, 0, 0, 0, 0, 6, 7, 0, 7, + 6, 8, 0, 7, 8, 9, 0, 9, 0, 0, 0, 0, 7, 9, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 8, 0, 8, 8, 9, 0, 9, 0, 0, 0, 0, 7, 8, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 0, 6, 6, 7, 0, 7, 0, 0, 0, 0, 7, 8, 0, 8, + 6, 8, 0, 8, 7, 9, 0, 8, 0, 0, 0, 0, 8, 9, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 8, 7, 8, 0, 9, 0, 0, 0, 0, 8, 9, 0, 9 + }; + + private static readonly short[] SpectrumA23Codes = + { + 0x006, 0x002, 0x000, 0x003, 0x016, 0x01E, 0x000, 0x021, 0x000, 0x000, 0x000, 0x000, + 0x017, 0x020, 0x000, 0x01F, 0x01C, 0x054, 0x000, 0x027, 0x010, 0x0A6, 0x000, 0x027, + 0x000, 0x000, 0x000, 0x000, 0x015, 0x0A4, 0x000, 0x02D, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01D, 0x026, 0x000, 0x055, 0x014, 0x02C, 0x000, 0x0A5, 0x000, 0x000, 0x000, 0x000, + 0x011, 0x026, 0x000, 0x0A7, 0x01E, 0x000, 0x000, 0x003, 0x04A, 0x074, 0x000, 0x071, + 0x000, 0x000, 0x000, 0x000, 0x023, 0x00A, 0x000, 0x009, 0x018, 0x072, 0x000, 0x00D, + 0x0A2, 0x15A, 0x000, 0x123, 0x000, 0x000, 0x000, 0x000, 0x00F, 0x158, 0x000, 0x05D, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x01B, 0x0AE, 0x000, 0x077, 0x092, 0x140, 0x000, 0x121, + 0x000, 0x000, 0x000, 0x000, 0x025, 0x05E, 0x000, 0x143, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01F, 0x002, 0x000, 0x001, 0x022, 0x008, 0x000, 0x00B, 0x000, 0x000, 0x000, 0x000, + 0x04B, 0x070, 0x000, 0x075, 0x01A, 0x076, 0x000, 0x0AF, 0x024, 0x142, 0x000, 0x05F, + 0x000, 0x000, 0x000, 0x000, 0x093, 0x120, 0x000, 0x141, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x019, 0x00C, 0x000, 0x073, 0x00E, 0x05C, 0x000, 0x159, 0x000, 0x000, 0x000, 0x000, + 0x0A3, 0x122, 0x000, 0x15B + }; + + private static readonly byte[] SpectrumA24Bits = + { + 02, 04, 00, 04, 05, 06, 00, 06, 00, 00, 00, 00, 05, 06, 00, 06, + 05, 07, 00, 06, 06, 08, 00, 08, 00, 00, 00, 00, 06, 08, 00, 08, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 05, 06, 00, 07, 06, 08, 00, 08, 00, 00, 00, 00, 06, 08, 00, 08, + 05, 07, 00, 07, 07, 09, 00, 09, 00, 00, 00, 00, 06, 08, 00, 08, + 06, 09, 00, 08, 08, 10, 00, 10, 00, 00, 00, 00, 08, 10, 00, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 06, 08, 00, 09, 09, 10, 00, 10, 00, 00, 00, 00, 08, 09, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 05, 07, 00, 07, 06, 08, 00, 08, 00, 00, 00, 00, 07, 09, 00, 09, + 06, 09, 00, 08, 08, 10, 00, 09, 00, 00, 00, 00, 09, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 06, 08, 00, 09, 08, 09, 00, 10, 00, 00, 00, 00, 08, 10, 00, 10 + }; + + private static readonly short[] SpectrumA24Codes = + { + 0x002, 0x002, 0x000, 0x003, 0x01E, 0x010, 0x000, 0x013, 0x000, 0x000, 0x000, 0x000, + 0x01F, 0x012, 0x000, 0x011, 0x01A, 0x030, 0x000, 0x01B, 0x000, 0x064, 0x000, 0x0C1, + 0x000, 0x000, 0x000, 0x000, 0x003, 0x052, 0x000, 0x07D, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01B, 0x01A, 0x000, 0x031, 0x002, 0x07C, 0x000, 0x053, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x0C0, 0x000, 0x065, 0x01C, 0x062, 0x000, 0x065, 0x02A, 0x198, 0x000, 0x19B, + 0x000, 0x000, 0x000, 0x000, 0x017, 0x078, 0x000, 0x07B, 0x004, 0x0FE, 0x000, 0x077, + 0x050, 0x33A, 0x000, 0x1F9, 0x000, 0x000, 0x000, 0x000, 0x073, 0x338, 0x000, 0x0E1, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x007, 0x066, 0x000, 0x187, 0x19E, 0x308, 0x000, 0x30B, + 0x000, 0x000, 0x000, 0x000, 0x075, 0x0E2, 0x000, 0x1FB, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x01D, 0x064, 0x000, 0x063, 0x016, 0x07A, 0x000, 0x079, 0x000, 0x000, 0x000, 0x000, + 0x02B, 0x19A, 0x000, 0x199, 0x006, 0x186, 0x000, 0x067, 0x074, 0x1FA, 0x000, 0x0E3, + 0x000, 0x000, 0x000, 0x000, 0x19F, 0x30A, 0x000, 0x309, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x005, 0x076, 0x000, 0x0FF, 0x072, 0x0E0, 0x000, 0x339, 0x000, 0x000, 0x000, 0x000, + 0x051, 0x1F8, 0x000, 0x33B + }; + + private static readonly byte[] SpectrumA31Bits = + { + 0, 0, 4, 5, 0, 5, 4, 0, 0, 0, 5, 5, 0, 5, 5, 0, + 5, 5, 6, 6, 0, 6, 5, 5, 5, 6, 6, 7, 0, 7, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 7, 0, 7, 6, 6, + 5, 5, 5, 6, 0, 6, 6, 5, 0, 0, 5, 5, 0, 5, 5, 0 + }; + + private static readonly short[] SpectrumA31Codes = + { + 0x00, 0x00, 0x02, 0x18, 0x00, 0x19, 0x03, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x09, 0x15, 0x00, + 0x1A, 0x0A, 0x3E, 0x2C, 0x00, 0x2F, 0x01, 0x0D, 0x0E, 0x38, 0x20, 0x78, 0x00, 0x7B, 0x23, 0x3B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x3A, 0x22, 0x7A, 0x00, 0x79, 0x21, 0x39, + 0x1B, 0x0C, 0x00, 0x2E, 0x00, 0x2D, 0x3F, 0x0B, 0x00, 0x00, 0x14, 0x08, 0x00, 0x03, 0x13, 0x00 + }; + + private static readonly byte[] SpectrumA32Bits = + { + 4, 5, 5, 6, 0, 6, 5, 5, 5, 6, 5, 6, 0, 6, 5, 5, + 5, 5, 6, 7, 0, 7, 6, 5, 6, 6, 7, 7, 0, 7, 7, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 7, 7, 0, 7, 7, 6, + 5, 5, 6, 7, 0, 7, 6, 5, 5, 5, 5, 6, 0, 6, 5, 6 + }; + + private static readonly short[] SpectrumA32Codes = + { + 0x0D, 0x18, 0x16, 0x3A, 0x00, 0x3B, 0x17, 0x19, 0x12, 0x3E, 0x08, 0x1C, 0x00, 0x1B, 0x07, 0x01, + 0x10, 0x02, 0x28, 0x78, 0x00, 0x7B, 0x1F, 0x05, 0x2A, 0x16, 0x72, 0x2A, 0x00, 0x29, 0x71, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x18, 0x70, 0x28, 0x00, 0x2B, 0x73, 0x17, + 0x11, 0x04, 0x1E, 0x7A, 0x00, 0x79, 0x29, 0x03, 0x13, 0x00, 0x06, 0x1A, 0x00, 0x1D, 0x09, 0x3F + }; + + private static readonly byte[] SpectrumA33Bits = + { + 3, 4, 5, 6, 0, 6, 5, 4, 4, 5, 6, 7, 0, 7, 6, 5, + 5, 6, 6, 7, 0, 7, 6, 6, 6, 7, 8, 8, 0, 8, 8, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 8, 0, 8, 8, 7, + 5, 6, 6, 7, 0, 7, 6, 6, 4, 5, 6, 7, 0, 7, 6, 5 + }; + + private static readonly short[] SpectrumA33Codes = + { + 0x05, 0x06, 0x10, 0x08, 0x00, 0x09, 0x11, 0x07, 0x04, 0x12, 0x3E, 0x6A, 0x00, 0x6D, 0x3D, 0x19, + 0x06, 0x3A, 0x06, 0x02, 0x00, 0x01, 0x05, 0x39, 0x02, 0x16, 0xDC, 0x2A, 0x00, 0x29, 0xDF, 0x69, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x68, 0xDE, 0x28, 0x00, 0x2B, 0xDD, 0x17, + 0x07, 0x38, 0x04, 0x00, 0x00, 0x03, 0x07, 0x3B, 0x05, 0x18, 0x3C, 0x6C, 0x00, 0x6B, 0x3F, 0x13 + }; + + private static readonly byte[] SpectrumA34Bits = + { + 02, 04, 05, 07, 00, 07, 05, 04, 04, 05, 06, 08, 00, 08, 06, 05, + 05, 06, 07, 08, 00, 08, 07, 06, 07, 08, 08, 10, 00, 10, 09, 08, + 00, 00, 00, 00, 00, 00, 00, 00, 07, 08, 09, 10, 00, 10, 08, 08, + 05, 06, 07, 08, 00, 08, 07, 06, 04, 05, 06, 08, 00, 08, 06, 05 + }; + + private static readonly short[] SpectrumA34Codes = + { + 0x000, 0x00A, 0x00A, 0x034, 0x000, 0x035, 0x00B, 0x00B, 0x008, 0x01C, 0x032, 0x0DA, + 0x000, 0x0DD, 0x035, 0x01F, 0x008, 0x01E, 0x03A, 0x06C, 0x000, 0x063, 0x039, 0x031, + 0x032, 0x06E, 0x060, 0x37A, 0x000, 0x379, 0x1BF, 0x0D9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x033, 0x0D8, 0x1BE, 0x378, 0x000, 0x37B, 0x061, 0x06F, + 0x009, 0x030, 0x038, 0x062, 0x000, 0x06D, 0x03B, 0x01F, 0x009, 0x01E, 0x034, 0x0DC, + 0x000, 0x0DB, 0x033, 0x01D + }; + + private static readonly byte[] SpectrumA41Bits = + { + 0, 0, 0, 0, 6, 6, 7, 7, 0, 7, 7, 6, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 8, 0, 8, 7, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 8, 8, 0, 8, 8, 7, 7, 0, 0, 0, + 7, 7, 7, 8, 7, 8, 8, 8, 0, 8, 8, 8, 7, 8, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 9, 0, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 0, 9, 8, 8, 8, 8, 8, 7, + 8, 8, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 9, 9, 9, 0, 9, 9, 9, 8, 8, 8, 8, + 7, 7, 8, 8, 8, 8, 8, 9, 0, 9, 9, 8, 8, 8, 8, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 0, 9, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 7, 8, 8, 8, 0, 8, 8, 8, 7, 8, 7, 7, + 0, 0, 0, 0, 7, 7, 8, 8, 0, 8, 8, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 8, 0, 8, 7, 7, 7, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 7, 0, 7, 7, 7, 7, 0, 0, 0 + }; + + private static readonly short[] SpectrumA41Codes = + { + 0x000, 0x000, 0x000, 0x000, 0x018, 0x00E, 0x05E, 0x028, 0x000, 0x029, 0x05F, 0x00F, + 0x019, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x076, 0x06E, 0x03E, 0x004, + 0x000, 0x017, 0x045, 0x07B, 0x013, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x04A, 0x048, 0x010, 0x0CE, 0x000, 0x0E1, 0x023, 0x055, 0x053, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x008, 0x018, 0x0D6, 0x09E, 0x000, 0x09D, 0x0E5, 0x02B, + 0x01B, 0x000, 0x000, 0x000, 0x07C, 0x05C, 0x038, 0x0FC, 0x002, 0x0D2, 0x09A, 0x05C, + 0x000, 0x06B, 0x0A3, 0x0D9, 0x00F, 0x0FF, 0x03D, 0x061, 0x074, 0x056, 0x036, 0x000, + 0x0CC, 0x08C, 0x058, 0x1E2, 0x000, 0x00F, 0x05F, 0x0A1, 0x0D5, 0x00D, 0x03B, 0x059, + 0x040, 0x014, 0x0DA, 0x0B6, 0x084, 0x040, 0x1E0, 0x196, 0x000, 0x1A1, 0x00D, 0x043, + 0x087, 0x0C7, 0x0E3, 0x00B, 0x0F2, 0x0C4, 0x08E, 0x05A, 0x024, 0x1CC, 0x194, 0x168, + 0x000, 0x16B, 0x1A3, 0x1CF, 0x027, 0x069, 0x099, 0x0C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0F3, 0x0C8, 0x098, 0x068, 0x026, 0x1CE, 0x1A2, 0x16A, 0x000, 0x169, 0x195, 0x1CD, + 0x025, 0x05B, 0x08F, 0x0C5, 0x041, 0x00A, 0x0E2, 0x0C6, 0x086, 0x042, 0x00C, 0x1A0, + 0x000, 0x197, 0x1E1, 0x041, 0x085, 0x0B7, 0x0DB, 0x015, 0x075, 0x058, 0x03A, 0x00C, + 0x0D4, 0x0A0, 0x05E, 0x00E, 0x000, 0x1E3, 0x059, 0x08D, 0x0CD, 0x001, 0x037, 0x057, + 0x07D, 0x060, 0x03C, 0x0FE, 0x00E, 0x0D8, 0x0A2, 0x06A, 0x000, 0x05D, 0x09B, 0x0D3, + 0x003, 0x0FD, 0x039, 0x05D, 0x000, 0x000, 0x000, 0x000, 0x01A, 0x02A, 0x0E4, 0x09C, + 0x000, 0x09F, 0x0D7, 0x019, 0x009, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x052, 0x054, 0x022, 0x0E0, 0x000, 0x0CF, 0x011, 0x049, 0x04B, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x012, 0x07A, 0x044, 0x016, 0x000, 0x005, 0x03F, 0x06F, + 0x077, 0x000, 0x000, 0x000 + }; + + private static readonly byte[] SpectrumA42Bits = + { + 05, 06, 07, 07, 07, 07, 08, 08, 00, 08, 08, 07, 07, 07, 07, 06, + 06, 07, 07, 08, 07, 07, 08, 08, 00, 08, 08, 07, 07, 08, 07, 07, + 07, 07, 08, 08, 07, 08, 08, 09, 00, 09, 08, 08, 07, 08, 08, 07, + 08, 08, 08, 08, 08, 08, 08, 09, 00, 09, 08, 08, 08, 08, 08, 08, + 07, 07, 07, 08, 08, 08, 09, 09, 00, 09, 09, 08, 08, 08, 07, 07, + 07, 07, 08, 08, 08, 09, 09, 09, 00, 09, 09, 09, 08, 08, 08, 07, + 08, 08, 08, 08, 09, 09, 09, 10, 00, 10, 09, 09, 09, 08, 08, 08, + 08, 08, 09, 09, 09, 09, 10, 10, 00, 10, 10, 09, 09, 09, 09, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 08, 09, 09, 09, 09, 09, 10, 10, 00, 10, 10, 09, 09, 09, 09, 08, + 08, 08, 08, 08, 09, 09, 09, 10, 00, 10, 09, 09, 09, 08, 08, 08, + 07, 07, 08, 08, 08, 09, 09, 09, 00, 09, 09, 09, 08, 08, 08, 07, + 07, 07, 07, 08, 08, 08, 09, 09, 00, 09, 09, 08, 08, 08, 07, 07, + 08, 08, 08, 08, 08, 08, 08, 09, 00, 09, 08, 08, 08, 08, 08, 08, + 07, 07, 08, 08, 07, 08, 08, 09, 00, 09, 08, 08, 07, 08, 08, 07, + 06, 07, 07, 08, 07, 07, 08, 08, 00, 08, 08, 07, 07, 08, 07, 07 + }; + + private static readonly short[] SpectrumA42Codes = + { + 0x003, 0x018, 0x058, 0x000, 0x066, 0x03C, 0x0D6, 0x07C, 0x000, 0x07D, 0x0D7, 0x03D, + 0x067, 0x001, 0x059, 0x019, 0x002, 0x064, 0x036, 0x0DA, 0x04C, 0x01C, 0x0BE, 0x02C, + 0x000, 0x037, 0x0C5, 0x029, 0x04B, 0x0E7, 0x03B, 0x069, 0x044, 0x02E, 0x0FA, 0x092, + 0x020, 0x0F8, 0x086, 0x1FC, 0x000, 0x1E7, 0x07F, 0x0F5, 0x023, 0x0AD, 0x0FD, 0x02D, + 0x0F6, 0x0DC, 0x09C, 0x03E, 0x0F0, 0x0B6, 0x026, 0x186, 0x000, 0x18D, 0x02F, 0x0B5, + 0x0E1, 0x03D, 0x0AF, 0x0D9, 0x054, 0x040, 0x014, 0x0EC, 0x0BC, 0x054, 0x1C6, 0x108, + 0x000, 0x10B, 0x1C5, 0x069, 0x0B9, 0x0DF, 0x019, 0x047, 0x026, 0x008, 0x0E4, 0x0A2, + 0x056, 0x1DC, 0x142, 0x06A, 0x000, 0x091, 0x123, 0x1DF, 0x04B, 0x0A7, 0x0EB, 0x00B, + 0x0C0, 0x09E, 0x06A, 0x022, 0x1AA, 0x140, 0x092, 0x3CA, 0x000, 0x3A7, 0x04B, 0x121, + 0x18F, 0x007, 0x071, 0x0A5, 0x020, 0x004, 0x1A8, 0x174, 0x0E4, 0x068, 0x3A4, 0x2EE, + 0x000, 0x2ED, 0x3C9, 0x049, 0x0E7, 0x185, 0x1D1, 0x1FF, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x021, 0x1FE, 0x1D0, 0x184, 0x0E6, 0x048, 0x3C8, 0x2EC, 0x000, 0x2EF, 0x3A5, 0x069, + 0x0E5, 0x175, 0x1A9, 0x005, 0x0C1, 0x0A4, 0x070, 0x006, 0x18E, 0x120, 0x04A, 0x3A6, + 0x000, 0x3CB, 0x093, 0x141, 0x1AB, 0x023, 0x06B, 0x09F, 0x027, 0x00A, 0x0EA, 0x0A6, + 0x04A, 0x1DE, 0x122, 0x090, 0x000, 0x06B, 0x143, 0x1DD, 0x057, 0x0A3, 0x0E5, 0x009, + 0x055, 0x046, 0x018, 0x0DE, 0x0B8, 0x068, 0x1C4, 0x10A, 0x000, 0x109, 0x1C7, 0x055, + 0x0BD, 0x0ED, 0x015, 0x041, 0x0F7, 0x0D8, 0x0AE, 0x03C, 0x0E0, 0x0B4, 0x02E, 0x18C, + 0x000, 0x187, 0x027, 0x0B7, 0x0F1, 0x03F, 0x09D, 0x0DD, 0x045, 0x02C, 0x0FC, 0x0AC, + 0x022, 0x0F4, 0x07E, 0x1E6, 0x000, 0x1FD, 0x087, 0x0F9, 0x021, 0x093, 0x0FB, 0x02F, + 0x003, 0x068, 0x03A, 0x0E6, 0x04A, 0x028, 0x0C4, 0x036, 0x000, 0x02D, 0x0BF, 0x01D, + 0x04D, 0x0DB, 0x037, 0x065 + }; + + private static readonly byte[] SpectrumA43Bits = + { + 04, 06, 06, 07, 07, 08, 08, 09, 00, 09, 08, 08, 07, 07, 06, 06, + 05, 06, 07, 07, 07, 08, 08, 09, 00, 09, 08, 08, 07, 07, 07, 06, + 06, 07, 07, 07, 08, 08, 09, 09, 00, 09, 09, 08, 08, 07, 07, 07, + 07, 07, 07, 08, 08, 08, 09, 10, 00, 10, 09, 09, 08, 08, 07, 07, + 07, 07, 08, 08, 08, 09, 10, 10, 00, 10, 10, 09, 08, 08, 08, 07, + 08, 08, 08, 09, 09, 09, 10, 10, 00, 10, 10, 09, 09, 09, 08, 08, + 08, 09, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 09, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 08, 09, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 09, + 08, 08, 08, 09, 09, 09, 10, 10, 00, 10, 10, 09, 09, 09, 08, 08, + 07, 07, 08, 08, 08, 09, 10, 10, 00, 10, 10, 09, 08, 08, 08, 07, + 07, 07, 07, 08, 08, 09, 09, 10, 00, 10, 09, 08, 08, 08, 07, 07, + 06, 07, 07, 07, 08, 08, 09, 09, 00, 09, 09, 08, 08, 07, 07, 07, + 05, 06, 07, 07, 07, 08, 08, 09, 00, 09, 08, 08, 07, 07, 07, 06 + }; + + private static readonly short[] SpectrumA43Codes = + { + 0x002, 0x03E, 0x016, 0x060, 0x04E, 0x0DC, 0x04A, 0x130, 0x000, 0x131, 0x04B, 0x0DD, + 0x04F, 0x061, 0x017, 0x03F, 0x002, 0x02C, 0x076, 0x042, 0x034, 0x0CE, 0x002, 0x0E8, + 0x000, 0x0CF, 0x001, 0x0D1, 0x037, 0x045, 0x07B, 0x02F, 0x014, 0x072, 0x052, 0x01A, + 0x0E0, 0x080, 0x198, 0x01E, 0x000, 0x01D, 0x19B, 0x083, 0x0DF, 0x019, 0x055, 0x079, + 0x050, 0x03C, 0x004, 0x0C4, 0x096, 0x00C, 0x0EA, 0x34A, 0x000, 0x34F, 0x0ED, 0x1D7, + 0x095, 0x0AF, 0x003, 0x03F, 0x046, 0x026, 0x0D6, 0x092, 0x046, 0x15A, 0x3A8, 0x108, + 0x000, 0x10F, 0x3A3, 0x135, 0x039, 0x091, 0x0D9, 0x031, 0x0D4, 0x0CA, 0x072, 0x1C6, + 0x136, 0x090, 0x2B2, 0x104, 0x000, 0x103, 0x111, 0x08B, 0x133, 0x1D3, 0x071, 0x0C9, + 0x03E, 0x1B4, 0x18C, 0x0CC, 0x38A, 0x2B0, 0x106, 0x0F2, 0x000, 0x0EF, 0x101, 0x113, + 0x3A1, 0x0CB, 0x18F, 0x1B7, 0x0EE, 0x092, 0x388, 0x348, 0x10A, 0x0F4, 0x0F0, 0x0EA, + 0x000, 0x0E9, 0x0ED, 0x0F7, 0x10D, 0x34D, 0x3AB, 0x0C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0EF, 0x0C8, 0x3AA, 0x34C, 0x10C, 0x0F6, 0x0EC, 0x0E8, 0x000, 0x0EB, 0x0F1, 0x0F5, + 0x10B, 0x349, 0x389, 0x093, 0x03F, 0x1B6, 0x18E, 0x0CA, 0x3A0, 0x112, 0x100, 0x0EE, + 0x000, 0x0F3, 0x107, 0x2B1, 0x38B, 0x0CD, 0x18D, 0x1B5, 0x0D5, 0x0C8, 0x070, 0x1D2, + 0x132, 0x08A, 0x110, 0x102, 0x000, 0x105, 0x2B3, 0x091, 0x137, 0x1C7, 0x073, 0x0CB, + 0x047, 0x030, 0x0D8, 0x090, 0x038, 0x134, 0x3A2, 0x10E, 0x000, 0x109, 0x3A9, 0x15B, + 0x047, 0x093, 0x0D7, 0x027, 0x051, 0x03E, 0x002, 0x0AE, 0x094, 0x1D6, 0x0EC, 0x34E, + 0x000, 0x34B, 0x0EB, 0x00D, 0x097, 0x0C5, 0x005, 0x03D, 0x015, 0x078, 0x054, 0x018, + 0x0DE, 0x082, 0x19A, 0x01C, 0x000, 0x01F, 0x199, 0x081, 0x0E1, 0x01B, 0x053, 0x073, + 0x003, 0x02E, 0x07A, 0x044, 0x036, 0x0D0, 0x000, 0x0CE, 0x000, 0x0E9, 0x003, 0x0CF, + 0x035, 0x043, 0x077, 0x02D + }; + + private static readonly byte[] SpectrumA44Bits = + { + 04, 05, 06, 07, 07, 08, 09, 10, 00, 10, 09, 08, 07, 07, 06, 05, + 05, 06, 06, 07, 07, 08, 09, 10, 00, 10, 09, 08, 07, 07, 06, 06, + 06, 06, 07, 07, 08, 09, 10, 10, 00, 10, 10, 09, 08, 07, 07, 06, + 07, 07, 07, 08, 08, 09, 10, 10, 00, 10, 10, 09, 08, 08, 07, 07, + 07, 08, 08, 08, 09, 10, 10, 10, 00, 10, 10, 10, 09, 08, 08, 07, + 08, 08, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 08, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 08, 08, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 08, + 07, 07, 08, 08, 09, 10, 10, 10, 00, 10, 10, 10, 09, 08, 08, 08, + 07, 07, 07, 08, 08, 09, 10, 10, 00, 10, 10, 09, 08, 08, 07, 07, + 06, 06, 07, 07, 08, 09, 10, 10, 00, 10, 10, 09, 08, 07, 07, 06, + 05, 06, 06, 07, 07, 08, 09, 10, 00, 10, 09, 08, 07, 07, 06, 06 + }; + + private static readonly short[] SpectrumA44Codes = + { + 0x00A, 0x012, 0x030, 0x06E, 0x024, 0x074, 0x0EC, 0x07E, 0x000, 0x07F, 0x0ED, 0x075, + 0x025, 0x06F, 0x031, 0x013, 0x010, 0x03C, 0x018, 0x05A, 0x002, 0x046, 0x09E, 0x07C, + 0x000, 0x079, 0x0E5, 0x04D, 0x007, 0x065, 0x01B, 0x03F, 0x02E, 0x016, 0x072, 0x01A, + 0x0D6, 0x1C6, 0x3B4, 0x066, 0x000, 0x06B, 0x3B7, 0x1D9, 0x0D5, 0x021, 0x075, 0x015, + 0x06C, 0x03E, 0x01E, 0x0CC, 0x044, 0x0F2, 0x082, 0x05C, 0x000, 0x05F, 0x087, 0x0F5, + 0x031, 0x0CF, 0x017, 0x059, 0x01C, 0x0EE, 0x0D0, 0x024, 0x1C0, 0x08E, 0x06E, 0x048, + 0x000, 0x04D, 0x06D, 0x089, 0x0F7, 0x033, 0x0D3, 0x001, 0x070, 0x028, 0x1C2, 0x0F0, + 0x08A, 0x074, 0x054, 0x040, 0x000, 0x043, 0x053, 0x073, 0x099, 0x0EF, 0x1C5, 0x02B, + 0x0E6, 0x04E, 0x08C, 0x080, 0x068, 0x058, 0x046, 0x02A, 0x000, 0x029, 0x045, 0x051, + 0x065, 0x085, 0x09B, 0x09D, 0x07A, 0x076, 0x060, 0x056, 0x04E, 0x02C, 0x024, 0x022, + 0x000, 0x021, 0x027, 0x02F, 0x04B, 0x05B, 0x063, 0x071, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x07B, 0x070, 0x062, 0x05A, 0x04A, 0x02E, 0x026, 0x020, 0x000, 0x023, 0x025, 0x02D, + 0x04F, 0x057, 0x061, 0x077, 0x0E7, 0x09C, 0x09A, 0x084, 0x064, 0x050, 0x044, 0x028, + 0x000, 0x02B, 0x047, 0x059, 0x069, 0x081, 0x08D, 0x04F, 0x071, 0x02A, 0x1C4, 0x0EE, + 0x098, 0x072, 0x052, 0x042, 0x000, 0x041, 0x055, 0x075, 0x08B, 0x0F1, 0x1C3, 0x029, + 0x01D, 0x000, 0x0D2, 0x032, 0x0F6, 0x088, 0x06C, 0x04C, 0x000, 0x049, 0x06F, 0x08F, + 0x1C1, 0x025, 0x0D1, 0x0EF, 0x06D, 0x058, 0x016, 0x0CE, 0x030, 0x0F4, 0x086, 0x05E, + 0x000, 0x05D, 0x083, 0x0F3, 0x045, 0x0CD, 0x01F, 0x03F, 0x02F, 0x014, 0x074, 0x020, + 0x0D4, 0x1D8, 0x3B6, 0x06A, 0x000, 0x067, 0x3B5, 0x1C7, 0x0D7, 0x01B, 0x073, 0x017, + 0x011, 0x03E, 0x01A, 0x064, 0x006, 0x04C, 0x0E4, 0x078, 0x000, 0x07D, 0x09F, 0x047, + 0x003, 0x05B, 0x019, 0x03D + }; + + private static readonly byte[] SpectrumA51Bits = + { + 5, 5, 5, 5, 5, 6, 6, 6, 4, 4, 5, 5, 5, 5, 5, 5, + 0, 5, 5, 5, 5, 5, 5, 4, 4, 6, 6, 6, 5, 5, 5, 5 + }; + + private static readonly short[] SpectrumA51Codes = + { + 0x19, 0x16, 0x12, 0x0E, 0x06, 0x3A, 0x38, 0x30, 0x00, 0x04, 0x1E, 0x1A, 0x14, 0x10, 0x0C, 0x04, + 0x00, 0x05, 0x0D, 0x11, 0x15, 0x1B, 0x1F, 0x05, 0x01, 0x31, 0x39, 0x3B, 0x07, 0x0F, 0x13, 0x17 + }; + + private static readonly byte[] SpectrumA52Bits = + { + 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 0, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4 + }; + + private static readonly short[] SpectrumA52Codes = + { + 0x09, 0x04, 0x00, 0x1E, 0x1A, 0x14, 0x0C, 0x06, 0x18, 0x16, 0x0E, 0x04, 0x3A, 0x38, 0x22, 0x20, + 0x00, 0x21, 0x23, 0x39, 0x3B, 0x05, 0x0F, 0x17, 0x19, 0x07, 0x0D, 0x15, 0x1B, 0x1F, 0x01, 0x05 + }; + + private static readonly byte[] SpectrumA53Bits = + { + 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4 + }; + + private static readonly short[] SpectrumA53Codes = + { + 0x00, 0x0C, 0x08, 0x04, 0x1E, 0x16, 0x14, 0x06, 0x0C, 0x04, 0x38, 0x1E, 0x76, 0x74, 0x3A, 0x38, + 0x00, 0x39, 0x3B, 0x75, 0x77, 0x1F, 0x39, 0x05, 0x0D, 0x07, 0x15, 0x17, 0x1F, 0x05, 0x09, 0x0D + }; + + private static readonly byte[] SpectrumA54Bits = + { + 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, + 0, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4 + }; + + private static readonly short[] SpectrumA54Codes = + { + 0x02, 0x0E, 0x0A, 0x08, 0x02, 0x1A, 0x0E, 0x02, 0x00, 0x30, 0x18, 0x66, 0x36, 0x34, 0xCA, 0xC8, + 0x00, 0xC9, 0xCB, 0x35, 0x37, 0x67, 0x19, 0x31, 0x01, 0x03, 0x0F, 0x1B, 0x03, 0x09, 0x0B, 0x0F + }; + + private static readonly byte[] SpectrumA61Bits = + { + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, + 5, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6 + }; + + private static readonly short[] SpectrumA61Codes = + { + 0x35, 0x30, 0x2A, 0x28, 0x24, 0x20, 0x18, 0x0E, 0x0C, 0x7E, 0x7C, 0x72, 0x70, 0x68, 0x5E, 0x5C, + 0x04, 0x0E, 0x08, 0x00, 0x3C, 0x3A, 0x36, 0x32, 0x2C, 0x26, 0x22, 0x1A, 0x16, 0x14, 0x06, 0x04, + 0x00, 0x05, 0x07, 0x15, 0x17, 0x1B, 0x23, 0x27, 0x2D, 0x33, 0x37, 0x3B, 0x3D, 0x01, 0x09, 0x0F, + 0x05, 0x5D, 0x5F, 0x69, 0x71, 0x73, 0x7D, 0x7F, 0x0D, 0x0F, 0x19, 0x21, 0x25, 0x29, 0x2B, 0x31 + }; + + private static readonly byte[] SpectrumA62Bits = + { + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 + }; + + private static readonly short[] SpectrumA62Codes = + { + 0x14, 0x0E, 0x08, 0x04, 0x02, 0x3E, 0x3C, 0x38, 0x34, 0x30, 0x2A, 0x24, 0x1A, 0x18, 0x0E, 0x02, + 0x32, 0x36, 0x2C, 0x26, 0x20, 0x16, 0x0C, 0x00, 0x76, 0x74, 0x5E, 0x5C, 0x46, 0x44, 0x2A, 0x28, + 0x00, 0x29, 0x2B, 0x45, 0x47, 0x5D, 0x5F, 0x75, 0x77, 0x01, 0x0D, 0x17, 0x21, 0x27, 0x2D, 0x37, + 0x33, 0x03, 0x0F, 0x19, 0x1B, 0x25, 0x2B, 0x31, 0x35, 0x39, 0x3D, 0x3F, 0x03, 0x05, 0x09, 0x0F + }; + + private static readonly byte[] SpectrumA63Bits = + { + 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5 + }; + + private static readonly short[] SpectrumA63Codes = + { + 0x00, 0x1C, 0x18, 0x14, 0x10, 0x0A, 0x08, 0x02, 0x3E, 0x36, 0x2E, 0x2C, 0x24, 0x1C, 0x0E, 0x08, + 0x1E, 0x1A, 0x0C, 0x7A, 0x6A, 0x68, 0x4C, 0x32, 0x16, 0x14, 0xF2, 0xF0, 0x9E, 0x9C, 0x62, 0x60, + 0x00, 0x61, 0x63, 0x9D, 0x9F, 0xF1, 0xF3, 0x15, 0x17, 0x33, 0x4D, 0x69, 0x6B, 0x7B, 0x0D, 0x1B, + 0x1F, 0x09, 0x0F, 0x1D, 0x25, 0x2D, 0x2F, 0x37, 0x3F, 0x03, 0x09, 0x0B, 0x11, 0x15, 0x19, 0x1D + }; + + private static readonly byte[] SpectrumA64Bits = + { + 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, + 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 6, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4 + }; + + private static readonly short[] SpectrumA64Codes = + { + 0x006, 0x002, 0x01C, 0x01A, 0x016, 0x012, 0x00E, 0x00A, 0x002, 0x03E, 0x032, 0x02A, + 0x022, 0x020, 0x010, 0x07A, 0x000, 0x078, 0x060, 0x050, 0x024, 0x006, 0x0C6, 0x0C4, + 0x0A4, 0x04E, 0x00A, 0x008, 0x14E, 0x14C, 0x09A, 0x098, 0x000, 0x099, 0x09B, 0x14D, + 0x14F, 0x009, 0x00B, 0x04F, 0x0A5, 0x0C5, 0x0C7, 0x007, 0x025, 0x051, 0x061, 0x079, + 0x001, 0x07B, 0x011, 0x021, 0x023, 0x02B, 0x033, 0x03F, 0x003, 0x00B, 0x00F, 0x013, + 0x017, 0x01B, 0x01D, 0x003 + }; + + private static readonly byte[] SpectrumA71Bits = + { + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, + 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + }; + + private static readonly short[] SpectrumA71Codes = + { + 0x6C, 0x66, 0x62, 0x5C, 0x56, 0x50, 0x52, 0x4E, 0x48, 0x3E, 0x36, 0x34, 0x2A, 0x26, 0x1E, 0x16, + 0x0E, 0x08, 0x00, 0xF6, 0xF4, 0xEE, 0xEC, 0xE2, 0xE0, 0xDA, 0xD2, 0xD0, 0xBE, 0xBC, 0xB2, 0xB0, + 0x0C, 0x20, 0x1C, 0x16, 0x10, 0x08, 0x02, 0x7E, 0x7C, 0x78, 0x74, 0x72, 0x6E, 0x6A, 0x64, 0x60, + 0x5A, 0x54, 0x4C, 0x4A, 0x46, 0x44, 0x3C, 0x32, 0x30, 0x28, 0x24, 0x1C, 0x14, 0x0C, 0x0A, 0x02, + 0x00, 0x03, 0x0B, 0x0D, 0x15, 0x1D, 0x25, 0x29, 0x31, 0x33, 0x3D, 0x45, 0x47, 0x4B, 0x4D, 0x55, + 0x5B, 0x61, 0x65, 0x6B, 0x6F, 0x73, 0x75, 0x79, 0x7D, 0x7F, 0x03, 0x09, 0x11, 0x17, 0x1D, 0x21, + 0x0D, 0xB1, 0xB3, 0xBD, 0xBF, 0xD1, 0xD3, 0xDB, 0xE1, 0xE3, 0xED, 0xEF, 0xF5, 0xF7, 0x01, 0x09, + 0x0F, 0x17, 0x1F, 0x27, 0x2B, 0x35, 0x37, 0x3F, 0x49, 0x4F, 0x53, 0x51, 0x57, 0x5D, 0x63, 0x67 + }; + + private static readonly byte[] SpectrumA72Bits = + { + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6 + }; + + private static readonly short[] SpectrumA72Codes = + { + 0x2A, 0x24, 0x1C, 0x18, 0x12, 0x0E, 0x0A, 0x06, 0x02, 0x7E, 0x7C, 0x7A, 0x76, 0x72, 0x70, 0x6A, + 0x68, 0x62, 0x5C, 0x5A, 0x52, 0x4E, 0x46, 0x42, 0x3C, 0x34, 0x2A, 0x28, 0x20, 0x12, 0x10, 0x08, + 0x66, 0x74, 0x6C, 0x64, 0x5E, 0x58, 0x50, 0x44, 0x40, 0x36, 0x2C, 0x22, 0x1A, 0x0A, 0x02, 0x00, + 0xF2, 0xF0, 0xDE, 0xDC, 0xC2, 0xC0, 0xAE, 0xAC, 0x9A, 0x98, 0x7E, 0x7C, 0x5E, 0x5C, 0x32, 0x30, + 0x00, 0x31, 0x33, 0x5D, 0x5F, 0x7D, 0x7F, 0x99, 0x9B, 0xAD, 0xAF, 0xC1, 0xC3, 0xDD, 0xDF, 0xF1, + 0xF3, 0x01, 0x03, 0x0B, 0x1B, 0x23, 0x2D, 0x37, 0x41, 0x45, 0x51, 0x59, 0x5F, 0x65, 0x6D, 0x75, + 0x67, 0x09, 0x11, 0x13, 0x21, 0x29, 0x2B, 0x35, 0x3D, 0x43, 0x47, 0x4F, 0x53, 0x5B, 0x5D, 0x63, + 0x69, 0x6B, 0x71, 0x73, 0x77, 0x7B, 0x7D, 0x7F, 0x03, 0x07, 0x0B, 0x0F, 0x13, 0x19, 0x1D, 0x25 + }; + + private static readonly byte[] SpectrumA73Bits = + { + 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 + }; + + private static readonly short[] SpectrumA73Codes = + { + 0x003, 0x03E, 0x038, 0x034, 0x030, 0x02C, 0x028, 0x024, 0x020, 0x01C, 0x016, 0x014, + 0x00E, 0x00A, 0x004, 0x000, 0x07A, 0x076, 0x06E, 0x06C, 0x064, 0x05E, 0x056, 0x04E, + 0x04C, 0x044, 0x036, 0x030, 0x022, 0x018, 0x012, 0x004, 0x03C, 0x03E, 0x032, 0x024, + 0x020, 0x010, 0x0F2, 0x0F0, 0x0E8, 0x0CE, 0x0BA, 0x0B8, 0x0A8, 0x08C, 0x06A, 0x04E, + 0x04C, 0x034, 0x00E, 0x00C, 0x1D6, 0x1D4, 0x19A, 0x198, 0x156, 0x154, 0x11E, 0x11C, + 0x0D2, 0x0D0, 0x06E, 0x06C, 0x000, 0x06D, 0x06F, 0x0D1, 0x0D3, 0x11D, 0x11F, 0x155, + 0x157, 0x199, 0x19B, 0x1D5, 0x1D7, 0x00D, 0x00F, 0x035, 0x04D, 0x04F, 0x06B, 0x08D, + 0x0A9, 0x0B9, 0x0BB, 0x0CF, 0x0E9, 0x0F1, 0x0F3, 0x011, 0x021, 0x025, 0x033, 0x03F, + 0x03D, 0x005, 0x013, 0x019, 0x023, 0x031, 0x037, 0x045, 0x04D, 0x04F, 0x057, 0x05F, + 0x065, 0x06D, 0x06F, 0x077, 0x07B, 0x001, 0x005, 0x00B, 0x00F, 0x015, 0x017, 0x01D, + 0x021, 0x025, 0x029, 0x02D, 0x031, 0x035, 0x039, 0x03F + }; + + private static readonly byte[] SpectrumA74Bits = + { + 05, 05, 05, 05, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, + 06, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 08, 08, + 07, 08, 08, 08, 08, 08, 08, 08, 08, 08, 08, 09, 09, 09, 09, 09, + 09, 09, 09, 09, 09, 09, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 00, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 09, 09, 09, 09, 09, + 09, 09, 09, 09, 09, 09, 08, 08, 08, 08, 08, 08, 08, 08, 08, 08, + 07, 08, 08, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, + 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 05, 05, 05 + }; + + private static readonly short[] SpectrumA74Codes = + { + 0x00D, 0x00A, 0x004, 0x000, 0x03A, 0x036, 0x032, 0x030, 0x02C, 0x028, 0x026, 0x022, + 0x01E, 0x018, 0x012, 0x00E, 0x006, 0x07E, 0x07A, 0x070, 0x06A, 0x05E, 0x056, 0x054, + 0x048, 0x040, 0x038, 0x022, 0x01A, 0x00A, 0x0F8, 0x0E6, 0x008, 0x0FA, 0x0F0, 0x0D2, + 0x0BA, 0x0B8, 0x094, 0x084, 0x074, 0x042, 0x032, 0x1E6, 0x1CA, 0x1C8, 0x1A2, 0x12E, + 0x10E, 0x10C, 0x0EC, 0x082, 0x062, 0x060, 0x3CA, 0x3C8, 0x342, 0x340, 0x25A, 0x258, + 0x1DE, 0x1DC, 0x102, 0x100, 0x000, 0x101, 0x103, 0x1DD, 0x1DF, 0x259, 0x25B, 0x341, + 0x343, 0x3C9, 0x3CB, 0x061, 0x063, 0x083, 0x0ED, 0x10D, 0x10F, 0x12F, 0x1A3, 0x1C9, + 0x1CB, 0x1E7, 0x033, 0x043, 0x075, 0x085, 0x095, 0x0B9, 0x0BB, 0x0D3, 0x0F1, 0x0FB, + 0x009, 0x0E7, 0x0F9, 0x00B, 0x01B, 0x023, 0x039, 0x041, 0x049, 0x055, 0x057, 0x05F, + 0x06B, 0x071, 0x07B, 0x07F, 0x007, 0x00F, 0x013, 0x019, 0x01F, 0x023, 0x027, 0x029, + 0x02D, 0x031, 0x033, 0x037, 0x03B, 0x001, 0x005, 0x00B + }; + + private static readonly byte[] SpectrumB22Bits = + { + 00, 04, 00, 04, 04, 05, 00, 05, 00, 00, 00, 00, 04, 05, 00, 05, + 04, 07, 00, 06, 06, 09, 00, 07, 00, 00, 00, 00, 06, 09, 00, 07, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 04, 06, 00, 07, 06, 07, 00, 09, 00, 00, 00, 00, 06, 07, 00, 09, + 04, 08, 00, 08, 08, 10, 00, 10, 00, 00, 00, 00, 06, 09, 00, 09, + 05, 10, 00, 09, 09, 10, 00, 10, 00, 00, 00, 00, 07, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 06, 09, 00, 10, 09, 10, 00, 10, 00, 00, 00, 00, 07, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 04, 08, 00, 08, 06, 09, 00, 09, 00, 00, 00, 00, 08, 10, 00, 10, + 06, 10, 00, 09, 07, 10, 00, 10, 00, 00, 00, 00, 09, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 05, 09, 00, 10, 07, 10, 00, 10, 00, 00, 00, 00, 09, 10, 00, 10 + }; + + private static readonly short[] SpectrumB22Codes = + { + 0x000, 0x00E, 0x000, 0x00F, 0x008, 0x006, 0x000, 0x00B, 0x000, 0x000, 0x000, 0x000, + 0x009, 0x00A, 0x000, 0x007, 0x006, 0x00A, 0x000, 0x029, 0x006, 0x158, 0x000, 0x023, + 0x000, 0x000, 0x000, 0x000, 0x013, 0x174, 0x000, 0x021, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x007, 0x028, 0x000, 0x00B, 0x012, 0x020, 0x000, 0x175, 0x000, 0x000, 0x000, 0x000, + 0x007, 0x022, 0x000, 0x159, 0x00C, 0x0BC, 0x000, 0x0BF, 0x022, 0x2B8, 0x000, 0x2BB, + 0x000, 0x000, 0x000, 0x000, 0x00B, 0x170, 0x000, 0x15B, 0x000, 0x04E, 0x000, 0x15F, + 0x042, 0x04A, 0x000, 0x041, 0x000, 0x000, 0x000, 0x000, 0x055, 0x044, 0x000, 0x04D, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x02D, 0x172, 0x000, 0x2ED, 0x040, 0x042, 0x000, 0x047, + 0x000, 0x000, 0x000, 0x000, 0x013, 0x2EE, 0x000, 0x049, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00D, 0x0BE, 0x000, 0x0BD, 0x00A, 0x15A, 0x000, 0x171, 0x000, 0x000, 0x000, 0x000, + 0x023, 0x2BA, 0x000, 0x2B9, 0x02C, 0x2EC, 0x000, 0x173, 0x012, 0x048, 0x000, 0x2EF, + 0x000, 0x000, 0x000, 0x000, 0x041, 0x046, 0x000, 0x043, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x15E, 0x000, 0x04F, 0x054, 0x04C, 0x000, 0x045, 0x000, 0x000, 0x000, 0x000, + 0x043, 0x040, 0x000, 0x04B + }; + + private static readonly byte[] SpectrumB23Bits = + { + 02, 04, 00, 04, 04, 06, 00, 06, 00, 00, 00, 00, 04, 06, 00, 06, + 04, 09, 00, 07, 07, 09, 00, 08, 00, 00, 00, 00, 07, 09, 00, 08, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 04, 07, 00, 09, 07, 08, 00, 09, 00, 00, 00, 00, 07, 08, 00, 09, + 04, 08, 00, 08, 09, 10, 00, 10, 00, 00, 00, 00, 07, 10, 00, 10, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 09, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 08, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 04, 08, 00, 08, 07, 10, 00, 10, 00, 00, 00, 00, 09, 10, 00, 10, + 07, 10, 00, 10, 08, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 07, 10, 00, 10, 09, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10 + }; + + private static readonly short[] SpectrumB23Codes = + { + 0x003, 0x008, 0x000, 0x009, 0x002, 0x018, 0x000, 0x01B, 0x000, 0x000, 0x000, 0x000, + 0x003, 0x01A, 0x000, 0x019, 0x000, 0x17C, 0x000, 0x055, 0x056, 0x0E8, 0x000, 0x07D, + 0x000, 0x000, 0x000, 0x000, 0x059, 0x0F6, 0x000, 0x07F, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x054, 0x000, 0x17D, 0x058, 0x07E, 0x000, 0x0F7, 0x000, 0x000, 0x000, 0x000, + 0x057, 0x07C, 0x000, 0x0E9, 0x004, 0x0A2, 0x000, 0x0A1, 0x17A, 0x1DA, 0x000, 0x1D9, + 0x000, 0x000, 0x000, 0x000, 0x053, 0x1E8, 0x000, 0x2F3, 0x05C, 0x1D6, 0x000, 0x1E7, + 0x1EA, 0x1E2, 0x000, 0x1CF, 0x000, 0x000, 0x000, 0x000, 0x17F, 0x1CA, 0x000, 0x1DD, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x05B, 0x2F0, 0x000, 0x1DF, 0x1E4, 0x1CC, 0x000, 0x1D5, + 0x000, 0x000, 0x000, 0x000, 0x071, 0x1E0, 0x000, 0x1C9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x005, 0x0A0, 0x000, 0x0A3, 0x052, 0x2F2, 0x000, 0x1E9, 0x000, 0x000, 0x000, 0x000, + 0x17B, 0x1D8, 0x000, 0x1DB, 0x05A, 0x1DE, 0x000, 0x2F1, 0x070, 0x1C8, 0x000, 0x1E1, + 0x000, 0x000, 0x000, 0x000, 0x1E5, 0x1D4, 0x000, 0x1CD, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x05D, 0x1E6, 0x000, 0x1D7, 0x17E, 0x1DC, 0x000, 0x1CB, 0x000, 0x000, 0x000, 0x000, + 0x1EB, 0x1CE, 0x000, 0x1E3 + }; + + private static readonly byte[] SpectrumB24Bits = + { + 01, 04, 00, 04, 05, 07, 00, 07, 00, 00, 00, 00, 05, 07, 00, 07, + 05, 09, 00, 07, 08, 10, 00, 09, 00, 00, 00, 00, 07, 10, 00, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 05, 07, 00, 09, 07, 09, 00, 10, 00, 00, 00, 00, 08, 09, 00, 10, + 05, 09, 00, 08, 09, 10, 00, 10, 00, 00, 00, 00, 07, 10, 00, 10, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 05, 08, 00, 09, 07, 10, 00, 10, 00, 00, 00, 00, 09, 10, 00, 10, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 07, 10, 00, 10, 10, 10, 00, 10, 00, 00, 00, 00, 10, 10, 00, 10 + }; + + private static readonly short[] SpectrumB24Codes = + { + 0x001, 0x000, 0x000, 0x001, 0x00A, 0x01C, 0x000, 0x033, 0x000, 0x000, 0x000, 0x000, + 0x00B, 0x032, 0x000, 0x01D, 0x008, 0x0D8, 0x000, 0x031, 0x06E, 0x0FA, 0x000, 0x0D7, + 0x000, 0x000, 0x000, 0x000, 0x011, 0x0F4, 0x000, 0x0D5, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x009, 0x030, 0x000, 0x0D9, 0x010, 0x0D4, 0x000, 0x0F5, 0x000, 0x000, 0x000, 0x000, + 0x06F, 0x0D6, 0x000, 0x0FB, 0x00E, 0x0DA, 0x000, 0x025, 0x0D2, 0x0D4, 0x000, 0x0DB, + 0x000, 0x000, 0x000, 0x000, 0x017, 0x0FE, 0x000, 0x0FD, 0x014, 0x0DC, 0x000, 0x0F9, + 0x0F2, 0x0D6, 0x000, 0x09B, 0x000, 0x000, 0x000, 0x000, 0x1A3, 0x09C, 0x000, 0x0D3, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x019, 0x0F6, 0x000, 0x0D9, 0x0F0, 0x09E, 0x000, 0x0D1, + 0x000, 0x000, 0x000, 0x000, 0x1A1, 0x0DE, 0x000, 0x099, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00F, 0x024, 0x000, 0x0DB, 0x016, 0x0FC, 0x000, 0x0FF, 0x000, 0x000, 0x000, 0x000, + 0x0D3, 0x0DA, 0x000, 0x0D5, 0x018, 0x0D8, 0x000, 0x0F7, 0x1A0, 0x098, 0x000, 0x0DF, + 0x000, 0x000, 0x000, 0x000, 0x0F1, 0x0D0, 0x000, 0x09F, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x015, 0x0F8, 0x000, 0x0DD, 0x1A2, 0x0D2, 0x000, 0x09D, 0x000, 0x000, 0x000, 0x000, + 0x0F3, 0x09A, 0x000, 0x0D7 + }; + + private static readonly byte[] SpectrumB32Bits = + { + 2, 4, 5, 6, 0, 6, 5, 4, 5, 6, 6, 7, 0, 6, 5, 6, + 5, 6, 7, 7, 0, 8, 7, 6, 6, 7, 8, 9, 0, 9, 8, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 0, 9, 8, 7, + 5, 6, 7, 8, 0, 7, 7, 6, 5, 6, 5, 6, 0, 7, 6, 6 + }; + + private static readonly short[] SpectrumB32Codes = + { + 0x001, 0x002, 0x01E, 0x02A, 0x000, 0x02B, 0x01F, 0x003, 0x016, 0x020, 0x03A, 0x064, + 0x000, 0x005, 0x001, 0x023, 0x01A, 0x026, 0x070, 0x00C, 0x000, 0x0CF, 0x073, 0x031, + 0x024, 0x00E, 0x0CC, 0x146, 0x000, 0x145, 0x0A1, 0x053, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x025, 0x052, 0x0A0, 0x144, 0x000, 0x147, 0x0CD, 0x00F, + 0x01B, 0x030, 0x072, 0x0CE, 0x000, 0x00D, 0x071, 0x027, 0x017, 0x022, 0x000, 0x004, + 0x000, 0x065, 0x03B, 0x021 + }; + + private static readonly byte[] SpectrumB33Bits = + { + 02, 04, 05, 07, 00, 07, 05, 04, 04, 05, 06, 08, 00, 07, 06, 05, + 05, 06, 07, 09, 00, 08, 07, 06, 07, 08, 09, 10, 00, 10, 09, 08, + 00, 00, 00, 00, 00, 00, 00, 00, 07, 08, 09, 10, 00, 10, 09, 08, + 05, 06, 07, 08, 00, 09, 07, 06, 04, 05, 06, 07, 00, 08, 06, 05 + }; + + private static readonly short[] SpectrumB33Codes = + { + 0x003, 0x008, 0x014, 0x05E, 0x000, 0x05F, 0x015, 0x009, 0x004, 0x002, 0x01C, 0x0BA, + 0x000, 0x011, 0x01F, 0x001, 0x00C, 0x00C, 0x014, 0x166, 0x000, 0x02D, 0x013, 0x00F, + 0x05A, 0x0B0, 0x05E, 0x0B8, 0x000, 0x0BB, 0x165, 0x0B9, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x05B, 0x0B8, 0x164, 0x0BA, 0x000, 0x0B9, 0x05F, 0x0B1, + 0x00D, 0x00E, 0x012, 0x02C, 0x000, 0x167, 0x015, 0x00D, 0x005, 0x000, 0x01E, 0x010, + 0x000, 0x0BB, 0x01D, 0x003 + }; + + private static readonly byte[] SpectrumB34Bits = + { + 01, 04, 06, 08, 00, 08, 06, 04, 04, 06, 07, 09, 00, 08, 07, 06, + 06, 07, 08, 10, 00, 10, 08, 07, 08, 09, 10, 10, 00, 10, 10, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 08, 09, 10, 10, 00, 10, 10, 09, + 06, 07, 08, 10, 00, 10, 08, 07, 04, 06, 07, 08, 00, 09, 07, 06 + }; + + private static readonly short[] SpectrumB34Codes = + { + 0x000, 0x00A, 0x038, 0x0EE, 0x000, 0x0EF, 0x039, 0x00B, 0x008, 0x03C, 0x06E, 0x1D8, + 0x000, 0x0C1, 0x075, 0x03F, 0x032, 0x068, 0x0C4, 0x358, 0x000, 0x30F, 0x0C7, 0x06D, + 0x0D4, 0x1AE, 0x30C, 0x308, 0x000, 0x30B, 0x35B, 0x1DB, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x0D5, 0x1DA, 0x35A, 0x30A, 0x000, 0x309, 0x30D, 0x1AF, + 0x033, 0x06C, 0x0C6, 0x30E, 0x000, 0x359, 0x0C5, 0x069, 0x009, 0x03E, 0x074, 0x0C0, + 0x000, 0x1D9, 0x06F, 0x03D + }; + + private static readonly byte[] SpectrumB42Bits = + { + 04, 05, 06, 08, 06, 07, 08, 08, 00, 08, 08, 07, 06, 08, 06, 05, + 05, 06, 07, 08, 07, 07, 08, 09, 00, 08, 08, 07, 07, 08, 07, 06, + 07, 07, 08, 09, 07, 08, 09, 09, 00, 09, 09, 08, 07, 09, 08, 07, + 08, 09, 09, 10, 08, 08, 09, 10, 00, 10, 09, 08, 08, 10, 09, 08, + 06, 07, 08, 08, 09, 09, 10, 10, 00, 10, 10, 09, 09, 08, 08, 07, + 07, 07, 08, 09, 09, 10, 10, 10, 00, 10, 10, 10, 09, 09, 08, 07, + 08, 08, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 08, + 08, 09, 09, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 09, 09, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 08, 09, 09, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 09, 09, + 08, 08, 09, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 09, 08, + 07, 07, 08, 09, 09, 10, 10, 10, 00, 10, 10, 10, 09, 09, 08, 07, + 06, 07, 08, 08, 09, 09, 10, 10, 00, 10, 10, 09, 09, 08, 08, 07, + 08, 08, 09, 10, 08, 08, 09, 10, 00, 10, 09, 08, 08, 10, 09, 09, + 07, 07, 08, 09, 07, 08, 09, 09, 00, 09, 09, 08, 07, 09, 08, 07, + 05, 06, 07, 08, 07, 07, 08, 08, 00, 09, 08, 07, 07, 08, 07, 06 + }; + + private static readonly short[] SpectrumB42Codes = + { + 0x00E, 0x018, 0x010, 0x0F0, 0x024, 0x05A, 0x0F6, 0x078, 0x000, 0x079, 0x0F7, 0x05B, + 0x025, 0x0F1, 0x011, 0x019, 0x00C, 0x014, 0x01C, 0x036, 0x05C, 0x012, 0x09E, 0x1E4, + 0x000, 0x00B, 0x0A9, 0x03B, 0x05F, 0x071, 0x019, 0x017, 0x06E, 0x000, 0x03E, 0x114, + 0x002, 0x0B0, 0x1AA, 0x07A, 0x000, 0x099, 0x1E7, 0x0B3, 0x00B, 0x131, 0x07F, 0x00D, + 0x0D8, 0x1FE, 0x112, 0x22E, 0x086, 0x010, 0x134, 0x35C, 0x000, 0x35F, 0x133, 0x013, + 0x081, 0x22D, 0x119, 0x07B, 0x00A, 0x050, 0x0F8, 0x04E, 0x1B4, 0x154, 0x3EC, 0x0D2, + 0x000, 0x0D7, 0x3D7, 0x137, 0x1FD, 0x073, 0x0FD, 0x057, 0x052, 0x010, 0x08E, 0x1E8, + 0x11A, 0x3EE, 0x0F2, 0x03C, 0x000, 0x03F, 0x0F1, 0x3D5, 0x111, 0x1F5, 0x09D, 0x025, + 0x0D2, 0x082, 0x1A0, 0x0F8, 0x36E, 0x0D4, 0x072, 0x03A, 0x000, 0x027, 0x071, 0x07D, + 0x36D, 0x0FB, 0x1AD, 0x085, 0x00C, 0x1A8, 0x03C, 0x346, 0x0D0, 0x076, 0x024, 0x020, + 0x000, 0x023, 0x039, 0x075, 0x07F, 0x345, 0x09B, 0x157, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x00D, 0x156, 0x09A, 0x344, 0x07E, 0x074, 0x038, 0x022, 0x000, 0x021, 0x025, 0x077, + 0x0D1, 0x347, 0x03D, 0x1A9, 0x0D3, 0x084, 0x1AC, 0x0FA, 0x36C, 0x07C, 0x070, 0x026, + 0x000, 0x03B, 0x073, 0x0D5, 0x36F, 0x0F9, 0x1A1, 0x083, 0x053, 0x024, 0x09C, 0x1F4, + 0x110, 0x3D4, 0x0F0, 0x03E, 0x000, 0x03D, 0x0F3, 0x3EF, 0x11B, 0x1E9, 0x08F, 0x011, + 0x00B, 0x056, 0x0FC, 0x072, 0x1FC, 0x136, 0x3D6, 0x0D6, 0x000, 0x0D3, 0x3ED, 0x155, + 0x1B5, 0x04F, 0x0F9, 0x051, 0x0D9, 0x07A, 0x118, 0x22C, 0x080, 0x012, 0x132, 0x35E, + 0x000, 0x35D, 0x135, 0x011, 0x087, 0x22F, 0x113, 0x1FF, 0x06F, 0x00C, 0x07E, 0x130, + 0x00A, 0x0B2, 0x1E6, 0x098, 0x000, 0x07B, 0x1AB, 0x0B1, 0x003, 0x115, 0x03F, 0x001, + 0x00D, 0x016, 0x018, 0x070, 0x05E, 0x03A, 0x0A8, 0x00A, 0x000, 0x1E5, 0x09F, 0x013, + 0x05D, 0x037, 0x01D, 0x015 + }; + + private static readonly byte[] SpectrumB43Bits = + { + 02, 05, 06, 07, 07, 08, 08, 09, 00, 09, 08, 08, 07, 07, 06, 05, + 05, 06, 07, 08, 07, 08, 09, 10, 00, 10, 09, 08, 07, 08, 07, 06, + 06, 07, 08, 09, 08, 09, 10, 10, 00, 10, 10, 09, 08, 09, 08, 07, + 07, 08, 09, 10, 09, 09, 10, 10, 00, 10, 10, 10, 09, 10, 09, 08, + 07, 08, 08, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 08, 07, + 08, 08, 09, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 09, 08, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 09, 09, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 09, + 08, 08, 09, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 09, 08, + 07, 07, 08, 09, 10, 10, 10, 10, 00, 10, 10, 10, 10, 09, 08, 08, + 07, 08, 09, 10, 09, 10, 10, 10, 00, 10, 10, 09, 09, 10, 09, 08, + 06, 07, 08, 09, 08, 09, 10, 10, 00, 10, 10, 09, 08, 09, 08, 07, + 05, 06, 07, 08, 07, 08, 09, 10, 00, 10, 09, 08, 07, 08, 07, 06 + }; + + private static readonly short[] SpectrumB43Codes = + { + 0x001, 0x01E, 0x022, 0x018, 0x064, 0x0EC, 0x008, 0x100, 0x000, 0x101, 0x009, 0x0ED, + 0x065, 0x019, 0x023, 0x01F, 0x01A, 0x030, 0x056, 0x09A, 0x00A, 0x090, 0x12C, 0x0A6, + 0x000, 0x0A9, 0x12F, 0x093, 0x00F, 0x09F, 0x059, 0x039, 0x00E, 0x054, 0x0BC, 0x19E, + 0x082, 0x176, 0x0AC, 0x088, 0x000, 0x08B, 0x0AF, 0x19D, 0x095, 0x1D1, 0x0BF, 0x051, + 0x002, 0x098, 0x1D4, 0x0B8, 0x170, 0x046, 0x090, 0x060, 0x000, 0x067, 0x095, 0x0BD, + 0x173, 0x0B5, 0x1D3, 0x09D, 0x052, 0x0EE, 0x034, 0x174, 0x0BA, 0x09C, 0x080, 0x044, + 0x000, 0x047, 0x06D, 0x099, 0x0BF, 0x16F, 0x085, 0x001, 0x0CC, 0x036, 0x16C, 0x0B0, + 0x09A, 0x084, 0x04E, 0x03E, 0x000, 0x037, 0x04B, 0x06B, 0x0A1, 0x0B3, 0x16B, 0x087, + 0x1D6, 0x102, 0x0A4, 0x092, 0x068, 0x04C, 0x034, 0x030, 0x000, 0x02D, 0x03D, 0x049, + 0x083, 0x097, 0x0AB, 0x169, 0x0B6, 0x09E, 0x06E, 0x064, 0x040, 0x038, 0x02E, 0x02A, + 0x000, 0x029, 0x033, 0x03B, 0x043, 0x063, 0x087, 0x0A3, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x0B7, 0x0A2, 0x086, 0x062, 0x042, 0x03A, 0x032, 0x028, 0x000, 0x02B, 0x02F, 0x039, + 0x041, 0x065, 0x06F, 0x09F, 0x1D7, 0x168, 0x0AA, 0x096, 0x082, 0x048, 0x03C, 0x02C, + 0x000, 0x031, 0x035, 0x04D, 0x069, 0x093, 0x0A5, 0x103, 0x0CD, 0x086, 0x16A, 0x0B2, + 0x0A0, 0x06A, 0x04A, 0x036, 0x000, 0x03F, 0x04F, 0x085, 0x09B, 0x0B1, 0x16D, 0x037, + 0x053, 0x000, 0x084, 0x16E, 0x0BE, 0x098, 0x06C, 0x046, 0x000, 0x045, 0x081, 0x09D, + 0x0BB, 0x175, 0x035, 0x0EF, 0x003, 0x09C, 0x1D2, 0x0B4, 0x172, 0x0BC, 0x094, 0x066, + 0x000, 0x061, 0x091, 0x047, 0x171, 0x0B9, 0x1D5, 0x099, 0x00F, 0x050, 0x0BE, 0x1D0, + 0x094, 0x19C, 0x0AE, 0x08A, 0x000, 0x089, 0x0AD, 0x177, 0x083, 0x19F, 0x0BD, 0x055, + 0x01B, 0x038, 0x058, 0x09E, 0x00E, 0x092, 0x12E, 0x0A8, 0x000, 0x0A7, 0x12D, 0x091, + 0x00B, 0x09B, 0x057, 0x031 + }; + + private static readonly byte[] SpectrumB44Bits = + { + 02, 04, 06, 07, 07, 08, 10, 10, 00, 10, 10, 08, 07, 07, 06, 04, + 05, 05, 07, 08, 08, 10, 10, 10, 00, 10, 10, 10, 08, 08, 07, 05, + 06, 07, 08, 09, 09, 10, 10, 10, 00, 10, 10, 10, 10, 09, 08, 07, + 08, 08, 09, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 08, + 08, 08, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 08, + 09, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 09, 10, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 10, + 08, 08, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 10, 08, + 08, 08, 10, 10, 10, 10, 10, 10, 00, 10, 10, 10, 10, 10, 09, 08, + 06, 07, 08, 09, 10, 10, 10, 10, 00, 10, 10, 10, 09, 09, 08, 07, + 05, 05, 07, 08, 08, 10, 10, 10, 00, 10, 10, 10, 08, 08, 07, 05 + }; + + private static readonly short[] SpectrumB44Codes = + { + 0x002, 0x002, 0x030, 0x000, 0x002, 0x00C, 0x1D2, 0x1AE, 0x000, 0x1AF, 0x1D3, 0x00D, + 0x003, 0x001, 0x031, 0x003, 0x01E, 0x002, 0x070, 0x0C8, 0x07E, 0x1E8, 0x1C0, 0x176, + 0x000, 0x17F, 0x1C3, 0x1EB, 0x0CF, 0x0D3, 0x073, 0x009, 0x018, 0x06A, 0x0EC, 0x1DE, + 0x1A2, 0x1CA, 0x1AA, 0x164, 0x000, 0x16D, 0x1AD, 0x1D1, 0x1EF, 0x1DD, 0x0EB, 0x06D, + 0x0E8, 0x0CA, 0x1BE, 0x1CE, 0x1DA, 0x1B6, 0x170, 0x154, 0x000, 0x153, 0x173, 0x1B1, + 0x1D7, 0x1D5, 0x343, 0x0CD, 0x0DC, 0x078, 0x340, 0x1CC, 0x1BA, 0x1A8, 0x156, 0x148, + 0x000, 0x145, 0x15F, 0x1A1, 0x1BD, 0x1D9, 0x1ED, 0x07D, 0x1BC, 0x1DC, 0x1C4, 0x1B2, + 0x17C, 0x15A, 0x14A, 0x03A, 0x000, 0x039, 0x147, 0x16B, 0x17B, 0x1B5, 0x1C9, 0x1DF, + 0x1C6, 0x1B8, 0x1A2, 0x168, 0x160, 0x14C, 0x02E, 0x024, 0x000, 0x027, 0x03D, 0x151, + 0x15D, 0x16F, 0x1A7, 0x1BF, 0x1A4, 0x174, 0x162, 0x14E, 0x140, 0x02C, 0x02A, 0x022, + 0x000, 0x021, 0x029, 0x03F, 0x143, 0x159, 0x167, 0x179, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x1A5, 0x178, 0x166, 0x158, 0x142, 0x03E, 0x028, 0x020, 0x000, 0x023, 0x02B, 0x02D, + 0x141, 0x14F, 0x163, 0x175, 0x1C7, 0x1BE, 0x1A6, 0x16E, 0x15C, 0x150, 0x03C, 0x026, + 0x000, 0x025, 0x02F, 0x14D, 0x161, 0x169, 0x1A3, 0x1B9, 0x1BD, 0x1DE, 0x1C8, 0x1B4, + 0x17A, 0x16A, 0x146, 0x038, 0x000, 0x03B, 0x14B, 0x15B, 0x17D, 0x1B3, 0x1C5, 0x1DD, + 0x0DD, 0x07C, 0x1EC, 0x1D8, 0x1BC, 0x1A0, 0x15E, 0x144, 0x000, 0x149, 0x157, 0x1A9, + 0x1BB, 0x1CD, 0x341, 0x079, 0x0E9, 0x0CC, 0x342, 0x1D4, 0x1D6, 0x1B0, 0x172, 0x152, + 0x000, 0x155, 0x171, 0x1B7, 0x1DB, 0x1CF, 0x1BF, 0x0CB, 0x019, 0x06C, 0x0EA, 0x1DC, + 0x1EE, 0x1D0, 0x1AC, 0x16C, 0x000, 0x165, 0x1AB, 0x1CB, 0x1A3, 0x1DF, 0x0ED, 0x06B, + 0x01F, 0x008, 0x072, 0x0D2, 0x0CE, 0x1EA, 0x1C2, 0x17E, 0x000, 0x177, 0x1C1, 0x1E9, + 0x07F, 0x0C9, 0x071, 0x003 + }; + + private static readonly byte[] SpectrumB52Bits = + { + 3, 4, 4, 4, 5, 5, 6, 6, 5, 5, 5, 6, 6, 6, 7, 7, + 0, 7, 7, 6, 6, 6, 5, 5, 5, 6, 6, 5, 5, 4, 4, 4 + }; + + private static readonly short[] SpectrumB52Codes = + { + 0x06, 0x0E, 0x06, 0x00, 0x0A, 0x04, 0x2C, 0x12, 0x14, 0x10, 0x06, 0x2E, 0x24, 0x10, 0x4E, 0x4C, + 0x00, 0x4D, 0x4F, 0x11, 0x25, 0x2F, 0x07, 0x11, 0x15, 0x13, 0x2D, 0x05, 0x0B, 0x01, 0x07, 0x0F + }; + + private static readonly byte[] SpectrumB53Bits = + { + 2, 3, 4, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, + 0, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 4, 3 + }; + + private static readonly short[] SpectrumB53Codes = + { + 0x02, 0x00, 0x06, 0x1C, 0x18, 0x3E, 0x16, 0x10, 0x3C, 0x36, 0x14, 0x6A, 0x26, 0x24, 0xD2, 0xD0, + 0x00, 0xD1, 0xD3, 0x25, 0x27, 0x6B, 0x15, 0x37, 0x3D, 0x11, 0x17, 0x3F, 0x19, 0x1D, 0x07, 0x01 + }; + + private static readonly byte[] SpectrumB54Bits = + { + 2, 3, 4, 4, 5, 6, 6, 7, 6, 6, 7, 8, 8, 8, 9, 9, + 0, 9, 9, 8, 8, 8, 7, 6, 6, 7, 6, 6, 5, 4, 4, 3 + }; + + private static readonly short[] SpectrumB54Codes = + { + 0x003, 0x002, 0x008, 0x000, 0x014, 0x02E, 0x00E, 0x05A, 0x00A, 0x008, 0x01A, 0x0B2, + 0x032, 0x030, 0x162, 0x160, 0x000, 0x161, 0x163, 0x031, 0x033, 0x0B3, 0x01B, 0x009, + 0x00B, 0x05B, 0x00F, 0x02F, 0x015, 0x001, 0x009, 0x003 + }; + + private static readonly byte[] SpectrumB62Bits = + { + 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 4 + }; + + private static readonly short[] SpectrumB62Codes = + { + 0x0D, 0x06, 0x1C, 0x14, 0x0A, 0x04, 0x3E, 0x2E, 0x22, 0x0E, 0x06, 0x00, 0x5A, 0x4E, 0x40, 0x20, + 0x30, 0x32, 0x24, 0x12, 0x0C, 0x02, 0x78, 0x58, 0x42, 0x22, 0x0A, 0x08, 0xF6, 0xF4, 0x9A, 0x98, + 0x00, 0x99, 0x9B, 0xF5, 0xF7, 0x09, 0x0B, 0x23, 0x43, 0x59, 0x79, 0x03, 0x0D, 0x13, 0x25, 0x33, + 0x31, 0x21, 0x41, 0x4F, 0x5B, 0x01, 0x07, 0x0F, 0x23, 0x2F, 0x3F, 0x05, 0x0B, 0x15, 0x1D, 0x07 + }; + + private static readonly byte[] SpectrumB63Bits = + { + 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, + 6, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4 + }; + + private static readonly short[] SpectrumB63Codes = + { + 0x006, 0x00E, 0x004, 0x014, 0x010, 0x006, 0x000, 0x026, 0x01C, 0x018, 0x004, 0x05C, + 0x04A, 0x03C, 0x016, 0x0BC, 0x006, 0x008, 0x058, 0x03E, 0x036, 0x014, 0x0B6, 0x0B4, + 0x090, 0x068, 0x17E, 0x17C, 0x126, 0x124, 0x0D6, 0x0D4, 0x000, 0x0D5, 0x0D7, 0x125, + 0x127, 0x17D, 0x17F, 0x069, 0x091, 0x0B5, 0x0B7, 0x015, 0x037, 0x03F, 0x059, 0x009, + 0x007, 0x0BD, 0x017, 0x03D, 0x04B, 0x05D, 0x005, 0x019, 0x01D, 0x027, 0x001, 0x007, + 0x011, 0x015, 0x005, 0x00F + }; + + private static readonly byte[] SpectrumB64Bits = + { + 03, 03, 04, 05, 05, 05, 06, 06, 06, 06, 07, 07, 07, 07, 07, 08, + 07, 07, 07, 08, 08, 08, 09, 09, 09, 09, 09, 09, 10, 10, 10, 10, + 00, 10, 10, 10, 10, 09, 09, 09, 09, 09, 09, 08, 08, 08, 07, 07, + 07, 08, 07, 07, 07, 07, 07, 06, 06, 06, 06, 05, 05, 05, 04, 03 + }; + + private static readonly short[] SpectrumB64Codes = + { + 0x007, 0x000, 0x008, 0x01A, 0x014, 0x00C, 0x032, 0x02E, 0x01E, 0x014, 0x062, 0x05A, + 0x03A, 0x026, 0x020, 0x0B2, 0x038, 0x02C, 0x022, 0x0C0, 0x05E, 0x04A, 0x186, 0x184, + 0x160, 0x0BA, 0x092, 0x090, 0x2C6, 0x2C4, 0x172, 0x170, 0x000, 0x171, 0x173, 0x2C5, + 0x2C7, 0x091, 0x093, 0x0BB, 0x161, 0x185, 0x187, 0x04B, 0x05F, 0x0C1, 0x023, 0x02D, + 0x039, 0x0B3, 0x021, 0x027, 0x03B, 0x05B, 0x063, 0x015, 0x01F, 0x02F, 0x033, 0x00D, + 0x015, 0x01B, 0x009, 0x001 + }; + + private static readonly byte[] SpectrumB72Bits = + { + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5 + }; + + private static readonly short[] SpectrumB72Codes = + { + 0x01E, 0x016, 0x00C, 0x000, 0x038, 0x032, 0x028, 0x022, 0x01C, 0x012, 0x00E, 0x006, + 0x076, 0x06C, 0x060, 0x04E, 0x03E, 0x02A, 0x022, 0x01A, 0x012, 0x00A, 0x0FC, 0x0DC, + 0x0C6, 0x0A8, 0x094, 0x086, 0x058, 0x042, 0x040, 0x02A, 0x068, 0x07C, 0x06A, 0x056, + 0x048, 0x040, 0x02E, 0x028, 0x016, 0x010, 0x008, 0x0EA, 0x0DE, 0x0AA, 0x09A, 0x096, + 0x07A, 0x078, 0x05A, 0x032, 0x030, 0x028, 0x1FE, 0x1FC, 0x1D2, 0x1D0, 0x18A, 0x188, + 0x132, 0x130, 0x10A, 0x108, 0x000, 0x109, 0x10B, 0x131, 0x133, 0x189, 0x18B, 0x1D1, + 0x1D3, 0x1FD, 0x1FF, 0x029, 0x031, 0x033, 0x05B, 0x079, 0x07B, 0x097, 0x09B, 0x0AB, + 0x0DF, 0x0EB, 0x009, 0x011, 0x017, 0x029, 0x02F, 0x041, 0x049, 0x057, 0x06B, 0x07D, + 0x069, 0x02B, 0x041, 0x043, 0x059, 0x087, 0x095, 0x0A9, 0x0C7, 0x0DD, 0x0FD, 0x00B, + 0x013, 0x01B, 0x023, 0x02B, 0x03F, 0x04F, 0x061, 0x06D, 0x077, 0x007, 0x00F, 0x013, + 0x01D, 0x023, 0x029, 0x033, 0x039, 0x001, 0x00D, 0x017 + }; + + private static readonly byte[] SpectrumB73Bits = + { + 03, 04, 05, 05, 05, 06, 06, 06, 06, 06, 06, 07, 07, 07, 07, 07, + 07, 07, 07, 07, 08, 08, 08, 08, 08, 08, 08, 08, 08, 08, 09, 09, + 08, 07, 08, 08, 08, 08, 08, 08, 08, 08, 09, 09, 09, 09, 09, 09, + 09, 09, 09, 09, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 00, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 09, 09, 09, + 09, 09, 09, 09, 09, 09, 09, 08, 08, 08, 08, 08, 08, 08, 08, 07, + 08, 09, 09, 08, 08, 08, 08, 08, 08, 08, 08, 08, 08, 07, 07, 07, + 07, 07, 07, 07, 07, 07, 06, 06, 06, 06, 06, 06, 05, 05, 05, 04 + }; + + private static readonly short[] SpectrumB73Codes = + { + 0x000, 0x006, 0x018, 0x010, 0x004, 0x03A, 0x034, 0x02A, 0x026, 0x014, 0x010, 0x07E, + 0x072, 0x06E, 0x05C, 0x052, 0x04A, 0x02C, 0x024, 0x018, 0x0F4, 0x0E0, 0x0DA, 0x0B6, + 0x0B2, 0x0A0, 0x05E, 0x04E, 0x038, 0x034, 0x1E6, 0x1B2, 0x0FA, 0x01E, 0x0F8, 0x0F0, + 0x0BE, 0x0B4, 0x0A2, 0x090, 0x04C, 0x03A, 0x1EE, 0x1E4, 0x1C6, 0x1B0, 0x178, 0x162, + 0x126, 0x124, 0x0B8, 0x06C, 0x3DA, 0x3D8, 0x38A, 0x388, 0x2F6, 0x2F4, 0x2C2, 0x2C0, + 0x176, 0x174, 0x0DC, 0x0DE, 0x000, 0x0DF, 0x0DD, 0x175, 0x177, 0x2C1, 0x2C3, 0x2F5, + 0x2F7, 0x389, 0x38B, 0x3D9, 0x3DB, 0x06D, 0x0B9, 0x125, 0x127, 0x163, 0x179, 0x1B1, + 0x1C7, 0x1E5, 0x1EF, 0x03B, 0x04D, 0x091, 0x0A3, 0x0B5, 0x0BF, 0x0F1, 0x0F9, 0x01F, + 0x0FB, 0x1B3, 0x1E7, 0x035, 0x039, 0x04F, 0x05F, 0x0A1, 0x0B3, 0x0B7, 0x0DB, 0x0E1, + 0x0F5, 0x019, 0x025, 0x02D, 0x04B, 0x053, 0x05D, 0x06F, 0x073, 0x07F, 0x011, 0x015, + 0x027, 0x02B, 0x035, 0x03B, 0x005, 0x011, 0x019, 0x007 + }; + + private static readonly byte[] SpectrumB74Bits = + { + 03, 04, 05, 05, 05, 05, 06, 06, 06, 06, 06, 06, 07, 07, 07, 07, + 07, 07, 07, 07, 08, 08, 08, 08, 08, 08, 08, 08, 08, 09, 09, 09, + 08, 08, 08, 08, 08, 09, 09, 09, 09, 09, 09, 09, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 00, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 09, 09, 09, 09, 09, 09, 09, 08, 08, 08, 08, + 08, 09, 09, 09, 08, 08, 08, 08, 08, 08, 08, 08, 08, 07, 07, 07, + 07, 07, 07, 07, 07, 06, 06, 06, 06, 06, 06, 05, 05, 05, 05, 04 + }; + + private static readonly short[] SpectrumB74Codes = + { + 0x001, 0x008, 0x01E, 0x018, 0x00C, 0x002, 0x03A, 0x034, 0x02C, 0x01E, 0x016, 0x012, + 0x072, 0x06E, 0x05E, 0x056, 0x050, 0x038, 0x022, 0x004, 0x0E2, 0x0DA, 0x0BA, 0x0A8, + 0x076, 0x054, 0x050, 0x002, 0x000, 0x1C0, 0x1B0, 0x156, 0x0A4, 0x0A6, 0x074, 0x052, + 0x004, 0x1C2, 0x1B2, 0x170, 0x154, 0x0AE, 0x0AC, 0x086, 0x2E6, 0x2E4, 0x10A, 0x108, + 0x106, 0x104, 0x102, 0x100, 0x03E, 0x03A, 0x03C, 0x038, 0x036, 0x034, 0x032, 0x030, + 0x01E, 0x01A, 0x01C, 0x018, 0x000, 0x019, 0x01D, 0x01B, 0x01F, 0x031, 0x033, 0x035, + 0x037, 0x039, 0x03D, 0x03B, 0x03F, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x2E5, + 0x2E7, 0x087, 0x0AD, 0x0AF, 0x155, 0x171, 0x1B3, 0x1C3, 0x005, 0x053, 0x075, 0x0A7, + 0x0A5, 0x157, 0x1B1, 0x1C1, 0x001, 0x003, 0x051, 0x055, 0x077, 0x0A9, 0x0BB, 0x0DB, + 0x0E3, 0x005, 0x023, 0x039, 0x051, 0x057, 0x05F, 0x06F, 0x073, 0x013, 0x017, 0x01F, + 0x02D, 0x035, 0x03B, 0x003, 0x00D, 0x019, 0x01F, 0x009 + }; + + public static readonly byte[][] HuffmanScaleFactorsABits = + { + null, + ScaleFactorsA1Bits, ScaleFactorsA2Bits, ScaleFactorsA3Bits, + ScaleFactorsA4Bits, ScaleFactorsA5Bits, ScaleFactorsA6Bits + }; + + public static readonly short[][] HuffmanScaleFactorsACodes = + { + null, + ScaleFactorsA1Codes, ScaleFactorsA2Codes, ScaleFactorsA3Codes, + ScaleFactorsA4Codes, ScaleFactorsA5Codes, ScaleFactorsA6Codes + }; + + public static readonly byte[][] HuffmanScaleFactorsBBits = + { + null, null, + ScaleFactorsB2Bits, ScaleFactorsB3Bits, ScaleFactorsB4Bits, ScaleFactorsB5Bits + }; + + public static readonly short[][] HuffmanScaleFactorsBCodes = + { + null, null, + ScaleFactorsB2Codes, ScaleFactorsB3Codes, ScaleFactorsB4Codes, ScaleFactorsB5Codes + }; + + public static readonly byte[] HuffmanScaleFactorsGroupSizes = { 0, 0, 0, 0, 0, 0, 0 }; + + public static readonly byte[][][] HuffmanSpectrumABits = + { + null, + null, + new[] {SpectrumA21Bits, SpectrumA22Bits, SpectrumA23Bits, SpectrumA24Bits}, + new[] {SpectrumA31Bits, SpectrumA32Bits, SpectrumA33Bits, SpectrumA34Bits}, + new[] {SpectrumA41Bits, SpectrumA42Bits, SpectrumA43Bits, SpectrumA44Bits}, + new[] {SpectrumA51Bits, SpectrumA52Bits, SpectrumA53Bits, SpectrumA54Bits}, + new[] {SpectrumA61Bits, SpectrumA62Bits, SpectrumA63Bits, SpectrumA64Bits}, + new[] {SpectrumA71Bits, SpectrumA72Bits, SpectrumA73Bits, SpectrumA74Bits} + }; + + public static readonly short[][][] HuffmanSpectrumACodes = + { + null, + null, + new[] {SpectrumA21Codes, SpectrumA22Codes, SpectrumA23Codes, SpectrumA24Codes}, + new[] {SpectrumA31Codes, SpectrumA32Codes, SpectrumA33Codes, SpectrumA34Codes}, + new[] {SpectrumA41Codes, SpectrumA42Codes, SpectrumA43Codes, SpectrumA44Codes}, + new[] {SpectrumA51Codes, SpectrumA52Codes, SpectrumA53Codes, SpectrumA54Codes}, + new[] {SpectrumA61Codes, SpectrumA62Codes, SpectrumA63Codes, SpectrumA64Codes}, + new[] {SpectrumA71Codes, SpectrumA72Codes, SpectrumA73Codes, SpectrumA74Codes} + }; + + public static readonly byte[][] HuffmanSpectrumAGroupSizes = + { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {1, 2, 2, 2}, + new byte[] {1, 1, 1, 1}, + new byte[] {1, 1, 1, 1}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0} + }; + + public static readonly byte[][][] HuffmanSpectrumBBits = + { + null, + null, + new[] {null, SpectrumB22Bits, SpectrumB23Bits, SpectrumB24Bits}, + new[] {null, SpectrumB32Bits, SpectrumB33Bits, SpectrumB34Bits}, + new[] {null, SpectrumB42Bits, SpectrumB43Bits, SpectrumB44Bits}, + new[] {null, SpectrumB52Bits, SpectrumB53Bits, SpectrumB54Bits}, + new[] {null, SpectrumB62Bits, SpectrumB63Bits, SpectrumB64Bits}, + new[] {null, SpectrumB72Bits, SpectrumB73Bits, SpectrumB74Bits} + }; + + public static readonly short[][][] HuffmanSpectrumBCodes = + { + null, + null, + new[] {null, SpectrumB22Codes, SpectrumB23Codes, SpectrumB24Codes}, + new[] {null, SpectrumB32Codes, SpectrumB33Codes, SpectrumB34Codes}, + new[] {null, SpectrumB42Codes, SpectrumB43Codes, SpectrumB44Codes}, + new[] {null, SpectrumB52Codes, SpectrumB53Codes, SpectrumB54Codes}, + new[] {null, SpectrumB62Codes, SpectrumB63Codes, SpectrumB64Codes}, + new[] {null, SpectrumB72Codes, SpectrumB73Codes, SpectrumB74Codes} + }; + + public static readonly byte[][] HuffmanSpectrumBGroupSizes = + { + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 2, 2, 2}, + new byte[] {0, 1, 1, 1}, + new byte[] {0, 1, 1, 1}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0}, + new byte[] {0, 0, 0, 0} + }; + } +} \ No newline at end of file diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/LibAtrac9.csproj b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/LibAtrac9.csproj new file mode 100644 index 000000000..39fc3c8d4 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/LibAtrac9.csproj @@ -0,0 +1,8 @@ + + + + netstandard1.0 + true + + + diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Quantization.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Quantization.cs new file mode 100644 index 000000000..beb3d7d82 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Quantization.cs @@ -0,0 +1,57 @@ +using System; + +namespace LibAtrac9 +{ + internal static class Quantization + { + public static void DequantizeSpectra(Block block) + { + foreach (Channel channel in block.Channels) + { + Array.Clear(channel.Spectra, 0, channel.Spectra.Length); + + for (int i = 0; i < channel.CodedQuantUnits; i++) + { + DequantizeQuantUnit(channel, i); + } + } + } + + private static void DequantizeQuantUnit(Channel channel, int band) + { + int subBandIndex = Tables.QuantUnitToCoeffIndex[band]; + int subBandCount = Tables.QuantUnitToCoeffCount[band]; + double stepSize = Tables.QuantizerStepSize[channel.Precisions[band]]; + double stepSizeFine = Tables.QuantizerFineStepSize[channel.PrecisionsFine[band]]; + + for (int sb = 0; sb < subBandCount; sb++) + { + double coarse = channel.QuantizedSpectra[subBandIndex + sb] * stepSize; + double fine = channel.QuantizedSpectraFine[subBandIndex + sb] * stepSizeFine; + channel.Spectra[subBandIndex + sb] = coarse + fine; + } + } + + public static void ScaleSpectrum(Block block) + { + foreach (Channel channel in block.Channels) + { + ScaleSpectrum(channel); + } + } + + private static void ScaleSpectrum(Channel channel) + { + int quantUnitCount = channel.Block.QuantizationUnitCount; + double[] spectra = channel.Spectra; + + for (int i = 0; i < quantUnitCount; i++) + { + for (int sb = Tables.QuantUnitToCoeffIndex[i]; sb < Tables.QuantUnitToCoeffIndex[i + 1]; sb++) + { + spectra[sb] *= Tables.SpectrumScale[channel.ScaleFactors[i]]; + } + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ScaleFactors.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ScaleFactors.cs new file mode 100644 index 000000000..55634a993 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/ScaleFactors.cs @@ -0,0 +1,171 @@ +using System; +using System.IO; +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + internal static class ScaleFactors + { + public static void Read(BitReader reader, Channel channel) + { + Array.Clear(channel.ScaleFactors, 0, channel.ScaleFactors.Length); + + channel.ScaleFactorCodingMode = reader.ReadInt(2); + if (channel.ChannelIndex == 0) + { + switch (channel.ScaleFactorCodingMode) + { + case 0: + ReadVlcDeltaOffset(reader, channel); + break; + case 1: + ReadClcOffset(reader, channel); + break; + case 2: + if (channel.Block.FirstInSuperframe) throw new InvalidDataException(); + ReadVlcDistanceToBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev); + break; + case 3: + if (channel.Block.FirstInSuperframe) throw new InvalidDataException(); + ReadVlcDeltaOffsetWithBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev); + break; + } + } + else + { + switch (channel.ScaleFactorCodingMode) + { + case 0: + ReadVlcDeltaOffset(reader, channel); + break; + case 1: + ReadVlcDistanceToBaseline(reader, channel, channel.Block.Channels[0].ScaleFactors, channel.Block.ExtensionUnit); + break; + case 2: + ReadVlcDeltaOffsetWithBaseline(reader, channel, channel.Block.Channels[0].ScaleFactors, channel.Block.ExtensionUnit); + break; + case 3: + if (channel.Block.FirstInSuperframe) throw new InvalidDataException(); + ReadVlcDistanceToBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev); + break; + } + } + + for (int i = 0; i < channel.Block.ExtensionUnit; i++) + { + if (channel.ScaleFactors[i] < 0 || channel.ScaleFactors[i] > 31) + { + throw new InvalidDataException("Scale factor values are out of range."); + } + } + + Array.Copy(channel.ScaleFactors, channel.ScaleFactorsPrev, channel.ScaleFactors.Length); + } + + private static void ReadClcOffset(BitReader reader, Channel channel) + { + const int maxBits = 5; + int[] sf = channel.ScaleFactors; + int bitLength = reader.ReadInt(2) + 2; + int baseValue = bitLength < maxBits ? reader.ReadInt(maxBits) : 0; + + for (int i = 0; i < channel.Block.ExtensionUnit; i++) + { + sf[i] = reader.ReadInt(bitLength) + baseValue; + } + } + + private static void ReadVlcDeltaOffset(BitReader reader, Channel channel) + { + int weightIndex = reader.ReadInt(3); + byte[] weights = ScaleFactorWeights[weightIndex]; + + int[] sf = channel.ScaleFactors; + int baseValue = reader.ReadInt(5); + int bitLength = reader.ReadInt(2) + 3; + HuffmanCodebook codebook = Tables.HuffmanScaleFactorsUnsigned[bitLength]; + + sf[0] = reader.ReadInt(bitLength); + + for (int i = 1; i < channel.Block.ExtensionUnit; i++) + { + int delta = Unpack.ReadHuffmanValue(codebook, reader); + sf[i] = (sf[i - 1] + delta) & (codebook.ValueMax - 1); + } + + for (int i = 0; i < channel.Block.ExtensionUnit; i++) + { + sf[i] += baseValue - weights[i]; + } + } + + private static void ReadVlcDistanceToBaseline(BitReader reader, Channel channel, int[] baseline, int baselineLength) + { + int[] sf = channel.ScaleFactors; + int bitLength = reader.ReadInt(2) + 2; + HuffmanCodebook codebook = Tables.HuffmanScaleFactorsSigned[bitLength]; + int unitCount = Math.Min(channel.Block.ExtensionUnit, baselineLength); + + for (int i = 0; i < unitCount; i++) + { + int distance = Unpack.ReadHuffmanValue(codebook, reader, true); + sf[i] = (baseline[i] + distance) & 31; + } + + for (int i = unitCount; i < channel.Block.ExtensionUnit; i++) + { + sf[i] = reader.ReadInt(5); + } + } + + private static void ReadVlcDeltaOffsetWithBaseline(BitReader reader, Channel channel, int[] baseline, int baselineLength) + { + int[] sf = channel.ScaleFactors; + int baseValue = reader.ReadOffsetBinary(5, BitReader.OffsetBias.Negative); + int bitLength = reader.ReadInt(2) + 1; + HuffmanCodebook codebook = Tables.HuffmanScaleFactorsUnsigned[bitLength]; + int unitCount = Math.Min(channel.Block.ExtensionUnit, baselineLength); + + sf[0] = reader.ReadInt(bitLength); + + for (int i = 1; i < unitCount; i++) + { + int delta = Unpack.ReadHuffmanValue(codebook, reader); + sf[i] = (sf[i - 1] + delta) & (codebook.ValueMax - 1); + } + + for (int i = 0; i < unitCount; i++) + { + sf[i] += baseValue + baseline[i]; + } + + for (int i = unitCount; i < channel.Block.ExtensionUnit; i++) + { + sf[i] = reader.ReadInt(5); + } + } + + public static readonly byte[][] ScaleFactorWeights = + { + new byte[] { + 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 3, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 10, 12, 12, 12 + }, new byte[] { + 3, 2, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 3, 3, 4, 5, 7, 10, 10, 10 + }, new byte[] { + 0, 2, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 9, 12, 12, 12 + }, new byte[] { + 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 8, 8, 10, 11, 11, 12, 13, 13, 13, 13 + }, new byte[] { + 0, 2, 2, 3, 3, 4, 4, 5, 4, 5, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 14, 14 + }, new byte[] { + 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 6, 7, 7, 9, 11, 11, 11 + }, new byte[] { + 0, 5, 8, 10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, + 12, 13, 15, 15, 15 + }, new byte[] { + 0, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, + 15, 15, 15 + } + }; + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Stereo.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Stereo.cs new file mode 100644 index 000000000..a7ebdaf67 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Stereo.cs @@ -0,0 +1,33 @@ +namespace LibAtrac9 +{ + internal static class Stereo + { + public static void ApplyIntensityStereo(Block block) + { + if (block.BlockType != BlockType.Stereo) return; + + int totalUnits = block.QuantizationUnitCount; + int stereoUnits = block.StereoQuantizationUnit; + if (stereoUnits >= totalUnits) return; + + Channel source = block.PrimaryChannel; + Channel dest = block.SecondaryChannel; + + for (int i = stereoUnits; i < totalUnits; i++) + { + int sign = block.JointStereoSigns[i]; + for (int sb = Tables.QuantUnitToCoeffIndex[i]; sb < Tables.QuantUnitToCoeffIndex[i + 1]; sb++) + { + if (sign > 0) + { + dest.Spectra[sb] = -source.Spectra[sb]; + } + else + { + dest.Spectra[sb] = source.Spectra[sb]; + } + } + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Tables.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Tables.cs new file mode 100644 index 000000000..150759c02 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Tables.cs @@ -0,0 +1,115 @@ +using System; +using static LibAtrac9.HuffmanCodebooks; + +namespace LibAtrac9 +{ + internal static class Tables + { + public static int MaxHuffPrecision(bool highSampleRate) => highSampleRate ? 1 : 7; + public static int MinBandCount(bool highSampleRate) => highSampleRate ? 1 : 3; + public static int MaxExtensionBand(bool highSampleRate) => highSampleRate ? 16 : 18; + + public static readonly int[] SampleRates = + { + 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 + }; + + public static readonly byte[] SamplingRateIndexToFrameSamplesPower = { 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 7, 7, 7, 8, 8, 8 }; + + // From sampling rate index + public static readonly byte[] MaxBandCount = { 8, 8, 12, 12, 12, 18, 18, 18, 8, 8, 12, 12, 12, 16, 16, 16 }; + public static readonly byte[] BandToQuantUnitCount = { 0, 4, 8, 10, 12, 13, 14, 15, 16, 18, 20, 21, 22, 23, 24, 25, 26, 28, 30 }; + + public static readonly byte[] QuantUnitToCoeffCount = + { + 02, 02, 02, 02, 02, 02, 02, 02, 04, 04, 04, 04, 08, 08, 08, + 08, 08, 08, 08, 08, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 + }; + + public static readonly short[] QuantUnitToCoeffIndex = + { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 72, 80, 88, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256 + }; + + public static readonly byte[] QuantUnitToCodebookIndex = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + }; + + public static readonly ChannelConfig[] ChannelConfig = + { + new ChannelConfig(BlockType.Mono), + new ChannelConfig(BlockType.Mono, BlockType.Mono), + new ChannelConfig(BlockType.Stereo), + new ChannelConfig(BlockType.Stereo, BlockType.Mono, BlockType.LFE, BlockType.Stereo), + new ChannelConfig(BlockType.Stereo, BlockType.Mono, BlockType.LFE, BlockType.Stereo, BlockType.Stereo), + new ChannelConfig(BlockType.Stereo, BlockType.Stereo) + }; + + public static readonly HuffmanCodebook[] HuffmanScaleFactorsUnsigned = + GenerateHuffmanCodebooks(HuffmanScaleFactorsACodes, HuffmanScaleFactorsABits, HuffmanScaleFactorsGroupSizes); + + public static readonly HuffmanCodebook[] HuffmanScaleFactorsSigned = + GenerateHuffmanCodebooks(HuffmanScaleFactorsBCodes, HuffmanScaleFactorsBBits, HuffmanScaleFactorsGroupSizes); + + public static readonly HuffmanCodebook[][][] HuffmanSpectrum = + { + GenerateHuffmanCodebooks(HuffmanSpectrumACodes, HuffmanSpectrumABits, HuffmanSpectrumAGroupSizes), + GenerateHuffmanCodebooks(HuffmanSpectrumBCodes, HuffmanSpectrumBBits, HuffmanSpectrumBGroupSizes) + }; + + public static readonly double[][] ImdctWindow = { GenerateImdctWindow(6), GenerateImdctWindow(7), GenerateImdctWindow(8) }; + + public static readonly double[] SpectrumScale = Generate(32, SpectrumScaleFunction); + public static readonly double[] QuantizerStepSize = Generate(16, QuantizerStepSizeFunction); + public static readonly double[] QuantizerFineStepSize = Generate(16, QuantizerFineStepSizeFunction); + + public static readonly byte[][] GradientCurves = BitAllocation.GenerateGradientCurves(); + + private static double QuantizerStepSizeFunction(int x) => 2.0 / ((1 << (x + 1)) - 1); + private static double QuantizerFineStepSizeFunction(int x) => QuantizerStepSizeFunction(x) / ushort.MaxValue; + private static double SpectrumScaleFunction(int x) => Math.Pow(2, x - 15); + + private static double[] GenerateImdctWindow(int frameSizePower) + { + int frameSize = 1 << frameSizePower; + var output = new double[frameSize]; + + double[] a1 = GenerateMdctWindow(frameSizePower); + + for (int i = 0; i < frameSize; i++) + { + output[i] = a1[i] / (a1[frameSize - 1 - i] * a1[frameSize - 1 - i] + a1[i] * a1[i]); + } + return output; + } + + private static double[] GenerateMdctWindow(int frameSizePower) + { + int frameSize = 1 << frameSizePower; + var output = new double[frameSize]; + + for (int i = 0; i < frameSize; i++) + { + output[i] = (Math.Sin(((i + 0.5) / frameSize - 0.5) * Math.PI) + 1.0) * 0.5; + } + + return output; + } + + private static T[] Generate(int count, Func elementGenerator) + { + var table = new T[count]; + for (int i = 0; i < count; i++) + { + table[i] = elementGenerator(i); + } + return table; + } + + + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Unpack.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Unpack.cs new file mode 100644 index 000000000..29c25fe0c --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Unpack.cs @@ -0,0 +1,425 @@ +using System; +using System.IO; +using LibAtrac9.Utilities; + +namespace LibAtrac9 +{ + internal static class Unpack + { + public static void UnpackFrame(BitReader reader, Frame frame) + { + foreach (Block block in frame.Blocks) + { + UnpackBlock(reader, block); + } + } + + private static void UnpackBlock(BitReader reader, Block block) + { + ReadBlockHeader(reader, block); + + if (block.BlockType == BlockType.LFE) + { + UnpackLfeBlock(reader, block); + } + else + { + UnpackStandardBlock(reader, block); + } + + reader.AlignPosition(8); + } + + private static void ReadBlockHeader(BitReader reader, Block block) + { + bool firstInSuperframe = block.Frame.FrameIndex == 0; + block.FirstInSuperframe = !reader.ReadBool(); + block.ReuseBandParams = reader.ReadBool(); + + if (block.FirstInSuperframe != firstInSuperframe) + { + throw new InvalidDataException(); + } + + if (firstInSuperframe && block.ReuseBandParams && block.BlockType != BlockType.LFE) + { + throw new InvalidDataException(); + } + } + + private static void UnpackStandardBlock(BitReader reader, Block block) + { + Channel[] channels = block.Channels; + + if (!block.ReuseBandParams) + { + ReadBandParams(reader, block); + } + + ReadGradientParams(reader, block); + BitAllocation.CreateGradient(block); + ReadStereoParams(reader, block); + ReadExtensionParams(reader, block); + + foreach (Channel channel in channels) + { + channel.UpdateCodedUnits(); + + ScaleFactors.Read(reader, channel); + BitAllocation.CalculateMask(channel); + BitAllocation.CalculatePrecisions(channel); + CalculateSpectrumCodebookIndex(channel); + + ReadSpectra(reader, channel); + ReadSpectraFine(reader, channel); + } + + block.QuantizationUnitsPrev = block.BandExtensionEnabled ? block.ExtensionUnit : block.QuantizationUnitCount; + } + + private static void ReadBandParams(BitReader reader, Block block) + { + int minBandCount = Tables.MinBandCount(block.Config.HighSampleRate); + int maxExtensionBand = Tables.MaxExtensionBand(block.Config.HighSampleRate); + block.BandCount = reader.ReadInt(4); + block.BandCount += minBandCount; + block.QuantizationUnitCount = Tables.BandToQuantUnitCount[block.BandCount]; + if (block.BandCount < minBandCount || block.BandCount > + Tables.MaxBandCount[block.Config.SampleRateIndex]) + { + return; + } + + if (block.BlockType == BlockType.Stereo) + { + block.StereoBand = reader.ReadInt(4); + block.StereoBand += minBandCount; + block.StereoQuantizationUnit = Tables.BandToQuantUnitCount[block.StereoBand]; + } + else + { + block.StereoBand = block.BandCount; + } + + block.BandExtensionEnabled = reader.ReadBool(); + if (block.BandExtensionEnabled) + { + block.ExtensionBand = reader.ReadInt(4); + block.ExtensionBand += minBandCount; + + if (block.ExtensionBand < block.BandCount || block.ExtensionBand > maxExtensionBand) + { + throw new InvalidDataException(); + } + + block.ExtensionUnit = Tables.BandToQuantUnitCount[block.ExtensionBand]; + } + else + { + block.ExtensionBand = block.BandCount; + block.ExtensionUnit = block.QuantizationUnitCount; + } + } + + private static void ReadGradientParams(BitReader reader, Block block) + { + block.GradientMode = reader.ReadInt(2); + if (block.GradientMode > 0) + { + block.GradientEndUnit = 31; + block.GradientEndValue = 31; + block.GradientStartUnit = reader.ReadInt(5); + block.GradientStartValue = reader.ReadInt(5); + } + else + { + block.GradientStartUnit = reader.ReadInt(6); + block.GradientEndUnit = reader.ReadInt(6) + 1; + block.GradientStartValue = reader.ReadInt(5); + block.GradientEndValue = reader.ReadInt(5); + } + block.GradientBoundary = reader.ReadInt(4); + + if (block.GradientBoundary > block.QuantizationUnitCount) + { + throw new InvalidDataException(); + } + if (block.GradientStartUnit < 1 || block.GradientStartUnit >= 48) + { + throw new InvalidDataException(); + } + if (block.GradientEndUnit < 1 || block.GradientEndUnit >= 48) + { + throw new InvalidDataException(); + } + if (block.GradientStartUnit > block.GradientEndUnit) + { + throw new InvalidDataException(); + } + if (block.GradientStartValue < 0 || block.GradientStartValue >= 32) + { + throw new InvalidDataException(); + } + if (block.GradientEndValue < 0 || block.GradientEndValue >= 32) + { + throw new InvalidDataException(); + } + } + + private static void ReadStereoParams(BitReader reader, Block block) + { + if (block.BlockType != BlockType.Stereo) return; + + block.PrimaryChannelIndex = reader.ReadInt(1); + block.HasJointStereoSigns = reader.ReadBool(); + if (block.HasJointStereoSigns) + { + for (int i = block.StereoQuantizationUnit; i < block.QuantizationUnitCount; i++) + { + block.JointStereoSigns[i] = reader.ReadInt(1); + } + } + else + { + Array.Clear(block.JointStereoSigns, 0, block.JointStereoSigns.Length); + } + } + + private static void ReadExtensionParams(BitReader reader, Block block) + { + // ReSharper disable once RedundantAssignment + int bexBand = 0; + if (block.BandExtensionEnabled) + { + BandExtension.GetBexBandInfo(out bexBand, out _, out _, block.QuantizationUnitCount); + if (block.BlockType == BlockType.Stereo) + { + ReadHeader(block.Channels[1]); + } + else + { + reader.Position += 1; + } + } + block.HasExtensionData = reader.ReadBool(); + + if (!block.HasExtensionData) return; + if (!block.BandExtensionEnabled) + { + block.BexMode = reader.ReadInt(2); + block.BexDataLength = reader.ReadInt(5); + reader.Position += block.BexDataLength; + return; + } + + ReadHeader(block.Channels[0]); + + block.BexDataLength = reader.ReadInt(5); + if (block.BexDataLength <= 0) return; + int bexDataEnd = reader.Position + block.BexDataLength; + + ReadData(block.Channels[0]); + + if (block.BlockType == BlockType.Stereo) + { + ReadData(block.Channels[1]); + } + + // Make sure we didn't read too many bits + if (reader.Position > bexDataEnd) + { + throw new InvalidDataException(); + } + + void ReadHeader(Channel channel) + { + int bexMode = reader.ReadInt(2); + channel.BexMode = bexBand > 2 ? bexMode : 4; + channel.BexValueCount = BandExtension.BexEncodedValueCounts[channel.BexMode][bexBand]; + } + + void ReadData(Channel channel) + { + for (int i = 0; i < channel.BexValueCount; i++) + { + int dataLength = BandExtension.BexDataLengths[channel.BexMode][bexBand][i]; + channel.BexValues[i] = reader.ReadInt(dataLength); + } + } + } + + private static void CalculateSpectrumCodebookIndex(Channel channel) + { + Array.Clear(channel.CodebookSet, 0, channel.CodebookSet.Length); + int quantUnits = channel.CodedQuantUnits; + int[] sf = channel.ScaleFactors; + + if (quantUnits <= 1) return; + if (channel.Config.HighSampleRate) return; + + // Temporarily setting this value allows for simpler code by + // making the last value a non-special case. + int originalScaleTmp = sf[quantUnits]; + sf[quantUnits] = sf[quantUnits - 1]; + + int avg = 0; + if (quantUnits > 12) + { + for (int i = 0; i < 12; i++) + { + avg += sf[i]; + } + avg = (avg + 6) / 12; + } + + for (int i = 8; i < quantUnits; i++) + { + int prevSf = sf[i - 1]; + int nextSf = sf[i + 1]; + int minSf = Math.Min(prevSf, nextSf); + if (sf[i] - minSf >= 3 || sf[i] - prevSf + sf[i] - nextSf >= 3) + { + channel.CodebookSet[i] = 1; + } + } + + for (int i = 12; i < quantUnits; i++) + { + if (channel.CodebookSet[i] == 0) + { + int minSf = Math.Min(sf[i - 1], sf[i + 1]); + if (sf[i] - minSf >= 2 && sf[i] >= avg - (Tables.QuantUnitToCoeffCount[i] == 16 ? 1 : 0)) + { + channel.CodebookSet[i] = 1; + } + } + } + + sf[quantUnits] = originalScaleTmp; + } + + private static void ReadSpectra(BitReader reader, Channel channel) + { + int[] values = channel.SpectraValuesBuffer; + Array.Clear(channel.QuantizedSpectra, 0, channel.QuantizedSpectra.Length); + int maxHuffPrecision = Tables.MaxHuffPrecision(channel.Config.HighSampleRate); + + for (int i = 0; i < channel.CodedQuantUnits; i++) + { + int subbandCount = Tables.QuantUnitToCoeffCount[i]; + int precision = channel.Precisions[i] + 1; + if (precision <= maxHuffPrecision) + { + HuffmanCodebook huff = Tables.HuffmanSpectrum[channel.CodebookSet[i]][precision][Tables.QuantUnitToCodebookIndex[i]]; + int groupCount = subbandCount >> huff.ValueCountPower; + for (int j = 0; j < groupCount; j++) + { + values[j] = ReadHuffmanValue(huff, reader); + } + + DecodeHuffmanValues(channel.QuantizedSpectra, Tables.QuantUnitToCoeffIndex[i], subbandCount, huff, values); + } + else + { + int subbandIndex = Tables.QuantUnitToCoeffIndex[i]; + for (int j = subbandIndex; j < Tables.QuantUnitToCoeffIndex[i + 1]; j++) + { + channel.QuantizedSpectra[j] = reader.ReadSignedInt(precision); + } + } + } + } + + private static void ReadSpectraFine(BitReader reader, Channel channel) + { + Array.Clear(channel.QuantizedSpectraFine, 0, channel.QuantizedSpectraFine.Length); + + for (int i = 0; i < channel.CodedQuantUnits; i++) + { + if (channel.PrecisionsFine[i] > 0) + { + int overflowBits = channel.PrecisionsFine[i] + 1; + int startSubband = Tables.QuantUnitToCoeffIndex[i]; + int endSubband = Tables.QuantUnitToCoeffIndex[i + 1]; + + for (int j = startSubband; j < endSubband; j++) + { + channel.QuantizedSpectraFine[j] = reader.ReadSignedInt(overflowBits); + } + } + } + } + + private static void DecodeHuffmanValues(int[] spectrum, int index, int bandCount, HuffmanCodebook huff, int[] values) + { + int valueCount = bandCount >> huff.ValueCountPower; + int mask = (1 << huff.ValueBits) - 1; + + for (int i = 0; i < valueCount; i++) + { + int value = values[i]; + for (int j = 0; j < huff.ValueCount; j++) + { + spectrum[index++] = Bit.SignExtend32(value & mask, huff.ValueBits); + value >>= huff.ValueBits; + } + } + } + + public static int ReadHuffmanValue(HuffmanCodebook huff, BitReader reader, bool signed = false) + { + int code = reader.PeekInt(huff.MaxBitSize); + byte value = huff.Lookup[code]; + int bits = huff.Bits[value]; + reader.Position += bits; + return signed ? Bit.SignExtend32(value, huff.ValueBits) : value; + } + + private static void UnpackLfeBlock(BitReader reader, Block block) + { + Channel channel = block.Channels[0]; + block.QuantizationUnitCount = 2; + + DecodeLfeScaleFactors(reader, channel); + CalculateLfePrecision(channel); + channel.CodedQuantUnits = block.QuantizationUnitCount; + ReadLfeSpectra(reader, channel); + } + + private static void DecodeLfeScaleFactors(BitReader reader, Channel channel) + { + Array.Clear(channel.ScaleFactors, 0, channel.ScaleFactors.Length); + for (int i = 0; i < channel.Block.QuantizationUnitCount; i++) + { + channel.ScaleFactors[i] = reader.ReadInt(5); + } + } + + private static void CalculateLfePrecision(Channel channel) + { + Block block = channel.Block; + int precision = block.ReuseBandParams ? 8 : 4; + for (int i = 0; i < block.QuantizationUnitCount; i++) + { + channel.Precisions[i] = precision; + channel.PrecisionsFine[i] = 0; + } + } + + private static void ReadLfeSpectra(BitReader reader, Channel channel) + { + Array.Clear(channel.QuantizedSpectra, 0, channel.QuantizedSpectra.Length); + + for (int i = 0; i < channel.CodedQuantUnits; i++) + { + if (channel.Precisions[i] <= 0) continue; + + int precision = channel.Precisions[i] + 1; + for (int j = Tables.QuantUnitToCoeffIndex[i]; j < Tables.QuantUnitToCoeffIndex[i + 1]; j++) + { + channel.QuantizedSpectra[j] = reader.ReadSignedInt(precision); + } + } + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Bit.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Bit.cs new file mode 100644 index 000000000..f8ff07946 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Bit.cs @@ -0,0 +1,22 @@ +namespace LibAtrac9.Utilities +{ + internal static class Bit + { + private static uint BitReverse32(uint value) + { + value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1); + value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2); + value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4); + value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8); + return (value >> 16) | (value << 16); + } + private static uint BitReverse32(uint value, int bitCount) => BitReverse32(value) >> (32 - bitCount); + public static int BitReverse32(int value, int bitCount) => (int) BitReverse32((uint) value, bitCount); + + public static int SignExtend32(int value, int bits) + { + int shift = 8 * sizeof(int) - bits; + return (value << shift) >> shift; + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/BitReader.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/BitReader.cs new file mode 100644 index 000000000..b89d9d765 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/BitReader.cs @@ -0,0 +1,132 @@ +using System; +using System.Diagnostics; + +namespace LibAtrac9.Utilities +{ + internal class BitReader + { + private byte[] Buffer { get; set; } + private int LengthBits { get; set; } + public int Position { get; set; } + private int Remaining => LengthBits - Position; + + public BitReader(byte[] buffer) => SetBuffer(buffer); + + public void SetBuffer(byte[] buffer) + { + Buffer = buffer; + LengthBits = Buffer?.Length * 8 ?? 0; + Position = 0; + } + + public int ReadInt(int bitCount) + { + int value = PeekInt(bitCount); + Position += bitCount; + return value; + } + + public int ReadSignedInt(int bitCount) + { + int value = PeekInt(bitCount); + Position += bitCount; + return Bit.SignExtend32(value, bitCount); + } + + public bool ReadBool() => ReadInt(1) == 1; + + public int ReadOffsetBinary(int bitCount, OffsetBias bias) + { + int offset = (1 << (bitCount - 1)) - (int)bias; + int value = PeekInt(bitCount) - offset; + Position += bitCount; + return value; + } + + public void AlignPosition(int multiple) + { + Position = Helpers.GetNextMultiple(Position, multiple); + } + + public int PeekInt(int bitCount) + { + Debug.Assert(bitCount >= 0 && bitCount <= 32); + + if (bitCount > Remaining) + { + if (Position >= LengthBits) return 0; + + int extraBits = bitCount - Remaining; + return PeekIntFallback(Remaining) << extraBits; + } + + int byteIndex = Position / 8; + int bitIndex = Position % 8; + + if (bitCount <= 9 && Remaining >= 16) + { + int value = Buffer[byteIndex] << 8 | Buffer[byteIndex + 1]; + value &= 0xFFFF >> bitIndex; + value >>= 16 - bitCount - bitIndex; + return value; + } + + if (bitCount <= 17 && Remaining >= 24) + { + int value = Buffer[byteIndex] << 16 | Buffer[byteIndex + 1] << 8 | Buffer[byteIndex + 2]; + value &= 0xFFFFFF >> bitIndex; + value >>= 24 - bitCount - bitIndex; + return value; + } + + if (bitCount <= 25 && Remaining >= 32) + { + int value = Buffer[byteIndex] << 24 | Buffer[byteIndex + 1] << 16 | Buffer[byteIndex + 2] << 8 | Buffer[byteIndex + 3]; + value &= (int)(0xFFFFFFFF >> bitIndex); + value >>= 32 - bitCount - bitIndex; + return value; + } + return PeekIntFallback(bitCount); + } + + private int PeekIntFallback(int bitCount) + { + int value = 0; + int byteIndex = Position / 8; + int bitIndex = Position % 8; + + while (bitCount > 0) + { + if (bitIndex >= 8) + { + bitIndex = 0; + byteIndex++; + } + + int bitsToRead = Math.Min(bitCount, 8 - bitIndex); + int mask = 0xFF >> bitIndex; + int currentByte = (mask & Buffer[byteIndex]) >> (8 - bitIndex - bitsToRead); + + value = (value << bitsToRead) | currentByte; + bitIndex += bitsToRead; + bitCount -= bitsToRead; + } + return value; + } + + /// + /// Specifies the bias of an offset binary value. A positive bias can represent one more + /// positive value than negative value, and a negative bias can represent one more + /// negative value than positive value. + /// + /// Example: + /// A 4-bit offset binary value with a positive bias can store + /// the values 8 through -7 inclusive. + /// A 4-bit offset binary value with a positive bias can store + /// the values 7 through -8 inclusive. + public enum OffsetBias + { + Negative = 0 + } + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Helpers.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Helpers.cs new file mode 100644 index 000000000..24f615ace --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Helpers.cs @@ -0,0 +1,50 @@ +using System.Runtime.CompilerServices; + +namespace LibAtrac9.Utilities +{ + internal static class Helpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Clamp16(int value) + { + if (value > short.MaxValue) + return short.MaxValue; + if (value < short.MinValue) + return short.MinValue; + return (short)value; + } + + public static int GetNextMultiple(int value, int multiple) + { + if (multiple <= 0) + return value; + + if (value % multiple == 0) + return value; + + return value + multiple - value % multiple; + } + + /// + /// Returns the floor of the base 2 logarithm of a specified number. + /// + /// The number whose logarithm is to be found. + /// The floor of the base 2 logarithm of . + public static int Log2(int value) + { + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + + return MultiplyDeBruijnBitPosition[(uint)(value * 0x07C4ACDDU) >> 27]; + } + + private static readonly int[] MultiplyDeBruijnBitPosition = + { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + } +} diff --git a/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Mdct.cs b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Mdct.cs new file mode 100644 index 000000000..dfc4c1b41 --- /dev/null +++ b/Frameworks/libatrac9/libatrac9/LibAtrac9/CSharp/LibAtrac9/Utilities/Mdct.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; + +namespace LibAtrac9.Utilities +{ + internal class Mdct + { + private int MdctBits { get; } + private int MdctSize { get; } + private double Scale { get; } + + private static readonly object TableLock = new object(); + private static int _tableBits = -1; + private static readonly List SinTables = new List(); + private static readonly List CosTables = new List(); + private static readonly List ShuffleTables = new List(); + + private readonly double[] _imdctPrevious; + private readonly double[] _imdctWindow; + + private readonly double[] _scratchMdct; + private readonly double[] _scratchDct; + + public Mdct(int mdctBits, double[] window, double scale = 1) + { + SetTables(mdctBits); + + MdctBits = mdctBits; + MdctSize = 1 << mdctBits; + Scale = scale; + + if (window.Length < MdctSize) + { + throw new ArgumentException("Window must be as long as the MDCT size.", nameof(window)); + } + + _imdctPrevious = new double[MdctSize]; + _scratchMdct = new double[MdctSize]; + _scratchDct = new double[MdctSize]; + _imdctWindow = window; + } + + private static void SetTables(int maxBits) + { + lock (TableLock) + { + if (maxBits > _tableBits) + { + for (int i = _tableBits + 1; i <= maxBits; i++) + { + GenerateTrigTables(i, out double[] sin, out double[] cos); + SinTables.Add(sin); + CosTables.Add(cos); + ShuffleTables.Add(GenerateShuffleTable(i)); + } + _tableBits = maxBits; + } + } + } + + public void RunImdct(double[] input, double[] output) + { + if (input.Length < MdctSize) + { + throw new ArgumentException("Input must be as long as the MDCT size.", nameof(input)); + } + + if (output.Length < MdctSize) + { + throw new ArgumentException("Output must be as long as the MDCT size.", nameof(output)); + } + + int size = MdctSize; + int half = size / 2; + double[] dctOut = _scratchMdct; + + Dct4(input, dctOut); + + for (int i = 0; i < half; i++) + { + output[i] = _imdctWindow[i] * dctOut[i + half] + _imdctPrevious[i]; + output[i + half] = _imdctWindow[i + half] * -dctOut[size - 1 - i] - _imdctPrevious[i + half]; + _imdctPrevious[i] = _imdctWindow[size - 1 - i] * -dctOut[half - i - 1]; + _imdctPrevious[i + half] = _imdctWindow[half - i - 1] * dctOut[i]; + } + } + + /// + /// Does a Type-4 DCT. + /// + /// The input array containing the time or frequency-domain samples + /// The output array that will contain the transformed time or frequency-domain samples + private void Dct4(double[] input, double[] output) + { + int[] shuffleTable = ShuffleTables[MdctBits]; + double[] sinTable = SinTables[MdctBits]; + double[] cosTable = CosTables[MdctBits]; + double[] dctTemp = _scratchDct; + + int size = MdctSize; + int lastIndex = size - 1; + int halfSize = size / 2; + + for (int i = 0; i < halfSize; i++) + { + int i2 = i * 2; + double a = input[i2]; + double b = input[lastIndex - i2]; + double sin = sinTable[i]; + double cos = cosTable[i]; + dctTemp[i2] = a * cos + b * sin; + dctTemp[i2 + 1] = a * sin - b * cos; + } + int stageCount = MdctBits - 1; + + for (int stage = 0; stage < stageCount; stage++) + { + int blockCount = 1 << stage; + int blockSizeBits = stageCount - stage; + int blockHalfSizeBits = blockSizeBits - 1; + int blockSize = 1 << blockSizeBits; + int blockHalfSize = 1 << blockHalfSizeBits; + sinTable = SinTables[blockHalfSizeBits]; + cosTable = CosTables[blockHalfSizeBits]; + + for (int block = 0; block < blockCount; block++) + { + for (int i = 0; i < blockHalfSize; i++) + { + int frontPos = (block * blockSize + i) * 2; + int backPos = frontPos + blockSize; + double a = dctTemp[frontPos] - dctTemp[backPos]; + double b = dctTemp[frontPos + 1] - dctTemp[backPos + 1]; + double sin = sinTable[i]; + double cos = cosTable[i]; + dctTemp[frontPos] += dctTemp[backPos]; + dctTemp[frontPos + 1] += dctTemp[backPos + 1]; + dctTemp[backPos] = a * cos + b * sin; + dctTemp[backPos + 1] = a * sin - b * cos; + } + } + } + + for (int i = 0; i < MdctSize; i++) + { + output[i] = dctTemp[shuffleTable[i]] * Scale; + } + } + + internal static void GenerateTrigTables(int sizeBits, out double[] sin, out double[] cos) + { + int size = 1 << sizeBits; + sin = new double[size]; + cos = new double[size]; + + for (int i = 0; i < size; i++) + { + double value = Math.PI * (4 * i + 1) / (4 * size); + sin[i] = Math.Sin(value); + cos[i] = Math.Cos(value); + } + } + + internal static int[] GenerateShuffleTable(int sizeBits) + { + int size = 1 << sizeBits; + var table = new int[size]; + + for (int i = 0; i < size; i++) + { + table[i] = Bit.BitReverse32(i ^ (i / 2), sizeBits); + } + + return table; + } + } +} diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 8796b0c67..96cbb0acc 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -12,6 +12,11 @@ 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165981F256BD000CA0941 /* ea_schl_fixed.c */; }; 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165991F256BD000CA0941 /* nds_strm_ffta2.c */; }; 830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165A11F256BF400CA0941 /* hwas_blocked.c */; }; + 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */; }; + 830EBE132004656E0023AA10 /* xnb.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE112004656E0023AA10 /* xnb.c */; }; + 830EBE142004656E0023AA10 /* ktss.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE122004656E0023AA10 /* ktss.c */; }; + 830EBE19200465B00023AA10 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830EBD9720045F1B0023AA10 /* libatrac9.framework */; }; + 830EBE1A200465C00023AA10 /* libatrac9.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830EBD9720045F1B0023AA10 /* libatrac9.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8313E3E61902020400B4B6F1 /* mpg123.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; }; 8313E3E71902021800B4B6F1 /* mpg123.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 831BA6181EAC61A500CF89B0 /* adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA60E1EAC61A500CF89B0 /* adx.c */; }; @@ -444,6 +449,27 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + 830EBD9620045F1B0023AA10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 830EBD8720045F190023AA10; + remoteInfo = libatrac9; + }; + 830EBE152004659B0023AA10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 830EBD8620045F190023AA10; + remoteInfo = libatrac9; + }; + 830EBE17200465A30023AA10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 730F23A1091827B100AB638C; + remoteInfo = Vorbis; + }; 8313E3421901FBDD00B4B6F1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */; @@ -523,6 +549,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 830EBE1A200465C00023AA10 /* libatrac9.framework in CopyFiles */, 832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */, 83D7318A1A749D2200CA1366 /* g719.framework in CopyFiles */, 83D731111A7394D300CA1366 /* g7221.framework in CopyFiles */, @@ -538,6 +565,10 @@ 830165981F256BD000CA0941 /* ea_schl_fixed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl_fixed.c; sourceTree = ""; }; 830165991F256BD000CA0941 /* nds_strm_ffta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm_ffta2.c; sourceTree = ""; }; 830165A11F256BF400CA0941 /* hwas_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hwas_blocked.c; sourceTree = ""; }; + 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = ""; }; + 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atrac9_decoder.c; sourceTree = ""; }; + 830EBE112004656E0023AA10 /* xnb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xnb.c; sourceTree = ""; }; + 830EBE122004656E0023AA10 /* ktss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktss.c; sourceTree = ""; }; 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = mpg123.xcodeproj; path = ../mpg123/mpg123.xcodeproj; sourceTree = ""; }; 831BA60E1EAC61A500CF89B0 /* adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx.c; sourceTree = ""; }; 831BA60F1EAC61A500CF89B0 /* ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogl.c; sourceTree = ""; }; @@ -960,6 +991,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 830EBE19200465B00023AA10 /* libatrac9.framework in Frameworks */, 838BDB7F1D3B1FD10022CA6F /* Cocoa.framework in Frameworks */, 838BDB7D1D3B1FCC0022CA6F /* CoreVideo.framework in Frameworks */, 838BDB7B1D3B1FC20022CA6F /* CoreMedia.framework in Frameworks */, @@ -984,6 +1016,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 830EBD9320045F190023AA10 /* Products */ = { + isa = PBXGroup; + children = ( + 830EBD9720045F1B0023AA10 /* libatrac9.framework */, + ); + name = Products; + sourceTree = ""; + }; 8313E33E1901FBDC00B4B6F1 /* Products */ = { isa = PBXGroup; children = ( @@ -1040,6 +1080,7 @@ 836F6B3E18BDB8880095E648 /* Other Frameworks */ = { isa = PBXGroup; children = ( + 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */, 838BDB611D3AF08C0022CA6F /* libavcodec.a */, 838BDB621D3AF08C0022CA6F /* libavformat.a */, 838BDB631D3AF08C0022CA6F /* libavutil.a */, @@ -1093,6 +1134,7 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( + 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */, 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, @@ -1204,6 +1246,8 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 830EBE122004656E0023AA10 /* ktss.c */, + 830EBE112004656E0023AA10 /* xnb.c */, 8349A9041FE6258100E26435 /* aax_streamfile.h */, 8349A9021FE6258100E26435 /* adx_keys.h */, 8349A9001FE6258000E26435 /* afc.c */, @@ -1576,6 +1620,8 @@ 48C265101A5D424500A0A3D6 /* PBXBuildRule */, ); dependencies = ( + 830EBE18200465A30023AA10 /* PBXTargetDependency */, + 830EBE162004659B0023AA10 /* PBXTargetDependency */, 83D731881A749D0D00CA1366 /* PBXTargetDependency */, 83D7310F1A7394B500CA1366 /* PBXTargetDependency */, 8313E3E91902021F00B4B6F1 /* PBXTargetDependency */, @@ -1619,6 +1665,10 @@ ProductGroup = 83D730E61A738EB200CA1366 /* Products */; ProjectRef = 83D730E51A738EB200CA1366 /* g7221.xcodeproj */; }, + { + ProductGroup = 830EBD9320045F190023AA10 /* Products */; + ProjectRef = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */; + }, { ProductGroup = 8313E33E1901FBDC00B4B6F1 /* Products */; ProjectRef = 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */; @@ -1636,6 +1686,13 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ + 830EBD9720045F1B0023AA10 /* libatrac9.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = libatrac9.framework; + remoteRef = 830EBD9620045F1B0023AA10 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 8313E3431901FBDD00B4B6F1 /* mpg123.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -1781,6 +1838,7 @@ 8349A91F1FE6258200E26435 /* naac.c in Sources */, 836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */, 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, + 830EBE132004656E0023AA10 /* xnb.c in Sources */, 835027131ED119E000C25929 /* mta2_decoder.c in Sources */, 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */, 8349A91A1FE6258200E26435 /* vxn.c in Sources */, @@ -1817,8 +1875,10 @@ 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, 836F701418BDC2190095E648 /* ps3_msf.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, + 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, 836F700818BDC2190095E648 /* ps2_vms.c in Sources */, 836F702418BDC2190095E648 /* rwsd.c in Sources */, + 830EBE142004656E0023AA10 /* ktss.c in Sources */, 836F6F5618BDC2190095E648 /* scd_int_layout.c in Sources */, 836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, @@ -2084,6 +2144,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 830EBE162004659B0023AA10 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = libatrac9; + targetProxy = 830EBE152004659B0023AA10 /* PBXContainerItemProxy */; + }; + 830EBE18200465A30023AA10 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Vorbis; + targetProxy = 830EBE17200465A30023AA10 /* PBXContainerItemProxy */; + }; 8313E3E91902021F00B4B6F1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = mpg123; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c index a8b444ecb..57f56ef9b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/at3_decoder.c @@ -4,6 +4,20 @@ #ifdef VGM_USE_MAIATRAC3PLUS #include "maiatrac3plus.h" +maiatrac3plus_codec_data *init_at3plus() { + + maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data)); + data->buffer = 0; + data->samples_discard = 0; + data->handle = Atrac3plusDecoder_openContext(); + if (!data->handle) goto fail; + + return data; + +fail: + return NULL; +} + void decode_at3plus(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL *ch = &vgmstream->ch[0]; maiatrac3plus_codec_data *data = vgmstream->codec_data; @@ -25,8 +39,7 @@ void decode_at3plus(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, data->samples_discard = 0; } - for (i = 0; i < samples_to_do; i++) - { + for (i = 0; i < samples_to_do; i++) { outbuf[i*channelspacing] = data->buffer[(first_sample+i)*data->channels+channel]; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c new file mode 100644 index 000000000..eb0b61166 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -0,0 +1,219 @@ +#include "coding.h" + +#ifdef VGM_USE_ATRAC9 +#include + + +atrac9_codec_data *init_atrac9(atrac9_config *cfg) { + int status; + uint8_t config_data[4]; + Atrac9CodecInfo info = {0}; + atrac9_codec_data *data = NULL; + + data = calloc(1, sizeof(atrac9_codec_data)); + + data->handle = Atrac9GetHandle(); + if (!data->handle) goto fail; + + put_32bitBE(config_data, cfg->config_data); + status = Atrac9InitDecoder(data->handle, config_data); + if (status < 0) goto fail; + + status = Atrac9GetCodecInfo(data->handle, &info); + if (status < 0) goto fail; + //;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples); + + if (cfg->channels && cfg->channels != info.channels) { + VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, info.channels); + goto fail; /* unknown multichannel layout */ + } + + + /* must hold at least one superframe and its samples */ + data->data_buffer_size = info.superframeSize; + data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size); + data->sample_buffer = calloc(sizeof(sample), info.channels * info.frameSamples * info.framesInSuperframe); + + data->samples_to_discard = cfg->encoder_delay; + + memcpy(&data->config, cfg, sizeof(atrac9_config)); + + return data; + +fail: + return NULL; +} + +void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + atrac9_codec_data * data = vgmstream->codec_data; + int samples_done = 0; + + + while (samples_done < samples_to_do) { + + if (data->samples_filled) { /* consume samples */ + int samples_to_get = data->samples_filled; + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and copy */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + + memcpy(outbuf + samples_done*channels, + data->sample_buffer + data->samples_used*channels, + samples_to_get*channels * sizeof(sample)); + + samples_done += samples_to_get; + } + + /* mark consumed samples */ + data->samples_used += samples_to_get; + data->samples_filled -= samples_to_get; + } + else { /* decode data */ + int iframe, status; + int bytes_used = 0; + uint8_t *buffer = data->data_buffer; + size_t bytes; + Atrac9CodecInfo info = {0}; + + data->samples_used = 0; + + /* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives + * superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */ + status = Atrac9GetCodecInfo(data->handle, &info); + if (status < 0) goto decode_fail; + + + /* preadjust */ //todo improve + switch(data->config.type) { + case ATRAC9_XVAG: + /* PS4 (ex. The Last of Us) has a RIFF AT9 (can be ignored) instead of the first superframe. + * As subsongs do too, needs to be skipped here instead of adjusting start_offset */ + if (stream->offset == stream->channel_start_offset) { + if (read_32bitBE(stream->offset, stream->streamfile) == 0x00000000 /* padding before RIFF */ + && read_32bitBE(stream->offset + info.superframeSize - 0x08,stream->streamfile) == 0x64617461) { /* RIFF's "data" */ + stream->offset += info.superframeSize; + } + } + break; + default: + break; + } + + /* read one raw block (superframe) and advance offsets */ + bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile); + if (bytes != data->data_buffer_size) { + VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset); + goto decode_fail; + } + + stream->offset += bytes; + + /* postadjust */ //todo improve + switch(data->config.type) { + case ATRAC9_XVAG: /* skip other subsong blocks in XVAG */ + if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) { + stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1); + } + break; + default: + break; + } + + + /* decode all frames in the superframe block */ + for (iframe = 0; iframe < info.framesInSuperframe; iframe++) { + status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used); + if (status < 0) goto decode_fail; + + buffer += bytes_used; + data->samples_filled += info.frameSamples; + } + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("ATRAC9: decode fail at %lx, missing %i samples\n", stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); +} + +void reset_atrac9(VGMSTREAM *vgmstream) { + atrac9_codec_data *data = vgmstream->codec_data; + + if (!data->handle) + goto fail; + +#if 0 + /* reopen/flush, not needed as superframes decode separatedly and there is no carried state */ + { + int status; + uint8_t config_data[4]; + + Atrac9ReleaseHandle(data->handle); + data->handle = Atrac9GetHandle(); + if (!data->handle) goto fail; + + put_32bitBE(config_data, data->config.config_data); + status = Atrac9InitDecoder(data->handle, config_data); + if (status < 0) goto fail; + } +#endif + + data->samples_used = 0; + data->samples_filled = 0; + data->samples_to_discard = 0; + + return; + +fail: + return; /* decode calls should fail... */ +} + +void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { + atrac9_codec_data *data = vgmstream->codec_data; + + reset_atrac9(vgmstream); + + data->samples_to_discard = num_sample; + data->samples_to_discard += data->config.encoder_delay; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +void free_atrac9(atrac9_codec_data *data) { + if (!data) + return; + + if (data->handle) Atrac9ReleaseHandle(data->handle); + free(data->data_buffer); + free(data->sample_buffer); + free(data); +} + + +size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) { + Atrac9CodecInfo info = {0}; + int status; + + status = Atrac9GetCodecInfo(data->handle, &info); + if (status < 0) goto fail; + + return bytes / info.superframeSize * (info.frameSamples * info.framesInSuperframe); + +fail: + return 0; +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 6e36600b9..dbca81d47 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -65,6 +65,7 @@ void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); @@ -79,7 +80,6 @@ size_t ps_bytes_to_samples(size_t bytes, int channels); /* xa_decoder */ void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void xa_init_get_high_nibble(VGMSTREAM * vgmstream); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked); /* ea_xa_decoder */ @@ -157,7 +157,7 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); void free_ogg_vorbis(ogg_vorbis_codec_data *data); /* vorbis_custom_decoder */ -vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config); +vorbis_custom_codec_data *init_vorbis_custom(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config); void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); void reset_vorbis_custom(VGMSTREAM *vgmstream); void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample); @@ -166,11 +166,9 @@ void free_vorbis_custom(vorbis_custom_codec_data *data); #ifdef VGM_USE_MPEG /* mpeg_decoder */ -mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); -mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config); - +mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); +mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config); void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); -void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do); void reset_mpeg(VGMSTREAM *vgmstream); void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample); void free_mpeg(mpeg_codec_data *data); @@ -204,18 +202,29 @@ void free_mp4_aac(mp4_aac_codec_data * data); #endif #ifdef VGM_USE_MAIATRAC3PLUS -/* at3_decoder */ +/* at3plus_decoder */ +maiatrac3plus_codec_data *init_at3plus(); void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); void reset_at3plus(VGMSTREAM *vgmstream); void seek_at3plus(VGMSTREAM *vgmstream, int32_t num_sample); void free_at3plus(maiatrac3plus_codec_data *data); #endif +#ifdef VGM_USE_ATRAC9 +/* atrac9_decoder */ +atrac9_codec_data *init_atrac9(atrac9_config *cfg); +void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels); +void reset_atrac9(VGMSTREAM *vgmstream); +void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample); +void free_atrac9(atrac9_codec_data *data); +size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data); +#endif + #ifdef VGM_USE_FFMPEG /* ffmpeg_decoder */ -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); -ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config); +ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); +ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); +ffmpeg_codec_data *init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config); void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); void reset_ffmpeg(VGMSTREAM *vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c index fd56320ce..c4dc0b625 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_ea_xma.c @@ -119,8 +119,8 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size virtual_base += data_size; } - /* exit on last block just in case, though should reach file size */ - if (block_size & 0x80000000) + /* exit on last block just in case, though should reach real_size */ + if ((block_size & 0x80000000) || (block_size & 0x45000000)) break; } @@ -199,10 +199,9 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea /* 0x04(4): decoded samples */ off_t packets_offset = real_offset + 0x08; - if ((block_size & 0xFF000000) && !(block_size & 0x80000000)) { - VGM_LOG("EA-XMA: unknown flag found at %lx\n", (off_t)real_offset); - goto fail; - } + /* At 0x00(1): block flag + * - in SNS: 0x00=normal block, 0x80=last block (not mandatory) + * - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */ max_packets = get_block_max_packets(num_streams, packets_offset, streamFile); if (max_packets == 0) goto fail; @@ -213,7 +212,7 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea real_offset += (block_size & 0x00FFFFFF); /* exit on last block just in case, though should reach real_size */ - if (block_size & 0x80000000) + if ((block_size & 0x80000000) || (block_size & 0x45000000)) break; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index c8f56ef1d..6cc343e7b 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -787,12 +787,13 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { /* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ - return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels; + return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels + + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); } size_t ima_bytes_to_samples(size_t bytes, int channels) { /* 2 samples per byte (2 nibbles) in stereo or mono config */ - return bytes / channels * 2; + return bytes * 2 / channels; } size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 6b8cb7e7b..1c1385acc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -21,7 +21,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data /* Inits regular MPEG */ -mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) { +mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) { mpeg_codec_data *data = NULL; /* init codec */ @@ -116,7 +116,7 @@ fail: /* Init custom MPEG, with given type and config */ -mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) { +mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) { mpeg_codec_data *data = NULL; int i, ok; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index 3a8e813a1..3e016f949 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -87,69 +87,83 @@ void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int chan } } -/* decodes u-law (ITU G.711 non-linear PCM), from g711.c */ -void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; - int sign, segment, quantization, sample; +static int expand_ulaw(uint8_t ulawbyte) { + int sign, segment, quantization, new_sample; const int bias = 0x84; + ulawbyte = ~ulawbyte; /* stored in complement */ + sign = (ulawbyte & 0x80); + segment = (ulawbyte & 0x70) >> 4; /* exponent */ + quantization = ulawbyte & 0x0F; /* mantissa */ + + new_sample = (quantization << 3) + bias; /* add bias */ + new_sample <<= segment; + new_sample = (sign) ? (bias - new_sample) : (new_sample - bias); /* remove bias */ + +#if 0 // the above follows Sun's implementation, but this works too + { + static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */ + new_sample = exp_lut[segment] + (quantization << (segment + 3)); + if (sign != 0) new_sample = -new_sample; + } +#endif + + return new_sample; +} + +/* decodes u-law (ITU G.711 non-linear PCM), from g711.c */ +void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_count; for (i=first_sample,sample_count=0; ioffset+i,stream->streamfile); - - ulawbyte = ~ulawbyte; /* stored in complement */ - sign = (ulawbyte & 0x80); - segment = (ulawbyte & 0x70) >> 4; /* exponent */ - quantization = ulawbyte & 0x0F; /* mantissa */ - - sample = (quantization << 3) + bias; /* add bias */ - sample <<= segment; - sample = (sign) ? (bias - sample) : (sample - bias); /* remove bias */ - -#if 0 // the above follows Sun's implementation, but this works too - { - static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */ - sample = exp_lut[segment] + (quantization << (segment + 3)); - if (sign != 0) sample = -sample; - } -#endif - - outbuf[sample_count] = sample; + outbuf[sample_count] = expand_ulaw(ulawbyte); } } + +void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_count; + + for (i=first_sample,sample_count=0; ioffset+i*channelspacing,stream->streamfile); + outbuf[sample_count] = expand_ulaw(ulawbyte); + } +} + +static int expand_alaw(uint8_t alawbyte) { + int sign, segment, quantization, new_sample; + + alawbyte ^= 0x55; + sign = (alawbyte & 0x80); + segment = (alawbyte & 0x70) >> 4; /* exponent */ + quantization = alawbyte & 0x0F; /* mantissa */ + + new_sample = (quantization << 4); + switch (segment) { + case 0: + new_sample += 8; + break; + case 1: + new_sample += 0x108; + break; + default: + new_sample += 0x108; + new_sample <<= segment - 1; + break; + } + new_sample = (sign) ? new_sample : -new_sample; + + return new_sample; +} + /* decodes a-law (ITU G.711 non-linear PCM), from g711.c */ void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; - int sign, segment, quantization, sample; - + int i, sample_count; for (i=first_sample,sample_count=0; ioffset+i,stream->streamfile); - - alawbyte ^= 0x55; - sign = (alawbyte & 0x80); - segment = (alawbyte & 0x70) >> 4; /* exponent */ - quantization = alawbyte & 0x0F; /* mantissa */ - - sample = (quantization << 4); - switch (segment) { - case 0: - sample += 8; - break; - case 1: - sample += 0x108; - break; - default: - sample += 0x108; - sample <<= segment - 1; - break; - } - sample = (sign) ? sample : -sample; - - outbuf[sample_count] = sample; + outbuf[sample_count] = expand_alaw(alawbyte);; } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c index 1cc12ae98..a85250ad5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/psx_decoder.c @@ -6,12 +6,25 @@ #define VAG_USE_INTEGER_TABLE 0 /* PS ADPCM table (precalculated divs) */ -static const double VAG_f[5][2] = { +static const double VAG_f[16][2] = { { 0.0 , 0.0 }, { 60.0 / 64.0 , 0.0 }, { 115.0 / 64.0 , -52.0 / 64.0 }, { 98.0 / 64.0 , -55.0 / 64.0 }, - { 122.0 / 64.0 , -60.0 / 64.0 } + { 122.0 / 64.0 , -60.0 / 64.0 }, + /* extended table from PPSSPP (PSP emu), found by tests + * (only seen in inFamous PS3, very rare, possibly "SVAG" or "VAG-HE") */ + { 0.0 , 0.0 }, + { 0.0 , 0.0 }, + { 52.0 / 64.0 , 0.0 }, + { 55.0 / 64.0 , -2.0 / 64.0 }, + { 60.0 / 64.0 ,-125.0 / 64.0 }, + { 0.0 , 0.0 }, + { 0.0 , -91.0 / 64.0 }, + { 0.0 , 0.0 }, + { 2.0 / 64.0 ,-216.0 / 64.0 }, + { 125.0 / 64.0 , -6.0 / 64.0 }, + { 0.0 ,-151.0 / 64.0 }, }; #if VAG_USE_INTEGER_TABLE /* PS ADPCM table */ @@ -20,7 +33,19 @@ static const int8_t VAG_coefs[5][2] = { { 60 , 0 }, { 115 , -52 }, { 98 , -55 }, - { 122 , -60 } + { 122 , -60 }, + /* extended */ + { 0 , 0 }, + { 0 , 0 }, + { 52 , 0 }, + { 55 , -2 }, + { 60 ,-125 }, + { 0 , 0 }, + { 0 , -91 }, + { 0 , 0 }, + { 2 ,-216 }, + { 125 , -6 }, + { 0 ,-151 }, }; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index 7c6cdf282..29d20a321 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -19,7 +19,7 @@ static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * ou * * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html */ -vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) { +vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) { vorbis_custom_codec_data * data = NULL; int ok; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c index deeada360..96b6568db 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c @@ -20,10 +20,6 @@ static int CLAMP(int value, int Minim, int Maxim) return value; } -void xa_init_get_high_nibble(VGMSTREAM *vgmstream) { - vgmstream->xa_get_high_nibble=1; -} - void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index f3572d48d..975a3c543 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -42,6 +42,7 @@ static const char* extension_list[] = { "ass", "ast", "at3", + "at9", "aud", "aus", "awc", @@ -106,7 +107,7 @@ static const char* extension_list[] = { "emff", "enth", "exa", - "ezw", + "ezw", "fag", "ffw", @@ -156,6 +157,8 @@ static const char* extension_list[] = { "khv", "kovs", "kraw", + "ktss", + "kvs", "laac", //fake extension, for AAC (tri-Ace/FFmpeg) "lac3", //fake extension, for AC3 @@ -181,7 +184,7 @@ static const char* extension_list[] = { "mic", "mihb", "mnstr", - "mogg", + "mogg", //"mp4", //common //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", @@ -423,6 +426,7 @@ static const coding_info coding_info_list[] = { {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"}, {coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave (block)"}, {coding_ULAW, "8-bit u-Law"}, + {coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"}, {coding_ALAW, "8-bit a-Law"}, {coding_PCMFLOAT, "32-bit float PCM"}, @@ -521,6 +525,9 @@ static const coding_info coding_info_list[] = { #ifdef VGM_USE_MAIATRAC3PLUS {coding_AT3plus, "ATRAC3plus"}, #endif +#ifdef VGM_USE_ATRAC9 + {coding_ATRAC9, "ATRAC9"}, +#endif #ifdef VGM_USE_FFMPEG {coding_FFmpeg, "FFmpeg"}, #endif @@ -879,6 +886,7 @@ static const meta_info meta_info_list[] = { {meta_CSTM, "Nintendo 3DS CSTM Header"}, {meta_FSTM, "Nintendo Wii U FSTM Header"}, {meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"}, + {meta_KTSS, "Koei Tecmo Switch Sound Header"}, {meta_3DS_IDSP, "Nintendo IDSP Header"}, {meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"}, {meta_MCA, "Capcom MCA header"}, @@ -900,6 +908,7 @@ static const meta_info meta_info_list[] = { {meta_GTD, "GTD/GHS header"}, {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, + {meta_TA_AAC_VORBIS, "tri-Ace AAC (Mobile Vorbis) header"}, {meta_PS3_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_PC_XA30, "Reflections XA30 PC header"}, @@ -923,8 +932,7 @@ static const meta_info meta_info_list[] = { {meta_EA_SPS, "Electronic Arts SPS header"}, {meta_NGC_VID1, "Neversoft VID1 header"}, {meta_PC_FLX, "Ultima IX .FLX header"}, - {meta_MOGG, "Harmonix Music Systems MOGG Vorbis "}, - + {meta_MOGG, "Harmonix Music Systems MOGG Vorbis"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c index bcb8e673e..b9d921b8b 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c @@ -4,36 +4,41 @@ /* set up for the block at the given offset */ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { - int i; STREAMFILE* streamFile = vgmstream->ch[0].streamfile; - uint32_t id; - size_t file_size, block_size = 0, block_header = 0; + int i; + size_t block_size = 0, block_header = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + size_t file_size = get_streamfile_size(streamFile); - /* find target block ID and skip the rest */ - file_size = get_streamfile_size(streamFile); while (block_offset < file_size) { - id = read_32bitBE(block_offset+0x00,streamFile); - block_size = read_32bit(block_offset+0x04,streamFile); /* includes id/size */ - block_header = 0x0; + uint32_t id = read_32bitBE(block_offset+0x00,streamFile); - if (id == 0x31534E68) { /* "1SNh" header block found */ - block_header = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353 ? 0x28 : 0x2c; /* "EACS" */ - if (block_header < block_size) /* sometimes has data */ - break; + block_size = read_32bitLE(block_offset+0x04,streamFile); + if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */ + block_size = read_32bitBE(block_offset+0x04,streamFile); + + block_header = 0; + + if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */ + int is_sead = (id == 0x53454144); + int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353; + + block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c); + if (block_header >= block_size) /* sometimes has audio data after header */ + block_header = 0; } - - if (id == 0x31534E64) { /* "1SNd" data block found */ + else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ block_header = 0x08; + } + else if (id == 0x00000000) { break; } - if (id == 0x00000000 || id == 0xFFFFFFFF) { /* EOF: possible? */ + if (block_header) { break; } - /* any other blocks "1SNl" "1SNe" etc */ //todo parse movie blocks block_offset += block_size; } @@ -45,6 +50,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { /* set new channel offsets and block sizes */ switch(vgmstream->coding_type) { case coding_PCM8_int: + case coding_ULAW_int: vgmstream->current_block_size /= vgmstream->channels; for (i=0;ichannels;i++) { vgmstream->ch[i].offset = block_offset + block_header + i; @@ -66,13 +72,24 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { break; case coding_DVI_IMA: - vgmstream->current_block_size -= 0x14; - for(i = 0; i < vgmstream->channels; i++) { - off_t adpcm_offset = block_offset + block_header + 0x04; - vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04, streamFile); - vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + 0x04*vgmstream->channels + i*0x04, streamFile); - // todo some demuxed vids don't have ADPCM hist? not sure how to correctly detect - vgmstream->ch[i].offset = block_offset + block_header + 0x14; + if (vgmstream->codec_version == 1) { /* ADPCM hist */ + vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile); + vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */ + + for(i = 0; i < vgmstream->channels; i++) { + off_t adpcm_offset = block_offset + block_header + 0x04; + vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, streamFile); + vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, streamFile); + vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels; + } + + VGM_ASSERT(vgmstream->current_block_samples != (block_size - block_header - 0x04 - 0x08*vgmstream->channels) * 2 / vgmstream->channels, + "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset); + } + else { + for(i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + block_header; + } } break; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 39c8f2648..5e222339c 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -4,66 +4,58 @@ /* set up for the block at the given offset */ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; int new_schl = 0; - STREAMFILE* streamFile = vgmstream->ch[0].streamfile; - uint32_t id; - size_t file_size, block_size = 0, block_samples; + size_t block_size = 0, block_samples = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; - //int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; + size_t file_size = get_streamfile_size(streamFile); - /* find target block ID and skip the rest */ - file_size = get_streamfile_size(streamFile); while (block_offset < file_size) { - id = read_32bitBE(block_offset+0x00,streamFile); + uint32_t id = read_32bitBE(block_offset+0x00,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ + if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - /* SCxx blocks have size in the header, but others may not. To simplify we just try to find - * a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block) - * there may be a new SCHl + SCDl too, so this pretends they are a single stream. */ - if (id == 0x5343446C) { /* "SCDl" data block found */ + block_samples = 0; - /* use num_samples from header if possible; don't calc as data may have padding (ex. PCM8) or not possible (ex. MP3) */ + if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ switch(vgmstream->coding_type) { case coding_PSX: block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels); break; - default: block_samples = read_32bit(block_offset+0x08,streamFile); break; } - - /* guard against false positives (happens in "pIQT" blocks) */ - if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */ - block_offset += 0x04; - continue; - } - - break; } - else { - /* movie "pIQT" may be bigger than what block_size says, but seems to help */ - if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */ - block_offset += block_size; - } else { - block_offset += 0x04; + else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" etc) */ + /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ + if (id == 0x00000000) { + block_size = 0x04; } - if (id == 0x5343456C) { /* "SCEl" end block found */ - block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* 32b-aligned, important */ - /* Usually there is padding between SCEl and SCHl too (aligned to 0x80) */ - } - - if (id == 0x5343486C) { /* "SCHl", new subfile */ + if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ new_schl = 1; } + } - continue; + /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ + if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { + block_size = 0x04; + block_samples = 0; + } + + + if (block_samples) /* audio found */ + break; + block_offset += block_size; + + /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + block_offset += 0x04 - (block_offset % 0x04); } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c index b8bdd4f1c..bd208fd28 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/xa_blocked.c @@ -8,13 +8,13 @@ void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) { int8_t currentChannel=0; int8_t subAudio=0; - xa_init_get_high_nibble(vgmstream); + vgmstream->xa_get_high_nibble = 1; /* reset nibble order */ /* don't change this variable in the init process */ if (vgmstream->samples_into_block != 0) vgmstream->xa_sector_length += 0x80; - /* XA mode2/form2 sector + /* XA mode2/form2 sector, size 0x930 * 0x00: sync word * 0x0c: header = minute, second, sector, mode (always 0x02) * 0x10: subheader = file, channel (marker), submode flags, xa header @@ -23,9 +23,10 @@ void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) { * 0x918: unused * 0x92c: EDC/checksum or null * 0x930: end + * (in non-blocked ISO 2048 mode1/data chunks are 0x800) */ - /* submode flags (typical audio value = 0x64) + /* submode flag bits (typical audio value = 0x64) * - 7: end of file * - 6: real time mode * - 5: sector form (0=form1, 1=form2) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index a1e2ac250..516773e02 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -64,7 +64,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { } vgmstream->layout_type = layout_none; - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg); if (!vgmstream->codec_data) goto fail; #else goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index 4d3bf847d..ab2a605bd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -74,7 +74,7 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { cfg.chunk_size = awc.block_chunk; cfg.big_endian = awc.big_endian; - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index bce32875f..d4b7317c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -3,7 +3,7 @@ #include "../layout/layout.h" #define EA_CODEC_PCM 0x00 -//#define EA_CODEC_??? 0x01 //used in SAT videos +#define EA_CODEC_ULAW 0x01 #define EA_CODEC_IMA 0x02 #define EA_CODEC_PSX 0xFF //fake value @@ -20,10 +20,13 @@ typedef struct { int big_endian; int loop_flag; + int is_sead; + int codec_version; } ea_header; static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); -static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea); +static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea); +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); /* EA 1SNh - from early EA games (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { @@ -37,9 +40,15 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { goto fail; /* check header (first block) */ - if (read_32bitBE(0,streamFile)!=0x31534E68) /* "1SNh" */ + if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ + read_32bitBE(0x00,streamFile) != 0x53454144) /* "SEAD" */ goto fail; + /* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end. + * Video uses various blocks (kVGT/fVGT/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */ + + ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144; + /* use block size as endian marker (Saturn = BE) */ ea.big_endian = !(read_32bitLE(0x04,streamFile) < 0x0000FFFF); @@ -63,16 +72,22 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { vgmstream->meta_type = meta_EA_1SNH; switch (ea.codec) { - case EA_CODEC_PCM: + case EA_CODEC_PCM: /* Need for Speed (PC) */ vgmstream->coding_type = ea.bits==1 ? coding_PCM8_int : coding_PCM16_int; break; - case EA_CODEC_IMA: - if (ea.bits!=2) goto fail; - vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ + case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */ + if (ea.bits && ea.bits!=2) goto fail; /* only set in EACS */ + vgmstream->coding_type = coding_ULAW_int; break; - case EA_CODEC_PSX: + case EA_CODEC_IMA: /* Need for Speed II (PC) */ + if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */ + vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ + vgmstream->codec_version = ea.codec_version; + break; + + case EA_CODEC_PSX: /* Need for Speed (PS) */ vgmstream->coding_type = coding_PSX; break; @@ -98,7 +113,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; if (read_32bitBE(offset+0x00, streamFile) == 0x45414353) { /* "EACS" */ - /* PC/SAT EACS subheader */ + /* EACS subheader (PC, SAT) */ ea->sample_rate = read_32bit(offset+0x04, streamFile); ea->bits = read_8bit(offset+0x08, streamFile); ea->channels = read_8bit(offset+0x09, streamFile); @@ -109,15 +124,32 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { ea->loop_end = read_32bit(offset+0x14, streamFile) + ea->loop_start; /* loop length */ /* 0x18: data start? (0x00), 0x1c: pan/volume/etc? (0x7F), rest can be padding/garbage */ VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */ + + if (ea->codec == EA_CODEC_IMA) + ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + } + else if (ea->is_sead) { + /* alt subheader (found in some PC videos) */ + ea->sample_rate = read_32bit(offset+0x00, streamFile); + ea->channels = read_32bit(offset+0x04, streamFile); + ea->codec = read_32bit(offset+0x08, streamFile); + + if (ea->codec == EA_CODEC_IMA) + ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + + set_ea_1snh_num_samples(streamFile, 0x00, ea); + if (ea->loop_start_offset) /* offset found, now find actual start sample */ + set_ea_1snh_num_samples(streamFile, 0x00, ea); } else { - /* PS subheader */ + /* alt subheader (PS) */ ea->sample_rate = read_32bit(offset+0x00, streamFile); ea->channels = read_8bit(offset+0x18, streamFile); ea->codec = EA_CODEC_PSX; - set_ea_1snh_psx_samples(streamFile, 0x00, ea); - if (ea->loop_start_offset)/* found offset, now find sample start */ - set_ea_1snh_psx_samples(streamFile, 0x00, ea); + + set_ea_1snh_num_samples(streamFile, 0x00, ea); + if (ea->loop_start_offset) /* offset found, now find actual start sample */ + set_ea_1snh_num_samples(streamFile, 0x00, ea); } ea->loop_flag = (ea->loop_end > 0); @@ -126,42 +158,61 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { } /* get total samples by parsing block headers, needed when EACS isn't present */ -static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea) { +static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea) { int num_samples = 0, loop_start = 0, loop_end = 0, loop_start_offset = 0; off_t block_offset = start_offset; - size_t file_size = get_streamfile_size(streamFile); + size_t block_size, block_header, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; + size_t file_size = get_streamfile_size(streamFile); + while (block_offset < file_size) { uint32_t id = read_32bitBE(block_offset+0x00,streamFile); - size_t block_size = read_32bit(block_offset+0x04,streamFile); /* includes id/size */ + block_size = read_32bit(block_offset+0x04,streamFile); + block_header = 0; + block_samples = 0; - if (id == 0x31534E68) { /* "1SNh" header block found */ - size_t block_header = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353 ? 0x28 : 0x2c; /* "EACS" */ - if (block_header < block_size) /* sometimes has data */ - num_samples += ps_bytes_to_samples(block_size - block_header, ea->channels); + if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */ + int is_sead = (id == 0x53454144); + int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353; + + block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c); + if (block_header >= block_size) /* sometimes has audio data after header */ + block_header = 0; } - - if (id == 0x31534E64) { /* "1SNd" data block found */ - num_samples += ps_bytes_to_samples(block_size - 0x08, ea->channels); + else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ + block_header = 0x08; } - - if (id == 0x31534E6C) { /* "1SNl" loop point found */ + else if (id == 0x00000000) { + break; + } + else if (id == 0x31534E6C) { /* "1SNl" loop point found */ loop_start_offset = read_32bit(block_offset+0x08,streamFile); loop_end = num_samples; } - if (id == 0x00000000 || id == 0xFFFFFFFF) { /* EOF: possible? */ - break; + if (block_header) { + switch(ea->codec) { + case EA_CODEC_PSX: + block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels); + break; + case EA_CODEC_IMA: + if (ea->codec_version == 1) + block_samples = read_32bit(block_offset + block_header, streamFile); + else + block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); + break; + } } - /* if there is a loop start offset this was called again just to find it */ + + /* if there is a loop start offset set, this was called again just to find it */ if (ea->loop_start_offset && ea->loop_start_offset == block_offset) { ea->loop_start = num_samples; return; } - /* any other blocks "1SNl" "1SNe" etc */ //todo parse movie blocks + num_samples += block_samples; block_offset += block_size; } @@ -171,3 +222,31 @@ static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset, ea->loop_end = loop_end; ea->loop_start_offset = loop_start_offset; } + +/* find codec version used, with or without ADPCM hist per block */ +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { + off_t block_offset = start_offset; + size_t file_size = get_streamfile_size(streamFile); + int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; + + while (block_offset < file_size) { + uint32_t id = read_32bitBE(block_offset+0x00,streamFile); + + size_t block_size = read_32bitLE(block_offset+0x04,streamFile); + if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */ + block_size = read_32bitBE(block_offset+0x04,streamFile); + + if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ + size_t ima_samples = read_32bit(block_offset + 0x08, streamFile); + size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels; + + if (ima_samples == expected_samples) { + return 1; /* has ADPCM hist (hopefully) */ + } + } + + block_offset += block_size; + } + + return 0; /* no ADPCM hist */ +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 03eb0ebda..534350afe 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -214,18 +214,36 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom_codec_data(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_blocked_ea_sns; break; } #endif + +#if 0 //todo unknown variation +#ifdef VGM_USE_ATRAC9 + case 0x0a: { /* EATrax */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead); + /* 0x0c: data size without blocks?, 0x10: frame size? (same as config data?) */ + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_blocked_ea_sns; + break; + } +#endif +#endif + case 0x00: /* "NONE" (internal 'codec not set' flag) */ case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ case 0x08: /* ? */ case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ - case 0x0a: /* EATrax (ATRAC9 variant, has deflated fillers a la EA-XMA) */ case 0x0b: /* ? */ case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */ case 0x0d: /* ? */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 5c409da92..e8cfa8a9a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -28,7 +28,7 @@ #define EA_CODEC1_NONE -1 #define EA_CODEC1_PCM 0x00 #define EA_CODEC1_VAG 0x01 // unsure -#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, Fifa 98 SAT +#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, FIFA 98 SAT #define EA_CODEC1_MT10 0x09 //#define EA_CODEC1_N64 ? @@ -91,12 +91,14 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { goto fail; /* check header */ - /* EA's stream files are made of blocks called "chunks" (SCxx, presumably Sound Chunk xx) - * typically: SCHl=header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=stream end. - * The number/size of blocks is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ - if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */ + if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */ + read_32bitBE(0x00,streamFile) != 0x5348454E) /* "SHEN" */ goto fail; + /* stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. + * Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHEN/SDEN/SEEN). + * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ + header_size = read_32bitLE(0x04,streamFile); if (header_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ header_size = read_32bitBE(0x04,streamFile); @@ -318,7 +320,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -331,7 +333,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ if (!mpeg_start_offset) goto fail; /* layout is still blocks, but should work fine with the custom mpeg decoder */ - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -451,7 +453,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be memset(ea,0,sizeof(ea_header)); /* null defaults as 0 can be valid */ - ea->version = EA_VERSION_NONE; + ea->version = EA_VERSION_NONE; ea->codec1 = EA_CODEC1_NONE; ea->codec2 = EA_CODEC2_NONE; @@ -747,70 +749,73 @@ fail: return 0; } -/* get total samples by parsing block headers, needed when multiple files are stitched together */ -/* Some EA files (.mus, .eam, .sng, etc) concat many small subfiles, used as mapped - * music (.map/lin). We get total possible samples (counting all subfiles) and pretend - * they are a single stream. Subfiles always share header, except num_samples. */ +/* Get total samples by parsing block headers, needed when multiple files are stitched together. + * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped + * music (.map/lin). Subfiles always share header, except num_samples. */ static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { int num_samples = 0; - size_t file_size = get_streamfile_size(streamFile); + int new_schl = 0; off_t block_offset = start_offset; + size_t block_size, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; + size_t file_size = get_streamfile_size(streamFile); + while (block_offset < file_size) { - uint32_t id, block_size, block_samples; - - id = read_32bitBE(block_offset+0x00,streamFile); + uint32_t id = read_32bitBE(block_offset+0x00,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ + if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); - /* SCxx blocks have size in the header, but others may not. To simplify we just try to - * find a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block) - * there may be a new SCHl + SCDl too, so this pretends they are a single stream. */ - if (id == 0x5343446C) { /* "SCDl" data block found */ + block_samples = 0; - /* use num_samples from header if possible */ + if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */ switch (ea->codec2) { - case EA_CODEC2_VAG: /* PS-ADPCM */ + case EA_CODEC2_VAG: block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels); break; - default: block_samples = read_32bit(block_offset+0x08,streamFile); break; } - - /* guard against false positives (happens in "pIQT" blocks) */ - if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */ - block_offset += 0x04; - continue; - } - - num_samples += block_samples; - - block_offset += block_size; /* size includes header */ } - else { - /* movie "pIQT" may be bigger than what block_size says, but seems to help */ - if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */ - block_offset += block_size; - } else { - block_offset += 0x04; + else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" "MPCh" etc) */ + /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ + if (id == 0x00000000) { + block_size = 0x04; } - if (id == 0x5343456C) { /* "SCEl" end block found */ - /* Usually there is padding between SCEl and SCHl (aligned to 0x80) */ - block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* also 32b-aligned, important */ + if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */ + new_schl = 1; } + } - block_offset += 0x04; - continue; + /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ + if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { + VGM_LOG("EA SCHl: bad block size %x at %lx\n", block_size, block_offset); + block_size = 0x04; + block_samples = 0; + } + + num_samples += block_samples; + block_offset += block_size; + + /* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */ + if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) { + VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n"); + block_offset += 0x04 - (block_offset % 0x04); } } - return num_samples; + /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ + if (new_schl) { + ;VGM_LOG("EA SCHl: multiple SCHl found\n"); + return num_samples; + } + else { + return 0; + } } /* find data start offset inside the first SCDl; not very elegant but oh well */ @@ -825,7 +830,7 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start id = read_32bitBE(block_offset+0x00,streamFile); block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ + if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); if (id == 0x5343446C) { /* "SCDl" data block found */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 388cad2e7..5b0e12e7e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -296,7 +296,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { //VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index c648db712..a29fa70e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -6,37 +6,42 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t StartOffset = 0, NameOffset = 0; - off_t SampleHeaderStart = 0, DSPInfoStart = 0; - size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0; + off_t SampleHeaderStart = 0, ExtraInfoStart = 0; + size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0; uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; - int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID; + int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; int TotalStreams, TargetStream = streamFile->stream_index; - uint32_t VorbisSetupId = 0; int i; /* check extension, case insensitive */ - if (!check_extensions(streamFile,"fsb")) goto fail; + if (!check_extensions(streamFile,"fsb")) + goto fail; - if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */ + if (read_32bitBE(0x00,streamFile) != 0x46534235) /* "FSB5" */ + goto fail; - //v0 has extra flags at 0x1c and BaseHeaderLength = 0x40? - if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */ + /* 0x00 is rare (seen in Tales from Space Vita) */ + Version = read_32bitLE(0x04,streamFile); + if (Version != 0x00 && Version != 0x01) goto fail; TotalStreams = read_32bitLE(0x08,streamFile); SampleHeaderLength = read_32bitLE(0x0C,streamFile); NameTableLength = read_32bitLE(0x10,streamFile); SampleDataLength = read_32bitLE(0x14,streamFile); CodingID = read_32bitLE(0x18,streamFile); - /* 0x1c (8): zero, 0x24 (16): hash, 0x34 (8): unk */ - BaseHeaderLength = 0x3C; + /* type 0x01 - 0x1c(8): zero, 0x24(16): hash, 0x34(8): unk + * type 0x00 has an extra field (always 0?) at 0x1c */ + BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C; - SampleHeaderStart = BaseHeaderLength; + if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) + goto fail; - if ((SampleHeaderLength + NameTableLength + SampleDataLength + 0x3C) != get_streamfile_size(streamFile)) goto fail; if (TargetStream == 0) TargetStream = 1; /* default to 1 */ if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail; + SampleHeaderStart = BaseHeaderLength; + /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ for (i = 1; i <= TotalStreams; i++) { @@ -113,18 +118,23 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x04: /* free comment, or maybe SFX info */ break; + //case 0x05: /* Unknown (32b) */ + // /* found in Tearaway Vita, value 0, first stream only */ + // break; case 0x06: /* XMA seek table */ /* no need for it */ break; - case 0x07: /* DSP Info (Coeffs) */ - DSPInfoStart = ExtraFlagStart + 0x04; + case 0x07: /* DSP coeffs */ + ExtraInfoStart = ExtraFlagStart + 0x04; break; - case 0x09: /* ATRAC9 data */ + case 0x09: /* ATRAC9 config */ + ExtraInfoStart = ExtraFlagStart + 0x04; + ExtraInfoSize = ExtraFlagSize; break; case 0x0a: /* XWMA data */ break; - case 0x0b: /* Vorbis data */ - VorbisSetupId = (uint32_t)read_32bitLE(ExtraFlagStart+0x04,streamFile); /* crc32? */ + case 0x0b: /* Vorbis setup ID and seek table */ + ExtraInfoStart = ExtraFlagStart + 0x04; /* seek table format: * 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0 * 0x0C: sample number (only some samples are saved in the table) @@ -132,7 +142,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { * (xN entries) */ break; - //case 0x0d: /* Unknown value (32b), found in some XMA2 and Vorbis */ + //case 0x0d: /* Unknown (32b) */ + // /* found in some XMA2 and Vorbis */ // break; default: VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); @@ -221,7 +232,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x02; - dsp_read_coefs_be(vgmstream,streamFile,DSPInfoStart,0x2E); + dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E); break; case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ @@ -263,7 +274,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */ - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; @@ -272,8 +283,36 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ goto fail; - case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */ - goto fail; +#ifdef VGM_USE_ATRAC9 + case 0x0D: {/* FMOD_SOUND_FORMAT_AT9 */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + switch(ExtraInfoSize) { + case 0x04: /* Little Big Planet 2ch (Vita), Guacamelee (Vita) */ + cfg.config_data = read_32bitBE(ExtraInfoStart,streamFile); + break; + case 0x08: /* Day of the Tentacle Remastered (Vita) */ + /* 0x00: superframe size (also in config_data) */ + cfg.config_data = read_32bitBE(ExtraInfoStart+0x04,streamFile); + break; + //case 0x0c: /* Little Big Planet 6ch (Vita) */ + // //todo: this is just 0x04 x3, in case of 4ch would be 0x08 --must improve detection + // //each stream has its own config_data (but seem to be the same), interleaves 1 super frame per stream + // break; + default: + VGM_LOG("FSB5: unknown extra info size 0x%x\n", ExtraInfoSize); + goto fail; + } + //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; + } +#endif case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ goto fail; @@ -284,11 +323,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { cfg.channels = vgmstream->channels; cfg.sample_rate = vgmstream->sample_rate; - cfg.setup_id = VorbisSetupId; + cfg.setup_id = read_32bitLE(ExtraInfoStart,streamFile); vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, StartOffset, VORBIS_FSB, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, StartOffset, VORBIS_FSB, &cfg); if (!vgmstream->codec_data) goto fail; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index f42d442e7..d0a451da8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -239,7 +239,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case coding_MPEG_layer3: vgmstream->layout_type = layout_none; - vgmstream->codec_data = init_mpeg_codec_data(streamFile, genh.start_offset, &coding, vgmstream->channels); + vgmstream->codec_data = init_mpeg(streamFile, genh.start_offset, &coding, vgmstream->channels); if (!vgmstream->codec_data) goto fail; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c index de7c5e13d..c334ad5cd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -typedef enum { XMA2 } gtd_codec; +typedef enum { XMA2, ATRAC9 } gtd_codec; /* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { @@ -10,6 +10,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { size_t data_size, chunk_size; int loop_flag, channel_count, sample_rate; int num_samples, loop_start_sample, loop_end_sample; + uint32_t at9_config_data; gtd_codec codec; @@ -34,30 +35,41 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { data_size = read_32bitBE(0x5c,streamFile); /* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */ - stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);; + stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile); if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */ name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */ } codec = XMA2; } + else if (0x34 + read_32bitLE(0x30,streamFile) + read_32bitLE(0x0c,streamFile) == get_streamfile_size(streamFile)) { /* ATRAC9 */ + + data_size = read_32bitLE(0x0c,streamFile); + start_offset = 0x34 + read_32bitLE(0x30,streamFile); + channel_count = read_32bitLE(0x10,streamFile); + sample_rate = read_32bitLE(0x14,streamFile); + at9_config_data = read_32bitBE(0x28,streamFile); + /* 0x18-0x28: fixed/unknown values */ + + stpr_offset = 0x2c; + if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */ + name_offset = stpr_offset + 0xE8; /* there are offsets fields but seems to work */ + } + + codec = ATRAC9; + } else { - /* there are PSV (LE, ATRAC9 data) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar */ - - /* for PSV: */ - /* 0x0c: data_size, 0x10: channles, 0x14: sample rate, 0x18-0x2c: fixed and unknown values */ - /* 0x2c: STPR chunk, with name_offset at + 0xE8 */ - + /* apparently there is a PS3 variation (MSF inside?) */ goto fail; } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->meta_type = meta_GTD; @@ -77,9 +89,29 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { if ( !vgmstream->codec_data ) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + vgmstream->num_samples = num_samples; break; } #endif +#ifdef VGM_USE_ATRAC9 + case ATRAC9: { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = at9_config_data; + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data); + break; + } + +#endif + default: goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 31af3f247..d83949070 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -147,7 +147,81 @@ static const hcakey_info hcakey_list[] = { // Kirara Fantasia (Android/iOS) {51408295487268137}, // 00B6A3928706E529 + // A3! (iOS/Android) + {914306251}, // 00000000367F34CB + + // Weekly Shonen Jump: Ore Collection! (iOS/Android) + {11708691}, // 0000000000B2A913 + + // Monster Gear Versus (iOS/Android) + {0xB1E30F346415B475}, // B1E30F346415B475 + + // Yumeiro Cast (iOS/Android) + {14418}, // 0000000000003852 + + // Ikki Tousen: Straight Striker (iOS/Android) + {1000}, // 00000000000003E8 + + // Zero kara Hajimeru Mahou no Sho (iOS/Android) + {0xD2E836E662F20000}, // D2E836E662F20000 + + // Soul Reverse Zero (iOS/Android) + {2873513618}, // 00000000AB465692 + + // Jojo's Bizarre Adventure: Diamond Records (iOS/Android) [additional data] + {0x820212864CAB35DE}, // 820212864CAB35DE + + // HUNTER x HUNTER: World Hunt (iOS/Android) + {71777214294589695}, // 00FF00FF00FF00FF + + // \Comepri/ Comedy Prince (iOS/Android) + {201537197868}, // 0000002EEC8D972C + + // Puzzle of Empires (iOS/Android) + {13687846}, // 0000000000D0DC26 + + // Aozora Under Girls! (iOS/Android) + {4988006236073}, // 000004895C56FFA9 + + // Castle & Dragon (iOS/Android) + {20140528}, // 00000000013351F0 + + // Uta no Prince sama Shining Live (iOS/Android) + {2122831366}, // 000000007E87D606 + + // Sevens Story (iOS/Android) + {629427372852}, // 000000928CCB8334 + + // MinGol: Everybody's Golf (iOS/Android) + {1430028151061218}, // 0005149A5FF67AE2 + + // AKB48 Group Tsui ni Koushiki Otoge demashita. (iOS/Android) + {831021912315111419}, // 0B886206BC1BA7FB + + // Sen no Kaizoku (iOS/Android) + {81368371967}, // 00000012F1EED2FF + + // I Chu (iOS/Android) + {13456}, // 0000000000003490 + + // Shinobi Nightmare (iOS/Android) + {369481198260487572}, // 0520A93135808594 + + // Bungo Stray Dogs: Mayoi Inu Kaikitan (iOS/Android) + {1655728931134731873}, // 16FA54B0C09F7661 + + // Super Sentai Legend Wars (iOS/Android) + {4017992759667450}, // 000E4657D7266AFA + + // Metal Saga: The Ark of Wastes (iOS/Android) + {100097101118097115}, // 01639DC87B30C6DB + + // Taga Tame no Alchemist (iOS/Android) + {5047159794308}, // 00000497222AAA84 + + // Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) + {4902201417679}, // 0000047561F95FCF + }; - #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c new file mode 100644 index 000000000..632285945 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -0,0 +1,49 @@ +#include "meta.h" +#include "../util.h" +#include "../stack_alloc.h" +#include "../coding/coding.h" + +VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int loop_flag, channel_count; + int32_t loop_length; + off_t start_offset; + + if (!check_extensions(streamFile, "ktss")) + goto fail; + + if (read_32bitBE(0, streamFile) != 0x4B545353) /* "KTSS" */ + goto fail; + + loop_length = read_32bitLE(0x38, streamFile); + loop_flag = loop_length > 0; + channel_count = read_8bit(0x29, streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = read_32bitLE(0x30, streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x2c, streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x34, streamFile); + vgmstream->loop_end_sample = vgmstream->loop_start_sample + loop_length; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_KTSS; + + vgmstream->interleave_block_size = 0x8; + + dsp_read_coefs_le(vgmstream, streamFile, 0x40, 0x2e); + start_offset = read_32bitLE(0x24, streamFile) + 0x20; + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index bc0ba6b03..cd7a78c7a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -152,7 +152,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_xnbm(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_xnb(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile); @@ -602,6 +602,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_kt_wiibgm(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_ktss(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_mca(STREAMFILE* streamFile); @@ -642,6 +643,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_vid1.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_vid1.c index ec2576423..6e4120eb5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_vid1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_vid1.c @@ -57,7 +57,7 @@ VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg); if (!vgmstream->codec_data) goto fail; } #else diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c index fe3a9ba16..02aaade46 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c @@ -189,7 +189,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { strcasecmp("ogg",filename_extension(filename))) { if (!strcasecmp("um3",filename_extension(filename))) { um3_ogg = 1; - } else if (!strcasecmp("kovs",filename_extension(filename))) { + } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie, kovs: header id only? */ kovs_ogg = 1; } else { goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogl.c b/Frameworks/vgmstream/vgmstream/src/meta/ogl.c index 2f655b09f..66587b82c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogl.c @@ -46,7 +46,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x14, VORBIS_OGL, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, 0x14, VORBIS_OGL, &cfg); if (!vgmstream->codec_data) goto fail; start_offset = cfg.data_start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c index 752e60148..7e5350a79 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/p3d.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/p3d.c @@ -151,7 +151,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { cfg.data_size = data_size; /* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */ - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index 2d3e4e72d..fc4e3cdba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -173,7 +173,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { mpeg_codec_data *mpeg_data = NULL; coding_t ct; - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &ct, vgmstream->channels); + mpeg_data = init_mpeg(streamFile, start_offset, &ct, vgmstream->channels); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index c7c4265b3..31412d5ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -4,10 +4,10 @@ #include "../util.h" #include -/* Resource Interchange File Format */ +/* RIFF - Resource Interchange File Format, standard container used in many games */ /* return milliseconds */ -static long parse_marker(unsigned char * marker) { +static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; if (memcmp("Marker ",marker,7)) return -1; @@ -21,7 +21,6 @@ static long parse_marker(unsigned char * marker) { static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { int loop_start_found = 0; int loop_end_found = 0; - off_t current_chunk = adtl_offset+4; while (current_chunk < adtl_offset+adtl_length) { @@ -31,39 +30,31 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream if (current_chunk+8+chunk_size > adtl_offset+adtl_length) return; switch(chunk_type) { - case 0x6c61626c: /* labl */ - { - unsigned char *labelcontent; - labelcontent = malloc(chunk_size-4); - if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0xc, - chunk_size-4,streamFile)!=chunk_size-4) { - free(labelcontent); - return; - } - - switch (read_32bitLE(current_chunk+8,streamFile)) { - case 1: - if (!loop_start_found && - (*loop_start = parse_marker(labelcontent))>=0) - { - loop_start_found = 1; - } - break; - case 2: - if (!loop_end_found && - (*loop_end = parse_marker(labelcontent))>=0) - { - loop_end_found = 1; - } - break; - default: - break; - } - + case 0x6c61626c: { /* labl */ + unsigned char *labelcontent; + labelcontent = malloc(chunk_size-4); + if (!labelcontent) return; + if (read_streamfile(labelcontent,current_chunk+0xc, chunk_size-4,streamFile)!=chunk_size-4) { free(labelcontent); + return; } + + switch (read_32bitLE(current_chunk+8,streamFile)) { + case 1: + if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent))>=0) + loop_start_found = 1; + break; + case 2: + if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent))>=0) + loop_end_found = 1; + break; + default: + break; + } + + free(labelcontent); break; + } default: break; } @@ -71,7 +62,8 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream current_chunk += 8 + chunk_size; } - if (loop_start_found && loop_end_found) *loop_flag = 1; + if (loop_start_found && loop_end_found) + *loop_flag = 1; /* labels don't seem to be consistently ordered */ if (*loop_start > *loop_end) { @@ -81,29 +73,21 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream } } -struct riff_fmt_chunk { +typedef struct { off_t offset; off_t size; int sample_rate; int channel_count; uint32_t block_size; + int coding_type; int interleave; -}; - -static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, struct riff_fmt_chunk * fmt, int sns, int mwv) { +} riff_fmt_chunk; +static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) { int codec, bps; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - - if (big_endian) { - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } + int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; fmt->offset = current_chunk; fmt->size = read_32bit(current_chunk+0x4,streamFile); @@ -111,6 +95,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->sample_rate = read_32bit(current_chunk+0x0c,streamFile); fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile); fmt->block_size = read_16bit(current_chunk+0x14,streamFile); + fmt->interleave = 0; bps = read_16bit(current_chunk+0x16,streamFile); codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); @@ -119,11 +104,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x01: /* PCM */ switch (bps) { case 16: - if (big_endian) { - fmt->coding_type = coding_PCM16BE; - } else { - fmt->coding_type = coding_PCM16LE; - } + fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; fmt->interleave = 2; break; case 8: @@ -136,24 +117,18 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk break; case 0x02: /* MS ADPCM */ - if (bps != 4) /* ensure 4bps */ - goto fail; + if (bps != 4) goto fail; fmt->coding_type = coding_MSADPCM; - fmt->interleave = 0; break; case 0x11: /* MS IMA ADPCM */ - if (bps != 4) /* ensure 4bps */ - goto fail; + if (bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; - fmt->interleave = 0; break; - case 0x69: /* MS IMA ADPCM - Rayman Raving Rabbids 2 (PC) */ - if (bps != 4) /* ensure 4bps */ - goto fail; + case 0x69: /* MS IMA ADPCM (XBOX) - Rayman Raving Rabbids 2 (PC) */ + if (bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; - fmt->interleave = 0; break; case 0x007A: /* MS IMA ADPCM (LA Rush, Psi Ops PC) */ @@ -167,7 +142,6 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk goto fail; //fmt->coding_type = coding_MS_IMA_3BIT; else goto fail; - fmt->interleave = 0; break; case 0x0555: /* Level-5 0x555 ADPCM */ @@ -179,34 +153,51 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x5050: /* Ubisoft .sns uses this for DSP */ if (!sns) goto fail; fmt->coding_type = coding_NGC_DSP; - fmt->interleave = 8; + fmt->interleave = 0x08; break; + case 0x270: /* ATRAC3 */ #ifdef VGM_USE_FFMPEG - case 0x270: /* ATRAC3 */ -#if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MAIATRAC3PLUS) - case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */ -#endif /* defined */ - fmt->coding_type = coding_FFmpeg; - fmt->interleave = 0; - break; -#endif /* VGM_USE_FFMPEG */ - -#ifdef VGM_USE_MAIATRAC3PLUS - case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */ - if (read_32bit(current_chunk+0x20,streamFile) == 0xE923AABF && - read_16bit(current_chunk+0x24,streamFile) == (int16_t)0xCB58 && - read_16bit(current_chunk+0x26,streamFile) == 0x4471 && - read_32bitLE(current_chunk+0x28,streamFile) == 0xFAFF19A1 && - read_32bitLE(current_chunk+0x2C,streamFile) == 0x62CEE401) { - uint16_t bztmp = read_16bit(current_chunk+0x32,streamFile); - bztmp = (bztmp >> 8) | (bztmp << 8); - fmt->coding_type = coding_AT3plus; - fmt->block_size = (bztmp & 0x3FF) * 8 + 8; - fmt->interleave = 0; - } - break; + fmt->coding_type = coding_FFmpeg; + break; +#else + goto fail; #endif + + case 0xFFFE: /* WAVEFORMATEXTENSIBLE */ + + /* ATRAC3plus GUID (0xBFAA23E9 58CB7144 A119FFFA 01E4CE62) */ + if (read_32bit(current_chunk+0x20,streamFile) == 0xE923AABF && + read_16bit(current_chunk+0x24,streamFile) == (int16_t)0xCB58 && + read_16bit(current_chunk+0x26,streamFile) == 0x4471 && + read_32bitLE(current_chunk+0x28,streamFile) == 0xFAFF19A1 && + read_32bitLE(current_chunk+0x2C,streamFile) == 0x62CEE401) { +#ifdef VGM_USE_MAIATRAC3PLUS + uint16_t bztmp = read_16bit(current_chunk+0x32,streamFile); + bztmp = (bztmp >> 8) | (bztmp << 8); + fmt->coding_type = coding_AT3plus; + fmt->block_size = (bztmp & 0x3FF) * 8 + 8; //should match fmt->block_size +#elif defined(VGM_USE_FFMPEG) + fmt->coding_type = coding_FFmpeg; +#else + goto fail; +#endif + } + + /* ATRAC9 GUID 47E142D2-36BA-4d8d-88FC-61654F8C836C (D242E147 BA368D4D 88FC6165 4F8C836C) */ + if (read_32bitBE(current_chunk+0x20,streamFile) == 0xD242E147 && + read_32bitBE(current_chunk+0x24,streamFile) == 0xBA368D4D && + read_32bitBE(current_chunk+0x28,streamFile) == 0x88FC6165 && + read_32bitBE(current_chunk+0x2c,streamFile) == 0x4F8C836C) { +#ifdef VGM_USE_ATRAC9 + fmt->coding_type = coding_ATRAC9; +#else + goto fail; +#endif + } + + break; + default: goto fail; } @@ -219,82 +210,61 @@ fail: VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + riff_fmt_chunk fmt = {0}; - struct riff_fmt_chunk fmt; + size_t file_size, riff_size, data_size = 0; + off_t start_offset = 0; -#ifdef VGM_USE_FFMPEG - ffmpeg_codec_data *ffmpeg_data = NULL; -#endif - - off_t file_size = -1; - int sample_count = 0; int fact_sample_count = -1; int fact_sample_skip = -1; - off_t start_offset = -1; int loop_flag = 0; long loop_start_ms = -1; long loop_end_ms = -1; off_t loop_start_offset = -1; off_t loop_end_offset = -1; - uint32_t riff_size; - uint32_t data_size = 0; int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; - /* Level-5 mwv */ - int mwv = 0; + int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */ off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; + int sns = 0; /* Ubisoft .sns LyN engine (Red Steel 2, Just Dance 3) */ + int at3 = 0; /* Sony ATRAC3 / ATRAC3plus */ + int at9 = 0; /* Sony ATRAC9 */ - /* Ubisoft sns */ - int sns = 0; - - /* Sony atrac3 / 3plus */ - int at3 = 0; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("wav",filename_extension(filename)) - && strcasecmp("lwav",filename_extension(filename)) - && strcasecmp("da",filename_extension(filename)) /* SD Gundam - Over Galaxian, The Great Battle VI (PS) */ - && strcasecmp("cd",filename_extension(filename)) /* Exector (PS) */ -#ifndef VGM_USE_FFMPEG - && strcasecmp("sgb",filename_extension(filename)) /* SGB has proper support with FFmpeg in sgxd */ -#endif - && strcasecmp("med",filename_extension(filename)) - ) - { - if (!strcasecmp("mwv",filename_extension(filename))) { - mwv = 1; - } - else if (!strcasecmp("sns",filename_extension(filename))) { - sns = 1; - } -#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG) - /* .RWS: AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP) - * .AUD: EA Replay */ - else if ( check_extensions(streamFile, "at3,rws,aud") ) { - at3 = 1; - } -#endif - else { - goto fail; - } + /* check extension, case insensitive + * .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC) */ + if ( check_extensions(streamFile, "wav,lwav,da,cd,med") ) { + ; + } + else if ( check_extensions(streamFile, "mwv") ) { + mwv = 1; + } + else if ( check_extensions(streamFile, "sns") ) { + sns = 1; + } + /* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */ + else if ( check_extensions(streamFile, "at3,rws,aud") ) { + at3 = 1; + } + else if ( check_extensions(streamFile, "at9") ) { + at9 = 1; + } + else { + goto fail; } /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494646) /* "RIFF" */ + if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ goto fail; - /* check for WAVE form */ - if ((uint32_t)read_32bitBE(8,streamFile)!=0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitLE(4,streamFile); + riff_size = read_32bitLE(0x04,streamFile); file_size = get_streamfile_size(streamFile); - /* check for tructated RIFF */ + /* check for truncated RIFF */ if (file_size < riff_size+8) goto fail; /* read through chunks to verify format and find metadata */ @@ -344,42 +314,34 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } break; case 0x736D706C: /* smpl */ - /* check loop count */ - if (read_32bitLE(current_chunk+0x24, streamFile)==1) - { - /* check loop info */ - if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) - { + /* check loop count and loop info */ + if (read_32bitLE(current_chunk+0x24, streamFile)==1) { + if (read_32bitLE(current_chunk+0x2c+4, streamFile)==0) { loop_flag = 1; - loop_start_offset = - read_32bitLE(current_chunk+0x2c+8, streamFile); - loop_end_offset = - read_32bitLE(current_chunk+0x2c+0xc,streamFile); + loop_start_offset = read_32bitLE(current_chunk+0x2c+8, streamFile); + loop_end_offset = read_32bitLE(current_chunk+0x2c+0xc,streamFile); } } break; case 0x70666c74: /* pflt */ if (!mwv) break; /* ignore if not in an mwv */ - /* predictor filters */ - mwv_pflt_offset = current_chunk; + + mwv_pflt_offset = current_chunk; /* predictor filters */ break; case 0x6374726c: /* ctrl */ - if (!mwv) break; /* ignore if not in an mwv */ - /* loops! */ - if (read_32bitLE(current_chunk+8, streamFile)) - { - loop_flag = 1; - } + if (!mwv) break; + + loop_flag = read_32bitLE(current_chunk+0x08, streamFile); mwv_ctrl_offset = current_chunk; break; case 0x66616374: /* fact */ if (sns && chunk_size == 0x10) { - fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); - } else if (at3 && chunk_size == 0x8) { - fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); - fact_sample_skip = read_32bitLE(current_chunk+0xc, streamFile); - } else if (at3 && chunk_size == 0xc) { - fact_sample_count = read_32bitLE(current_chunk+0x8, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + } else if ((at3 || at9) && chunk_size == 0x08) { + fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); + } else if ((at3 || at9) && chunk_size == 0x0c) { + fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); } @@ -408,103 +370,136 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { goto fail; + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = fmt.sample_rate; + + /* init, samples */ switch (fmt.coding_type) { case coding_PCM16LE: - sample_count = data_size/2/fmt.channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16); break; case coding_PCM8_U_int: - sample_count = data_size/fmt.channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; case coding_L5_555: - sample_count = data_size/0x12/fmt.channel_count*32; - break; - case coding_MSADPCM: - sample_count = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); - break; - case coding_MS_IMA: - sample_count = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count + - ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0); - break; - case coding_NGC_DSP: - //sample_count = data_size / fmt.channel_count / 8 * 14; /* expected from the "fact" chunk */ - break; -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - { - ffmpeg_data = init_ffmpeg_offset(streamFile, 0, streamFile->get_size(streamFile) ); - if ( !ffmpeg_data ) goto fail; + vgmstream->num_samples = data_size / 0x12 / fmt.channel_count * 32; - sample_count = ffmpeg_data->totalSamples; /* fact_sample_count */ + /* coefs */ + if (mwv) { + int i, ch; + const int filter_order = 3; + int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, streamFile); + if (filter_count > 0x20) goto fail; - if (at3) { - /* the encoder introduces some garbage (not always silent) samples to skip before the stream */ - /* manually set skip_samples if FFmpeg didn't do it */ - if (ffmpeg_data->skipSamples <= 0) { - ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip); - } + if (mwv_pflt_offset == -1 || + read_32bitLE(mwv_pflt_offset+0x08, streamFile) != filter_order || + read_32bitLE(mwv_pflt_offset+0x04, streamFile) < 8 + filter_count * 4 * filter_order) + goto fail; - /* RIFF loop/sample values are absolute (with skip samples), adjust */ - if (loop_flag) { - loop_start_offset -= ffmpeg_data->skipSamples; - loop_end_offset -= ffmpeg_data->skipSamples; + for (ch = 0; ch < fmt.channel_count; ch++) { + for (i = 0; i < filter_count * filter_order; i++) { + int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, streamFile); + vgmstream->ch[ch].adpcm_coef_3by32[i] = coef; } } } + break; + case coding_MSADPCM: + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + break; + case coding_MS_IMA: + vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); + break; + case coding_NGC_DSP: + //sample_count = dsp_bytes_to_samples(data_size, fmt.channel_count); /* expected from the "fact" chunk */ + + /* coefs */ + if (sns) { + int i, ch; + static const int16_t coef[16] = { /* common codebook? */ + 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, + 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5 + }; + + for (ch = 0; ch < fmt.channel_count; ch++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = coef[i]; + } + } + } + + break; +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: { + ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + + vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact_sample_count */ + + if (at3) { + /* the encoder introduces some garbage (not always silent) samples to skip before the stream */ + /* manually set skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip); + } + + /* RIFF loop/sample values are absolute (with skip samples), adjust */ + if (loop_flag) { + loop_start_offset -= ffmpeg_data->skipSamples; + loop_end_offset -= ffmpeg_data->skipSamples; + } + } + break; + } #endif #ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - /* rough total samples (block_size may be incorrect if not using joint stereo) */ - sample_count = (data_size / fmt.block_size) * 2048; - /* favor fact_samples if available (skip isn't correctly handled for now) */ - if (fact_sample_count > 0 && fact_sample_count + fact_sample_skip < sample_count) - sample_count = fact_sample_count + fact_sample_skip; + case coding_AT3plus: { + vgmstream->codec_data = init_at3plus(); - break; + /* get rough total samples but favor fact_samples if available (skip isn't correctly handled for now) */ + vgmstream->num_samples = atrac3plus_bytes_to_samples(data_size, fmt.block_size); + if (fact_sample_count > 0 && fact_sample_count + fact_sample_skip < vgmstream->num_samples) + vgmstream->num_samples = fact_sample_count + fact_sample_skip; + break; + } +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(fmt.offset+0x08+0x2c,streamFile); + cfg.encoder_delay = fact_sample_skip; + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + + vgmstream->num_samples = fact_sample_count; + /* RIFF loop/sample values are absolute (with skip samples), adjust */ + if (loop_flag) { + loop_start_offset -= fact_sample_skip; + loop_end_offset -= fact_sample_skip; + } + + break; + } #endif default: goto fail; } - /* .sns uses fact chunk */ - if (sns) - { + if (sns) { if (-1 == fact_sample_count) goto fail; - sample_count = fact_sample_count; + vgmstream->num_samples = fact_sample_count; } - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = sample_count; - vgmstream->sample_rate = fmt.sample_rate; - + /* coding, layout, interleave */ vgmstream->coding_type = fmt.coding_type; - - vgmstream->layout_type = layout_none; - if (fmt.channel_count > 1) { - switch (fmt.coding_type) { - case coding_PCM8_U_int: - case coding_MS_IMA: - case coding_MSADPCM: -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: -#endif - // use layout_none from above - break; - default: - vgmstream->layout_type = layout_interleave; - break; - } - } - - vgmstream->interleave_block_size = fmt.interleave; switch (fmt.coding_type) { case coding_MSADPCM: case coding_MS_IMA: @@ -512,171 +507,84 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case coding_FFmpeg: #endif #ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: + case coding_AT3plus: #endif - // override interleave_block_size with frame size +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: +#endif + vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = fmt.block_size; break; default: - // use interleave from above + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = fmt.interleave; break; } -#ifdef VGM_USE_FFMPEG - if (fmt.coding_type == coding_FFmpeg) { - vgmstream->codec_data = ffmpeg_data; - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - if (fmt.coding_type == coding_AT3plus) { - maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data)); - data->buffer = 0; - data->samples_discard = 0; - data->handle = Atrac3plusDecoder_openContext(); - vgmstream->codec_data = data; - } -#endif - + /* meta, loops */ + vgmstream->meta_type = meta_RIFF_WAVE; if (loop_flag) { - if (loop_start_ms >= 0) - { - vgmstream->loop_start_sample = - (long long)loop_start_ms*fmt.sample_rate/1000; - vgmstream->loop_end_sample = - (long long)loop_end_ms*fmt.sample_rate/1000; + if (loop_start_ms >= 0) { + vgmstream->loop_start_sample = (long long)loop_start_ms*fmt.sample_rate/1000; + vgmstream->loop_end_sample = (long long)loop_end_ms*fmt.sample_rate/1000; vgmstream->meta_type = meta_RIFF_WAVE_labl; } - else if (loop_start_offset >= 0) - { + else if (loop_start_offset >= 0) { vgmstream->loop_start_sample = loop_start_offset; vgmstream->loop_end_sample = loop_end_offset; vgmstream->meta_type = meta_RIFF_WAVE_smpl; } - else if (mwv && mwv_ctrl_offset != -1) - { - vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, - streamFile); - vgmstream->loop_end_sample = sample_count; + else if (mwv && mwv_ctrl_offset != -1) { + vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); + vgmstream->loop_end_sample = vgmstream->num_samples; } } - else - { - vgmstream->meta_type = meta_RIFF_WAVE; - } - - if (mwv) - { - int i, c; - if (fmt.coding_type == coding_L5_555) - { - const int filter_order = 3; - int filter_count = read_32bitLE(mwv_pflt_offset+12, streamFile); - - if (mwv_pflt_offset == -1 || - read_32bitLE(mwv_pflt_offset+8, streamFile) != filter_order || - read_32bitLE(mwv_pflt_offset+4, streamFile) < 8 + filter_count * 4 * filter_order) - goto fail; - if (filter_count > 0x20) goto fail; - for (c = 0; c < fmt.channel_count; c++) - { - for (i = 0; i < filter_count * filter_order; i++) - { - vgmstream->ch[c].adpcm_coef_3by32[i] = read_32bitLE( - mwv_pflt_offset+16+i*4, streamFile - ); - } - } - } + if (mwv) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - - if (sns) - { - int c; - /* common codebook? */ - static const int16_t coef[16] = - {0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, - 0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5}; - - for (c = 0; c < fmt.channel_count; c++) - { - int i; - for (i = 0; i < 16; i++) - { - vgmstream->ch[c].adpcm_coef[i] = coef[i]; - } - } + if (sns) { vgmstream->meta_type = meta_RIFF_WAVE_SNS; } - /* open the file, set up each channel */ - { - int i; - - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - - for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = - start_offset+i*fmt.interleave; - } - } + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; return vgmstream; - /* clean up anything we may have opened */ + fail: -#ifdef VGM_USE_FFMPEG - if (ffmpeg_data) { - free_ffmpeg(ffmpeg_data); - if (vgmstream) vgmstream->codec_data = NULL; - } -#endif - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + riff_fmt_chunk fmt = {0}; - struct riff_fmt_chunk fmt; - - off_t file_size = -1; - int sample_count = 0; - //int fact_sample_count = -1; - off_t start_offset = -1; + size_t file_size, riff_size, data_size = 0; + off_t start_offset = 0; int loop_flag = 0; off_t loop_start_offset = -1; off_t loop_end_offset = -1; - uint32_t riff_size; - uint32_t data_size = 0; - int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; + int FormatChunkFound = 0, DataChunkFound = 0; + /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("wav",filename_extension(filename)) && - strcasecmp("lwav",filename_extension(filename))) - { + if ( !check_extensions(streamFile, "wav,lwav") ) goto fail; - } /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494658) /* "RIFX" */ + if (read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" */ goto fail; - /* check for WAVE form */ - if ((uint32_t)read_32bitBE(8,streamFile)!=0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitBE(4,streamFile); + riff_size = read_32bitBE(0x04,streamFile); file_size = get_streamfile_size(streamFile); - /* check for tructated RIFF */ + /* check for truncated RIFF */ if (file_size < riff_size+8) goto fail; /* read through chunks to verify format and find metadata */ @@ -713,27 +621,15 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { data_size = chunk_size; break; case 0x736D706C: /* smpl */ - /* check loop count */ - if (read_32bitBE(current_chunk+0x24, streamFile)==1) - { - /* check loop info */ - if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) - { + /* check loop count and loop info */ + if (read_32bitBE(current_chunk+0x24, streamFile)==1) { + if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) { loop_flag = 1; - loop_start_offset = - read_32bitBE(current_chunk+0x2c+8, streamFile); - loop_end_offset = - read_32bitBE(current_chunk+0x2c+0xc,streamFile); + loop_start_offset = read_32bitBE(current_chunk+0x2c+8, streamFile); + loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile); } } break; - case 0x66616374: /* fact */ - if (chunk_size != 4) break; - //fact_sample_count = read_32bitBE(current_chunk+8, streamFile); - break; - case 0x4A554E4B: /* JUNK */ - JunkFound = 1; - break; default: /* ignorance is bliss */ break; @@ -745,241 +641,50 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { if (!FormatChunkFound || !DataChunkFound) goto fail; - /* JUNK is an optional Wwise chunk, and Wwise hijacks the MSADPCM/MS_IMA/XBOX IMA ids (how nice). - * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. - * As JUNK is legal (if unusual) we only reject those codecs. - * (ex. Cave PC games have PCM16LE + JUNK + smpl created by "Samplitude software") */ - if (JunkFound && (fmt.coding_type==coding_MSADPCM || fmt.coding_type==coding_MS_IMA)) goto fail; + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = fmt.sample_rate; + + /* init, samples */ switch (fmt.coding_type) { case coding_PCM16BE: - sample_count = data_size/2/fmt.channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); break; case coding_PCM8_U_int: - sample_count = data_size/fmt.channel_count; - break; - case coding_NGC_DSP: - //sample_count = data_size / fmt.channel_count / 8 * 14; /* expected from the "fact" chunk */ + vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; default: goto fail; } - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = sample_count; - vgmstream->sample_rate = fmt.sample_rate; - + /* coding, layout, interleave */ vgmstream->coding_type = fmt.coding_type; - - vgmstream->layout_type = layout_none; - if (fmt.channel_count > 1) { - switch (fmt.coding_type) { - case coding_PCM8_U_int: - case coding_MS_IMA: - case coding_MSADPCM: - // use layout_none from above - break; - default: - vgmstream->layout_type = layout_interleave; - break; - } - } - - vgmstream->interleave_block_size = fmt.interleave; switch (fmt.coding_type) { - case coding_MSADPCM: - case coding_MS_IMA: - // override interleave_block_size with frame size - vgmstream->interleave_block_size = fmt.block_size; - break; default: - // use interleave from above + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = fmt.interleave; break; } - if (fmt.coding_type == coding_MS_IMA) - vgmstream->interleave_block_size = fmt.block_size; - + /* meta, loops */ + vgmstream->meta_type = meta_RIFX_WAVE; if (loop_flag) { - if (loop_start_offset >= 0) - { + if (loop_start_offset >= 0) { vgmstream->loop_start_sample = loop_start_offset; vgmstream->loop_end_sample = loop_end_offset; vgmstream->meta_type = meta_RIFX_WAVE_smpl; } } - else - { - vgmstream->meta_type = meta_RIFX_WAVE; - } - - /* open the file, set up each channel */ - { - int i; - - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - - for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = - start_offset+i*fmt.interleave; - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -//todo move -/* XNB - Microsoft XNA Game Studio 4.0 format */ -VGMSTREAM * init_vgmstream_xnbm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, version, flags, num_samples = 0; - size_t xnb_size, data_size; - - struct riff_fmt_chunk fmt; - - - /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"xnb")) - goto fail; - - /* check header */ - if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */ - goto fail; - /* 0x04: platform: w = Microsoft Windows, m = Windows Phone 7, x = Xbox 360, 'a' = Android */ - - version = read_8bit(0x04,streamFile); - if (version != 5) goto fail; /* XNA 4.0 only */ - - flags = read_8bit(0x05,streamFile); - if (flags & 0x80) goto fail; /* compressed with XMemCompress, not public */ - //if (flags & 0x01) goto fail; /* XMA flag? */ - - /* "check for truncated XNB" (???) */ - xnb_size = read_32bitLE(0x06,streamFile); - if (get_streamfile_size(streamFile) < xnb_size) goto fail; - - /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */ - { - char reader_name[255+1]; - off_t current_chunk = 0xa; - int reader_string_len; - uint32_t fmt_chunk_size; - const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ - //const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */ - - /* type reader count, accept only one for now */ - if (read_8bit(current_chunk++, streamFile) != 1) - goto fail; - - reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */ - if (reader_string_len > 255) goto fail; - - /* check SoundEffect type string */ - if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len) - goto fail; - if ( strcmp(reader_name, type_sound) != 0 ) - goto fail; - current_chunk += reader_string_len + 1; - current_chunk += 4; /* reader version */ - - /* shared resource count */ - if (read_8bit(current_chunk++, streamFile) != 1) - goto fail; - - /* shared resource: partial "fmt" chunk */ - fmt_chunk_size = read_32bitLE(current_chunk, streamFile); - current_chunk += 4; - - if (-1 == read_fmt(0, /* big endian == false */ - streamFile, - current_chunk-8, /* read_fmt() expects to skip "fmt "+size */ - &fmt, - 0, /* sns == false */ - 0)) /* mwv == false */ - goto fail; - current_chunk += fmt_chunk_size; - - data_size = read_32bitLE(current_chunk, streamFile); - current_chunk += 4; - - start_offset = current_chunk; - } - - switch (fmt.coding_type) { - case coding_PCM16LE: - num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16); - break; - case coding_PCM8_U_int: - num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 8); - break; - case coding_MSADPCM: - num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); - break; - case coding_MS_IMA: - num_samples = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count + - ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0); - break; - default: - VGM_LOG("XNB: unknown codec 0x%x\n", fmt.coding_type); - goto fail; - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->num_samples = num_samples; - vgmstream->sample_rate = fmt.sample_rate; - - vgmstream->meta_type = meta_XNB; - vgmstream->coding_type = fmt.coding_type; - - if (fmt.channel_count > 1) { - switch (fmt.coding_type) { - case coding_PCM8_U_int: - case coding_MS_IMA: - case coding_MSADPCM: - vgmstream->layout_type = layout_none; - break; - default: - vgmstream->layout_type = layout_interleave; - break; - } - } - - switch (fmt.coding_type) { - case coding_MSADPCM: - case coding_MS_IMA: - vgmstream->interleave_block_size = fmt.block_size; - break; - default: - vgmstream->interleave_block_size = fmt.interleave; - break; - } if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; - return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sk_aud.c b/Frameworks/vgmstream/vgmstream/src/meta/sk_aud.c index ce3bc55fb..6dd60a777 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sk_aud.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sk_aud.c @@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x00, VORBIS_SK, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, 0x00, VORBIS_SK, &cfg); if (!vgmstream->codec_data) goto fail; start_offset = cfg.data_start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index faffc0a02..2dfa87052 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -211,7 +211,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */ cfg.data_size = stream_size; - mpeg_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index 71243ec78..a42ff2876 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -1,16 +1,18 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SXD - Sony's SDK format? (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ +/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; + size_t chunk_size; int is_separate; - int loop_flag, channels, type; + int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; + uint32_t at9_config_data = 0; int total_streams, target_stream = streamFile->stream_index; @@ -33,8 +35,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { /* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */ - /* WAVE chunk (0 + streams + 4*streams table + streams * variable? + optional padding) */ - if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ + if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */ /* check multi-streams (usually only in SFX containers) */ total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); @@ -43,53 +44,60 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { /* read stream header */ { - off_t table_offset, header_offset, stream_offset, data_offset; - int i; + off_t table_offset, header_offset, stream_offset; /* get target offset using table of relative offsets within WAVE */ table_offset = chunk_offset + 0x08 + 4*(target_stream-1); header_offset = table_offset + read_32bitLE(table_offset,streamHeader); - type = read_32bitLE(header_offset+0x00,streamHeader); - /* 0x04 (1): unk (HEVAG: 21, ATRAC9: 42 */ - channels = read_8bit (header_offset+0x05,streamHeader); - sample_rate = read_32bitLE(header_offset+0x08,streamHeader); - /* 0x0c (4): unk size? */ - /* 0x10 (4): ? + volume? + pan? (can be 0 for music) */ - num_samples = read_32bitLE(header_offset+0x14,streamHeader); + /* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */ + codec = read_8bit (header_offset+0x04,streamHeader); + channels = read_8bit (header_offset+0x05,streamHeader); + sample_rate = read_32bitLE(header_offset+0x08,streamHeader); + /* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */ + /* 0x10(4): ? + volume? + pan? (can be 0 for music) */ + num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); - /* 0x20 (4): data size */ - /* 0x24 (-): extra data, variable size and format dependant - (ATRAC9 can contain truncated part of the data, for preloading I guess) */ + /* 0x20(4): data size */ + stream_offset = read_32bitLE(header_offset+0x24,streamHeader); + + /* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller). + * One tag seems to add a small part of the ATRAC9 data, for RAM preloding I guess. */ + if (codec == 0x42) { + off_t extra_offset = header_offset+0x28; + off_t max_offset = chunk_offset + chunk_size; + + /* manually try to find certain tag, no idea about the actual format + * (most variable in Soul Sacrifice; extra size isn't found in the SXD AFAIK) */ + while (extra_offset < max_offset) { + uint32_t tag = read_32bitBE(extra_offset, streamHeader); + if (tag == 0x0A010000 || tag == 0x0A010600) { + at9_config_data = read_32bitLE(extra_offset+0x04,streamHeader); /* yes, LE */ + break; + } + + extra_offset += 0x04; + } + if (!at9_config_data) + goto fail; + } + loop_flag = loop_start_sample != -1 && loop_end_sample != -1; - /* calc stream offset by reading stream sizes */ - stream_offset = 0; - for (i = 0; i < total_streams; i++) { - off_t subtable_offset, subheader_offset; - if (i == target_stream-1) break; - - subtable_offset = chunk_offset + 0x08 + 4*(i); - subheader_offset = subtable_offset + read_32bitLE(subtable_offset,streamHeader); - stream_offset += read_32bitLE(subheader_offset+0x20,streamHeader); /* data size */ - } - + /* from current offset in sxd, absolute in sxd2 */ if (is_separate) { - data_offset = first_offset; + start_offset = stream_offset; } else { - if (!find_chunk_le(streamHeader, 0x44415441,first_offset,0, &data_offset,NULL)) goto fail; /* "DATA" */ - data_offset += 0x08; + start_offset = header_offset+0x24 + stream_offset; } - - start_offset = data_offset + stream_offset; } /* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */ if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */ /* table: relative offset (32b) + hash? (32b) + cue index (32b) */ int i; - int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /*can be more than streams */ + int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ for (i = 0; i < num_entries; i++) { uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); if (index+1 == target_stream) { @@ -113,18 +121,30 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); - switch (type) { - case 0x01: /* HEVAG */ + switch (codec) { + case 0x21: /* HEVAG */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; - case 0x03: /* ATRAC9 */ - goto fail; +#ifdef VGM_USE_ATRAC9 + case 0x42: { /* ATRAC9 */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = at9_config_data; + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; + } +#endif default: - VGM_LOG("SXD: unknown codec %i", type); + VGM_LOG("SXD: unknown codec 0x%x", codec); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index 136ef057f..f7984bd9b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -208,3 +208,50 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* Android/iOS Variants (Star Ocean Anamnesis (APK v1.9.2), Heaven x Inferno (iOS)) */ +VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { +#ifdef VGM_USE_VORBIS + off_t start_offset; + char filename[PATH_LIMIT]; + int8_t codec_id; + + streamFile->get_name(streamFile, filename, sizeof(filename)); + /* check extension, case insensitive */ + /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(streamFile, "aac,laac,ace")) + goto fail; + + if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ + goto fail; + + if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + + codec_id = read_8bit(0x104, streamFile); + if (codec_id == 0xe) /* Vorbis */ + { + vgm_vorbis_info_t inf; + VGMSTREAM * result = NULL; + + memset(&inf, 0, sizeof(inf)); + inf.layout_type = layout_ogg_vorbis; + inf.meta_type = meta_TA_AAC_VORBIS; + inf.loop_start = read_32bitLE(0x140, streamFile); + inf.loop_end = read_32bitLE(0x144, streamFile); + inf.loop_flag = inf.loop_end > inf.loop_start; + inf.loop_end_found = inf.loop_flag; + + start_offset = read_32bitLE(0x120, streamFile); + result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + + if (result != NULL) { + return result; + } + } + +fail: + /* clean up anything we may have opened */ +#endif + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 7d6dd602a..ee5ddcfd2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -253,7 +253,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case coding_MPEG_layer3: vgmstream->layout_type = layout_none; - vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.start_offset, &coding, vgmstream->channels); + vgmstream->codec_data = init_mpeg(streamFile, txth.start_offset, &coding, vgmstream->channels); if (!vgmstream->codec_data) goto fail; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c index 118998f1c..486ace356 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, off, fmt_offset; size_t header_size, data_size; - int little_endian; + int big_endian; int loop_flag, channel_count, block_align, bits_per_sample; uint32_t platform, type; @@ -28,25 +28,33 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { else goto fail; - /* endianness is given with the platform field, but this is more versatile */ - little_endian = read_32bitBE(off+0x10,streamFile) > 0x00ffffff; - if (little_endian) { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } else { - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } - /* 0x04: version? (0x00, 0x07, 0x0a, etc); */ platform = read_32bitBE(off+0x08,streamFile); /* string */ type = read_32bitBE(off+0x0c,streamFile); /* string */ + + switch(platform) { + case 0x57696920: /* "Wii " */ + case 0x43616665: /* "Cafe" */ + case 0x50533320: /* "PS3 " */ + case 0x58333630: /* "X360" */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + break; + default: + big_endian = 0; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + break; + } + header_size = read_32bit(off+0x10,streamFile); start_offset = read_32bit(off+0x14,streamFile); /* 0x18: number of chunks */ /* 0x1c: unk */ - /* The first chunk is always "fmt" and points to a RIFF "fmt" chunk (even for WiiU or PS3) */ + /* the format has a chunk offset table, and the first one always "fmt" and points + * to a RIFF "fmt" chunk (even for WiiU or PS3) */ if (read_32bitBE(off+0x20,streamFile) != 0x666D7420) goto fail; /*"fmt "*/ fmt_offset = read_32bit(off+0x24,streamFile); //fmt_size = read_32bit(off+0x28,streamFile); @@ -74,7 +82,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { /* chunks: "data" */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = block_align; /* usually 0x04 */ + vgmstream->interleave_block_size = 0x2; vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample); break; @@ -114,7 +122,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { { /* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */ off_t dsp_coefs = read_32bitBE(off+0x30,streamFile); /* after "dspL"; spacing is consistent but could vary */ - dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, !little_endian); + dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, big_endian); /* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */ } @@ -123,8 +131,8 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x505333206D703320: { /* "PS3 mp3 " */ - /* chunks: "MARK" optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */ - vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); + /* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */ + vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; @@ -153,9 +161,25 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { } #endif - case 0x5649544161743920: /*"VITAat9 "*/ +#ifdef VGM_USE_ATRAC9 + case 0x5649544161743920: { /*"VITAat9 "*/ /* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */ - goto fail; + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(fmt_offset+0x2c,streamFile); + cfg.encoder_delay = read_32bit(fmt_offset+0x3c,streamFile); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + /* could get the "fact" offset but seems it always follows "fmt " */ + vgmstream->num_samples = read_32bit(fmt_offset+0x34,streamFile); + break; + } +#endif default: VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index b871da8ab..e4619791a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -285,7 +285,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } } - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } else { @@ -330,11 +330,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } /* try with the selected codebooks */ - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) { /* codebooks failed: try again with the other type */ cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; - vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); + vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } } @@ -528,9 +528,24 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { vgmstream->num_samples = ps_bytes_to_samples(ww.data_size, ww.channels); break; - case ATRAC9: /* PSV/PS4 */ - VGM_LOG("WWISE: ATRAC9 found (unsupported)\n"); - goto fail; +#ifdef VGM_USE_ATRAC9 + case ATRAC9: { /* PSV/PS4 */ + atrac9_config cfg = {0}; + if (ww.extra_size != 0x12) goto fail; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(ww.fmt_offset+0x18,streamFile); + cfg.encoder_delay = read_32bit(ww.fmt_offset+0x20,streamFile); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = read_32bit(ww.fmt_offset+0x1c,streamFile); + break; + } +#endif default: goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xnb.c b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c new file mode 100644 index 000000000..c0034a71e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xnb.c @@ -0,0 +1,123 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* XNB - Microsoft XNA Game Studio 4.0 format */ +VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count; + int flags, codec, sample_rate, block_size, bps; + size_t xnb_size, data_size; + + + /* check extension, case insensitive */ + if ( !check_extensions(streamFile,"xnb")) + goto fail; + + /* check header */ + if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */ + goto fail; + /* 0x04: platform: w = Microsoft Windows, m = Windows Phone 7, x = Xbox 360, 'a' = Android */ + + if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */ + goto fail; + + flags = read_8bit(0x05,streamFile); + if (flags & 0x80) goto fail; /* compressed with XMemCompress */ + //if (flags & 0x01) goto fail; /* XMA/big endian flag? */ + + /* "check for truncated XNB" (???) */ + xnb_size = read_32bitLE(0x06,streamFile); + if (get_streamfile_size(streamFile) < xnb_size) goto fail; + + /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */ + { + char reader_name[255+1]; + off_t current_chunk = 0xa; + int reader_string_len; + uint32_t fmt_chunk_size; + const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ + //const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */ + + /* type reader count, accept only one for now */ + if (read_8bit(current_chunk++, streamFile) != 1) + goto fail; + + reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */ + if (reader_string_len > 255) goto fail; + + /* check SoundEffect type string */ + if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len) + goto fail; + if ( strcmp(reader_name, type_sound) != 0 ) + goto fail; + current_chunk += reader_string_len + 1; + current_chunk += 4; /* reader version */ + + /* shared resource count */ + if (read_8bit(current_chunk++, streamFile) != 1) + goto fail; + + /* shared resource: partial "fmt" chunk */ + fmt_chunk_size = read_32bitLE(current_chunk, streamFile); + current_chunk += 4; + + { + codec = read_16bitLE(current_chunk+0x00, streamFile); + channel_count = read_16bitLE(current_chunk+0x02, streamFile); + sample_rate = read_32bitLE(current_chunk+0x04, streamFile); + block_size = read_16bitLE(current_chunk+0x0c, streamFile); + bps = read_16bitLE(current_chunk+0x0e, streamFile); + } + + current_chunk += fmt_chunk_size; + + data_size = read_32bitLE(current_chunk, streamFile); + current_chunk += 4; + + start_offset = current_chunk; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_XNB; + + switch (codec) { + case 0x01: + vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = block_size / channel_count; + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps); + break; + + case 0x02: + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = block_size; + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_size, channel_count); + break; + + case 0x11: + vgmstream->coding_type = coding_MS_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = block_size; + vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_size, channel_count); + break; + + default: + VGM_LOG("XNB: unknown codec 0x%x\n", codec); + goto fail; + } + + if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c index c187aebfc..0a84c1e81 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c @@ -4,50 +4,71 @@ static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); -/* XVAG - Sony's (second party?) format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */ +/* XVAG - Sony's Scream Tool/Stream Creator format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int loop_flag = 0, channel_count, codec; + int big_endian; + int sample_rate, num_samples, multiplier, multistreams = 0; + int total_subsongs = 0, target_subsong = streamFile->stream_index; off_t start_offset, loop_start, loop_end, chunk_offset; off_t first_offset = 0x20; - int little_endian; - int sample_rate, num_samples, multiplier; + size_t chunk_size; /* check extension, case insensitive */ - if (!check_extensions(streamFile,"xvag")) goto fail; + if (!check_extensions(streamFile,"xvag")) + goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */ goto fail; - little_endian = read_8bit(0x07,streamFile)==0; /* empty start_offset > little endian */ - if (little_endian) { - read_32bit = read_32bitLE; - } else { + /* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */ + big_endian = read_8bit(0x08,streamFile) & 0x01; + if (big_endian) { read_32bit = read_32bitBE; + } else { + read_32bit = read_32bitLE; } start_offset = read_32bit(0x04,streamFile); - /* 0x08: flags? (&0x01=big endian?) 0x0a: version (chunk sizes vary) */ + /* 0x08: flags? (&0x01=big endian, 0x02=?, 0x06=full RIFF AT9?) + * 0x09: flags2? (0x00/0x01/0x04, speaker mode?) + * 0x0a: always 0? + * 0x0b: version-flag? (0x5f/0x60/0x61, last has extra data) */ - /* "fmat": base format */ - if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"fmat"*/ + /* "fmat": base format (always first) */ + if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/ + goto fail; channel_count = read_32bit(chunk_offset+0x00,streamFile); codec = read_32bit(chunk_offset+0x04,streamFile); num_samples = read_32bit(chunk_offset+0x08,streamFile); - /* 0x0c: samples again? */ - multiplier = read_32bit(chunk_offset+0x10,streamFile); + /* 0x0c: samples again? playable section? */ + VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); + + multiplier = read_32bit(chunk_offset+0x10,streamFile); /* 'interleave factor' */ sample_rate = read_32bit(chunk_offset+0x14,streamFile); /* 0x18: datasize */ + /* extra data, seen in MPEG/ATRAC9 */ + if (chunk_size > 0x1c) { + total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); /* number of interleaved layers */ + multistreams = read_32bit(chunk_offset+0x20,streamFile); /* number of bitstreams per layer (for multistream Nch MPEG/ATRAC9) */ + } + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* other chunks: */ /* "cpan": pan/volume per channel */ + /* "cues": cue/labels (rare) */ /* "0000": end chunk before start_offset */ - //if ((uint16_t)read_16bitBE(start_offset,streamFile)==0xFFFB) codec = 0x08; - if (codec == 0x06) { /* todo not sure if there are any looping XVAGs */ + /* some XVAG seem to do full loops, this should detect them as looping */ + //todo remove, looping seems external and specified in Scream Tool's bank formats + if (codec == 0x06) { loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); } @@ -57,52 +78,85 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; + vgmstream->num_streams = total_subsongs; vgmstream->meta_type = meta_XVAG; switch (codec) { - case 0x06: /* PS ADPCM: God of War III, Uncharted 1/2, Ratchet and Clank Future */ - case 0x07: { /* Bizarro 6ch PS ADPCM: infamous 1 (todo won't play properly; algo tweak + bigger predictor table?) */ + //case 0x??: /* PCM? */ + case 0x06: /* VAG (PS ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ + case 0x07: /* SVAG? (PS ADPCM with extended table): inFamous 1 (PS3) */ + if (total_subsongs > 1 || multistreams > 1) goto fail; + if (multiplier > 1) goto fail; + vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10;//* multiplier? (doesn't seem necessary, always 1); + vgmstream->interleave_block_size = 0x10; vgmstream->coding_type = coding_PSX; if (loop_flag) { - if (loop_start!=0) { - vgmstream->loop_start_sample = ((((loop_start/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; - if(loop_start%vgmstream->interleave_block_size) - vgmstream->loop_start_sample += (((loop_start%vgmstream->interleave_block_size)-1)/16*14*channel_count); - } - vgmstream->loop_end_sample = ((((loop_end/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; - if (loop_end%vgmstream->interleave_block_size) - vgmstream->loop_end_sample += (((loop_end%vgmstream->interleave_block_size)-1)/16*14*channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); } - break; - } #ifdef VGM_USE_MPEG - case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */ + case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ mpeg_custom_config cfg = {0}; + if (total_subsongs > 1 || (multistreams > 1 && multistreams == vgmstream->channels)) goto fail; + /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ - if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) - goto fail; /*"mpin"*/ + if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"mpin"*/ + goto fail; cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ cfg.interleave = cfg.chunk_size * multiplier; - vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; } #endif - case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus */ +#ifdef VGM_USE_ATRAC9 + case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus (Vita), The Last of Us Remastered (PS4) */ + atrac9_config cfg = {0}; + /* "a9in": ATRAC9 info */ - goto fail; + /* 0x00: block align, 0x04: samples per block, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */ + if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"a9in"*/ + goto fail; + + cfg.type = ATRAC9_XVAG; + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile); + cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile); + + if (total_subsongs > 1 && multistreams > 1) { + goto fail; /* not known */ + } + else if (total_subsongs > 1) { + /* interleaves 'multiplier' superframes per subsong (all share config_data) */ + cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * multiplier; + cfg.subsong_skip = total_subsongs; + /* start in subsong's first superframe */ + start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); + } + else if (multistreams > 1) { + /* Vita multichannel (flower) interleaves streams like MPEG + * PS4 (The Last of Us) uses ATRAC9's multichannel directly instead (multistreams==1) */ + goto fail;//todo add + } + //if (multistreams == vgmstream->channels) goto fail; + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; } +#endif default: goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index c3001d215..68f8290e3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -17,10 +17,10 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_adx, init_vgmstream_brstm, - init_vgmstream_bfwav, - init_vgmstream_bfstm, - init_vgmstream_mca, - init_vgmstream_btsnd, + init_vgmstream_bfwav, + init_vgmstream_bfstm, + init_vgmstream_mca, + init_vgmstream_btsnd, init_vgmstream_nds_strm, init_vgmstream_agsc, init_vgmstream_ngc_adpdtk, @@ -31,7 +31,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_rs03, init_vgmstream_ngc_dsp_std, init_vgmstream_ngc_mdsp_std, - init_vgmstream_ngc_dsp_csmp, + init_vgmstream_ngc_dsp_csmp, init_vgmstream_cstr, init_vgmstream_gcsw, init_vgmstream_ps2_ads, @@ -67,10 +67,10 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_sfl, #endif #if 0 - init_vgmstream_mp4_aac, + init_vgmstream_mp4_aac, #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - init_vgmstream_akb, + init_vgmstream_akb, #endif init_vgmstream_sadb, init_vgmstream_ps2_bmdx, @@ -117,7 +117,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_dxh, init_vgmstream_ps2_psh, init_vgmstream_scd_pcm, - init_vgmstream_ps2_pcm, + init_vgmstream_ps2_pcm, init_vgmstream_ps2_rkv, init_vgmstream_ps2_psw, init_vgmstream_ps2_vas, @@ -162,13 +162,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_rsd2vag, init_vgmstream_rsd2pcmb, init_vgmstream_rsd2xadp, - init_vgmstream_rsd3vag, - init_vgmstream_rsd3gadp, + init_vgmstream_rsd3vag, + init_vgmstream_rsd3gadp, init_vgmstream_rsd3pcm, - init_vgmstream_rsd3pcmb, + init_vgmstream_rsd3pcmb, init_vgmstream_rsd4pcmb, init_vgmstream_rsd4pcm, - init_vgmstream_rsd4radp, + init_vgmstream_rsd4radp, init_vgmstream_rsd4vag, init_vgmstream_rsd6vag, init_vgmstream_rsd6wadp, @@ -222,19 +222,19 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_sps, init_vgmstream_ps2_xa2_rrp, init_vgmstream_nds_hwas, - init_vgmstream_ngc_lps, + init_vgmstream_ngc_lps, init_vgmstream_ps2_snd, init_vgmstream_naomi_adpcm, - init_vgmstream_sd9, - init_vgmstream_2dx9, - init_vgmstream_dsp_ygo, + init_vgmstream_sd9, + init_vgmstream_2dx9, + init_vgmstream_dsp_ygo, init_vgmstream_ps2_vgv, init_vgmstream_ngc_gcub, init_vgmstream_maxis_xa, init_vgmstream_ngc_sck_dsp, init_vgmstream_apple_caff, - init_vgmstream_pc_mxst, - init_vgmstream_sab, + init_vgmstream_pc_mxst, + init_vgmstream_sab, init_vgmstream_exakt_sc, init_vgmstream_wii_bns, init_vgmstream_wii_was, @@ -244,8 +244,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_stx, init_vgmstream_myspd, init_vgmstream_his, - init_vgmstream_ps2_ast, - init_vgmstream_dmsg, + init_vgmstream_ps2_ast, + init_vgmstream_dmsg, init_vgmstream_ngc_dsp_aaap, init_vgmstream_ngc_dsp_konami, init_vgmstream_ps2_ster, @@ -261,8 +261,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_bo2, init_vgmstream_dsp_ddsp, init_vgmstream_p3d, - init_vgmstream_ps2_tk1, - init_vgmstream_ps2_adsc, + init_vgmstream_ps2_tk1, + init_vgmstream_ps2_adsc, init_vgmstream_ngc_dsp_mpds, init_vgmstream_dsp_str_ig, init_vgmstream_psx_mgav, @@ -274,57 +274,58 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_dsp_xiii, init_vgmstream_dsp_cabelas, init_vgmstream_ps2_adm, - init_vgmstream_ps2_lpcm, + init_vgmstream_ps2_lpcm, init_vgmstream_dsp_bdsp, - init_vgmstream_ps2_vms, - init_vgmstream_xau, + init_vgmstream_ps2_vms, + init_vgmstream_xau, init_vgmstream_bar, init_vgmstream_ffw, init_vgmstream_dsp_dspw, init_vgmstream_ps2_jstm, init_vgmstream_xvag, - init_vgmstream_ps3_cps, + init_vgmstream_ps3_cps, init_vgmstream_sqex_scd, init_vgmstream_ngc_nst_dsp, init_vgmstream_baf, init_vgmstream_ps3_msf, - init_vgmstream_nub_vag, - init_vgmstream_ps3_past, + init_vgmstream_nub_vag, + init_vgmstream_ps3_past, init_vgmstream_sgxd, - init_vgmstream_ngca, - init_vgmstream_wii_ras, - init_vgmstream_ps2_spm, - init_vgmstream_x360_tra, - init_vgmstream_ps2_iab, - init_vgmstream_ps2_strlr, + init_vgmstream_ngca, + init_vgmstream_wii_ras, + init_vgmstream_ps2_spm, + init_vgmstream_x360_tra, + init_vgmstream_ps2_iab, + init_vgmstream_ps2_strlr, init_vgmstream_lsf_n1nj4n, - init_vgmstream_vawx, + init_vgmstream_vawx, init_vgmstream_pc_snds, - init_vgmstream_ps2_wmus, - init_vgmstream_hyperscan_kvag, - init_vgmstream_ios_psnd, - init_vgmstream_pc_adp_bos, - init_vgmstream_pc_adp_otns, + init_vgmstream_ps2_wmus, + init_vgmstream_hyperscan_kvag, + init_vgmstream_ios_psnd, + init_vgmstream_pc_adp_bos, + init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, - init_vgmstream_ps3_klbs, + init_vgmstream_ps3_klbs, init_vgmstream_ps2_mtaf, - init_vgmstream_tun, - init_vgmstream_wpd, - init_vgmstream_mn_str, - init_vgmstream_mss, - init_vgmstream_ps2_hsf, - init_vgmstream_ps3_ivag, - init_vgmstream_ps2_2pfs, - init_vgmstream_xnbm, - init_vgmstream_rsd6oogv, - init_vgmstream_ubi_ckd, - init_vgmstream_ps2_vbk, - init_vgmstream_otm, - init_vgmstream_bcstm, - init_vgmstream_3ds_idsp, + init_vgmstream_tun, + init_vgmstream_wpd, + init_vgmstream_mn_str, + init_vgmstream_mss, + init_vgmstream_ps2_hsf, + init_vgmstream_ps3_ivag, + init_vgmstream_ps2_2pfs, + init_vgmstream_xnb, + init_vgmstream_rsd6oogv, + init_vgmstream_ubi_ckd, + init_vgmstream_ps2_vbk, + init_vgmstream_otm, + init_vgmstream_bcstm, + init_vgmstream_3ds_idsp, init_vgmstream_kt_g1l, init_vgmstream_kt_wiibgm, + init_vgmstream_ktss, init_vgmstream_hca, init_vgmstream_ps2_svag_snk, init_vgmstream_ps2_vds_vdm, @@ -349,6 +350,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_rsd6xma, init_vgmstream_ta_aac_x360, init_vgmstream_ta_aac_ps3, + init_vgmstream_ta_aac_mobile, init_vgmstream_ps3_mta2, init_vgmstream_ngc_ulw, init_vgmstream_pc_xa30, @@ -370,7 +372,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_sps, init_vgmstream_ngc_vid1, init_vgmstream_flx, - init_vgmstream_mogg, + init_vgmstream_mogg, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -512,9 +514,9 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { } #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type==coding_MP4_AAC) { - reset_mp4_aac(vgmstream); - } + if (vgmstream->coding_type==coding_MP4_AAC) { + reset_mp4_aac(vgmstream); + } #endif #ifdef VGM_USE_MPEG @@ -541,11 +543,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type==coding_AT3plus) { - reset_at3plus(vgmstream); - } + if (vgmstream->coding_type==coding_AT3plus) { + reset_at3plus(vgmstream); + } #endif - + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type==coding_ATRAC9) { + reset_atrac9(vgmstream); + } +#endif + #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type==coding_FFmpeg) { reset_ffmpeg(vgmstream); @@ -701,10 +709,10 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type==coding_MP4_AAC) { - free_mp4_aac(vgmstream->codec_data); + if (vgmstream->coding_type==coding_MP4_AAC) { + free_mp4_aac(vgmstream->codec_data); vgmstream->codec_data = NULL; - } + } #endif #ifdef VGM_USE_MPEG @@ -734,10 +742,17 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type == coding_AT3plus) { - free_at3plus(vgmstream->codec_data); + if (vgmstream->coding_type == coding_AT3plus) { + free_at3plus(vgmstream->codec_data); vgmstream->codec_data = NULL; - } + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + free_atrac9(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } #endif if (vgmstream->coding_type==coding_ACM) { @@ -926,7 +941,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); break; - case layout_mxch_blocked: + case layout_mxch_blocked: case layout_ast_blocked: case layout_halpst_blocked: case layout_xa_blocked: @@ -949,8 +964,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_psx_mgav_blocked: case layout_ps2_adm_blocked: case layout_dsp_bdsp_blocked: - case layout_tra_blocked: - case layout_ps2_iab_blocked: + case layout_tra_blocked: + case layout_ps2_iab_blocked: case layout_ps2_strlr_blocked: case layout_rws_blocked: case layout_hwas_blocked: @@ -986,7 +1001,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_CRI_ADX_exp: case coding_CRI_ADX_enc_8: case coding_CRI_ADX_enc_9: - return (vgmstream->interleave_block_size - 2) * 2; + return (vgmstream->interleave_block_size - 2) * 2; case coding_L5_555: return 32; case coding_NGC_DSP: @@ -1002,6 +1017,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM8_SB_int: case coding_PCM8_U_int: case coding_ULAW: + case coding_ULAW_int: case coding_ALAW: case coding_PCMFLOAT: return 1; @@ -1056,14 +1072,14 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PSX_cfg: return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */ case coding_XBOX: - case coding_XBOX_int: + case coding_XBOX_int: case coding_FSB_IMA: return 64; case coding_EA_XA: case coding_EA_XA_int: case coding_EA_XA_V2: case coding_MAXIS_XA: - return 28; + return 28; case coding_EA_XAS: return 128; case coding_WS: @@ -1099,7 +1115,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; if (data) { - /* must know the full block size for edge loops */ + /* must know the full block size for edge loops */ return data->sampleBufferBlock; } return 0; @@ -1119,12 +1135,16 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_CRI_HCA: return clHCA_samplesPerBlock; #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - case coding_MP4_AAC: - return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; + case coding_MP4_AAC: + return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; #endif #ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard; + case coding_AT3plus: + return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return 0; /* varies with config data, usually 256 or 1024 */ #endif default: return 0; @@ -1157,6 +1177,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_PCM8_SB_int: case coding_PCM8_U_int: case coding_ULAW: + case coding_ULAW_int: case coding_ALAW: return 1; case coding_PCMFLOAT: @@ -1204,7 +1225,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_XA: return 14*vgmstream->channels; case coding_XBOX: - case coding_XBOX_int: + case coding_XBOX_int: case coding_FSB_IMA: return 36; case coding_EA_XA: @@ -1239,7 +1260,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_G719: #endif #ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: + case coding_AT3plus: #endif #ifdef VGM_USE_FFMPEG case coding_FFmpeg: @@ -1253,6 +1274,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x04; case coding_EA_MT: return 0; /* variable (frames of bit counts or PCM frames) */ +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return 0; /* varies with config data, usually 0x100-200 */ +#endif default: return 0; } @@ -1405,6 +1430,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_ULAW_int: + for (chan=0;chanchannels;chan++) { + decode_ulaw_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_ALAW: for (chan=0;chanchannels;chan++) { decode_alaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1595,11 +1627,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - case coding_MP4_AAC: - decode_mp4_aac(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); - break; + case coding_MP4_AAC: + decode_mp4_aac(vgmstream->codec_data, + buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream->channels); + break; #endif case coding_SDX2: for (chan=0;chanchannels;chan++) { @@ -1752,15 +1784,23 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; #endif #ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - for (chan=0;chanchannels;chan++) { - decode_at3plus(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); - } - break; + case coding_AT3plus: + for (chan=0;chanchannels;chan++) { + decode_at3plus(vgmstream, + buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels, + samples_to_do, + chan); + } + break; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + decode_atrac9(vgmstream, + buffer+samples_written*vgmstream->channels, + samples_to_do, + vgmstream->channels); + break; #endif case coding_ACM: /* handled in its own layout, here to quiet compiler */ @@ -1964,6 +2004,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { } #endif +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type==coding_ATRAC9) { + seek_atrac9(vgmstream, vgmstream->loop_sample); + } +#endif + #ifdef VGM_USE_MPEG if (vgmstream->coding_type==coding_MPEG_custom || vgmstream->coding_type==coding_MPEG_ealayer3 || diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 41b00f519..3cbe097b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -22,13 +22,14 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #define VGM_USE_MPEG #endif -/* disabled by default, defined on compile-time for builds that support it*/ +/* disabled by default, defined on compile-time for builds that support it */ #define VGM_USE_G7221 #define VGM_USE_G719 //#define VGM_USE_MP4V2 //#define VGM_USE_FDKAAC //#define VGM_USE_MAIATRAC3PLUS #define VGM_USE_FFMPEG +#define VGM_USE_ATRAC9 #ifdef VGM_USE_VORBIS #ifdef __APPLE__ @@ -44,6 +45,7 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #ifdef VGM_USE_G7221 #include #endif + #ifdef VGM_USE_G719 #include #endif @@ -88,12 +90,13 @@ typedef enum { coding_PCM16_int, /* 16-bit PCM with sample-level interleave (for blocks) */ coding_PCM8, /* 8-bit PCM */ - coding_PCM8_int, /* 8-Bit PCM with sample-level interleave (for blocks) */ + coding_PCM8_int, /* 8-bit PCM with sample-level interleave (for blocks) */ coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */ coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */ coding_PCM8_SB_int, /* 8-bit PCM, sign bit (others are 2's complement) with sample-level interleave (for blocks) */ coding_ULAW, /* 8-bit u-Law (non-linear PCM) */ + coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */ coding_ALAW, /* 8-bit a-Law (non-linear PCM) */ coding_PCMFLOAT, /* 32 bit float PCM */ @@ -113,7 +116,7 @@ typedef enum { coding_G721, /* CCITT G.721 */ coding_XA, /* CD-ROM XA */ - coding_PSX, /* Sony PS ADPCM (VAG) */ + coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ coding_PSX_bmdx, /* Sony PS ADPCM with BMDX encryption */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */ @@ -128,7 +131,7 @@ typedef enum { coding_IMA, /* IMA ADPCM (stereo or mono, low nibble first) */ coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */ coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */ - coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ + coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ coding_3DS_IMA, /* 3DS IMA ADPCM */ coding_MS_IMA, /* Microsoft IMA ADPCM */ coding_XBOX, /* XBOX IMA ADPCM */ @@ -197,11 +200,15 @@ typedef enum { #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - coding_MP4_AAC, /* AAC (MDCT-based) */ + coding_MP4_AAC, /* AAC (MDCT-based) */ #endif #ifdef VGM_USE_MAIATRAC3PLUS - coding_AT3plus, /* Sony ATRAC3plus (MDCT-based) */ + coding_AT3plus, /* Sony ATRAC3plus (MDCT-based) */ +#endif + +#ifdef VGM_USE_ATRAC9 + coding_ATRAC9, /* Sony ATRAC9 (MDCT-based) */ #endif #ifdef VGM_USE_FFMPEG @@ -257,7 +264,7 @@ typedef enum { layout_mus_acm, /* mus has multi-files to deal with */ layout_aix, /* CRI AIX's wheels within wheels */ layout_aax, /* CRI AAX's wheels within databases */ - layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ + layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ #ifdef VGM_USE_VORBIS layout_ogg_vorbis, /* ogg vorbis file */ @@ -278,14 +285,14 @@ typedef enum { meta_DSP_JETTERS, /* Bomberman Jetters .dsp */ meta_DSP_MSS, /* ? */ meta_DSP_GCM, /* ? */ - meta_DSP_STR, /* Conan .str files */ + meta_DSP_STR, /* Conan .str files */ meta_DSP_SADB, /* .sad */ meta_DSP_WSI, /* .wsi */ - meta_DSP_WII_IDSP, /* .gcm with IDSP header */ + meta_DSP_WII_IDSP, /* .gcm with IDSP header */ meta_DSP_WII_MUS, /* .mus */ - meta_DSP_WII_WSD, /* Phantom Brave (WII) */ - meta_WII_NDP, /* Vertigo (Wii) */ - meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */ + meta_DSP_WII_WSD, /* Phantom Brave (WII) */ + meta_WII_NDP, /* Vertigo (Wii) */ + meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */ /* Nintendo */ meta_STRM, /* Nintendo STRM */ @@ -296,15 +303,15 @@ typedef enum { meta_RWAR, /* single-stream RWAR */ meta_RWAV, /* contents of RWAR */ meta_CWAV, /* contents of CWAR */ - meta_FWAV, /* contents of FWAR */ + meta_FWAV, /* contents of FWAR */ meta_RSTM_SPM, /* RSTM with 44->22khz hack */ meta_THP, /* THP movie files */ meta_RSTM_shrunken, /* Atlus' mutant shortened RSTM */ - meta_NDS_SWAV, /* Asphalt Urban GT 1 & 2 */ - meta_NDS_RRDS, /* Ridge Racer DS */ + meta_NDS_SWAV, /* Asphalt Urban GT 1 & 2 */ + meta_NDS_RRDS, /* Ridge Racer DS */ meta_WII_BNS, /* Wii BNS Banner Sound (similar to RSTM) */ meta_STX, /* Pikmin .stx */ - meta_WIIU_BTSND, /* Wii U Boot Sound */ + meta_WIIU_BTSND, /* Wii U Boot Sound */ meta_ADX_03, /* CRI ADX "type 03" */ meta_ADX_04, /* CRI ADX "type 04" */ @@ -317,157 +324,157 @@ typedef enum { meta_RSF, /* Retro Studios RSF (Metroid Prime .rsf) [no header_id] */ meta_HALPST, /* HAL Labs HALPST */ meta_GCSW, /* GCSW (PCM) */ - meta_CFN, /* Namco CAF Audio File */ + meta_CFN, /* Namco CAF Audio File */ meta_MYSPD, /* U-Sing .myspd */ meta_HIS, /* Her Ineractive .his */ meta_BNSF, /* Bandai Namco Sound Format */ meta_PSX_XA, /* CD-ROM XA */ - meta_PS2_SShd, /* .ADS with SShd header */ - meta_PS2_NPSF, /* Namco Production Sound File */ + meta_PS2_SShd, /* .ADS with SShd header */ + meta_PS2_NPSF, /* Namco Production Sound File */ meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ - meta_PS2_RAW, /* RAW Interleaved Format */ - meta_PS2_EXST, /* Shadow of Colossus EXST */ - meta_PS2_SVAG, /* Konami SVAG */ - meta_PS2_MIB, /* MIB File */ - meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ - meta_PS2_MIC, /* KOEI MIC File */ - meta_PS2_VAGi, /* VAGi Interleaved File */ - meta_PS2_VAGp, /* VAGp Mono File */ - meta_PS2_VAGm, /* VAGp Mono File */ - meta_PS2_pGAV, /* VAGp with Little Endian Header */ + meta_PS2_RAW, /* RAW Interleaved Format */ + meta_PS2_EXST, /* Shadow of Colossus EXST */ + meta_PS2_SVAG, /* Konami SVAG */ + meta_PS2_MIB, /* MIB File */ + meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ + meta_PS2_MIC, /* KOEI MIC File */ + meta_PS2_VAGi, /* VAGi Interleaved File */ + meta_PS2_VAGp, /* VAGp Mono File */ + meta_PS2_VAGm, /* VAGp Mono File */ + meta_PS2_pGAV, /* VAGp with Little Endian Header */ meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ - meta_PS2_STR, /* Pacman STR+STH files */ - meta_PS2_ILD, /* ILD File */ - meta_PS2_PNB, /* PsychoNauts Bgm File */ - meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */ - meta_PS2_VPK, /* VPK Audio File */ + meta_PS2_STR, /* Pacman STR+STH files */ + meta_PS2_ILD, /* ILD File */ + meta_PS2_PNB, /* PsychoNauts Bgm File */ + meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */ + meta_PS2_VPK, /* VPK Audio File */ meta_PS2_BMDX, /* Beatmania thing */ meta_PS2_IVB, /* Langrisser 3 IVB */ meta_PS2_SND, /* some Might & Magics SSND header */ meta_PS2_SVS, /* Square SVS */ - meta_XSS, /* Dino Crisis 3 */ - meta_SL3, /* Test Drive Unlimited */ - meta_HGC1, /* Knights of the Temple 2 */ - meta_AUS, /* Various Capcom games */ - meta_RWS, /* RenderWare games (only when using RW Audio middleware) */ + meta_XSS, /* Dino Crisis 3 */ + meta_SL3, /* Test Drive Unlimited */ + meta_HGC1, /* Knights of the Temple 2 */ + meta_AUS, /* Various Capcom games */ + meta_RWS, /* RenderWare games (only when using RW Audio middleware) */ meta_FSB1, /* FMOD Sample Bank, version 1 */ meta_FSB2, /* FMOD Sample Bank, version 2 */ meta_FSB3, /* FMOD Sample Bank, version 3.0/3.1 */ meta_FSB4, /* FMOD Sample Bank, version 4 */ meta_FSB5, /* FMOD Sample Bank, version 5 */ - meta_RWX, /* Air Force Delta Storm (XBOX) */ - meta_XWB, /* Microsoft XACT framework (Xbox, X360, Windows) */ + meta_RWX, /* Air Force Delta Storm (XBOX) */ + meta_XWB, /* Microsoft XACT framework (Xbox, X360, Windows) */ meta_PS2_XA30, /* Driver - Parallel Lines (PS2) */ - meta_MUSC, /* Krome PS2 games */ - meta_MUSX_V004, /* Spyro Games, possibly more */ - meta_MUSX_V005, /* Spyro Games, possibly more */ - meta_MUSX_V006, /* Spyro Games, possibly more */ - meta_MUSX_V010, /* Spyro Games, possibly more */ - meta_MUSX_V201, /* Sphinx and the cursed Mummy */ - meta_LEG, /* Legaia 2 [no header_id] */ - meta_FILP, /* Resident Evil - Dead Aim */ - meta_IKM, /* Zwei! */ - meta_SFS, /* Baroque */ - meta_BG00, /* Ibara, Mushihimesama */ - meta_PS2_RSTM, /* Midnight Club 3 */ - meta_PS2_KCES, /* Dance Dance Revolution */ - meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */ - meta_PS2_PSH, /* Dawn of Mana - Seiken Densetsu 4 */ - meta_SCD_PCM, /* Lunar - Eternal Blue */ - meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */ - meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 */ - meta_PS2_PSW, /* Rayman Raving Rabbids */ - meta_PS2_VAS, /* Pro Baseball Spirits 5 */ - meta_PS2_TEC, /* TECMO badflagged stream */ - meta_PS2_ENTH, /* Enthusia */ - meta_SDT, /* Baldur's Gate - Dark Alliance */ - meta_NGC_TYDSP, /* Ty - The Tasmanian Tiger */ - meta_NGC_SWD, /* Conflict - Desert Storm 1 & 2 */ + meta_MUSC, /* Krome PS2 games */ + meta_MUSX_V004, /* Spyro Games, possibly more */ + meta_MUSX_V005, /* Spyro Games, possibly more */ + meta_MUSX_V006, /* Spyro Games, possibly more */ + meta_MUSX_V010, /* Spyro Games, possibly more */ + meta_MUSX_V201, /* Sphinx and the cursed Mummy */ + meta_LEG, /* Legaia 2 [no header_id] */ + meta_FILP, /* Resident Evil - Dead Aim */ + meta_IKM, /* Zwei! */ + meta_SFS, /* Baroque */ + meta_BG00, /* Ibara, Mushihimesama */ + meta_PS2_RSTM, /* Midnight Club 3 */ + meta_PS2_KCES, /* Dance Dance Revolution */ + meta_PS2_DXH, /* Tokobot Plus - Myteries of the Karakuri */ + meta_PS2_PSH, /* Dawn of Mana - Seiken Densetsu 4 */ + meta_SCD_PCM, /* Lunar - Eternal Blue */ + meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */ + meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 */ + meta_PS2_PSW, /* Rayman Raving Rabbids */ + meta_PS2_VAS, /* Pro Baseball Spirits 5 */ + meta_PS2_TEC, /* TECMO badflagged stream */ + meta_PS2_ENTH, /* Enthusia */ + meta_SDT, /* Baldur's Gate - Dark Alliance */ + meta_NGC_TYDSP, /* Ty - The Tasmanian Tiger */ + meta_NGC_SWD, /* Conflict - Desert Storm 1 & 2 */ meta_CAPDSP, /* Capcom DSP Header [no header_id] */ - meta_DC_STR, /* SEGA Stream Asset Builder */ - meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */ - meta_NGC_BH2PCM, /* Bio Hazard 2 */ - meta_SAT_SAP, /* Bubble Symphony */ - meta_DC_IDVI, /* Eldorado Gate */ - meta_KRAW, /* Geometry Wars - Galaxies */ - meta_PS2_OMU, /* PS2 Int file with Header */ - meta_PS2_XA2, /* XG3 Extreme-G Racing */ - meta_IDSP, /* Chronicles of Narnia, Soul Calibur Legends, Mario Strikers Charged */ - meta_SPT_SPD, /* Various (SPT+SPT DSP) */ - meta_ISH_ISD, /* Various (ISH+ISD DSP) */ - meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */ - meta_YDSP, /* WWE Day of Reckoning */ + meta_DC_STR, /* SEGA Stream Asset Builder */ + meta_DC_STR_V2, /* variant of SEGA Stream Asset Builder */ + meta_NGC_BH2PCM, /* Bio Hazard 2 */ + meta_SAT_SAP, /* Bubble Symphony */ + meta_DC_IDVI, /* Eldorado Gate */ + meta_KRAW, /* Geometry Wars - Galaxies */ + meta_PS2_OMU, /* PS2 Int file with Header */ + meta_PS2_XA2, /* XG3 Extreme-G Racing */ + meta_IDSP, /* Chronicles of Narnia, Soul Calibur Legends, Mario Strikers Charged */ + meta_SPT_SPD, /* Various (SPT+SPT DSP) */ + meta_ISH_ISD, /* Various (ISH+ISD DSP) */ + meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */ + meta_YDSP, /* WWE Day of Reckoning */ meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */ - meta_WAA_WAC_WAD_WAM, /* Beyond Good & Evil */ - meta_GCA, /* Metal Slug Anthology */ - meta_MSVP, /* Popcap Hits */ - meta_NGC_SSM, /* Golden Gashbell Full Power */ - meta_PS2_JOE, /* Wall-E / Pixar games */ + meta_WAA_WAC_WAD_WAM, /* Beyond Good & Evil */ + meta_GCA, /* Metal Slug Anthology */ + meta_MSVP, /* Popcap Hits */ + meta_NGC_SSM, /* Golden Gashbell Full Power */ + meta_PS2_JOE, /* Wall-E / Pixar games */ - meta_NGC_YMF, /* WWE WrestleMania X8 */ + meta_NGC_YMF, /* WWE WrestleMania X8 */ meta_SADL, /* .sad */ meta_PS2_CCC, /* Tokyo Xtreme Racer DRIFT 2 */ meta_PSX_FAG, /* Jackie Chan - Stuntmaster */ meta_PS2_MIHB, /* Merged MIH+MIB */ meta_NGC_PDT, /* Mario Party 6 */ - meta_DC_ASD, /* Miss Moonligh */ - meta_NAOMI_SPSD, /* Guilty Gear X */ + meta_DC_ASD, /* Miss Moonligh */ + meta_NAOMI_SPSD, /* Guilty Gear X */ - meta_RSD2VAG, /* RSD2VAG */ - meta_RSD2PCMB, /* RSD2PCMB */ - meta_RSD2XADP, /* RSD2XADP */ - meta_RSD3VAG, /* RSD3VAG */ - meta_RSD3GADP, /* RSD3GADP */ - meta_RSD3PCM, /* RSD3PCM */ - meta_RSD3PCMB, /* RSD3PCMB */ - meta_RSD4PCMB, /* RSD4PCMB */ - meta_RSD4PCM, /* RSD4PCM */ - meta_RSD4RADP, /* RSD4RADP */ - meta_RSD4VAG, /* RSD4VAG */ - meta_RSD6VAG, /* RSD6VAG */ - meta_RSD6WADP, /* RSD6WADP */ - meta_RSD6XADP, /* RSD6XADP */ - meta_RSD6RADP, /* RSD6RADP */ - meta_RSD6OOGV, /* RSD6OOGV */ + meta_RSD2VAG, /* RSD2VAG */ + meta_RSD2PCMB, /* RSD2PCMB */ + meta_RSD2XADP, /* RSD2XADP */ + meta_RSD3VAG, /* RSD3VAG */ + meta_RSD3GADP, /* RSD3GADP */ + meta_RSD3PCM, /* RSD3PCM */ + meta_RSD3PCMB, /* RSD3PCMB */ + meta_RSD4PCMB, /* RSD4PCMB */ + meta_RSD4PCM, /* RSD4PCM */ + meta_RSD4RADP, /* RSD4RADP */ + meta_RSD4VAG, /* RSD4VAG */ + meta_RSD6VAG, /* RSD6VAG */ + meta_RSD6WADP, /* RSD6WADP */ + meta_RSD6XADP, /* RSD6XADP */ + meta_RSD6RADP, /* RSD6RADP */ + meta_RSD6OOGV, /* RSD6OOGV */ meta_RSD6XMA, /* RSD6XMA */ - meta_PS2_ASS, /* ASS */ - meta_PS2_SEG, /* Eragon */ + meta_PS2_ASS, /* ASS */ + meta_PS2_SEG, /* Eragon */ meta_XBOX_SEG, /* Eragon */ - meta_NDS_STRM_FFTA2, /* Final Fantasy Tactics A2 */ - meta_STR_ASR, /* Donkey Kong Jet Race */ - meta_ZWDSP, /* Zack and Wiki */ - meta_VGS, /* Guitar Hero Encore - Rocks the 80s */ - meta_DC_DCSW_DCS, /* Evil Twin - Cypriens Chronicles (DC) */ - meta_WII_SMP, /* Mushroom Men - The Spore Wars */ + meta_NDS_STRM_FFTA2, /* Final Fantasy Tactics A2 */ + meta_STR_ASR, /* Donkey Kong Jet Race */ + meta_ZWDSP, /* Zack and Wiki */ + meta_VGS, /* Guitar Hero Encore - Rocks the 80s */ + meta_DC_DCSW_DCS, /* Evil Twin - Cypriens Chronicles (DC) */ + meta_WII_SMP, /* Mushroom Men - The Spore Wars */ meta_WII_SNG, /* Excite Trucks */ - meta_EMFF_PS2, /* Eidos Music File Format for PS2*/ - meta_EMFF_NGC, /* Eidos Music File Format for NGC/WII */ - meta_SAT_BAKA, /* Crypt Killer */ + meta_EMFF_PS2, /* Eidos Music File Format for PS2*/ + meta_EMFF_NGC, /* Eidos Music File Format for NGC/WII */ + meta_SAT_BAKA, /* Crypt Killer */ meta_PS2_VSF, /* Musashi: Samurai Legend */ - meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */ - meta_ADS, /* Gauntlet Dark Legends (GC) */ - meta_PS2_SPS, /* Ape Escape 2 */ + meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */ + meta_ADS, /* Gauntlet Dark Legends (GC) */ + meta_PS2_SPS, /* Ape Escape 2 */ meta_PS2_XA2_RRP, /* RC Revenge Pro */ meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */ - meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ + meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ - meta_XBOX_WAVM, /* XBOX WAVM File */ - meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */ - meta_XBOX_WVS, /* XBOX WVS */ - meta_NGC_WVS, /* Metal Arms - Glitch in the System */ - meta_XBOX_MATX, /* XBOX MATX */ - meta_XBOX_XMU, /* XBOX XMU */ - meta_XBOX_XVAS, /* XBOX VAS */ + meta_XBOX_WAVM, /* XBOX WAVM File */ + meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */ + meta_XBOX_WVS, /* XBOX WVS */ + meta_NGC_WVS, /* Metal Arms - Glitch in the System */ + meta_XBOX_MATX, /* XBOX MATX */ + meta_XBOX_XMU, /* XBOX XMU */ + meta_XBOX_XVAS, /* XBOX VAS */ meta_EA_SCHL, /* Electronic Arts SCHl with variable header */ meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */ meta_EA_BNK, /* Electronic Arts BNK */ meta_EA_1SNH, /* Electronic Arts 1SNh/EACS */ - meta_RAW, /* RAW PCM file */ + meta_RAW, /* RAW PCM file */ meta_GENH, /* generic header */ @@ -486,7 +493,7 @@ typedef enum { meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */ meta_XNB, /* XNA Game Studio 4.0 */ meta_PC_MXST, /* Lego Island MxSt */ - meta_SAB, /* Worms 4 Mayhem SAB+SOB file */ + meta_SAB, /* Worms 4 Mayhem SAB+SOB file */ meta_NWA, /* Visual Art's NWA */ meta_NWA_NWAINFOINI, /* Visual Art's NWA w/ NWAINFO.INI for looping */ meta_NWA_GAMEEXEINI, /* Visual Art's NWA w/ Gameexe.ini for looping */ @@ -495,24 +502,24 @@ typedef enum { meta_ACM, /* InterPlay ACM header */ meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */ meta_DEC, /* Falcom PC games (Xanadu Next, Gurumin) */ - meta_VS, /* Men in Black .vs */ + meta_VS, /* Men in Black .vs */ meta_FFXI_BGW, /* FFXI (PC) BGW */ meta_FFXI_SPW, /* FFXI (PC) SPW */ - meta_STS_WII, /* Shikigami No Shiro 3 STS Audio File */ - meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */ - meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */ + meta_STS_WII, /* Shikigami No Shiro 3 STS Audio File */ + meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */ + meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */ meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */ - meta_PS2_TK5, /* Tekken 5 Stream Files */ - meta_WII_STR, /* House of The Dead Overkill STR+STH */ - meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ + meta_PS2_TK5, /* Tekken 5 Stream Files */ + meta_WII_STR, /* House of The Dead Overkill STR+STH */ + meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */ meta_ZSD, /* Dragon Booster ZSD */ meta_RedSpark, /* "RedSpark" RSD (MadWorld) */ - meta_PC_IVAUD, /* .ivaud GTA IV */ - meta_NDS_HWAS, /* Spider-Man 3, Tony Hawk's Downhill Jam, possibly more... */ - meta_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */ - meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */ - meta_SD9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */ - meta_2DX9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */ + meta_PC_IVAUD, /* .ivaud GTA IV */ + meta_NDS_HWAS, /* Spider-Man 3, Tony Hawk's Downhill Jam, possibly more... */ + meta_NGC_LPS, /* Rave Master (Groove Adventure Rave)(GC) */ + meta_NAOMI_ADPCM, /* NAOMI/NAOMI2 ARcade games */ + meta_SD9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */ + meta_2DX9, /* beatmaniaIIDX16 - EMPRESS (Arcade) */ meta_PS2_VGV, /* Rune: Viking Warlord */ meta_NGC_GCUB, /* Sega Soccer Slam */ meta_MAXIS_XA, /* Sim City 3000 (PC) */ @@ -523,9 +530,9 @@ typedef enum { meta_PONA_3DO, /* Policenauts (3DO) */ meta_PONA_PSX, /* Policenauts (PSX) */ meta_XBOX_HLWAV, /* Half Life 2 (XBOX) */ - meta_PS2_AST, /* Some KOEI game (PS2) */ - meta_DMSG, /* Nightcaster II - Equinox (XBOX) */ - meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */ + meta_PS2_AST, /* Some KOEI game (PS2) */ + meta_DMSG, /* Nightcaster II - Equinox (XBOX) */ + meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */ meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */ meta_PS2_WB, /* Shooting Love. ~TRIZEAL~ */ meta_S14, /* raw Siren 14, 24kbit mono */ @@ -537,7 +544,7 @@ typedef enum { meta_PS2_KHV, /* Kingdom Hearts 2 VAG streams */ meta_PC_SMP, /* Ghostbusters PC .smp */ meta_P3D, /* Prototype P3D */ - meta_PS2_TK1, /* Tekken (NamCollection) */ + meta_PS2_TK1, /* Tekken (NamCollection) */ meta_PS2_ADSC, /* Kenka Bancho 2: Full Throttle */ meta_NGC_BO2, /* Blood Omen 2 (NGC) */ meta_DSP_DDSP, /* Various (2 dsp files stuck together */ @@ -550,10 +557,10 @@ typedef enum { meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_CABELAS, /* Cabelas games */ meta_PS2_ADM, /* Dragon Quest 5 */ - meta_PS2_LPCM, /* Ah! My Goddess */ + meta_PS2_LPCM, /* Ah! My Goddess */ meta_DSP_BDSP, /* Ah! My Goddess */ - meta_PS2_VMS, /* Autobahn Raser - Police Madness */ - meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */ + meta_PS2_VMS, /* Autobahn Raser - Police Madness */ + meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */ meta_GH3_BAR, /* Guitar Hero III Mobile .bar */ meta_FFW, /* Freedom Fighters [NGC] */ meta_DSP_DSPW, /* Sengoku Basara 3 [WII] */ @@ -561,46 +568,47 @@ typedef enum { meta_SQEX_SCD, /* Square-Enix SCD */ meta_NGC_NST_DSP, /* Animaniacs [NGC] */ meta_BAF, /* Bizarre Creations (Blur, James Bond) */ - meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */ - meta_PS3_CPS, /* Eternal Sonata (PS3) */ + meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */ + meta_PS3_CPS, /* Eternal Sonata (PS3) */ meta_PS3_MSF, /* MSF header */ - meta_NUB_VAG, /* Namco VAG from NUB archives */ - meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ + meta_NUB_VAG, /* Namco VAG from NUB archives */ + meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ - meta_NGCA, /* GoldenEye 007 (Wii) */ - meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ - meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */ - meta_X360_TRA, /* Def Jam Rapstar */ - meta_PS2_VGS, /* Princess Soft PS2 games */ - meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ - meta_PS2_STRLR, /* The Bouncer */ + meta_NGCA, /* GoldenEye 007 (Wii) */ + meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ + meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */ + meta_X360_TRA, /* Def Jam Rapstar */ + meta_PS2_VGS, /* Princess Soft PS2 games */ + meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ + meta_PS2_STRLR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ - meta_PC_SNDS, // Incredibles PC .snds - meta_PS2_WMUS, // The Warriors (PS2) - meta_HYPERSCAN_KVAG, // Hyperscan KVAG/BVG - meta_IOS_PSND, // Crash Bandicoot Nitro Kart 2 (iOS) - meta_BOS_ADP, // ADP! (Balls of Steel, PC) - meta_OTNS_ADP, // Omikron: The Nomad Soul .adp (PC/DC) - meta_EB_SFX, // Excitebots .sfx - meta_EB_SF0, // Excitebots .sf0 - meta_PS3_KLBS, // L@VE ONCE (PS3) - meta_PS2_MTAF, // Metal Gear Solid 3 MTAF - meta_PS2_VAG1, // Metal Gear Solid 3 VAG1 - meta_PS2_VAG2, // Metal Gear Solid 3 VAG2 - meta_TUN, // LEGO Racers (PC) - meta_WPD, // Shuffle! (PC) - meta_MN_STR, // Mini Ninjas (PC/PS3/WII) - meta_MSS, // Guerilla: ShellShock Nam '67 (PS2/Xbox), Killzone (PS2) - meta_PS2_HSF, // Lowrider (PS2) - meta_PS3_IVAG, // Interleaved VAG files (PS3) - meta_PS2_2PFS, // Konami: Mahoromatic: Moetto - KiraKira Maid-San, GANTZ (PS2) - meta_PS2_VBK, // Disney's Stitch - Experiment 626 - meta_OTM, // Otomedius (Arcade) - meta_CSTM, // Nintendo 3DS CSTM (Century Stream) - meta_FSTM, // Nintendo Wii U FSTM (caFe? Stream) - meta_3DS_IDSP, // Nintendo 3DS/Wii U IDSP - meta_KT_WIIBGM, // Koei Tecmo WiiBGM + meta_PC_SNDS, /* Incredibles PC .snds */ + meta_PS2_WMUS, /* The Warriors (PS2) */ + meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */ + meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */ + meta_BOS_ADP, /* ADP! (Balls of Steel, PC) */ + meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */ + meta_EB_SFX, /* Excitebots .sfx */ + meta_EB_SF0, /* Excitebots .sf0 */ + meta_PS3_KLBS, /* L@VE ONCE (PS3) */ + meta_PS2_MTAF, /* Metal Gear Solid 3 MTAF */ + meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */ + meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */ + meta_TUN, /* LEGO Racers (PC) */ + meta_WPD, /* Shuffle! (PC) */ + meta_MN_STR, /* Mini Ninjas (PC/PS3/WII) */ + meta_MSS, /* Guerilla: ShellShock Nam '67 (PS2/Xbox), Killzone (PS2) */ + meta_PS2_HSF, /* Lowrider (PS2) */ + meta_PS3_IVAG, /* Interleaved VAG files (PS3) */ + meta_PS2_2PFS, /* Konami: Mahoromatic: Moetto - KiraKira Maid-San, GANTZ (PS2) */ + meta_PS2_VBK, /* Disney's Stitch - Experiment 626 */ + meta_OTM, /* Otomedius (Arcade) */ + meta_CSTM, /* Nintendo 3DS CSTM (Century Stream) */ + meta_FSTM, /* Nintendo Wii U FSTM (caFe? Stream) */ + meta_3DS_IDSP, /* Nintendo 3DS/Wii U IDSP */ + meta_KT_WIIBGM, /* Koei Tecmo WiiBGM */ + meta_KTSS, /* Koei Tecmo Switch Sound */ meta_MCA, /* Capcom MCA "MADP" */ meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */ meta_HCA, /* CRI HCA */ @@ -618,8 +626,9 @@ typedef enum { meta_OGL, /* Shin'en Wii/WiiU (Jett Rocket (Wii), FAST Racing NEO (WiiU)) */ meta_MC3, /* Paradigm games (T3 PS2, MX Rider PS2, MI: Operation Surma PS2) */ meta_GTD, /* Knights Contract (X360/PS3), Valhalla Knights 3 (PSV) */ - meta_TA_AAC_X360, /* tri-ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ - meta_TA_AAC_PS3, /* tri-ace AAC (Star Ocean International, Resonance of Fate) */ + meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ + meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */ + meta_TA_AAC_VORBIS, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_PC_XA30, /* Driver - Parallel Lines (PC) */ @@ -642,7 +651,7 @@ typedef enum { meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */ meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */ meta_PC_FLX, /* Ultima IX PC */ - meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */ + meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ @@ -693,8 +702,8 @@ typedef struct { int32_t adpcm_history4_32; }; - double adpcm_history1_double; - double adpcm_history2_double; + double adpcm_history1_double; + double adpcm_history2_double; int adpcm_step_index; /* for IMA */ int adpcm_scale; /* for MS ADPCM */ @@ -771,8 +780,8 @@ typedef struct { int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */ int codec_version; /* flag for codecs with minor variations */ - uint8_t xa_channel; /* XA ADPCM: selected channel */ - int32_t xa_sector_length; /* XA ADPCM: XA block */ + uint8_t xa_channel; /* XA ADPCM: selected channel */ + int32_t xa_sector_length; /* XA ADPCM: XA block */ uint8_t xa_headerless; /* XA ADPCM: headerless XA */ int8_t xa_get_high_nibble; /* XA ADPCM: mono/stereo nibble selection (XA state could be simplified) */ @@ -780,7 +789,7 @@ typedef struct { void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream (for AAX/AIX/SCD) */ - /* Data the codec needs for the whole stream. This is for codecs too + /* Data the codec needs for the whole stream. This is for codecs too * different from vgmstream's structure to be reasonably shoehorned into * using the ch structures. * Note also that support must be added for resetting, looping and @@ -930,7 +939,6 @@ typedef struct { size_t current_size_target; /* max data, until something happens */ size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ - } mpeg_custom_stream; typedef struct { @@ -980,13 +988,50 @@ typedef struct { #ifdef VGM_USE_MAIATRAC3PLUS typedef struct { - sample *buffer; - int channels; - int samples_discard; - void *handle; + sample *buffer; + int channels; + int samples_discard; + void *handle; } maiatrac3plus_codec_data; #endif +#ifdef VGM_USE_ATRAC9 + +/* custom ATRAC9 modes */ +typedef enum { + ATRAC9_DEFAULT = 0, /* ATRAC9 standard */ + ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */ + //ATRAC9_FSB, /* FMOD FSB: Vita multichannel interleaves 2ch xN superframes */ + //ATRAC9_EATRAX, /* EA EATrax: buffered ATRAC9 in SPS blocks (superframes can be split between blocks) */ +} atrac9_custom_t; + +typedef struct { + atrac9_custom_t type; + + int channels; /* to detect weird multichannel */ + uint32_t config_data; /* ATRAC9 config header */ + int encoder_delay; /* initial samples to discard */ + + size_t interleave_skip; /* XVAG */ + size_t subsong_skip; /* XVAG */ +} atrac9_config; + +typedef struct { + uint8_t *data_buffer; + size_t data_buffer_size; + + sample *sample_buffer; + size_t samples_filled; /* number of samples in the buffer */ + size_t samples_used; /* number of samples extracted from the buffer */ + + int samples_to_discard; + + atrac9_config config; + + void *handle; /* decoder handle */ +} atrac9_codec_data; +#endif + /* with one file this is also used for just ACM */ typedef struct { int file_count; @@ -1138,22 +1183,22 @@ typedef struct { #ifdef VGM_USE_MP4V2 typedef struct { - STREAMFILE *streamfile; - uint64_t start; - uint64_t offset; - uint64_t size; + STREAMFILE *streamfile; + uint64_t start; + uint64_t offset; + uint64_t size; } mp4_streamfile; #ifdef VGM_USE_FDKAAC typedef struct { - mp4_streamfile if_file; - MP4FileHandle h_mp4file; - MP4TrackId track_id; - unsigned long sampleId, numSamples; - UINT codec_init_data_size; - HANDLE_AACDECODER h_aacdecoder; - unsigned int sample_ptr, samples_per_frame, samples_discard; - INT_PCM sample_buffer[( (6) * (2048)*4 )]; + mp4_streamfile if_file; + MP4FileHandle h_mp4file; + MP4TrackId track_id; + unsigned long sampleId, numSamples; + UINT codec_init_data_size; + HANDLE_AACDECODER h_aacdecoder; + unsigned int sample_ptr, samples_per_frame, samples_discard; + INT_PCM sample_buffer[( (6) * (2048)*4 )]; } mp4_aac_codec_data; #endif #endif From 2a9836569ec6b096f3459310dbf6c707463c8435 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 27 Jan 2018 15:08:06 -0800 Subject: [PATCH 016/104] Implemented AdPlug decoder. --- .gitmodules | 9 + Cog.xcodeproj/project.pbxproj | 56 ++ .../AdPlug/AdPlug.xcodeproj/project.pbxproj | 882 ++++++++++++++++++ Frameworks/AdPlug/AdPlug/Info.plist | 26 + Frameworks/AdPlug/AdPlug/adplug | 1 + Frameworks/AdPlug/AdPlug/database | 1 + Frameworks/AdPlug/AdPlug/version.h | 1 + .../libbinio.xcodeproj/project.pbxproj | 345 +++++++ Frameworks/libbinio/libbinio/Info.plist | 26 + Frameworks/libbinio/libbinio/binio.h | 175 ++++ Frameworks/libbinio/libbinio/libbinio | 1 + .../AdPlug/AdPlug.xcodeproj/project.pbxproj | 413 ++++++++ Plugins/AdPlug/AdPlug/AdPlugContainer.h | 17 + Plugins/AdPlug/AdPlug/AdPlugContainer.mm | 79 ++ Plugins/AdPlug/AdPlug/AdPlugDecoder.h | 30 + Plugins/AdPlug/AdPlug/AdPlugDecoder.mm | 184 ++++ Plugins/AdPlug/AdPlug/AdPlugMetadataReader.h | 17 + Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm | 72 ++ Plugins/AdPlug/AdPlug/Info.plist | 26 + Plugins/AdPlug/AdPlug/fileprovider.h | 35 + Plugins/AdPlug/AdPlug/fileprovider.mm | 118 +++ 21 files changed, 2514 insertions(+) create mode 100644 Frameworks/AdPlug/AdPlug.xcodeproj/project.pbxproj create mode 100644 Frameworks/AdPlug/AdPlug/Info.plist create mode 160000 Frameworks/AdPlug/AdPlug/adplug create mode 160000 Frameworks/AdPlug/AdPlug/database create mode 100644 Frameworks/AdPlug/AdPlug/version.h create mode 100644 Frameworks/libbinio/libbinio.xcodeproj/project.pbxproj create mode 100644 Frameworks/libbinio/libbinio/Info.plist create mode 100644 Frameworks/libbinio/libbinio/binio.h create mode 160000 Frameworks/libbinio/libbinio/libbinio create mode 100644 Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj create mode 100755 Plugins/AdPlug/AdPlug/AdPlugContainer.h create mode 100755 Plugins/AdPlug/AdPlug/AdPlugContainer.mm create mode 100755 Plugins/AdPlug/AdPlug/AdPlugDecoder.h create mode 100755 Plugins/AdPlug/AdPlug/AdPlugDecoder.mm create mode 100644 Plugins/AdPlug/AdPlug/AdPlugMetadataReader.h create mode 100644 Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm create mode 100644 Plugins/AdPlug/AdPlug/Info.plist create mode 100644 Plugins/AdPlug/AdPlug/fileprovider.h create mode 100644 Plugins/AdPlug/AdPlug/fileprovider.mm diff --git a/.gitmodules b/.gitmodules index 1a9a85783..f4ceabbed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,12 @@ [submodule "Frameworks/OpenMPT/OpenMPT"] path = Frameworks/OpenMPT/OpenMPT url = https://gitlab.kode54.net/kode54/OpenMPT.git +[submodule "Frameworks/AdPlug/AdPlug/adplug"] + path = Frameworks/AdPlug/AdPlug/adplug + url = https://github.com/adplug/adplug.git +[submodule "Frameworks/libbinio/libbinio/libbinio"] + path = Frameworks/libbinio/libbinio/libbinio + url = https://github.com/adplug/libbinio.git +[submodule "Frameworks/AdPlug/AdPlug/database"] + path = Frameworks/AdPlug/AdPlug/database + url = https://github.com/adplug/database.git diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 618ff85c1..68d97682f 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -183,6 +183,7 @@ 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066A1180D5669008E3612 /* MIDI.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 83D3C6AD201D39E1005564CB /* AdPlug.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D3C601201C674F005564CB /* AdPlug.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; }; 83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */; }; 83E5FE771FFF0C9D00659F0F /* OpenMPT.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E5EFB11FFEF78300659F0F /* OpenMPT.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -542,6 +543,27 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = "FFMPEG Plugin"; }; + 83D3C600201C674F005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83D3C5F3201C674D005564CB; + remoteInfo = AdPlug; + }; + 83D3C6A9201D39BC005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83D3C5F2201C674D005564CB; + remoteInfo = AdPlug; + }; + 83D3C6AB201D39C3005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 836F6B0F18BDB80D0095E648; + remoteInfo = vgmstream; + }; 83E5EFB01FFEF78300659F0F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */; @@ -679,6 +701,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 83D3C6AD201D39E1005564CB /* AdPlug.bundle in CopyFiles */, 83E5FE771FFF0C9D00659F0F /* OpenMPT.bundle in CopyFiles */, 83EEAB241C965C56002761C5 /* Syntrax.bundle in CopyFiles */, 83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */, @@ -982,6 +1005,7 @@ 8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = ""; }; 8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = ""; }; 83B0669C180D5668008E3612 /* MIDI.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MIDI.xcodeproj; path = Plugins/MIDI/MIDI.xcodeproj; sourceTree = ""; }; + 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AdPlug.xcodeproj; path = Plugins/AdPlug/AdPlug.xcodeproj; sourceTree = ""; }; 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOffTemplate.pdf; path = Images/miniModeOffTemplate.pdf; sourceTree = ""; }; 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOnTemplate.pdf; path = Images/miniModeOnTemplate.pdf; sourceTree = ""; }; 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = Plugins/OpenMPT/OpenMPT.xcodeproj; sourceTree = ""; }; @@ -1262,6 +1286,7 @@ 17B619FF0B909ED400BC003F /* PlugIns */ = { isa = PBXGroup; children = ( + 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */, 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */, 83EEAAF51C9651D8002761C5 /* Syntrax.xcodeproj */, 8360EF0017F92B23005208A4 /* HighlyComplete.xcodeproj */, @@ -1677,6 +1702,14 @@ name = Products; sourceTree = ""; }; + 83D3C5FD201C674D005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C601201C674F005564CB /* AdPlug.bundle */, + ); + name = Products; + sourceTree = ""; + }; 83E5EFAD1FFEF78100659F0F /* Products */ = { isa = PBXGroup; children = ( @@ -1848,6 +1881,8 @@ buildRules = ( ); dependencies = ( + 83D3C6AC201D39C3005564CB /* PBXTargetDependency */, + 83D3C6AA201D39BC005564CB /* PBXTargetDependency */, 83E5FE791FFF0CAC00659F0F /* PBXTargetDependency */, 83EEAB261C965C61002761C5 /* PBXTargetDependency */, 83F9D8061A884C33007ABEC2 /* PBXTargetDependency */, @@ -1915,6 +1950,10 @@ mainGroup = 29B97314FDCFA39411CA2CEA /* Cog */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 83D3C5FD201C674D005564CB /* Products */; + ProjectRef = 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */; + }, { ProductGroup = 566D32170D538550004466A5 /* Products */; ProjectRef = 566D32160D538550004466A5 /* APL.xcodeproj */; @@ -2215,6 +2254,13 @@ remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 83D3C601201C674F005564CB /* AdPlug.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = AdPlug.bundle; + remoteRef = 83D3C600201C674F005564CB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 83E5EFB11FFEF78300659F0F /* OpenMPT.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2604,6 +2650,16 @@ name = "FFMPEG Plugin"; targetProxy = 83BCB8DC17FC96FC00760340 /* PBXContainerItemProxy */; }; + 83D3C6AA201D39BC005564CB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = AdPlug; + targetProxy = 83D3C6A9201D39BC005564CB /* PBXContainerItemProxy */; + }; + 83D3C6AC201D39C3005564CB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = vgmstream; + targetProxy = 83D3C6AB201D39C3005564CB /* PBXContainerItemProxy */; + }; 83E5FE791FFF0CAC00659F0F /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = OpenMPT; diff --git a/Frameworks/AdPlug/AdPlug.xcodeproj/project.pbxproj b/Frameworks/AdPlug/AdPlug.xcodeproj/project.pbxproj new file mode 100644 index 000000000..299bc7b2a --- /dev/null +++ b/Frameworks/AdPlug/AdPlug.xcodeproj/project.pbxproj @@ -0,0 +1,882 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 83D3C577201C66E3005564CB /* rad.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C504201C66C1005564CB /* rad.h */; }; + 83D3C578201C66E3005564CB /* adplug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C505201C66C1005564CB /* adplug.cpp */; }; + 83D3C579201C66E3005564CB /* mtk.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C506201C66C2005564CB /* mtk.h */; }; + 83D3C57A201C66E3005564CB /* psi.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C507201C66C2005564CB /* psi.h */; }; + 83D3C57C201C66E3005564CB /* ksm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C509201C66C2005564CB /* ksm.h */; }; + 83D3C57D201C66E3005564CB /* nukedopl.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C50A201C66C3005564CB /* nukedopl.c */; }; + 83D3C57E201C66E3005564CB /* flash.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C50B201C66C3005564CB /* flash.h */; }; + 83D3C57F201C66E3005564CB /* hybrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C50C201C66C3005564CB /* hybrid.cpp */; }; + 83D3C580201C66E3005564CB /* bam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C50D201C66C3005564CB /* bam.cpp */; }; + 83D3C581201C66E3005564CB /* amd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C50E201C66C4005564CB /* amd.cpp */; }; + 83D3C582201C66E3005564CB /* adplug.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C50F201C66C4005564CB /* adplug.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C583201C66E3005564CB /* mus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C510201C66C4005564CB /* mus.cpp */; }; + 83D3C584201C66E3005564CB /* mad.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C511201C66C4005564CB /* mad.h */; }; + 83D3C585201C66E3005564CB /* hsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C512201C66C5005564CB /* hsp.h */; }; + 83D3C586201C66E3005564CB /* cmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C513201C66C5005564CB /* cmf.cpp */; }; + 83D3C587201C66E3005564CB /* u6m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C514201C66C5005564CB /* u6m.cpp */; }; + 83D3C588201C66E3005564CB /* dmo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C515201C66C6005564CB /* dmo.cpp */; }; + 83D3C589201C66E3005564CB /* adl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C516201C66C6005564CB /* adl.cpp */; }; + 83D3C58A201C66E3005564CB /* player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C517201C66C6005564CB /* player.cpp */; }; + 83D3C58B201C66E3005564CB /* cmf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C518201C66C6005564CB /* cmf.h */; }; + 83D3C58C201C66E3005564CB /* d00.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C519201C66C7005564CB /* d00.h */; }; + 83D3C58D201C66E3005564CB /* mad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C51A201C66C7005564CB /* mad.cpp */; }; + 83D3C58E201C66E3005564CB /* hsc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C51B201C66C7005564CB /* hsc.h */; }; + 83D3C58F201C66E3005564CB /* msc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C51C201C66C7005564CB /* msc.cpp */; }; + 83D3C590201C66E3005564CB /* rad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C51D201C66C8005564CB /* rad.cpp */; }; + 83D3C591201C66E3005564CB /* mdi.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C51E201C66C8005564CB /* mdi.h */; }; + 83D3C592201C66E3005564CB /* mididata.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C51F201C66C8005564CB /* mididata.h */; }; + 83D3C593201C66E3005564CB /* s3m.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C520201C66C8005564CB /* s3m.h */; }; + 83D3C594201C66E3005564CB /* dtm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C521201C66C9005564CB /* dtm.h */; }; + 83D3C595201C66E3005564CB /* sng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C522201C66C9005564CB /* sng.cpp */; }; + 83D3C596201C66E3005564CB /* rat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C523201C66C9005564CB /* rat.cpp */; }; + 83D3C597201C66E3005564CB /* xsm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C524201C66C9005564CB /* xsm.cpp */; }; + 83D3C599201C66E3005564CB /* herad.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C526201C66CA005564CB /* herad.h */; }; + 83D3C59A201C66E3005564CB /* bam.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C527201C66CA005564CB /* bam.h */; }; + 83D3C59B201C66E3005564CB /* bmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C528201C66CA005564CB /* bmf.cpp */; }; + 83D3C59C201C66E3005564CB /* hsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C529201C66CB005564CB /* hsp.cpp */; }; + 83D3C59D201C66E3005564CB /* dro.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C52A201C66CB005564CB /* dro.h */; }; + 83D3C59E201C66E3005564CB /* mkj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C52B201C66CB005564CB /* mkj.cpp */; }; + 83D3C59F201C66E3005564CB /* hyp.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C52C201C66CB005564CB /* hyp.h */; }; + 83D3C5A0201C66E3005564CB /* s3m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C52D201C66CC005564CB /* s3m.cpp */; }; + 83D3C5A1201C66E3005564CB /* mkj.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C52E201C66CC005564CB /* mkj.h */; }; + 83D3C5A2201C66E3005564CB /* mus.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C52F201C66CC005564CB /* mus.h */; }; + 83D3C5A3201C66E3005564CB /* fmc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C530201C66CD005564CB /* fmc.cpp */; }; + 83D3C5A4201C66E3005564CB /* vgm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C531201C66CD005564CB /* vgm.h */; }; + 83D3C5A5201C66E3005564CB /* nemuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C532201C66CD005564CB /* nemuopl.cpp */; }; + 83D3C5A6201C66E3005564CB /* protrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C533201C66CD005564CB /* protrack.h */; }; + 83D3C5A7201C66E3005564CB /* sng.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C534201C66CE005564CB /* sng.h */; }; + 83D3C5A8201C66E3005564CB /* mtk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C535201C66CE005564CB /* mtk.cpp */; }; + 83D3C5A9201C66E3005564CB /* lds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C536201C66CE005564CB /* lds.cpp */; }; + 83D3C5AA201C66E3005564CB /* adtrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C537201C66CE005564CB /* adtrack.cpp */; }; + 83D3C5AB201C66E3005564CB /* msc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C538201C66CF005564CB /* msc.h */; }; + 83D3C5AC201C66E3005564CB /* hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C539201C66CF005564CB /* hybrid.h */; }; + 83D3C5AD201C66E3005564CB /* psi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C53A201C66CF005564CB /* psi.cpp */; }; + 83D3C5AE201C66E3005564CB /* adtrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C53B201C66D0005564CB /* adtrack.h */; }; + 83D3C5AF201C66E3005564CB /* dtm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C53C201C66D0005564CB /* dtm.cpp */; }; + 83D3C5B0201C66E3005564CB /* imf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C53D201C66D0005564CB /* imf.cpp */; }; + 83D3C5B1201C66E3005564CB /* players.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C53E201C66D1005564CB /* players.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C5B2201C66E3005564CB /* u6m.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C53F201C66D1005564CB /* u6m.h */; }; + 83D3C5B3201C66E3005564CB /* sa2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C540201C66D1005564CB /* sa2.cpp */; }; + 83D3C5B4201C66E3005564CB /* cmfmcsop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C541201C66D2005564CB /* cmfmcsop.cpp */; }; + 83D3C5B6201C66E3005564CB /* hsc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C543201C66D2005564CB /* hsc.cpp */; }; + 83D3C5B7201C66E3005564CB /* nemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C544201C66D3005564CB /* nemuopl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C5B8201C66E3005564CB /* dmo.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C545201C66D3005564CB /* dmo.h */; }; + 83D3C5B9201C66E3005564CB /* imf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C546201C66D3005564CB /* imf.h */; }; + 83D3C5BB201C66E3005564CB /* players.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C548201C66D4005564CB /* players.cpp */; }; + 83D3C5BC201C66E3005564CB /* protrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C549201C66D4005564CB /* protrack.cpp */; }; + 83D3C5BD201C66E3005564CB /* amd.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C54A201C66D4005564CB /* amd.h */; }; + 83D3C5BE201C66E3005564CB /* dro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C54B201C66D5005564CB /* dro.cpp */; }; + 83D3C5BF201C66E3005564CB /* adlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C54C201C66D5005564CB /* adlib.cpp */; }; + 83D3C5C0201C66E3005564CB /* rix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C54D201C66D5005564CB /* rix.cpp */; }; + 83D3C5C1201C66E3005564CB /* jbm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C54E201C66D6005564CB /* jbm.cpp */; }; + 83D3C5C2201C66E3005564CB /* dfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C54F201C66D6005564CB /* dfm.cpp */; }; + 83D3C5C3201C66E3005564CB /* dro2.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C550201C66D6005564CB /* dro2.h */; }; + 83D3C5C4201C66E3005564CB /* cff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C551201C66D6005564CB /* cff.cpp */; }; + 83D3C5C5201C66E3005564CB /* fmc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C552201C66D7005564CB /* fmc.h */; }; + 83D3C5C6201C66E3005564CB /* mdi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C553201C66D7005564CB /* mdi.cpp */; }; + 83D3C5C7201C66E3005564CB /* sa2.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C554201C66D7005564CB /* sa2.h */; }; + 83D3C5C8201C66E3005564CB /* rol.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C555201C66D8005564CB /* rol.h */; }; + 83D3C5C9201C66E3005564CB /* dfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C556201C66D8005564CB /* dfm.h */; }; + 83D3C5CA201C66E3005564CB /* got.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C557201C66D8005564CB /* got.h */; }; + 83D3C5CB201C66E3005564CB /* adlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C558201C66DA005564CB /* adlib.h */; }; + 83D3C5CC201C66E3005564CB /* cmfmcsop.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C559201C66DA005564CB /* cmfmcsop.h */; }; + 83D3C5CD201C66E3005564CB /* mid.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C55A201C66DA005564CB /* mid.h */; }; + 83D3C5CE201C66E3005564CB /* mid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C55B201C66DA005564CB /* mid.cpp */; }; + 83D3C5CF201C66E3005564CB /* flash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C55C201C66DB005564CB /* flash.cpp */; }; + 83D3C5D0201C66E3005564CB /* lds.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C55D201C66DB005564CB /* lds.h */; }; + 83D3C5D1201C66E3005564CB /* xsm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C55E201C66DB005564CB /* xsm.h */; }; + 83D3C5D2201C66E3005564CB /* a2m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C55F201C66DC005564CB /* a2m.cpp */; }; + 83D3C5D3201C66E3005564CB /* jbm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C560201C66DC005564CB /* jbm.h */; }; + 83D3C5D4201C66E3005564CB /* d00.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C561201C66DC005564CB /* d00.cpp */; }; + 83D3C5D5201C66E3005564CB /* dro2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C562201C66DD005564CB /* dro2.cpp */; }; + 83D3C5D6201C66E3005564CB /* sop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C563201C66DD005564CB /* sop.cpp */; }; + 83D3C5D7201C66E3005564CB /* xad.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C564201C66DD005564CB /* xad.h */; }; + 83D3C5D8201C66E3005564CB /* xad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C565201C66DE005564CB /* xad.cpp */; }; + 83D3C5D9201C66E3005564CB /* raw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C566201C66DE005564CB /* raw.cpp */; }; + 83D3C5DA201C66E3005564CB /* hyp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C567201C66DE005564CB /* hyp.cpp */; }; + 83D3C5DB201C66E3005564CB /* rol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C568201C66DE005564CB /* rol.cpp */; }; + 83D3C5DC201C66E3005564CB /* a2m.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C569201C66DF005564CB /* a2m.h */; }; + 83D3C5DD201C66E3005564CB /* herad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C56A201C66DF005564CB /* herad.cpp */; }; + 83D3C5DE201C66E3005564CB /* nukedopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C56B201C66DF005564CB /* nukedopl.h */; }; + 83D3C5DF201C66E3005564CB /* raw.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C56C201C66E0005564CB /* raw.h */; }; + 83D3C5E0201C66E3005564CB /* ksm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C56D201C66E0005564CB /* ksm.cpp */; }; + 83D3C5E1201C66E3005564CB /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C56E201C66E0005564CB /* got.cpp */; }; + 83D3C5E3201C66E3005564CB /* vgm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C570201C66E1005564CB /* vgm.cpp */; }; + 83D3C5E4201C66E3005564CB /* rat.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C571201C66E1005564CB /* rat.h */; }; + 83D3C5E5201C66E3005564CB /* adl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C572201C66E2005564CB /* adl.h */; }; + 83D3C5E6201C66E3005564CB /* sop.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C573201C66E2005564CB /* sop.h */; }; + 83D3C5E7201C66E3005564CB /* rix.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C574201C66E2005564CB /* rix.h */; }; + 83D3C5E8201C66E3005564CB /* bmf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C575201C66E3005564CB /* bmf.h */; }; + 83D3C5E9201C66E3005564CB /* cff.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C576201C66E3005564CB /* cff.h */; }; + 83D3C639201C6A81005564CB /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C638201C6A81005564CB /* version.h */; }; + 83D3C63B201C6AE2005564CB /* player.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C63A201C6AE2005564CB /* player.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C648201C6BB9005564CB /* database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C644201C6BB8005564CB /* database.cpp */; }; + 83D3C649201C6BB9005564CB /* fprovide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C645201C6BB8005564CB /* fprovide.cpp */; }; + 83D3C64A201C6BB9005564CB /* fprovide.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C646201C6BB8005564CB /* fprovide.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C64B201C6BB9005564CB /* database.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C647201C6BB8005564CB /* database.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C64E201C6D38005564CB /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C64C201C6D37005564CB /* debug.c */; }; + 83D3C64F201C6D38005564CB /* debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C64D201C6D38005564CB /* debug.h */; }; + 83D3C662201C6EC4005564CB /* opl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C661201C6EC3005564CB /* opl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C670201D2C2C005564CB /* silentopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C66F201D2C2C005564CB /* silentopl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C6A2201D38DF005564CB /* libbinio.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D3C691201D3864005564CB /* libbinio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 83D3C6A5201D3939005564CB /* libbinio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D3C691201D3864005564CB /* libbinio.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 83D3C690201D3864005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C68C201D3864005564CB /* libbinio.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83D3C67B201D37D8005564CB; + remoteInfo = libbinio; + }; + 83D3C6A3201D38FE005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C68C201D3864005564CB /* libbinio.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83D3C67A201D37D8005564CB; + remoteInfo = libbinio; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 83D3C65F201C6E56005564CB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 83D3C6A2201D38DF005564CB /* libbinio.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 83D3C4D4201C654F005564CB /* AdPlug.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AdPlug.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 83D3C4D8201C6550005564CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 83D3C504201C66C1005564CB /* rad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rad.h; path = adplug/src/rad.h; sourceTree = ""; }; + 83D3C505201C66C1005564CB /* adplug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = adplug.cpp; path = adplug/src/adplug.cpp; sourceTree = ""; }; + 83D3C506201C66C2005564CB /* mtk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mtk.h; path = adplug/src/mtk.h; sourceTree = ""; }; + 83D3C507201C66C2005564CB /* psi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = psi.h; path = adplug/src/psi.h; sourceTree = ""; }; + 83D3C509201C66C2005564CB /* ksm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ksm.h; path = adplug/src/ksm.h; sourceTree = ""; }; + 83D3C50A201C66C3005564CB /* nukedopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nukedopl.c; path = adplug/src/nukedopl.c; sourceTree = ""; }; + 83D3C50B201C66C3005564CB /* flash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = flash.h; path = adplug/src/flash.h; sourceTree = ""; }; + 83D3C50C201C66C3005564CB /* hybrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hybrid.cpp; path = adplug/src/hybrid.cpp; sourceTree = ""; }; + 83D3C50D201C66C3005564CB /* bam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bam.cpp; path = adplug/src/bam.cpp; sourceTree = ""; }; + 83D3C50E201C66C4005564CB /* amd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = amd.cpp; path = adplug/src/amd.cpp; sourceTree = ""; }; + 83D3C50F201C66C4005564CB /* adplug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adplug.h; path = adplug/src/adplug.h; sourceTree = ""; }; + 83D3C510201C66C4005564CB /* mus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mus.cpp; path = adplug/src/mus.cpp; sourceTree = ""; }; + 83D3C511201C66C4005564CB /* mad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mad.h; path = adplug/src/mad.h; sourceTree = ""; }; + 83D3C512201C66C5005564CB /* hsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hsp.h; path = adplug/src/hsp.h; sourceTree = ""; }; + 83D3C513201C66C5005564CB /* cmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmf.cpp; path = adplug/src/cmf.cpp; sourceTree = ""; }; + 83D3C514201C66C5005564CB /* u6m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = u6m.cpp; path = adplug/src/u6m.cpp; sourceTree = ""; }; + 83D3C515201C66C6005564CB /* dmo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dmo.cpp; path = adplug/src/dmo.cpp; sourceTree = ""; }; + 83D3C516201C66C6005564CB /* adl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = adl.cpp; path = adplug/src/adl.cpp; sourceTree = ""; }; + 83D3C517201C66C6005564CB /* player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = player.cpp; path = adplug/src/player.cpp; sourceTree = ""; }; + 83D3C518201C66C6005564CB /* cmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cmf.h; path = adplug/src/cmf.h; sourceTree = ""; }; + 83D3C519201C66C7005564CB /* d00.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = d00.h; path = adplug/src/d00.h; sourceTree = ""; }; + 83D3C51A201C66C7005564CB /* mad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mad.cpp; path = adplug/src/mad.cpp; sourceTree = ""; }; + 83D3C51B201C66C7005564CB /* hsc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hsc.h; path = adplug/src/hsc.h; sourceTree = ""; }; + 83D3C51C201C66C7005564CB /* msc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = msc.cpp; path = adplug/src/msc.cpp; sourceTree = ""; }; + 83D3C51D201C66C8005564CB /* rad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rad.cpp; path = adplug/src/rad.cpp; sourceTree = ""; }; + 83D3C51E201C66C8005564CB /* mdi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mdi.h; path = adplug/src/mdi.h; sourceTree = ""; }; + 83D3C51F201C66C8005564CB /* mididata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mididata.h; path = adplug/src/mididata.h; sourceTree = ""; }; + 83D3C520201C66C8005564CB /* s3m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s3m.h; path = adplug/src/s3m.h; sourceTree = ""; }; + 83D3C521201C66C9005564CB /* dtm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dtm.h; path = adplug/src/dtm.h; sourceTree = ""; }; + 83D3C522201C66C9005564CB /* sng.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sng.cpp; path = adplug/src/sng.cpp; sourceTree = ""; }; + 83D3C523201C66C9005564CB /* rat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rat.cpp; path = adplug/src/rat.cpp; sourceTree = ""; }; + 83D3C524201C66C9005564CB /* xsm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xsm.cpp; path = adplug/src/xsm.cpp; sourceTree = ""; }; + 83D3C526201C66CA005564CB /* herad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = herad.h; path = adplug/src/herad.h; sourceTree = ""; }; + 83D3C527201C66CA005564CB /* bam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bam.h; path = adplug/src/bam.h; sourceTree = ""; }; + 83D3C528201C66CA005564CB /* bmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bmf.cpp; path = adplug/src/bmf.cpp; sourceTree = ""; }; + 83D3C529201C66CB005564CB /* hsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hsp.cpp; path = adplug/src/hsp.cpp; sourceTree = ""; }; + 83D3C52A201C66CB005564CB /* dro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dro.h; path = adplug/src/dro.h; sourceTree = ""; }; + 83D3C52B201C66CB005564CB /* mkj.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mkj.cpp; path = adplug/src/mkj.cpp; sourceTree = ""; }; + 83D3C52C201C66CB005564CB /* hyp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hyp.h; path = adplug/src/hyp.h; sourceTree = ""; }; + 83D3C52D201C66CC005564CB /* s3m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = s3m.cpp; path = adplug/src/s3m.cpp; sourceTree = ""; }; + 83D3C52E201C66CC005564CB /* mkj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mkj.h; path = adplug/src/mkj.h; sourceTree = ""; }; + 83D3C52F201C66CC005564CB /* mus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mus.h; path = adplug/src/mus.h; sourceTree = ""; }; + 83D3C530201C66CD005564CB /* fmc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fmc.cpp; path = adplug/src/fmc.cpp; sourceTree = ""; }; + 83D3C531201C66CD005564CB /* vgm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vgm.h; path = adplug/src/vgm.h; sourceTree = ""; }; + 83D3C532201C66CD005564CB /* nemuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nemuopl.cpp; path = adplug/src/nemuopl.cpp; sourceTree = ""; }; + 83D3C533201C66CD005564CB /* protrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protrack.h; path = adplug/src/protrack.h; sourceTree = ""; }; + 83D3C534201C66CE005564CB /* sng.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sng.h; path = adplug/src/sng.h; sourceTree = ""; }; + 83D3C535201C66CE005564CB /* mtk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mtk.cpp; path = adplug/src/mtk.cpp; sourceTree = ""; }; + 83D3C536201C66CE005564CB /* lds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lds.cpp; path = adplug/src/lds.cpp; sourceTree = ""; }; + 83D3C537201C66CE005564CB /* adtrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = adtrack.cpp; path = adplug/src/adtrack.cpp; sourceTree = ""; }; + 83D3C538201C66CF005564CB /* msc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = msc.h; path = adplug/src/msc.h; sourceTree = ""; }; + 83D3C539201C66CF005564CB /* hybrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hybrid.h; path = adplug/src/hybrid.h; sourceTree = ""; }; + 83D3C53A201C66CF005564CB /* psi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = psi.cpp; path = adplug/src/psi.cpp; sourceTree = ""; }; + 83D3C53B201C66D0005564CB /* adtrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adtrack.h; path = adplug/src/adtrack.h; sourceTree = ""; }; + 83D3C53C201C66D0005564CB /* dtm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dtm.cpp; path = adplug/src/dtm.cpp; sourceTree = ""; }; + 83D3C53D201C66D0005564CB /* imf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imf.cpp; path = adplug/src/imf.cpp; sourceTree = ""; }; + 83D3C53E201C66D1005564CB /* players.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = players.h; path = adplug/src/players.h; sourceTree = ""; }; + 83D3C53F201C66D1005564CB /* u6m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = u6m.h; path = adplug/src/u6m.h; sourceTree = ""; }; + 83D3C540201C66D1005564CB /* sa2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sa2.cpp; path = adplug/src/sa2.cpp; sourceTree = ""; }; + 83D3C541201C66D2005564CB /* cmfmcsop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmfmcsop.cpp; path = adplug/src/cmfmcsop.cpp; sourceTree = ""; }; + 83D3C543201C66D2005564CB /* hsc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hsc.cpp; path = adplug/src/hsc.cpp; sourceTree = ""; }; + 83D3C544201C66D3005564CB /* nemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nemuopl.h; path = adplug/src/nemuopl.h; sourceTree = ""; }; + 83D3C545201C66D3005564CB /* dmo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dmo.h; path = adplug/src/dmo.h; sourceTree = ""; }; + 83D3C546201C66D3005564CB /* imf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imf.h; path = adplug/src/imf.h; sourceTree = ""; }; + 83D3C548201C66D4005564CB /* players.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = players.cpp; path = adplug/src/players.cpp; sourceTree = ""; }; + 83D3C549201C66D4005564CB /* protrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = protrack.cpp; path = adplug/src/protrack.cpp; sourceTree = ""; }; + 83D3C54A201C66D4005564CB /* amd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = amd.h; path = adplug/src/amd.h; sourceTree = ""; }; + 83D3C54B201C66D5005564CB /* dro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dro.cpp; path = adplug/src/dro.cpp; sourceTree = ""; }; + 83D3C54C201C66D5005564CB /* adlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = adlib.cpp; path = adplug/src/adlib.cpp; sourceTree = ""; }; + 83D3C54D201C66D5005564CB /* rix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rix.cpp; path = adplug/src/rix.cpp; sourceTree = ""; }; + 83D3C54E201C66D6005564CB /* jbm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jbm.cpp; path = adplug/src/jbm.cpp; sourceTree = ""; }; + 83D3C54F201C66D6005564CB /* dfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dfm.cpp; path = adplug/src/dfm.cpp; sourceTree = ""; }; + 83D3C550201C66D6005564CB /* dro2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dro2.h; path = adplug/src/dro2.h; sourceTree = ""; }; + 83D3C551201C66D6005564CB /* cff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cff.cpp; path = adplug/src/cff.cpp; sourceTree = ""; }; + 83D3C552201C66D7005564CB /* fmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmc.h; path = adplug/src/fmc.h; sourceTree = ""; }; + 83D3C553201C66D7005564CB /* mdi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mdi.cpp; path = adplug/src/mdi.cpp; sourceTree = ""; }; + 83D3C554201C66D7005564CB /* sa2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sa2.h; path = adplug/src/sa2.h; sourceTree = ""; }; + 83D3C555201C66D8005564CB /* rol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rol.h; path = adplug/src/rol.h; sourceTree = ""; }; + 83D3C556201C66D8005564CB /* dfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dfm.h; path = adplug/src/dfm.h; sourceTree = ""; }; + 83D3C557201C66D8005564CB /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = got.h; path = adplug/src/got.h; sourceTree = ""; }; + 83D3C558201C66DA005564CB /* adlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adlib.h; path = adplug/src/adlib.h; sourceTree = ""; }; + 83D3C559201C66DA005564CB /* cmfmcsop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cmfmcsop.h; path = adplug/src/cmfmcsop.h; sourceTree = ""; }; + 83D3C55A201C66DA005564CB /* mid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mid.h; path = adplug/src/mid.h; sourceTree = ""; }; + 83D3C55B201C66DA005564CB /* mid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mid.cpp; path = adplug/src/mid.cpp; sourceTree = ""; }; + 83D3C55C201C66DB005564CB /* flash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = flash.cpp; path = adplug/src/flash.cpp; sourceTree = ""; }; + 83D3C55D201C66DB005564CB /* lds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lds.h; path = adplug/src/lds.h; sourceTree = ""; }; + 83D3C55E201C66DB005564CB /* xsm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xsm.h; path = adplug/src/xsm.h; sourceTree = ""; }; + 83D3C55F201C66DC005564CB /* a2m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = a2m.cpp; path = adplug/src/a2m.cpp; sourceTree = ""; }; + 83D3C560201C66DC005564CB /* jbm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = jbm.h; path = adplug/src/jbm.h; sourceTree = ""; }; + 83D3C561201C66DC005564CB /* d00.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = d00.cpp; path = adplug/src/d00.cpp; sourceTree = ""; }; + 83D3C562201C66DD005564CB /* dro2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dro2.cpp; path = adplug/src/dro2.cpp; sourceTree = ""; }; + 83D3C563201C66DD005564CB /* sop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sop.cpp; path = adplug/src/sop.cpp; sourceTree = ""; }; + 83D3C564201C66DD005564CB /* xad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xad.h; path = adplug/src/xad.h; sourceTree = ""; }; + 83D3C565201C66DE005564CB /* xad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xad.cpp; path = adplug/src/xad.cpp; sourceTree = ""; }; + 83D3C566201C66DE005564CB /* raw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = raw.cpp; path = adplug/src/raw.cpp; sourceTree = ""; }; + 83D3C567201C66DE005564CB /* hyp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hyp.cpp; path = adplug/src/hyp.cpp; sourceTree = ""; }; + 83D3C568201C66DE005564CB /* rol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rol.cpp; path = adplug/src/rol.cpp; sourceTree = ""; }; + 83D3C569201C66DF005564CB /* a2m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = a2m.h; path = adplug/src/a2m.h; sourceTree = ""; }; + 83D3C56A201C66DF005564CB /* herad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = herad.cpp; path = adplug/src/herad.cpp; sourceTree = ""; }; + 83D3C56B201C66DF005564CB /* nukedopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nukedopl.h; path = adplug/src/nukedopl.h; sourceTree = ""; }; + 83D3C56C201C66E0005564CB /* raw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = raw.h; path = adplug/src/raw.h; sourceTree = ""; }; + 83D3C56D201C66E0005564CB /* ksm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ksm.cpp; path = adplug/src/ksm.cpp; sourceTree = ""; }; + 83D3C56E201C66E0005564CB /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = got.cpp; path = adplug/src/got.cpp; sourceTree = ""; }; + 83D3C570201C66E1005564CB /* vgm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = vgm.cpp; path = adplug/src/vgm.cpp; sourceTree = ""; }; + 83D3C571201C66E1005564CB /* rat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rat.h; path = adplug/src/rat.h; sourceTree = ""; }; + 83D3C572201C66E2005564CB /* adl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adl.h; path = adplug/src/adl.h; sourceTree = ""; }; + 83D3C573201C66E2005564CB /* sop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sop.h; path = adplug/src/sop.h; sourceTree = ""; }; + 83D3C574201C66E2005564CB /* rix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rix.h; path = adplug/src/rix.h; sourceTree = ""; }; + 83D3C575201C66E3005564CB /* bmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bmf.h; path = adplug/src/bmf.h; sourceTree = ""; }; + 83D3C576201C66E3005564CB /* cff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cff.h; path = adplug/src/cff.h; sourceTree = ""; }; + 83D3C638201C6A81005564CB /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; + 83D3C63A201C6AE2005564CB /* player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = player.h; path = adplug/src/player.h; sourceTree = ""; }; + 83D3C644201C6BB8005564CB /* database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = database.cpp; path = adplug/src/database.cpp; sourceTree = ""; }; + 83D3C645201C6BB8005564CB /* fprovide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fprovide.cpp; path = adplug/src/fprovide.cpp; sourceTree = ""; }; + 83D3C646201C6BB8005564CB /* fprovide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fprovide.h; path = adplug/src/fprovide.h; sourceTree = ""; }; + 83D3C647201C6BB8005564CB /* database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = database.h; path = adplug/src/database.h; sourceTree = ""; }; + 83D3C64C201C6D37005564CB /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = debug.c; path = adplug/src/debug.c; sourceTree = ""; }; + 83D3C64D201C6D38005564CB /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = debug.h; path = adplug/src/debug.h; sourceTree = ""; }; + 83D3C661201C6EC3005564CB /* opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = opl.h; path = adplug/src/opl.h; sourceTree = ""; }; + 83D3C66F201D2C2C005564CB /* silentopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = silentopl.h; path = adplug/src/silentopl.h; sourceTree = ""; }; + 83D3C68C201D3864005564CB /* libbinio.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libbinio.xcodeproj; path = ../libbinio/libbinio.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83D3C4D0201C654F005564CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C6A5201D3939005564CB /* libbinio.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83D3C4CA201C654F005564CB = { + isa = PBXGroup; + children = ( + 83D3C4D6201C654F005564CB /* AdPlug */, + 83D3C609201C6788005564CB /* Frameworks */, + 83D3C4D5201C654F005564CB /* Products */, + ); + sourceTree = ""; + }; + 83D3C4D5201C654F005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C4D4201C654F005564CB /* AdPlug.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83D3C4D6201C654F005564CB /* AdPlug */ = { + isa = PBXGroup; + children = ( + 83D3C66F201D2C2C005564CB /* silentopl.h */, + 83D3C661201C6EC3005564CB /* opl.h */, + 83D3C55F201C66DC005564CB /* a2m.cpp */, + 83D3C569201C66DF005564CB /* a2m.h */, + 83D3C516201C66C6005564CB /* adl.cpp */, + 83D3C572201C66E2005564CB /* adl.h */, + 83D3C54C201C66D5005564CB /* adlib.cpp */, + 83D3C558201C66DA005564CB /* adlib.h */, + 83D3C505201C66C1005564CB /* adplug.cpp */, + 83D3C50F201C66C4005564CB /* adplug.h */, + 83D3C537201C66CE005564CB /* adtrack.cpp */, + 83D3C53B201C66D0005564CB /* adtrack.h */, + 83D3C50E201C66C4005564CB /* amd.cpp */, + 83D3C54A201C66D4005564CB /* amd.h */, + 83D3C50D201C66C3005564CB /* bam.cpp */, + 83D3C527201C66CA005564CB /* bam.h */, + 83D3C528201C66CA005564CB /* bmf.cpp */, + 83D3C575201C66E3005564CB /* bmf.h */, + 83D3C551201C66D6005564CB /* cff.cpp */, + 83D3C576201C66E3005564CB /* cff.h */, + 83D3C513201C66C5005564CB /* cmf.cpp */, + 83D3C518201C66C6005564CB /* cmf.h */, + 83D3C541201C66D2005564CB /* cmfmcsop.cpp */, + 83D3C559201C66DA005564CB /* cmfmcsop.h */, + 83D3C561201C66DC005564CB /* d00.cpp */, + 83D3C519201C66C7005564CB /* d00.h */, + 83D3C644201C6BB8005564CB /* database.cpp */, + 83D3C647201C6BB8005564CB /* database.h */, + 83D3C64C201C6D37005564CB /* debug.c */, + 83D3C64D201C6D38005564CB /* debug.h */, + 83D3C54F201C66D6005564CB /* dfm.cpp */, + 83D3C556201C66D8005564CB /* dfm.h */, + 83D3C515201C66C6005564CB /* dmo.cpp */, + 83D3C545201C66D3005564CB /* dmo.h */, + 83D3C54B201C66D5005564CB /* dro.cpp */, + 83D3C52A201C66CB005564CB /* dro.h */, + 83D3C562201C66DD005564CB /* dro2.cpp */, + 83D3C550201C66D6005564CB /* dro2.h */, + 83D3C53C201C66D0005564CB /* dtm.cpp */, + 83D3C521201C66C9005564CB /* dtm.h */, + 83D3C55C201C66DB005564CB /* flash.cpp */, + 83D3C50B201C66C3005564CB /* flash.h */, + 83D3C530201C66CD005564CB /* fmc.cpp */, + 83D3C552201C66D7005564CB /* fmc.h */, + 83D3C645201C6BB8005564CB /* fprovide.cpp */, + 83D3C646201C6BB8005564CB /* fprovide.h */, + 83D3C56E201C66E0005564CB /* got.cpp */, + 83D3C557201C66D8005564CB /* got.h */, + 83D3C56A201C66DF005564CB /* herad.cpp */, + 83D3C526201C66CA005564CB /* herad.h */, + 83D3C543201C66D2005564CB /* hsc.cpp */, + 83D3C51B201C66C7005564CB /* hsc.h */, + 83D3C529201C66CB005564CB /* hsp.cpp */, + 83D3C512201C66C5005564CB /* hsp.h */, + 83D3C50C201C66C3005564CB /* hybrid.cpp */, + 83D3C539201C66CF005564CB /* hybrid.h */, + 83D3C567201C66DE005564CB /* hyp.cpp */, + 83D3C52C201C66CB005564CB /* hyp.h */, + 83D3C53D201C66D0005564CB /* imf.cpp */, + 83D3C546201C66D3005564CB /* imf.h */, + 83D3C4D8201C6550005564CB /* Info.plist */, + 83D3C54E201C66D6005564CB /* jbm.cpp */, + 83D3C560201C66DC005564CB /* jbm.h */, + 83D3C56D201C66E0005564CB /* ksm.cpp */, + 83D3C509201C66C2005564CB /* ksm.h */, + 83D3C536201C66CE005564CB /* lds.cpp */, + 83D3C55D201C66DB005564CB /* lds.h */, + 83D3C51A201C66C7005564CB /* mad.cpp */, + 83D3C511201C66C4005564CB /* mad.h */, + 83D3C553201C66D7005564CB /* mdi.cpp */, + 83D3C51E201C66C8005564CB /* mdi.h */, + 83D3C55B201C66DA005564CB /* mid.cpp */, + 83D3C55A201C66DA005564CB /* mid.h */, + 83D3C51F201C66C8005564CB /* mididata.h */, + 83D3C52B201C66CB005564CB /* mkj.cpp */, + 83D3C52E201C66CC005564CB /* mkj.h */, + 83D3C51C201C66C7005564CB /* msc.cpp */, + 83D3C538201C66CF005564CB /* msc.h */, + 83D3C535201C66CE005564CB /* mtk.cpp */, + 83D3C506201C66C2005564CB /* mtk.h */, + 83D3C510201C66C4005564CB /* mus.cpp */, + 83D3C52F201C66CC005564CB /* mus.h */, + 83D3C532201C66CD005564CB /* nemuopl.cpp */, + 83D3C544201C66D3005564CB /* nemuopl.h */, + 83D3C50A201C66C3005564CB /* nukedopl.c */, + 83D3C56B201C66DF005564CB /* nukedopl.h */, + 83D3C517201C66C6005564CB /* player.cpp */, + 83D3C63A201C6AE2005564CB /* player.h */, + 83D3C548201C66D4005564CB /* players.cpp */, + 83D3C53E201C66D1005564CB /* players.h */, + 83D3C549201C66D4005564CB /* protrack.cpp */, + 83D3C533201C66CD005564CB /* protrack.h */, + 83D3C53A201C66CF005564CB /* psi.cpp */, + 83D3C507201C66C2005564CB /* psi.h */, + 83D3C51D201C66C8005564CB /* rad.cpp */, + 83D3C504201C66C1005564CB /* rad.h */, + 83D3C523201C66C9005564CB /* rat.cpp */, + 83D3C571201C66E1005564CB /* rat.h */, + 83D3C566201C66DE005564CB /* raw.cpp */, + 83D3C56C201C66E0005564CB /* raw.h */, + 83D3C54D201C66D5005564CB /* rix.cpp */, + 83D3C574201C66E2005564CB /* rix.h */, + 83D3C568201C66DE005564CB /* rol.cpp */, + 83D3C555201C66D8005564CB /* rol.h */, + 83D3C52D201C66CC005564CB /* s3m.cpp */, + 83D3C520201C66C8005564CB /* s3m.h */, + 83D3C540201C66D1005564CB /* sa2.cpp */, + 83D3C554201C66D7005564CB /* sa2.h */, + 83D3C522201C66C9005564CB /* sng.cpp */, + 83D3C534201C66CE005564CB /* sng.h */, + 83D3C563201C66DD005564CB /* sop.cpp */, + 83D3C573201C66E2005564CB /* sop.h */, + 83D3C514201C66C5005564CB /* u6m.cpp */, + 83D3C53F201C66D1005564CB /* u6m.h */, + 83D3C638201C6A81005564CB /* version.h */, + 83D3C570201C66E1005564CB /* vgm.cpp */, + 83D3C531201C66CD005564CB /* vgm.h */, + 83D3C565201C66DE005564CB /* xad.cpp */, + 83D3C564201C66DD005564CB /* xad.h */, + 83D3C524201C66C9005564CB /* xsm.cpp */, + 83D3C55E201C66DB005564CB /* xsm.h */, + ); + path = AdPlug; + sourceTree = ""; + }; + 83D3C609201C6788005564CB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83D3C68C201D3864005564CB /* libbinio.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83D3C68D201D3864005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C691201D3864005564CB /* libbinio.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 83D3C4D1201C654F005564CB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C582201C66E3005564CB /* adplug.h in Headers */, + 83D3C63B201C6AE2005564CB /* player.h in Headers */, + 83D3C662201C6EC4005564CB /* opl.h in Headers */, + 83D3C64A201C6BB9005564CB /* fprovide.h in Headers */, + 83D3C5B1201C66E3005564CB /* players.h in Headers */, + 83D3C64B201C6BB9005564CB /* database.h in Headers */, + 83D3C5B7201C66E3005564CB /* nemuopl.h in Headers */, + 83D3C670201D2C2C005564CB /* silentopl.h in Headers */, + 83D3C5B9201C66E3005564CB /* imf.h in Headers */, + 83D3C5DC201C66E3005564CB /* a2m.h in Headers */, + 83D3C594201C66E3005564CB /* dtm.h in Headers */, + 83D3C5A6201C66E3005564CB /* protrack.h in Headers */, + 83D3C5B2201C66E3005564CB /* u6m.h in Headers */, + 83D3C599201C66E3005564CB /* herad.h in Headers */, + 83D3C5E5201C66E3005564CB /* adl.h in Headers */, + 83D3C5A7201C66E3005564CB /* sng.h in Headers */, + 83D3C59A201C66E3005564CB /* bam.h in Headers */, + 83D3C592201C66E3005564CB /* mididata.h in Headers */, + 83D3C58E201C66E3005564CB /* hsc.h in Headers */, + 83D3C5E6201C66E3005564CB /* sop.h in Headers */, + 83D3C5A1201C66E3005564CB /* mkj.h in Headers */, + 83D3C5CC201C66E3005564CB /* cmfmcsop.h in Headers */, + 83D3C5DF201C66E3005564CB /* raw.h in Headers */, + 83D3C58B201C66E3005564CB /* cmf.h in Headers */, + 83D3C58C201C66E3005564CB /* d00.h in Headers */, + 83D3C5C8201C66E3005564CB /* rol.h in Headers */, + 83D3C584201C66E3005564CB /* mad.h in Headers */, + 83D3C57A201C66E3005564CB /* psi.h in Headers */, + 83D3C577201C66E3005564CB /* rad.h in Headers */, + 83D3C5D1201C66E3005564CB /* xsm.h in Headers */, + 83D3C639201C6A81005564CB /* version.h in Headers */, + 83D3C5A4201C66E3005564CB /* vgm.h in Headers */, + 83D3C5AC201C66E3005564CB /* hybrid.h in Headers */, + 83D3C5E4201C66E3005564CB /* rat.h in Headers */, + 83D3C5C7201C66E3005564CB /* sa2.h in Headers */, + 83D3C5C5201C66E3005564CB /* fmc.h in Headers */, + 83D3C5E8201C66E3005564CB /* bmf.h in Headers */, + 83D3C5CA201C66E3005564CB /* got.h in Headers */, + 83D3C5D3201C66E3005564CB /* jbm.h in Headers */, + 83D3C5D7201C66E3005564CB /* xad.h in Headers */, + 83D3C5C3201C66E3005564CB /* dro2.h in Headers */, + 83D3C64F201C6D38005564CB /* debug.h in Headers */, + 83D3C57E201C66E3005564CB /* flash.h in Headers */, + 83D3C593201C66E3005564CB /* s3m.h in Headers */, + 83D3C59D201C66E3005564CB /* dro.h in Headers */, + 83D3C5CB201C66E3005564CB /* adlib.h in Headers */, + 83D3C5E9201C66E3005564CB /* cff.h in Headers */, + 83D3C57C201C66E3005564CB /* ksm.h in Headers */, + 83D3C579201C66E3005564CB /* mtk.h in Headers */, + 83D3C5AE201C66E3005564CB /* adtrack.h in Headers */, + 83D3C591201C66E3005564CB /* mdi.h in Headers */, + 83D3C5CD201C66E3005564CB /* mid.h in Headers */, + 83D3C5DE201C66E3005564CB /* nukedopl.h in Headers */, + 83D3C585201C66E3005564CB /* hsp.h in Headers */, + 83D3C5A2201C66E3005564CB /* mus.h in Headers */, + 83D3C5E7201C66E3005564CB /* rix.h in Headers */, + 83D3C5B8201C66E3005564CB /* dmo.h in Headers */, + 83D3C59F201C66E3005564CB /* hyp.h in Headers */, + 83D3C5C9201C66E3005564CB /* dfm.h in Headers */, + 83D3C5BD201C66E3005564CB /* amd.h in Headers */, + 83D3C5AB201C66E3005564CB /* msc.h in Headers */, + 83D3C5D0201C66E3005564CB /* lds.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 83D3C4D3201C654F005564CB /* AdPlug */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83D3C4DC201C6550005564CB /* Build configuration list for PBXNativeTarget "AdPlug" */; + buildPhases = ( + 83D3C4CF201C654F005564CB /* Sources */, + 83D3C4D0201C654F005564CB /* Frameworks */, + 83D3C4D1201C654F005564CB /* Headers */, + 83D3C4D2201C654F005564CB /* Resources */, + 83D3C65F201C6E56005564CB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 83D3C6A4201D38FE005564CB /* PBXTargetDependency */, + ); + name = AdPlug; + productName = AdPlug; + productReference = 83D3C4D4201C654F005564CB /* AdPlug.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83D3C4CB201C654F005564CB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 83D3C4D3201C654F005564CB = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83D3C4CE201C654F005564CB /* Build configuration list for PBXProject "AdPlug" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83D3C4CA201C654F005564CB; + productRefGroup = 83D3C4D5201C654F005564CB /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 83D3C68D201D3864005564CB /* Products */; + ProjectRef = 83D3C68C201D3864005564CB /* libbinio.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 83D3C4D3201C654F005564CB /* AdPlug */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 83D3C691201D3864005564CB /* libbinio.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = libbinio.framework; + remoteRef = 83D3C690201D3864005564CB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 83D3C4D2201C654F005564CB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83D3C4CF201C654F005564CB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C5D6201C66E3005564CB /* sop.cpp in Sources */, + 83D3C5C6201C66E3005564CB /* mdi.cpp in Sources */, + 83D3C648201C6BB9005564CB /* database.cpp in Sources */, + 83D3C5E1201C66E3005564CB /* got.cpp in Sources */, + 83D3C595201C66E3005564CB /* sng.cpp in Sources */, + 83D3C5E0201C66E3005564CB /* ksm.cpp in Sources */, + 83D3C5CE201C66E3005564CB /* mid.cpp in Sources */, + 83D3C5C1201C66E3005564CB /* jbm.cpp in Sources */, + 83D3C590201C66E3005564CB /* rad.cpp in Sources */, + 83D3C587201C66E3005564CB /* u6m.cpp in Sources */, + 83D3C5C0201C66E3005564CB /* rix.cpp in Sources */, + 83D3C59C201C66E3005564CB /* hsp.cpp in Sources */, + 83D3C589201C66E3005564CB /* adl.cpp in Sources */, + 83D3C581201C66E3005564CB /* amd.cpp in Sources */, + 83D3C5A9201C66E3005564CB /* lds.cpp in Sources */, + 83D3C5AD201C66E3005564CB /* psi.cpp in Sources */, + 83D3C5A3201C66E3005564CB /* fmc.cpp in Sources */, + 83D3C5AF201C66E3005564CB /* dtm.cpp in Sources */, + 83D3C5B4201C66E3005564CB /* cmfmcsop.cpp in Sources */, + 83D3C58F201C66E3005564CB /* msc.cpp in Sources */, + 83D3C597201C66E3005564CB /* xsm.cpp in Sources */, + 83D3C5A0201C66E3005564CB /* s3m.cpp in Sources */, + 83D3C5BF201C66E3005564CB /* adlib.cpp in Sources */, + 83D3C59E201C66E3005564CB /* mkj.cpp in Sources */, + 83D3C5D4201C66E3005564CB /* d00.cpp in Sources */, + 83D3C5CF201C66E3005564CB /* flash.cpp in Sources */, + 83D3C649201C6BB9005564CB /* fprovide.cpp in Sources */, + 83D3C5BB201C66E3005564CB /* players.cpp in Sources */, + 83D3C5DA201C66E3005564CB /* hyp.cpp in Sources */, + 83D3C64E201C6D38005564CB /* debug.c in Sources */, + 83D3C5B6201C66E3005564CB /* hsc.cpp in Sources */, + 83D3C5C4201C66E3005564CB /* cff.cpp in Sources */, + 83D3C5DD201C66E3005564CB /* herad.cpp in Sources */, + 83D3C580201C66E3005564CB /* bam.cpp in Sources */, + 83D3C5D2201C66E3005564CB /* a2m.cpp in Sources */, + 83D3C5BC201C66E3005564CB /* protrack.cpp in Sources */, + 83D3C5AA201C66E3005564CB /* adtrack.cpp in Sources */, + 83D3C5D8201C66E3005564CB /* xad.cpp in Sources */, + 83D3C57F201C66E3005564CB /* hybrid.cpp in Sources */, + 83D3C5D5201C66E3005564CB /* dro2.cpp in Sources */, + 83D3C5A5201C66E3005564CB /* nemuopl.cpp in Sources */, + 83D3C5A8201C66E3005564CB /* mtk.cpp in Sources */, + 83D3C578201C66E3005564CB /* adplug.cpp in Sources */, + 83D3C5E3201C66E3005564CB /* vgm.cpp in Sources */, + 83D3C596201C66E3005564CB /* rat.cpp in Sources */, + 83D3C59B201C66E3005564CB /* bmf.cpp in Sources */, + 83D3C5BE201C66E3005564CB /* dro.cpp in Sources */, + 83D3C588201C66E3005564CB /* dmo.cpp in Sources */, + 83D3C5DB201C66E3005564CB /* rol.cpp in Sources */, + 83D3C5B0201C66E3005564CB /* imf.cpp in Sources */, + 83D3C5C2201C66E3005564CB /* dfm.cpp in Sources */, + 83D3C58A201C66E3005564CB /* player.cpp in Sources */, + 83D3C58D201C66E3005564CB /* mad.cpp in Sources */, + 83D3C583201C66E3005564CB /* mus.cpp in Sources */, + 83D3C586201C66E3005564CB /* cmf.cpp in Sources */, + 83D3C5D9201C66E3005564CB /* raw.cpp in Sources */, + 83D3C5B3201C66E3005564CB /* sa2.cpp in Sources */, + 83D3C57D201C66E3005564CB /* nukedopl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 83D3C6A4201D38FE005564CB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = libbinio; + targetProxy = 83D3C6A3201D38FE005564CB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 83D3C4DA201C6550005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 83D3C4DB201C6550005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 83D3C4DD201C6550005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "stricmp=strcasecmp", + ); + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../libbinio/libbinio/libbinio/src", + "$(SRCROOT)/../libbinio/libbinio", + ); + INFOPLIST_FILE = AdPlug/Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.AdPlug; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 83D3C4DE201C6550005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PREPROCESSOR_DEFINITIONS = "stricmp=strcasecmp"; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../libbinio/libbinio/libbinio/src", + "$(SRCROOT)/../libbinio/libbinio", + ); + INFOPLIST_FILE = AdPlug/Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.AdPlug; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83D3C4CE201C654F005564CB /* Build configuration list for PBXProject "AdPlug" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C4DA201C6550005564CB /* Debug */, + 83D3C4DB201C6550005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83D3C4DC201C6550005564CB /* Build configuration list for PBXNativeTarget "AdPlug" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C4DD201C6550005564CB /* Debug */, + 83D3C4DE201C6550005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83D3C4CB201C654F005564CB /* Project object */; +} diff --git a/Frameworks/AdPlug/AdPlug/Info.plist b/Frameworks/AdPlug/AdPlug/Info.plist new file mode 100644 index 000000000..a351796f3 --- /dev/null +++ b/Frameworks/AdPlug/AdPlug/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2018 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/AdPlug/AdPlug/adplug b/Frameworks/AdPlug/AdPlug/adplug new file mode 160000 index 000000000..14557a40b --- /dev/null +++ b/Frameworks/AdPlug/AdPlug/adplug @@ -0,0 +1 @@ +Subproject commit 14557a40b120e4361cc57575e82cc0be85d9191d diff --git a/Frameworks/AdPlug/AdPlug/database b/Frameworks/AdPlug/AdPlug/database new file mode 160000 index 000000000..7ac0819ec --- /dev/null +++ b/Frameworks/AdPlug/AdPlug/database @@ -0,0 +1 @@ +Subproject commit 7ac0819ec55d6dd1ffe42890f82c3ada05d101b5 diff --git a/Frameworks/AdPlug/AdPlug/version.h b/Frameworks/AdPlug/AdPlug/version.h new file mode 100644 index 000000000..2d9994b85 --- /dev/null +++ b/Frameworks/AdPlug/AdPlug/version.h @@ -0,0 +1 @@ +#define ADPLUG_VERSION "2.3-5-g14557a4" diff --git a/Frameworks/libbinio/libbinio.xcodeproj/project.pbxproj b/Frameworks/libbinio/libbinio.xcodeproj/project.pbxproj new file mode 100644 index 000000000..86ef97d90 --- /dev/null +++ b/Frameworks/libbinio/libbinio.xcodeproj/project.pbxproj @@ -0,0 +1,345 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 83D3C693201D3870005564CB /* binio.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C692201D3870005564CB /* binio.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C69B201D388C005564CB /* binfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C694201D388B005564CB /* binfile.cpp */; }; + 83D3C69C201D388C005564CB /* binwrap.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C695201D388B005564CB /* binwrap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C69D201D388C005564CB /* binstr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C696201D388B005564CB /* binstr.cpp */; }; + 83D3C69E201D388C005564CB /* binfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C697201D388B005564CB /* binfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C69F201D388C005564CB /* binwrap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C698201D388C005564CB /* binwrap.cpp */; }; + 83D3C6A0201D388C005564CB /* binstr.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D3C699201D388C005564CB /* binstr.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83D3C6A1201D388C005564CB /* binio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C69A201D388C005564CB /* binio.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 83D3C67B201D37D8005564CB /* libbinio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libbinio.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 83D3C67F201D37D8005564CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 83D3C692201D3870005564CB /* binio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = binio.h; sourceTree = ""; }; + 83D3C694201D388B005564CB /* binfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binfile.cpp; path = libbinio/src/binfile.cpp; sourceTree = ""; }; + 83D3C695201D388B005564CB /* binwrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = binwrap.h; path = libbinio/src/binwrap.h; sourceTree = ""; }; + 83D3C696201D388B005564CB /* binstr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binstr.cpp; path = libbinio/src/binstr.cpp; sourceTree = ""; }; + 83D3C697201D388B005564CB /* binfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = binfile.h; path = libbinio/src/binfile.h; sourceTree = ""; }; + 83D3C698201D388C005564CB /* binwrap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binwrap.cpp; path = libbinio/src/binwrap.cpp; sourceTree = ""; }; + 83D3C699201D388C005564CB /* binstr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = binstr.h; path = libbinio/src/binstr.h; sourceTree = ""; }; + 83D3C69A201D388C005564CB /* binio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binio.cpp; path = libbinio/src/binio.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83D3C677201D37D8005564CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83D3C671201D37D8005564CB = { + isa = PBXGroup; + children = ( + 83D3C67D201D37D8005564CB /* libbinio */, + 83D3C67C201D37D8005564CB /* Products */, + ); + sourceTree = ""; + }; + 83D3C67C201D37D8005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C67B201D37D8005564CB /* libbinio.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83D3C67D201D37D8005564CB /* libbinio */ = { + isa = PBXGroup; + children = ( + 83D3C694201D388B005564CB /* binfile.cpp */, + 83D3C697201D388B005564CB /* binfile.h */, + 83D3C69A201D388C005564CB /* binio.cpp */, + 83D3C696201D388B005564CB /* binstr.cpp */, + 83D3C699201D388C005564CB /* binstr.h */, + 83D3C698201D388C005564CB /* binwrap.cpp */, + 83D3C695201D388B005564CB /* binwrap.h */, + 83D3C692201D3870005564CB /* binio.h */, + 83D3C67F201D37D8005564CB /* Info.plist */, + ); + path = libbinio; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 83D3C678201D37D8005564CB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C69E201D388C005564CB /* binfile.h in Headers */, + 83D3C693201D3870005564CB /* binio.h in Headers */, + 83D3C6A0201D388C005564CB /* binstr.h in Headers */, + 83D3C69C201D388C005564CB /* binwrap.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 83D3C67A201D37D8005564CB /* libbinio */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83D3C683201D37D8005564CB /* Build configuration list for PBXNativeTarget "libbinio" */; + buildPhases = ( + 83D3C676201D37D8005564CB /* Sources */, + 83D3C677201D37D8005564CB /* Frameworks */, + 83D3C678201D37D8005564CB /* Headers */, + 83D3C679201D37D8005564CB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libbinio; + productName = libbinio; + productReference = 83D3C67B201D37D8005564CB /* libbinio.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83D3C672201D37D8005564CB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 83D3C67A201D37D8005564CB = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83D3C675201D37D8005564CB /* Build configuration list for PBXProject "libbinio" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83D3C671201D37D8005564CB; + productRefGroup = 83D3C67C201D37D8005564CB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 83D3C67A201D37D8005564CB /* libbinio */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 83D3C679201D37D8005564CB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83D3C676201D37D8005564CB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C69B201D388C005564CB /* binfile.cpp in Sources */, + 83D3C69D201D388C005564CB /* binstr.cpp in Sources */, + 83D3C69F201D388C005564CB /* binwrap.cpp in Sources */, + 83D3C6A1201D388C005564CB /* binio.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 83D3C681201D37D8005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 83D3C682201D37D8005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 83D3C684201D37D8005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = libbinio/Info.plist; + INSTALL_PATH = "@loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libbinio; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 83D3C685201D37D8005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = libbinio/Info.plist; + INSTALL_PATH = "@loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libbinio; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83D3C675201D37D8005564CB /* Build configuration list for PBXProject "libbinio" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C681201D37D8005564CB /* Debug */, + 83D3C682201D37D8005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83D3C683201D37D8005564CB /* Build configuration list for PBXNativeTarget "libbinio" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C684201D37D8005564CB /* Debug */, + 83D3C685201D37D8005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83D3C672201D37D8005564CB /* Project object */; +} diff --git a/Frameworks/libbinio/libbinio/Info.plist b/Frameworks/libbinio/libbinio/Info.plist new file mode 100644 index 000000000..a351796f3 --- /dev/null +++ b/Frameworks/libbinio/libbinio/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2018 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/libbinio/libbinio/binio.h b/Frameworks/libbinio/libbinio/binio.h new file mode 100644 index 000000000..6b8c27233 --- /dev/null +++ b/Frameworks/libbinio/libbinio/binio.h @@ -0,0 +1,175 @@ +/* -*-C++-*- + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * binio.h - Binary stream I/O classes + * Copyright (C) 2002, 2003, 2005 Simon Peter + */ + +#ifndef H_BINIO_BINIO +#define H_BINIO_BINIO + +/***** Configuration *****/ + +// BINIO_ENABLE_STRING - Build std::string supporting methods +// +// Set to 1 to build std::string supporting methods. You need the STL to +// do this. +#define BINIO_ENABLE_STRING 1 + +// BINIO_ENABLE_IOSTREAM - Build iostream wrapper classes +// +// Set to 1 to build the iostream wrapper classes. You need the standard +// C++ library to do this. +#define BINIO_ENABLE_IOSTREAM 1 + +// BINIO_ISO_STDLIB - Build with ISO C++ standard library compliance +// +// Set to 1 to build for the ISO standard C++ library (i.e. namespaces, STL and +// templatized iostream). Set to 0 to build for the traditional C++ library. +#define BINIO_ISO_STDLIB 1 + +// BINIO_WITH_MATH - Build with 'math.h' dependency to allow float conversions +// +// Set to 1 to also build routines that depend on the 'math.h' standard C header +// file (this sometimes also implies a 'libm' or 'libmath' dependency). These +// routines are needed in order to write IEEE-754 floating-point numbers on a +// system that doesn't support this format natively. For only reading these +// numbers, however, these routines are not needed. If set to 0, writing +// IEEE-754 numbers on an incompatible system will be disabled. +#define BINIO_WITH_MATH 1 + +/***** Implementation *****/ + +// Disable annoying multiple inheritance compiler warning on MSVC6 +#ifdef _MSC_VER +# pragma warning(disable: 4250) +#endif + +#if BINIO_ENABLE_STRING +#include +#endif + +class binio +{ +public: + typedef enum { + BigEndian = 1 << 0, + FloatIEEE = 1 << 1 + } Flag; + + typedef enum { + NoError = 0, + Fatal = 1 << 0, + Unsupported = 1 << 1, + NotOpen = 1 << 2, + Denied = 1 << 3, + NotFound = 1 << 4, + Eof = 1 << 5 + } ErrorCode; + + typedef enum { Set, Add, End } Offset; + typedef enum { Single, Double } FType; + typedef int Error; + + binio(); + virtual ~binio(); + + void setFlag(Flag f, bool set = true); + bool getFlag(Flag f); + + Error error(); + bool eof(); + + virtual void seek(long, Offset = Set) = 0; + virtual long pos() = 0; + +protected: + typedef long long Int; + typedef long double Float; + typedef unsigned char Byte; // has to be unsigned! + + typedef int Flags; + + Flags my_flags; + static const Flags system_flags; + Error err; + + // Some math.h emulation functions... +#if !BINIO_WITH_MATH + Float pow(Float base, signed int exp); + Float ldexp(Float x, signed int exp) { return x * pow(2, exp); } +#endif + +private: + static const Flags detect_system_flags(); +}; + +class binistream: virtual public binio +{ +public: + binistream(); + virtual ~binistream(); + + Int readInt(unsigned int size); + Float readFloat(FType ft); + unsigned long readString(char *str, unsigned long maxlen); + unsigned long readString(char *str, unsigned long maxlen, const char delim); +#if BINIO_ENABLE_STRING + std::string readString(const char delim = '\0'); +#endif + + Int peekInt(unsigned int size); + Float peekFloat(FType ft); + + bool ateof(); + void ignore(unsigned long amount = 1); + +protected: + virtual Byte getByte() = 0; + +private: + Float ieee_single2float(Byte *data); + Float ieee_double2float(Byte *data); +}; + +class binostream: virtual public binio +{ +public: + binostream(); + virtual ~binostream(); + + void writeInt(Int val, unsigned int size); + void writeFloat(Float f, FType ft); + unsigned long writeString(const char *str, unsigned long amount = 0); +#if BINIO_ENABLE_STRING + unsigned long writeString(const std::string &str); +#endif + +protected: + virtual void putByte(Byte) = 0; + +private: + void float2ieee_single(Float f, Byte *data); + void float2ieee_double(Float f, Byte *data); +}; + +class binstream: public binistream, public binostream +{ +public: + binstream(); + virtual ~binstream(); +}; + +#endif diff --git a/Frameworks/libbinio/libbinio/libbinio b/Frameworks/libbinio/libbinio/libbinio new file mode 160000 index 000000000..282de6071 --- /dev/null +++ b/Frameworks/libbinio/libbinio/libbinio @@ -0,0 +1 @@ +Subproject commit 282de6071b28f85703d40860d4eb8d87d6a0f5dd diff --git a/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj b/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj new file mode 100644 index 000000000..61f7f7c2e --- /dev/null +++ b/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj @@ -0,0 +1,413 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 83D3C659201C6E24005564CB /* AdPlugContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C654201C6E24005564CB /* AdPlugContainer.mm */; }; + 83D3C65A201C6E24005564CB /* AdPlugMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C656201C6E24005564CB /* AdPlugMetadataReader.mm */; }; + 83D3C65B201C6E24005564CB /* AdPlugDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C658201C6E24005564CB /* AdPlugDecoder.mm */; }; + 83D3C65E201C6E4D005564CB /* AdPlug.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D3C608201C6775005564CB /* AdPlug.framework */; }; + 83D3C668201C7020005564CB /* adplug.db in Resources */ = {isa = PBXBuildFile; fileRef = 83D3C667201C7020005564CB /* adplug.db */; }; + 83D3C66C201C70F7005564CB /* fileprovider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83D3C66A201C70F7005564CB /* fileprovider.mm */; }; + 83D3C66E201C72ED005564CB /* AdPlug.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D3C608201C6775005564CB /* AdPlug.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 83D3C6A6201D3951005564CB /* libbinio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D3C6A7201D3951005564CB /* libbinio.framework */; }; + 83D3C6A8201D3961005564CB /* libbinio.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D3C6A7201D3951005564CB /* libbinio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 83D3C607201C6775005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C603201C6775005564CB /* AdPlug.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83D3C4D4201C654F005564CB; + remoteInfo = AdPlug; + }; + 83D3C65C201C6E48005564CB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83D3C603201C6775005564CB /* AdPlug.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83D3C4D3201C654F005564CB; + remoteInfo = AdPlug; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 83D3C66D201C72E3005564CB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 83D3C6A8201D3961005564CB /* libbinio.framework in CopyFiles */, + 83D3C66E201C72ED005564CB /* AdPlug.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 83D3C5F3201C674D005564CB /* AdPlug.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AdPlug.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 83D3C5F6201C674D005564CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 83D3C603201C6775005564CB /* AdPlug.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AdPlug.xcodeproj; path = ../../Frameworks/AdPlug/AdPlug.xcodeproj; sourceTree = ""; }; + 83D3C650201C6DE9005564CB /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = ""; }; + 83D3C651201C6E00005564CB /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = ""; }; + 83D3C652201C6E10005564CB /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = ""; }; + 83D3C653201C6E23005564CB /* AdPlugDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdPlugDecoder.h; sourceTree = ""; }; + 83D3C654201C6E24005564CB /* AdPlugContainer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdPlugContainer.mm; sourceTree = ""; }; + 83D3C655201C6E24005564CB /* AdPlugMetadataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdPlugMetadataReader.h; sourceTree = ""; }; + 83D3C656201C6E24005564CB /* AdPlugMetadataReader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdPlugMetadataReader.mm; sourceTree = ""; }; + 83D3C657201C6E24005564CB /* AdPlugContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdPlugContainer.h; sourceTree = ""; }; + 83D3C658201C6E24005564CB /* AdPlugDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdPlugDecoder.mm; sourceTree = ""; }; + 83D3C667201C7020005564CB /* adplug.db */ = {isa = PBXFileReference; lastKnownFileType = file; name = adplug.db; path = ../../../Frameworks/AdPlug/AdPlug/database/adplug.db; sourceTree = ""; }; + 83D3C669201C70E7005564CB /* fileprovider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fileprovider.h; sourceTree = ""; }; + 83D3C66A201C70F7005564CB /* fileprovider.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fileprovider.mm; sourceTree = ""; }; + 83D3C6A7201D3951005564CB /* libbinio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = libbinio.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83D3C5F0201C674D005564CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C6A6201D3951005564CB /* libbinio.framework in Frameworks */, + 83D3C65E201C6E4D005564CB /* AdPlug.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83D3C5EA201C674D005564CB = { + isa = PBXGroup; + children = ( + 83D3C5F5201C674D005564CB /* AdPlug */, + 83D3C602201C675D005564CB /* Frameworks */, + 83D3C5F4201C674D005564CB /* Products */, + ); + sourceTree = ""; + }; + 83D3C5F4201C674D005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C5F3201C674D005564CB /* AdPlug.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 83D3C5F5201C674D005564CB /* AdPlug */ = { + isa = PBXGroup; + children = ( + 83D3C667201C7020005564CB /* adplug.db */, + 83D3C657201C6E24005564CB /* AdPlugContainer.h */, + 83D3C654201C6E24005564CB /* AdPlugContainer.mm */, + 83D3C653201C6E23005564CB /* AdPlugDecoder.h */, + 83D3C658201C6E24005564CB /* AdPlugDecoder.mm */, + 83D3C655201C6E24005564CB /* AdPlugMetadataReader.h */, + 83D3C656201C6E24005564CB /* AdPlugMetadataReader.mm */, + 83D3C650201C6DE9005564CB /* Logging.h */, + 83D3C651201C6E00005564CB /* PlaylistController.h */, + 83D3C652201C6E10005564CB /* Plugin.h */, + 83D3C5F6201C674D005564CB /* Info.plist */, + 83D3C669201C70E7005564CB /* fileprovider.h */, + 83D3C66A201C70F7005564CB /* fileprovider.mm */, + ); + path = AdPlug; + sourceTree = ""; + }; + 83D3C602201C675D005564CB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83D3C6A7201D3951005564CB /* libbinio.framework */, + 83D3C603201C6775005564CB /* AdPlug.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83D3C604201C6775005564CB /* Products */ = { + isa = PBXGroup; + children = ( + 83D3C608201C6775005564CB /* AdPlug.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 83D3C5F2201C674D005564CB /* AdPlug */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83D3C5F9201C674D005564CB /* Build configuration list for PBXNativeTarget "AdPlug" */; + buildPhases = ( + 83D3C5EF201C674D005564CB /* Sources */, + 83D3C5F0201C674D005564CB /* Frameworks */, + 83D3C5F1201C674D005564CB /* Resources */, + 83D3C66D201C72E3005564CB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 83D3C65D201C6E48005564CB /* PBXTargetDependency */, + ); + name = AdPlug; + productName = AdPlug; + productReference = 83D3C5F3201C674D005564CB /* AdPlug.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83D3C5EB201C674D005564CB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Christopher Snowhill"; + TargetAttributes = { + 83D3C5F2201C674D005564CB = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83D3C5EE201C674D005564CB /* Build configuration list for PBXProject "AdPlug" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83D3C5EA201C674D005564CB; + productRefGroup = 83D3C5F4201C674D005564CB /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 83D3C604201C6775005564CB /* Products */; + ProjectRef = 83D3C603201C6775005564CB /* AdPlug.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 83D3C5F2201C674D005564CB /* AdPlug */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 83D3C608201C6775005564CB /* AdPlug.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = AdPlug.framework; + remoteRef = 83D3C607201C6775005564CB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 83D3C5F1201C674D005564CB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C668201C7020005564CB /* adplug.db in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83D3C5EF201C674D005564CB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83D3C66C201C70F7005564CB /* fileprovider.mm in Sources */, + 83D3C65A201C6E24005564CB /* AdPlugMetadataReader.mm in Sources */, + 83D3C65B201C6E24005564CB /* AdPlugDecoder.mm in Sources */, + 83D3C659201C6E24005564CB /* AdPlugContainer.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 83D3C65D201C6E48005564CB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = AdPlug; + targetProxy = 83D3C65C201C6E48005564CB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 83D3C5F7201C674D005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 83D3C5F8201C674D005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 83D3C5FA201C674D005564CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../../Frameworks/libbinio/libbinio/libbinio/src", + "$(SRCROOT)/../../Frameworks/libbinio/libbinio", + ); + INFOPLIST_FILE = AdPlug/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.AdPlug; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 83D3C5FB201C674D005564CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = N6E749HJ2X; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../../Frameworks/libbinio/libbinio/libbinio/src", + "$(SRCROOT)/../../Frameworks/libbinio/libbinio", + ); + INFOPLIST_FILE = AdPlug/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = net.kode54.AdPlug; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83D3C5EE201C674D005564CB /* Build configuration list for PBXProject "AdPlug" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C5F7201C674D005564CB /* Debug */, + 83D3C5F8201C674D005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83D3C5F9201C674D005564CB /* Build configuration list for PBXNativeTarget "AdPlug" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83D3C5FA201C674D005564CB /* Debug */, + 83D3C5FB201C674D005564CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83D3C5EB201C674D005564CB /* Project object */; +} diff --git a/Plugins/AdPlug/AdPlug/AdPlugContainer.h b/Plugins/AdPlug/AdPlug/AdPlugContainer.h new file mode 100755 index 000000000..997020da5 --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugContainer.h @@ -0,0 +1,17 @@ +// +// AdPlugContainer.h +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface AdPlugContainer : NSObject { + +} + +@end diff --git a/Plugins/AdPlug/AdPlug/AdPlugContainer.mm b/Plugins/AdPlug/AdPlug/AdPlugContainer.mm new file mode 100755 index 000000000..84418c307 --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugContainer.mm @@ -0,0 +1,79 @@ +// +// AdPlugContainer.m +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import +#import + +#import "AdPlugContainer.h" +#import "AdPlugDecoder.h" + +#import "Logging.h" + +#import "fileprovider.h" + +@implementation AdPlugContainer + ++ (NSArray *)fileTypes +{ + return [AdPlugDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return nil; +} + ++ (float)priority +{ + return [AdPlugDecoder priority]; +} + ++ (NSArray *)urlsForContainerURL:(NSURL *)url +{ + if ([url fragment]) { + // input url already has fragment defined - no need to expand further + return [NSMutableArray arrayWithObject:url]; + } + + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + Copl * p_emu = new CSilentopl; + + std::string path = [[[url absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; + CPlayer * p_player = CAdPlug::factory(path, p_emu, CAdPlug::players, CProvider_cog( path, source )); + + if ( !p_player ) + { + delete p_emu; + return 0; + } + + NSMutableArray *tracks = [NSMutableArray array]; + + unsigned int i; + unsigned int subsongs = p_player->getsubsongs(); + + delete p_player; + delete p_emu; + + for (i = 0; i < subsongs; ++i) { + [tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]]; + } + + return tracks; +} + + +@end diff --git a/Plugins/AdPlug/AdPlug/AdPlugDecoder.h b/Plugins/AdPlug/AdPlug/AdPlugDecoder.h new file mode 100755 index 000000000..acb89a7bb --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugDecoder.h @@ -0,0 +1,30 @@ +// +// AdPlugDecoder.h +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import +#import + +#import "Plugin.h" + +@interface AdPlugDecoder : NSObject { + CPlayer * m_player; + Copl * m_emu; + + unsigned int subsong, samples_todo; + + id source; + unsigned long current_pos; + unsigned long length; +} + +- (void)setSource:(id)s; +- (id)source; +- (void)cleanUp; +@end diff --git a/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm b/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm new file mode 100755 index 000000000..0dba6893f --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm @@ -0,0 +1,184 @@ +// +// AdPlugDecoder.m +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import "AdPlugDecoder.h" + +#import + +#import "fileprovider.h" + +#import "Logging.h" + +#import "PlaylistController.h" + +@implementation AdPlugDecoder + +- (id)init +{ + self = [super init]; + if (self) { + m_player = NULL; + m_emu = NULL; + } + return self; +} + +- (BOOL)open:(id)s +{ + [self setSource:s]; + + m_emu = new CNemuopl(44100); + + std::string path = [[[[source url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; + m_player = CAdPlug::factory(path, m_emu, CAdPlug::players, CProvider_cog( path, source )); + + if ( !m_player ) + return 0; + + if ([[source.url fragment] length] == 0) + subsong = 0; + else + subsong = [[source.url fragment] intValue]; + + samples_todo = 0; + + length = m_player->songlength(subsong) * 441 / 10; + current_pos = 0; + + m_player->rewind(subsong); + + [self willChangeValueForKey:@"properties"]; + [self didChangeValueForKey:@"properties"]; + + return YES; +} + +- (NSDictionary *)properties +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"bitrate", + [NSNumber numberWithFloat:44100], @"sampleRate", + [NSNumber numberWithDouble:length], @"totalFrames", + [NSNumber numberWithInt:16], @"bitsPerSample", //Samples are short + [NSNumber numberWithBool:NO], @"floatingPoint", + [NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo + [NSNumber numberWithBool:YES], @"seekable", + @"host", @"endian", + nil]; +} + +- (int)readAudio:(void *)buf frames:(UInt32)frames +{ + int total = 0; + bool dont_loop = !IsRepeatOneSet(); + if ( dont_loop && current_pos + frames > length ) + frames = (UInt32) (length - current_pos); + while ( total < frames ) + { + bool running = true; + if ( !samples_todo ) + { + running = m_player->update() && running; + if ( !dont_loop || running ) + { + samples_todo = 44100 / m_player->getrefresh(); + current_pos += samples_todo; + } + } + if ( !samples_todo ) + break; + int samples_now = samples_todo; + if ( samples_now > (frames - total) ) + samples_now = frames - total; + m_emu->update( (short*)buf, samples_now ); + buf = ((short *)buf) + samples_now * 2; + samples_todo -= samples_now; + total += samples_now; + } + + return total; +} + +- (long)seek:(long)frame +{ + if ( frame < current_pos ) + { + current_pos = 0; + m_player->rewind( subsong ); + } + + while ( current_pos < frame ) + { + m_player->update(); + current_pos += 44100 / m_player->getrefresh(); + } + + samples_todo = (UInt32) (frame - current_pos); + + return frame; +} + +- (void)cleanUp +{ + delete m_player; m_player = NULL; + delete m_emu; m_emu = NULL; +} + +- (void)close +{ + [self cleanUp]; + + if (source) { + [source close]; + [self setSource:nil]; + } +} + +- (void)dealloc +{ + [self close]; +} + +- (void)setSource:(id)s +{ + source = s; +} + +- (id)source +{ + return source; +} + ++ (NSArray *)fileTypes +{ + const CPlayers & pl = CAdPlug::players; + CPlayers::const_iterator i; + unsigned j; + NSMutableArray * array = [NSMutableArray array]; + + for (i = pl.begin(); i != pl.end(); ++i) { + for ( j = 0; ( *i )->get_extension(j); ++j ) + { + [array addObject:[NSString stringWithUTF8String:(*i)->get_extension(j) + 1]]; + } + } + + return array; +} + ++ (NSArray *)mimeTypes +{ + return nil; +} + ++ (float)priority +{ + return 0.5; +} + +@end diff --git a/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.h b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.h new file mode 100644 index 000000000..cf6d309ad --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.h @@ -0,0 +1,17 @@ +// +// AdPlugMetadataReader.h +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface AdPlugMetadataReader : NSObject { + +} + +@end diff --git a/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm new file mode 100644 index 000000000..b5d8bd89b --- /dev/null +++ b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm @@ -0,0 +1,72 @@ +// +// AdPlugMetadataReader.m +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright 2018 __LoSnoCo__. All rights reserved. +// + +#import "AdPlugMetadataReader.h" +#import "AdPlugDecoder.h" + +#import +#import + +#import "fileprovider.h" + +#import "Logging.H" + +@implementation AdPlugMetadataReader + ++ (NSArray *)fileTypes +{ + return [AdPlugDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return [AdPlugDecoder mimeTypes]; +} + ++ (float)priority +{ + return [AdPlugDecoder priority]; +} + ++ (NSDictionary *)metadataForURL:(NSURL *)url +{ + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + Copl * p_emu = new CSilentopl; + + std::string path = [[[url absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; + CPlayer * p_player = CAdPlug::factory(path, p_emu, CAdPlug::players, CProvider_cog( path, source )); + + if ( !p_player ) + { + delete p_emu; + return 0; + } + + NSString * title = @""; + NSString * artist = @""; + + if ( !p_player->gettitle().empty() ) + title = [NSString stringWithUTF8String: p_player->gettitle().c_str()]; + if ( !p_player->getauthor().empty() ) + artist = [NSString stringWithUTF8String: p_player->getauthor().c_str()]; + + delete p_player; + delete p_emu; + + return [NSDictionary dictionaryWithObjectsAndKeys:title, @"title", artist, @"artist", nil]; +} + +@end diff --git a/Plugins/AdPlug/AdPlug/Info.plist b/Plugins/AdPlug/AdPlug/Info.plist new file mode 100644 index 000000000..494824be3 --- /dev/null +++ b/Plugins/AdPlug/AdPlug/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2018 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Plugins/AdPlug/AdPlug/fileprovider.h b/Plugins/AdPlug/AdPlug/fileprovider.h new file mode 100644 index 000000000..273960aa8 --- /dev/null +++ b/Plugins/AdPlug/AdPlug/fileprovider.h @@ -0,0 +1,35 @@ +// +// fileprovider.h +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright © 2018 Christopher Snowhill. All rights reserved. +// + +#ifndef fileprovider_h +#define fileprovider_h + +#import + +#import "Plugin.h" + +#include +#include +#include + +class CProvider_cog : public CFileProvider +{ + id m_file_hint; + std::string m_file_path; + +public: + virtual binistream *open(std::string filename) const; + virtual void close(binistream *f) const; + + CProvider_cog() { } + + CProvider_cog(std::string filename, id file) + : m_file_path( filename ), m_file_hint(file) { } +}; + +#endif /* fileprovider_h */ diff --git a/Plugins/AdPlug/AdPlug/fileprovider.mm b/Plugins/AdPlug/AdPlug/fileprovider.mm new file mode 100644 index 000000000..90283341f --- /dev/null +++ b/Plugins/AdPlug/AdPlug/fileprovider.mm @@ -0,0 +1,118 @@ +// +// fileprovider.cpp +// AdPlug +// +// Created by Christopher Snowhill on 1/27/18. +// Copyright © 2018 Christopher Snowhill. All rights reserved. +// + +#include "fileprovider.h" + +class binistream_cog : public binistream +{ + id m_file; + + Byte m_buffer[4096]; + int m_buffer_filled, m_buffer_position; + +public: + binistream_cog( id p_file ) + : m_file( p_file ), m_buffer_filled( 0 ), m_buffer_position( 0 ) { } + + void seek( long pos, Offset offs ) + { + switch (offs) + { + case Set: break; + + case Add: + if ( ( pos < 0 && m_buffer_position + pos >= 0 ) || + ( pos >= 0 && m_buffer_position + pos < m_buffer_filled + m_buffer_position ) ) + { + m_buffer_filled -= pos; + m_buffer_position += pos; + err &= ~Eof; + return; + } + else + pos += [m_file tell] - m_buffer_filled; + break; + + case End: + [m_file seek:0 whence:SEEK_END]; + pos += [m_file tell]; + break; + } + [m_file seek:0 whence:SEEK_END]; + if (pos < [m_file tell]) + { + err &= ~Eof; + [m_file seek:pos whence:SEEK_SET]; + } + + m_buffer_filled = 0; + m_buffer_position = 0; + } + + long pos() + { + return [m_file tell] - m_buffer_filled; + } + + Byte getByte() + { + if ( err & Eof ) return -1; + + if ( ! m_buffer_filled ) + { + m_buffer_filled = [m_file read:m_buffer amount:4096]; + if ( ! m_buffer_filled ) + { + err |= Eof; + return -1; + } + m_buffer_position = 0; + } + + Byte value = m_buffer[ m_buffer_position ]; + ++m_buffer_position; + --m_buffer_filled; + return value; + } +}; + +binistream * CProvider_cog::open(std::string filename) const +{ + id p_file; + + if ( filename == m_file_path ) + { + p_file = m_file_hint; + [p_file seek:0 whence:SEEK_SET]; + } + + if ( p_file == nil ) + { + NSString * urlString = [NSString stringWithUTF8String:filename.c_str()]; + NSURL * url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + id audioSourceClass = NSClassFromString(@"AudioSource"); + p_file = [audioSourceClass audioSourceForURL:url]; + + if (![p_file open:url]) + return 0; + } + + binistream_cog * f = new binistream_cog( p_file ); + if ( f->error() ) { delete f; return 0; } + + f->setFlag(binio::BigEndian, false); f->setFlag(binio::FloatIEEE); + + return f; +} + +void CProvider_cog::close(binistream *f) const +{ + binistream_cog * ff = ( binistream_cog * ) f; + if ( f ) + delete ff; +} From 2d685baa4ab75861b3177a18823032054c21c47c Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 27 Jan 2018 15:11:08 -0800 Subject: [PATCH 017/104] Don't code sign OpenMPT.bundle on build. --- Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj index 819063a04..baac6820f 100644 --- a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj +++ b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -321,6 +321,7 @@ 83E5EFAA1FFEF78100659F0F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = N6E749HJ2X; @@ -336,6 +337,7 @@ 83E5EFAB1FFEF78100659F0F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = N6E749HJ2X; From ce1d938b3a2caeb0174bee632ba6dc51fa7a87a6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 27 Jan 2018 16:09:02 -0800 Subject: [PATCH 018/104] Fix AdPlug decoder. --- Plugins/AdPlug/AdPlug/AdPlugDecoder.mm | 10 ++++++++-- Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm b/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm index 0dba6893f..b50182306 100755 --- a/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm +++ b/Plugins/AdPlug/AdPlug/AdPlugDecoder.mm @@ -33,9 +33,15 @@ [self setSource:s]; m_emu = new CNemuopl(44100); + + NSString * path = [[s url] absoluteString]; + NSRange fragmentRange = [path rangeOfString:@"#" options:NSBackwardsSearch]; + if (fragmentRange.location != NSNotFound) { + path = [path substringToIndex:fragmentRange.location]; + } - std::string path = [[[[source url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; - m_player = CAdPlug::factory(path, m_emu, CAdPlug::players, CProvider_cog( path, source )); + std::string _path = [[path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; + m_player = CAdPlug::factory(_path, m_emu, CAdPlug::players, CProvider_cog( _path, source )); if ( !m_player ) return 0; diff --git a/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm index b5d8bd89b..8bbe1aea8 100644 --- a/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm +++ b/Plugins/AdPlug/AdPlug/AdPlugMetadataReader.mm @@ -46,8 +46,14 @@ Copl * p_emu = new CSilentopl; - std::string path = [[[url absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; - CPlayer * p_player = CAdPlug::factory(path, p_emu, CAdPlug::players, CProvider_cog( path, source )); + NSString * path = [url absoluteString]; + NSRange fragmentRange = [path rangeOfString:@"#" options:NSBackwardsSearch]; + if (fragmentRange.location != NSNotFound) { + path = [path substringToIndex:fragmentRange.location]; + } + + std::string _path = [[path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String]; + CPlayer * p_player = CAdPlug::factory(_path, p_emu, CAdPlug::players, CProvider_cog( _path, source )); if ( !p_player ) { From 56ed9c34295a01bceeda761c4e531989f4ea4a13 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 27 Jan 2018 20:32:27 -0800 Subject: [PATCH 019/104] Updated VGMStream to r1050-1001-gebdfed99. --- .../vgmstream.xcodeproj/project.pbxproj | 270 +++++----- .../vgmstream/src/coding/atrac9_decoder.c | 4 +- .../vgmstream/src/coding/ffmpeg_decoder.c | 6 - .../src/coding/ffmpeg_decoder_utils.h | 4 - .../coding/ffmpeg_decoder_utils_bgw_atrac3.c | 58 --- .../src/coding/vorbis_custom_utils_wwise.c | 20 +- Frameworks/vgmstream/vgmstream/src/formats.c | 41 +- .../vgmstream/vgmstream/src/layout/blocked.c | 3 + .../vgmstream/src/layout/blocked_ea_schl.c | 6 +- .../vgmstream/src/layout/blocked_xvag.c | 19 + .../vgmstream/vgmstream/src/layout/layout.h | 1 + .../vgmstream/src/meta/aax_streamfile.h | 4 - Frameworks/vgmstream/vgmstream/src/meta/adx.c | 2 +- .../vgmstream/vgmstream/src/meta/adx_keys.h | 3 + Frameworks/vgmstream/vgmstream/src/meta/ahx.c | 2 +- .../vgmstream/src/meta/aix_streamfile.h | 4 - .../vgmstream/vgmstream/src/meta/atsl3.c | 76 +++ Frameworks/vgmstream/vgmstream/src/meta/atx.c | 109 ++++ Frameworks/vgmstream/vgmstream/src/meta/awc.c | 19 +- .../vgmstream/src/meta/bar_streamfile.h | 13 - Frameworks/vgmstream/vgmstream/src/meta/bgw.c | 103 +++- Frameworks/vgmstream/vgmstream/src/meta/bik.c | 41 +- Frameworks/vgmstream/vgmstream/src/meta/flx.c | 19 +- Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 356 +++++++------ .../vgmstream/vgmstream/src/meta/fsb5.c | 19 +- .../vgmstream/src/meta/fsb_encrypted.c | 158 ++++++ .../vgmstream/vgmstream/src/meta/fsb_keys.h | 126 +++++ Frameworks/vgmstream/vgmstream/src/meta/gtd.c | 10 +- Frameworks/vgmstream/vgmstream/src/meta/hca.c | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 6 + .../vgmstream/vgmstream/src/meta/kma9.c | 79 +++ .../vgmstream/vgmstream/src/meta/ktss.c | 19 +- .../vgmstream/vgmstream/src/meta/meta.h | 22 +- .../vgmstream/vgmstream/src/meta/nsw_opus.c | 32 +- .../vgmstream/vgmstream/src/meta/ogg_vorbis.c | 453 ++++++++++++++++ .../vgmstream/src/meta/ogg_vorbis_file.c | 461 ---------------- .../vgmstream/vgmstream/src/meta/ps2_rxws.c | 29 +- .../vgmstream/vgmstream/src/meta/psx_cdxa.c | 27 +- .../vgmstream/vgmstream/src/meta/riff.c | 165 +++++- Frameworks/vgmstream/vgmstream/src/meta/rws.c | 40 +- Frameworks/vgmstream/vgmstream/src/meta/sab.c | 40 +- .../vgmstream/vgmstream/src/meta/sgxd.c | 35 +- .../vgmstream/vgmstream/src/meta/sps_n1.c | 82 +++ .../vgmstream/vgmstream/src/meta/sqex_scd.c | 276 +++++----- .../vgmstream/vgmstream/src/meta/sqex_sead.c | 273 ++++++++++ Frameworks/vgmstream/vgmstream/src/meta/sxd.c | 33 +- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 1 + Frameworks/vgmstream/vgmstream/src/meta/vxn.c | 18 +- .../vgmstream/vgmstream/src/meta/wwise.c | 32 +- .../vgmstream/vgmstream/src/meta/xvag.c | 66 ++- Frameworks/vgmstream/vgmstream/src/meta/xwb.c | 39 +- Frameworks/vgmstream/vgmstream/src/meta/xwc.c | 108 ++++ .../vgmstream/vgmstream/src/streamfile.c | 490 ++++++++++++++++-- .../vgmstream/vgmstream/src/streamfile.h | 51 +- .../vgmstream/vgmstream/src/vgmstream.c | 24 +- .../vgmstream/vgmstream/src/vgmstream.h | 40 +- 56 files changed, 3097 insertions(+), 1342 deletions(-) delete mode 100644 Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c create mode 100644 Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/atsl3.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/atx.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/kma9.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/xwc.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 96cbb0acc..bf528f3fc 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; }; - 8349A8E01FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */; }; 8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; }; 8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; }; 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */; }; @@ -221,7 +220,6 @@ 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7E18BDC2180095E648 /* ngc_ymf.c */; }; 836F6FBB18BDC2190095E648 /* ngca.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7F18BDC2180095E648 /* ngca.c */; }; 836F6FBD18BDC2190095E648 /* nwa.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8118BDC2180095E648 /* nwa.c */; }; - 836F6FBE18BDC2190095E648 /* ogg_vorbis_file.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */; }; 836F6FBF18BDC2190095E648 /* otm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8318BDC2180095E648 /* otm.c */; }; 836F6FC018BDC2190095E648 /* p3d.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8418BDC2180095E648 /* p3d.c */; }; 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8518BDC2180095E648 /* pc_adp.c */; }; @@ -405,6 +403,16 @@ 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */; }; 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */; }; 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */; }; + 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7A201D895B000F04B9 /* blocked_xvag.c */; }; + 83A21F85201D8981000F04B9 /* atx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7C201D897F000F04B9 /* atx.c */; }; + 83A21F86201D8981000F04B9 /* xwc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7D201D8980000F04B9 /* xwc.c */; }; + 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A21F7E201D8980000F04B9 /* fsb_keys.h */; }; + 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */; }; + 83A21F89201D8982000F04B9 /* atsl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F80201D8980000F04B9 /* atsl3.c */; }; + 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F81201D8981000F04B9 /* fsb_encrypted.c */; }; + 83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F82201D8981000F04B9 /* sps_n1.c */; }; + 83A21F8C201D8982000F04B9 /* kma9.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F83201D8981000F04B9 /* kma9.c */; }; + 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F84201D8981000F04B9 /* sqex_sead.c */; }; 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A3F0711E3AD8B900D6A794 /* formats.c */; }; 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */; }; 83A5F75F198DF021009AF94C /* bfwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A5F75E198DF021009AF94C /* bfwav.c */; }; @@ -595,7 +603,6 @@ 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = ""; }; - 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_bgw_atrac3.c; sourceTree = ""; }; 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = ""; }; 8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = ""; }; 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = ""; }; @@ -775,7 +782,6 @@ 836F6E7E18BDC2180095E648 /* ngc_ymf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_ymf.c; sourceTree = ""; }; 836F6E7F18BDC2180095E648 /* ngca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngca.c; sourceTree = ""; }; 836F6E8118BDC2180095E648 /* nwa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nwa.c; sourceTree = ""; }; - 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis_file.c; sourceTree = ""; }; 836F6E8318BDC2180095E648 /* otm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = otm.c; sourceTree = ""; }; 836F6E8418BDC2180095E648 /* p3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = p3d.c; sourceTree = ""; }; 836F6E8518BDC2180095E648 /* pc_adp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp.c; sourceTree = ""; }; @@ -957,6 +963,16 @@ 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_decoder.h; sourceTree = ""; }; 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_sk.c; sourceTree = ""; }; 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sk_aud.c; sourceTree = ""; }; + 83A21F7A201D895B000F04B9 /* blocked_xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvag.c; sourceTree = ""; }; + 83A21F7C201D897F000F04B9 /* atx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atx.c; sourceTree = ""; }; + 83A21F7D201D8980000F04B9 /* xwc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwc.c; sourceTree = ""; }; + 83A21F7E201D8980000F04B9 /* fsb_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_keys.h; sourceTree = ""; }; + 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis.c; sourceTree = ""; }; + 83A21F80201D8980000F04B9 /* atsl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atsl3.c; sourceTree = ""; }; + 83A21F81201D8981000F04B9 /* fsb_encrypted.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb_encrypted.c; sourceTree = ""; }; + 83A21F82201D8981000F04B9 /* sps_n1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sps_n1.c; sourceTree = ""; }; + 83A21F83201D8981000F04B9 /* kma9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kma9.c; sourceTree = ""; }; + 83A21F84201D8981000F04B9 /* sqex_sead.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_sead.c; sourceTree = ""; }; 83A3F0711E3AD8B900D6A794 /* formats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = formats.c; sourceTree = ""; }; 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = ""; }; 83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = ""; }; @@ -1134,51 +1150,40 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( - 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, - 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, - 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */, - 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, - 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */, - 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, - 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */, - 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */, - 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, - 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, - 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, - 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, - 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */, - 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, - 839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */, - 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */, - 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, - 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, - 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, - 839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */, - 839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */, - 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, - 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, - 835027121ED119E000C25929 /* mta2_decoder.c */, - 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, - 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, - 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, - 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, - 832389511D224C0800482226 /* hca_decoder.c */, - 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, 836F6DE018BDC2180095E648 /* acm_decoder.c */, 836F6DE118BDC2180095E648 /* acm_decoder.h */, 836F6DE218BDC2180095E648 /* adx_decoder.c */, 836F6DE318BDC2180095E648 /* aica_decoder.c */, 836F6DE418BDC2180095E648 /* at3_decoder.c */, + 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, + 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 836F6DE518BDC2180095E648 /* coding.h */, + 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, + 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, + 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, + 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, + 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */, + 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */, + 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */, + 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, + 836F6DE918BDC2180095E648 /* g72x_state.h */, + 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, 836F6DE718BDC2180095E648 /* g721_decoder.c */, 836F6DE818BDC2180095E648 /* g7221_decoder.c */, - 836F6DE918BDC2180095E648 /* g72x_state.h */, + 832389511D224C0800482226 /* hca_decoder.c */, 836F6DEA18BDC2180095E648 /* ima_decoder.c */, 836F6DEB18BDC2180095E648 /* l5_555_decoder.c */, 836F6DEC18BDC2180095E648 /* lsf_decoder.c */, + 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, 836F6DEE18BDC2180095E648 /* mp4_aac_decoder.c */, + 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, + 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, + 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, + 839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */, 836F6DEF18BDC2180095E648 /* mpeg_decoder.c */, + 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */, 836F6DF018BDC2180095E648 /* msadpcm_decoder.c */, + 835027121ED119E000C25929 /* mta2_decoder.c */, 836F6DF118BDC2180095E648 /* mtaf_decoder.c */, 836F6DF218BDC2180095E648 /* nds_procyon_decoder.c */, 836F6DF318BDC2180095E648 /* ngc_afc_decoder.c */, @@ -1188,9 +1193,19 @@ 836F6DF718BDC2180095E648 /* nwa_decoder.h */, 836F6DF818BDC2180095E648 /* ogg_vorbis_decoder.c */, 836F6DF918BDC2180095E648 /* pcm_decoder.c */, + 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, 836F6DFA18BDC2180095E648 /* psx_decoder.c */, 836F6DFB18BDC2180095E648 /* SASSC_decoder.c */, 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */, + 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, + 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, + 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, + 839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */, + 839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */, + 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */, + 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, + 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, + 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, ); @@ -1200,26 +1215,26 @@ 836F6DFF18BDC2180095E648 /* layout */ = { isa = PBXGroup; children = ( + 836F6E0018BDC2180095E648 /* aax_layout.c */, + 836F6E0118BDC2180095E648 /* aix_layout.c */, + 836F6E0218BDC2180095E648 /* ast_blocked.c */, + 836F6E0318BDC2180095E648 /* bdsp_blocked.c */, + 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */, 8349A8E21FE6253800E26435 /* blocked_dec.c */, 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */, 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */, 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */, 8349A8E51FE6253800E26435 /* blocked_ivaud.c */, 8349A8E61FE6253900E26435 /* blocked_vawx.c */, - 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */, 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */, - 830165A11F256BF400CA0941 /* hwas_blocked.c */, - 831BD1201EEE1D2A00198540 /* rws_blocked.c */, - 836F6E0018BDC2180095E648 /* aax_layout.c */, - 836F6E0118BDC2180095E648 /* aix_layout.c */, - 836F6E0218BDC2180095E648 /* ast_blocked.c */, - 836F6E0318BDC2180095E648 /* bdsp_blocked.c */, + 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 836F6E0418BDC2180095E648 /* blocked.c */, 836F6E0518BDC2180095E648 /* caf_blocked.c */, 836F6E0818BDC2180095E648 /* emff_blocked.c */, 836F6E0918BDC2180095E648 /* filp_blocked.c */, 836F6E0A18BDC2180095E648 /* gsb_blocked.c */, 836F6E0B18BDC2180095E648 /* halpst_blocked.c */, + 830165A11F256BF400CA0941 /* hwas_blocked.c */, 836F6E0C18BDC2180095E648 /* ims_block.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, 836F6E1018BDC2180095E648 /* layout.h */, @@ -1230,6 +1245,7 @@ 836F6E1618BDC2180095E648 /* ps2_iab_blocked.c */, 836F6E1718BDC2180095E648 /* ps2_strlr_blocked.c */, 836F6E1818BDC2180095E648 /* psx_mgav_blocked.c */, + 831BD1201EEE1D2A00198540 /* rws_blocked.c */, 836F6E1918BDC2180095E648 /* scd_int_layout.c */, 836F6E1A18BDC2180095E648 /* str_snds_blocked.c */, 836F6E1B18BDC2180095E648 /* thp_blocked.c */, @@ -1246,95 +1262,36 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( - 830EBE122004656E0023AA10 /* ktss.c */, - 830EBE112004656E0023AA10 /* xnb.c */, - 8349A9041FE6258100E26435 /* aax_streamfile.h */, - 8349A9021FE6258100E26435 /* adx_keys.h */, - 8349A9001FE6258000E26435 /* afc.c */, - 8349A8F61FE6257E00E26435 /* aix_streamfile.h */, - 8349A8F81FE6257E00E26435 /* bar_streamfile.h */, - 8349A9051FE6258100E26435 /* bar.c */, - 8349A8EE1FE6257C00E26435 /* dec.c */, - 8349A8FF1FE6258000E26435 /* ea_1snh.c */, - 8349A8F71FE6257E00E26435 /* ea_eaac.c */, - 8349A8EF1FE6257C00E26435 /* ezw.c */, - 8349A8FD1FE6257F00E26435 /* flx.c */, - 8349A9031FE6258100E26435 /* mogg.c */, - 8349A9061FE6258100E26435 /* naac.c */, - 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, - 8349A8FB1FE6257F00E26435 /* omu.c */, - 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, - 8349A8F01FE6257C00E26435 /* pc_ast.c */, - 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, - 8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */, - 8349A8F11FE6257D00E26435 /* sab.c */, - 8349A8F51FE6257D00E26435 /* scd_pcm.c */, - 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, - 8349A8F41FE6257D00E26435 /* ubi_sb.c */, - 8349A8F91FE6257E00E26435 /* vsf_tta.c */, - 8349A9011FE6258000E26435 /* vxn.c */, - 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */, - 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, - 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, - 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, - 83AA5D201F6E2F9B0020821C /* awc.c */, - 83AA5D211F6E2F9C0020821C /* hca_keys.h */, - 83AA5D231F6E2F9C0020821C /* stm.c */, - 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, - 830165971F256BD000CA0941 /* txth.c */, - 830165981F256BD000CA0941 /* ea_schl_fixed.c */, - 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, - 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, - 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, - 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, - 8350270C1ED119D200C25929 /* ps3_mta2.c */, - 833A7A2D1ED11961003EC53E /* xau.c */, - 83709DFF1ECBC1A4005C03D3 /* gtd.c */, - 83709E001ECBC1A4005C03D3 /* mc3.c */, - 83709E011ECBC1A4005C03D3 /* mss.c */, - 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */, - 83709E031ECBC1A4005C03D3 /* ta_aac.c */, - 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */, - 831BA60E1EAC61A500CF89B0 /* adx.c */, - 831BA60F1EAC61A500CF89B0 /* ogl.c */, - 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, - 831BA6111EAC61A500CF89B0 /* sgxd.c */, - 831BA6121EAC61A500CF89B0 /* sxd.c */, - 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, - 831BA6141EAC61A500CF89B0 /* vawx.c */, - 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, - 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, - 83FF0EBB1E93282100C58054 /* wwise.c */, - 83AB8C731E8072A100086084 /* nub_vag.c */, - 83AB8C741E8072A100086084 /* x360_ast.c */, - 83299FCE1E7660C7003A3242 /* bik.c */, - 83299FCF1E7660C7003A3242 /* dsp_adx.c */, - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, - 8350C0541E071881009E0A93 /* xma.c */, - 838BDB6D1D3B043C0022CA6F /* ffmpeg.c */, - 8323894F1D2246C300482226 /* hca.c */, - 83EDE5D61A70951A005F5D84 /* mca.c */, - 83EDE5D71A70951A005F5D84 /* btsnd.c */, - 834D3A6D19F47C98001C54F6 /* g1l.c */, - 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, - 83A5F75E198DF021009AF94C /* bfwav.c */, - 83F5F8821908D0A400C8E65F /* fsb5.c */, 836F6E2918BDC2180095E648 /* 2dx9.c */, + 8349A9041FE6258100E26435 /* aax_streamfile.h */, 836F6E2A18BDC2180095E648 /* aax.c */, 836F6E2B18BDC2180095E648 /* acm.c */, 836F6E2C18BDC2180095E648 /* ads.c */, + 8349A9021FE6258100E26435 /* adx_keys.h */, + 831BA60E1EAC61A500CF89B0 /* adx.c */, + 8349A9001FE6258000E26435 /* afc.c */, 836F6E2F18BDC2180095E648 /* agsc.c */, 836F6E3018BDC2180095E648 /* ahx.c */, 836F6E3118BDC2180095E648 /* aifc.c */, + 8349A8F61FE6257E00E26435 /* aix_streamfile.h */, 836F6E3218BDC2180095E648 /* aix.c */, 836F6E3318BDC2180095E648 /* akb.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */, 836F6E3518BDC2180095E648 /* ast.c */, + 83A21F80201D8980000F04B9 /* atsl3.c */, + 83A21F7C201D897F000F04B9 /* atx.c */, + 83AA5D201F6E2F9B0020821C /* awc.c */, 836F6E3618BDC2180095E648 /* baf.c */, + 8349A8F81FE6257E00E26435 /* bar_streamfile.h */, + 8349A9051FE6258100E26435 /* bar.c */, 836F6E3718BDC2180095E648 /* bcstm.c */, + 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, + 83A5F75E198DF021009AF94C /* bfwav.c */, 836F6E3818BDC2180095E648 /* bgw.c */, + 83299FCE1E7660C7003A3242 /* bik.c */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, + 83EDE5D71A70951A005F5D84 /* btsnd.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */, @@ -1342,43 +1299,66 @@ 836F6E3F18BDC2180095E648 /* dc_idvi.c */, 836F6E4018BDC2180095E648 /* dc_kcey.c */, 836F6E4118BDC2180095E648 /* dc_str.c */, + 8349A8EE1FE6257C00E26435 /* dec.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */, + 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 836F6E4518BDC2180095E648 /* dsp_sth_str.c */, + 8349A8FF1FE6258000E26435 /* ea_1snh.c */, + 8349A8F71FE6257E00E26435 /* ea_eaac.c */, + 830165981F256BD000CA0941 /* ea_schl_fixed.c */, 836F6E4618BDC2180095E648 /* ea_schl.c */, 836F6E4818BDC2180095E648 /* emff.c */, 836F6E4918BDC2180095E648 /* exakt_sc.c */, 836F6E4A18BDC2180095E648 /* excitebots.c */, + 8349A8EF1FE6257C00E26435 /* ezw.c */, + 838BDB6D1D3B043C0022CA6F /* ffmpeg.c */, 836F6E4B18BDC2180095E648 /* ffw.c */, + 8349A8FD1FE6257F00E26435 /* flx.c */, + 83A21F81201D8981000F04B9 /* fsb_encrypted.c */, + 83A21F7E201D8980000F04B9 /* fsb_keys.h */, 836F6E4C18BDC2180095E648 /* fsb.c */, + 83F5F8821908D0A400C8E65F /* fsb5.c */, + 834D3A6D19F47C98001C54F6 /* g1l.c */, 836F6E4D18BDC2180095E648 /* gca.c */, 836F6E4E18BDC2180095E648 /* gcsw.c */, 836F6E4F18BDC2180095E648 /* genh.c */, 836F6E5118BDC2180095E648 /* gsp_gsb.c */, + 83709DFF1ECBC1A4005C03D3 /* gtd.c */, 836F6E5218BDC2180095E648 /* halpst.c */, + 83AA5D211F6E2F9C0020821C /* hca_keys.h */, + 8323894F1D2246C300482226 /* hca.c */, 836F6E5318BDC2180095E648 /* his.c */, 836F6E5418BDC2180095E648 /* idsp.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5818BDC2180095E648 /* ivb.c */, + 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, + 830EBE122004656E0023AA10 /* ktss.c */, 836F6E5A18BDC2180095E648 /* lsf.c */, 836F6E5C18BDC2180095E648 /* mattel_hyperscan.c */, 836F6E5D18BDC2180095E648 /* maxis_xa.c */, + 83709E001ECBC1A4005C03D3 /* mc3.c */, + 83EDE5D61A70951A005F5D84 /* mca.c */, 836F6E5E18BDC2180095E648 /* meta.h */, 836F6E5F18BDC2180095E648 /* mn_str.c */, + 8349A9031FE6258100E26435 /* mogg.c */, 836F6E6018BDC2180095E648 /* mp4.c */, + 83709E011ECBC1A4005C03D3 /* mss.c */, 836F6E6118BDC2180095E648 /* msvp.c */, 836F6E6218BDC2180095E648 /* mus_acm.c */, 836F6E6318BDC2180095E648 /* musc.c */, 836F6E6418BDC2180095E648 /* musx.c */, 836F6E6518BDC2180095E648 /* myspd.c */, + 8349A9061FE6258100E26435 /* naac.c */, 836F6E6618BDC2180095E648 /* naomi_adpcm.c */, 836F6E6718BDC2180095E648 /* naomi_spsd.c */, 836F6E6818BDC2180095E648 /* nds_hwas.c */, 836F6E6918BDC2180095E648 /* nds_rrds.c */, 836F6E6A18BDC2180095E648 /* nds_sad.c */, + 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, 836F6E6B18BDC2180095E648 /* nds_strm.c */, 836F6E6C18BDC2180095E648 /* nds_swav.c */, 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */, @@ -1397,16 +1377,27 @@ 836F6E7A18BDC2180095E648 /* ngc_sck_dsp.c */, 836F6E7B18BDC2180095E648 /* ngc_ssm.c */, 836F6E7C18BDC2180095E648 /* ngc_tydsp.c */, + 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, + 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, 836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7F18BDC2180095E648 /* ngca.c */, + 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */, + 83AB8C731E8072A100086084 /* nub_vag.c */, + 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, 836F6E8118BDC2180095E648 /* nwa.c */, - 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */, + 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, + 831BA60F1EAC61A500CF89B0 /* ogl.c */, + 8349A8FB1FE6257F00E26435 /* omu.c */, 836F6E8318BDC2180095E648 /* otm.c */, 836F6E8418BDC2180095E648 /* p3d.c */, + 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, 836F6E8518BDC2180095E648 /* pc_adp.c */, + 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, + 8349A8F01FE6257C00E26435 /* pc_ast.c */, 836F6E8618BDC2180095E648 /* pc_mxst.c */, 836F6E8718BDC2180095E648 /* pc_smp.c */, 836F6E8818BDC2180095E648 /* pc_snds.c */, + 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, 836F6E8B18BDC2180095E648 /* pona.c */, 836F6E8C18BDC2180095E648 /* pos.c */, 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, @@ -1446,12 +1437,13 @@ 836F6EB018BDC2180095E648 /* ps2_mtaf.c */, 836F6EB118BDC2180095E648 /* ps2_npsf.c */, 836F6EB218BDC2180095E648 /* ps2_p2bt.c */, + 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, 836F6EB318BDC2180095E648 /* ps2_pnb.c */, 836F6EB418BDC2180095E648 /* ps2_psh.c */, 836F6EB518BDC2180095E648 /* ps2_psw.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */, - 836F6EB818BDC2180095E648 /* rws.c */, + 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */, 836F6EBA18BDC2180095E648 /* ps2_sfs.c */, 836F6EBB18BDC2180095E648 /* ps2_sl3.c */, 836F6EBC18BDC2180095E648 /* ps2_smpl.c */, @@ -1461,12 +1453,14 @@ 836F6EC018BDC2190095E648 /* ps2_ster.c */, 836F6EC218BDC2190095E648 /* ps2_str.c */, 836F6EC318BDC2190095E648 /* ps2_strlr.c */, + 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, 836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */, 836F6EC718BDC2190095E648 /* ps2_vag.c */, 836F6EC818BDC2190095E648 /* ps2_vas.c */, 836F6EC918BDC2190095E648 /* ps2_vbk.c */, + 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, 836F6ECA18BDC2190095E648 /* ps2_vgs.c */, 836F6ECB18BDC2190095E648 /* ps2_vgv.c */, 836F6ECC18BDC2190095E648 /* ps2_vms.c */, @@ -1475,12 +1469,14 @@ 836F6ECF18BDC2190095E648 /* ps2_wad.c */, 836F6ED018BDC2190095E648 /* ps2_wb.c */, 836F6ED118BDC2190095E648 /* ps2_wmus.c */, + 8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */, 836F6ED218BDC2190095E648 /* ps2_xa2.c */, 836F6ED318BDC2190095E648 /* ps2_xa30.c */, 836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */, 836F6ED718BDC2190095E648 /* ps3_klbs.c */, 836F6ED818BDC2190095E648 /* ps3_msf.c */, + 8350270C1ED119D200C25929 /* ps3_mta2.c */, 836F6ED918BDC2190095E648 /* ps3_past.c */, 836F6EDD18BDC2190095E648 /* psx_cdxa.c */, 836F6EDE18BDC2190095E648 /* psx_fag.c */, @@ -1493,29 +1489,48 @@ 836F6EE518BDC2190095E648 /* rs03.c */, 836F6EE618BDC2190095E648 /* rsd.c */, 836F6EE718BDC2190095E648 /* rsf.c */, + 836F6EB818BDC2180095E648 /* rws.c */, 836F6EE818BDC2190095E648 /* rwsd.c */, 836F6EE918BDC2190095E648 /* rwx.c */, 836F6EEA18BDC2190095E648 /* s14_sss.c */, + 8349A8F11FE6257D00E26435 /* sab.c */, 836F6EEB18BDC2190095E648 /* sat_baka.c */, 836F6EEC18BDC2190095E648 /* sat_dvi.c */, 836F6EED18BDC2190095E648 /* sat_sap.c */, + 8349A8F51FE6257D00E26435 /* scd_pcm.c */, 836F6EEE18BDC2190095E648 /* sd9.c */, 836F6EEF18BDC2190095E648 /* sdt.c */, 836F6EF018BDC2190095E648 /* seg.c */, 836F6EF118BDC2190095E648 /* sfl.c */, + 831BA6111EAC61A500CF89B0 /* sgxd.c */, + 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, + 83A21F82201D8981000F04B9 /* sps_n1.c */, 836F6EF318BDC2190095E648 /* spt_spd.c */, + 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, + 83A21F84201D8981000F04B9 /* sqex_sead.c */, + 83AA5D231F6E2F9C0020821C /* stm.c */, 836F6EF618BDC2190095E648 /* str_asr.c */, 836F6EF718BDC2190095E648 /* str_snds.c */, 836F6EF818BDC2190095E648 /* stx.c */, 836F6EF918BDC2190095E648 /* svs.c */, + 831BA6121EAC61A500CF89B0 /* sxd.c */, + 83709E031ECBC1A4005C03D3 /* ta_aac.c */, 836F6EFA18BDC2190095E648 /* thp.c */, 836F6EFB18BDC2190095E648 /* tun.c */, + 830165971F256BD000CA0941 /* txth.c */, 836F6EFC18BDC2190095E648 /* ubi_ckd.c */, + 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, + 8349A8F41FE6257D00E26435 /* ubi_sb.c */, + 831BA6141EAC61A500CF89B0 /* vawx.c */, 836F6EFD18BDC2190095E648 /* vgs.c */, 836F6EFE18BDC2190095E648 /* vs.c */, + 8349A8F91FE6257E00E26435 /* vsf_tta.c */, 836F6EFF18BDC2190095E648 /* vsf.c */, + 8349A9011FE6258000E26435 /* vxn.c */, + 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */, + 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */, 836F6F0218BDC2190095E648 /* wii_ras.c */, @@ -1526,15 +1541,24 @@ 836F6F0718BDC2190095E648 /* wpd.c */, 836F6F0818BDC2190095E648 /* ws_aud.c */, 836F6F0918BDC2190095E648 /* wvs.c */, + 83FF0EBB1E93282100C58054 /* wwise.c */, + 83AB8C741E8072A100086084 /* x360_ast.c */, + 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, + 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, 836F6F0A18BDC2190095E648 /* x360_tra.c */, + 833A7A2D1ED11961003EC53E /* xau.c */, 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */, 836F6F0E18BDC2190095E648 /* xbox_wavm.c */, 836F6F0F18BDC2190095E648 /* xbox_xmu.c */, 836F6F1018BDC2190095E648 /* xbox_xvas.c */, 836F6F1118BDC2190095E648 /* xbox_xwav.c */, + 8350C0541E071881009E0A93 /* xma.c */, + 830EBE112004656E0023AA10 /* xnb.c */, 836F6F1218BDC2190095E648 /* xss.c */, + 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 836F6F1318BDC2190095E648 /* xwb.c */, + 83A21F7D201D8980000F04B9 /* xwc.c */, 836F6F1418BDC2190095E648 /* ydsp.c */, 836F6F1518BDC2190095E648 /* zsd.c */, 836F6F1618BDC2190095E648 /* zwdsp.c */, @@ -1580,6 +1604,7 @@ 836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 8349A91D1FE6258200E26435 /* aax_streamfile.h in Headers */, + 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, @@ -1775,9 +1800,9 @@ 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, + 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, 839B54571EEE1DA000048A2D /* rws_blocked.c in Sources */, 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */, - 8349A8E01FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c in Sources */, 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */, 836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */, 836F6F8118BDC2190095E648 /* dsp_sth_str.c in Sources */, @@ -1873,6 +1898,7 @@ 836F6FEC18BDC2190095E648 /* ps2_mtaf.c in Sources */, 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */, 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, + 83A21F85201D8981000F04B9 /* atx.c in Sources */, 836F701418BDC2190095E648 /* ps3_msf.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, @@ -1997,6 +2023,7 @@ 836F701518BDC2190095E648 /* ps3_past.c in Sources */, 836F6F7C18BDC2190095E648 /* dc_kcey.c in Sources */, 836F6FED18BDC2190095E648 /* ps2_npsf.c in Sources */, + 83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */, 836F6F9E18BDC2190095E648 /* mus_acm.c in Sources */, 83709E0E1ECBC1C3005C03D3 /* psv_decoder.c in Sources */, 831BA6191EAC61A500CF89B0 /* ogl.c in Sources */, @@ -2033,6 +2060,7 @@ 836F6F8918BDC2190095E648 /* gca.c in Sources */, 836F6F5718BDC2190095E648 /* str_snds_blocked.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, + 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, @@ -2052,6 +2080,7 @@ 836F6F9518BDC2190095E648 /* kraw.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, 83709E051ECBC1A4005C03D3 /* gtd.c in Sources */, + 83A21F86201D8981000F04B9 /* xwc.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, @@ -2080,9 +2109,9 @@ 8349A9081FE6258200E26435 /* ezw.c in Sources */, 836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */, 836F6FD918BDC2190095E648 /* ps2_gcm.c in Sources */, + 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */, 836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, - 836F6FBE18BDC2190095E648 /* ogg_vorbis_file.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, 836F6F4818BDC2190095E648 /* halpst_blocked.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, @@ -2111,6 +2140,7 @@ 836F6FBF18BDC2190095E648 /* otm.c in Sources */, 836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, + 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, @@ -2127,7 +2157,9 @@ 836F703518BDC2190095E648 /* svs.c in Sources */, 836F6FA318BDC2190095E648 /* naomi_spsd.c in Sources */, 836F6F9D18BDC2190095E648 /* msvp.c in Sources */, + 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */, 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */, + 83A21F89201D8982000F04B9 /* atsl3.c in Sources */, 836F705018BDC2190095E648 /* ydsp.c in Sources */, 836F702718BDC2190095E648 /* sat_baka.c in Sources */, 83345A4A1F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index eb0b61166..95c8c0020 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -119,7 +119,9 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, /* postadjust */ //todo improve switch(data->config.type) { - case ATRAC9_XVAG: /* skip other subsong blocks in XVAG */ + case ATRAC9_XVAG: + case ATRAC9_KMA9: + /* skip other subsong blocks */ if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) { stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index ed3c3d7d2..0e3652626 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -222,7 +222,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { switch(data->config.type) { case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; - case FFMPEG_BGW_ATRAC3: ret = ffmpeg_custom_read_bgw_atrac3(data, buf, buf_size); break; //case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; //case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break; default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break; @@ -291,7 +290,6 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { switch(data->config.type) { case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break; - case FFMPEG_BGW_ATRAC3: offset = ffmpeg_custom_seek_bgw_atrac3(data, offset); break; //case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break; //case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break; default: offset = ffmpeg_custom_seek_standard(data, offset); break; @@ -309,7 +307,6 @@ static int64_t ffmpeg_size(ffmpeg_codec_data * data) { switch(data->config.type) { case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break; case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break; - case FFMPEG_BGW_ATRAC3: bytes = ffmpeg_custom_size_bgw_atrac3(data); break; //case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break; //case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break; default: bytes = ffmpeg_custom_size_standard(data); break; @@ -806,9 +803,6 @@ void free_ffmpeg(ffmpeg_codec_data *data) { close_streamfile(data->streamfile); data->streamfile = NULL; } - if (data->config.key) { - free(data->config.key); - } free(data); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h index a7e129123..64461fe9f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h @@ -31,10 +31,6 @@ int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int bu int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset); int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data); -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data); - //int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); //int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset); //int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c deleted file mode 100644 index 5b6bd441d..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c +++ /dev/null @@ -1,58 +0,0 @@ -#if 1 -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -#define BGM_ATRAC3_FRAME_SIZE 0xC0 - -/** - * Encrypted ATRAC3 used in BGW (Final Fantasy XI PC). - * Info from Moogle Toolbox: https://sourceforge.net/projects/mogbox/ - */ - -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - int i, ch; - size_t bytes; - size_t block_align = BGM_ATRAC3_FRAME_SIZE * data->config.channels; - - - /* init key: first frame + modified channel header */ - if (data->config.key == NULL) { - data->config.key = malloc(block_align); - if (!data->config.key) return 0; - - read_streamfile(data->config.key, data->real_start, block_align, data->streamfile); - for (ch = 0; ch < data->config.channels; ch++) { - uint32_t xor = get_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE); - put_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE, xor ^ 0xA0024E9F); - } - } - - - /* read normally and unXOR the data */ - bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile); - for (i = 0; i < bytes; i++) { - int key_pos = (data->real_offset - data->real_start + i) % block_align; - buf[i] = buf[i] ^ data->config.key[key_pos]; - } - - - data->real_offset += bytes; - return bytes; -} - -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - data->real_offset = data->real_start + seek_virtual_offset; - return virtual_offset; -} - -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data) { - return data->real_size + data->header_size; -} - - -#endif -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c index 7fba94174..73ee1508c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -47,7 +47,7 @@ int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, v size_t header_size, packet_size; vorbis_custom_config cfg = data->config; - if (cfg.setup_type == HEADER_TRIAD) { + if (cfg.setup_type == WWV_HEADER_TRIAD) { /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ off_t offset = start_offset; @@ -132,17 +132,17 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_ /* packet size doesn't include header size */ switch(header_type) { - case TYPE_8: /* size 4+4 */ + case WWV_TYPE_8: /* size 4+4 */ *packet_size = (uint32_t)read_32bit(offset, streamFile); *granulepos = read_32bit(offset+4, streamFile); return 8; - case TYPE_6: /* size 4+2 */ + case WWV_TYPE_6: /* size 4+2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = read_32bit(offset+2, streamFile); return 6; - case TYPE_2: /* size 2 */ + case WWV_TYPE_2: /* size 2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */ return 2; @@ -306,7 +306,7 @@ static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - if (data->config.packet_type == MODIFIED) { + if (data->config.packet_type == WWV_MODIFIED) { /* rebuild first bits of packet type and window info (for the i-MDCT) */ uint32_t packet_type = 0, mode_number = 0, remainder = 0; @@ -423,13 +423,13 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 8, codebook_count_less1); codebook_count = codebook_count_less1 + 1; - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rebuild Wwise codebooks: untouched */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail; } } - else if (data->config.setup_type == INLINE_CODEBOOKS) { + else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) { /* rebuild Wwise codebooks: inline in simplified format */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; @@ -456,7 +456,7 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 16, dummy_time_value); - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rest of setup is untouched, copy bits */ uint32_t bitly = 0; uint32_t total_bits_read = iw->b_off; @@ -1174,11 +1174,11 @@ static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, w const wvc_info * wvc_list; switch (setup_type) { - case EXTERNAL_CODEBOOKS: + case WWV_EXTERNAL_CODEBOOKS: wvc_list = wvc_list_standard; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; - case AOTUV603_CODEBOOKS: + case WWV_AOTUV603_CODEBOOKS: wvc_list = wvc_list_aotuv603; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 975a3c543..08b683cf0 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -17,7 +17,7 @@ static const char* extension_list[] = { "aaap", "aax", //"ac3", //FFmpeg, not parsed //common? - "ace", //fake, for tri-Ace's formats + "ace", //fake, for tri-Ace's formats (to be removed) "acm", "adm", "adp", @@ -33,7 +33,7 @@ static const char* extension_list[] = { "aix", "akb", "al2", - "amts", //fake extension (to be removed) + "amts", //fake extension/header id for .stm (to be removed) "ao", //txth/reserved [Cloudphobia (PC)] "as4", "asd", @@ -43,6 +43,8 @@ static const char* extension_list[] = { "ast", "at3", "at9", + "atsl3", + "atx", "aud", "aus", "awc", @@ -138,7 +140,7 @@ static const char* extension_list[] = { "iab", "iadp", "idsp", - "idvi", //fake extension (to be removed) + "idvi", //fake extension for .pcm (to be removed) "ikm", "ild", "int", @@ -153,9 +155,10 @@ static const char* extension_list[] = { "jstm", "kces", - "kcey", //fake extension (to be removed) + "kcey", //fake extension/header id (to be removed) "khv", - "kovs", + "km9", + "kovs", //.kvs header id "kraw", "ktss", "kvs", @@ -172,6 +175,7 @@ static const char* extension_list[] = { "lstm", //fake extension, for STMs "lwav", //fake extension, for WAVs + "mab", "matx", "mc3", "mca", @@ -205,8 +209,9 @@ static const char* extension_list[] = { "naac", "ndp", "ngca", + "nop", "nps", - "npsf", //fake extension (to be removed) + "npsf", //fake extension/header id for .nps (to be removed) "nus3bank", "nwa", @@ -226,7 +231,7 @@ static const char* extension_list[] = { "pnb", "pona", "pos", - "ps2stm", //fake extension (to be removed) + "ps2stm", //fake extension for .stm (to be removed) "psh", "psnd", "psw", @@ -261,6 +266,7 @@ static const char* extension_list[] = { "sb5", "sb6", "sb7", + "sbin", "sc", "scd", "sck", @@ -282,6 +288,7 @@ static const char* extension_list[] = { "snd", "snds", "sng", + "sngw", "snr", "sns", "snu", @@ -374,6 +381,7 @@ static const char* extension_list[] = { "xvas", "xwav", "xwb", + "xwc", "xwm", //FFmpeg, not parsed (XWMA) "xwma", //FFmpeg, not parsed (XWMA) "xws", @@ -471,7 +479,7 @@ static const coding_info coding_info_list[] = { {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, - {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, + {coding_FSB_IMA, "FSB 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, @@ -574,6 +582,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, {layout_blocked_vawx, "blocked (VAWX)"}, + {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, #ifdef VGM_USE_VORBIS {layout_ogg_vorbis, "Ogg"}, #endif @@ -933,16 +942,20 @@ static const meta_info meta_info_list[] = { {meta_NGC_VID1, "Neversoft VID1 header"}, {meta_PC_FLX, "Ultima IX .FLX header"}, {meta_MOGG, "Harmonix Music Systems MOGG Vorbis"}, - -#ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, {meta_OGG_SLI, "Ogg Vorbis with .sli (start,length) for looping"}, {meta_OGG_SLI2, "Ogg Vorbis with .sli (from,to) for looping"}, {meta_OGG_SFL, "Ogg Vorbis with SFPL for looping"}, - {meta_OGG_UM3, "Ogg Vorbis, Ultramarine3 'encryption'"}, - {meta_OGG_KOVS, "Ogg Vorbis, KOVS header"}, - {meta_OGG_PSYCH, "Ogg Vorbis, Psychic Software obfuscation"}, - #endif + {meta_OGG_UM3, "Ogg Vorbis (Ultramarine3)"}, + {meta_OGG_KOVS, "Ogg Vorbis (KOVS header)"}, + {meta_OGG_PSYCHIC, "Ogg Vorbis (Psychic Software)"}, + {meta_OGG_SNGW, "Ogg Vorbis (Capcom)"}, + {meta_OGG_ISD, "Ogg Vorbis (ISD)"}, + {meta_KMA9, "Koei Tecmo KMA9 header"}, + {meta_XWC, "Starbreeze XWC header"}, + {meta_SQEX_SAB, "Square-Enix SAB header"}, + {meta_SQEX_MAB, "Square-Enix MAB header"}, + #ifdef VGM_USE_MP4V2 {meta_MP4, "AAC header"}, #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 645ec6946..b8bd1c60e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -164,6 +164,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_vawx: block_update_vawx(vgmstream->next_block_offset,vgmstream); break; + case layout_blocked_xvag_subsong: + block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); + break; default: break; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 5e222339c..e71844d0e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -95,6 +95,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* id, size, samples, hists-per-channel, stereo/interleaved data */ case coding_EA_XA: + //case coding_EA_XA_V2: /* handled in default */ case coding_EA_XA_int: for (i = 0; i < vgmstream->channels; i++) { int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; @@ -106,7 +107,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* the block can have padding so find the channel size from num_samples */ interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; - vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave; + + /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ + vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; } break; @@ -146,6 +149,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset); } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c new file mode 100644 index 000000000..1c1ab2d7b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c @@ -0,0 +1,19 @@ +#include "layout.h" +#include "../coding/coding.h" +#include "../vgmstream.h" + + +/* XVAG with subsongs layers, interleaves chunks of each subsong (a hack to support them) */ +void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t channel_size = 0x10; + + /* set offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + channel_size*i; + } + + //vgmstream->current_block_size = ; /* fixed */ + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + vgmstream->full_block_size; +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 9e946ed2c..41a40c4a9 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -66,6 +66,7 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h index 9caacab6e..1490e7c4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h @@ -72,10 +72,6 @@ static STREAMFILE *open_aax_with_STREAMFILE(STREAMFILE *file,off_t start_offset, streamfile->sf.get_realname = (void*)get_name_aax; streamfile->sf.open = (void*)open_aax_impl; streamfile->sf.close = (void*)close_aax; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = NULL; - streamfile->sf.get_error_count = NULL; -#endif streamfile->real_file = file; streamfile->start_physical_offset = start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 683e5cc0b..5e6a875c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -249,7 +249,7 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_ { uint8_t keybuf[6]; - if ( read_key_file(keybuf, 6, file) ) { + if (read_key_file(keybuf, 6, file) == 6) { *xor_start = get_16bitBE(keybuf+0); *xor_mult = get_16bitBE(keybuf+2); *xor_add = get_16bitBE(keybuf+4); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 29cdbbcd9..9d5b750e8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -176,6 +176,9 @@ static const adxkey_info adxkey8_list[] = { /* Storm Lover Natsu Koi!! (2011-08-04)(Vridge)(D3 Publisher)[PSP] */ {0x4133,0x5a01,0x5723, NULL,0}, // ? + /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */ + {0x55d9,0x46d3,0x5b01, "SONMYOJI",0}, + }; static const adxkey_info adxkey9_list[] = { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index 516773e02..595707504 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { if (cfg.encryption) { uint8_t keybuf[6]; - if (read_key_file(keybuf, 6, streamFile)) { + if (read_key_file(keybuf, 6, streamFile) == 6) { cfg.cri_key1 = get_16bitBE(keybuf+0); cfg.cri_key2 = get_16bitBE(keybuf+2); cfg.cri_key3 = get_16bitBE(keybuf+4); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h index 785ba8b9f..7fd36fd83 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h @@ -151,10 +151,6 @@ static STREAMFILE *open_aix_impl(AIXSTREAMFILE *streamfile,const char * const fi streamfile->sf.get_realname = (void*)get_name_aix; streamfile->sf.open = (void*)open_aix_impl; streamfile->sf.close = (void*)close_aix; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = NULL; - streamfile->sf.get_error_count = NULL; -#endif streamfile->real_file = file; streamfile->current_physical_offset = start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c b/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c new file mode 100644 index 000000000..722c3f77f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c @@ -0,0 +1,76 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */ +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + off_t subfile_offset; + size_t subfile_size, header_size, entry_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"atsl3")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ + goto fail; + + /* main header (LE) */ + header_size = read_32bitLE(0x04,streamFile); + /* 0x08/0c: flags?, 0x10: some size? */ + total_subsongs = read_32bitLE(0x14,streamFile); + entry_size = read_32bitLE(0x18,streamFile); + /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + /* entry header (BE) */ + /* 0x00: id */ + subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile); + subfile_size = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile); + /* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */ + /* some kind of seek/switch table follows */ + + temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atx.c b/Frameworks/vgmstream/vgmstream/src/meta/atx.c new file mode 100644 index 000000000..a58a88337 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/atx.c @@ -0,0 +1,109 @@ +#include "meta.h" +#include "../coding/coding.h" + +#define ATX_MAX_SEGMENTS 2 + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile); + +/* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */ +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + + /* check extensions */ + if ( !check_extensions(streamFile,"atx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */ + goto fail; + + /* .ATX is made of subfile segments, handled by the streamFile. + * Each segment has a header/footer, and part of the whole data + * (i.e. ATRAC3 data ends in a subfile and continues in the next) */ + temp_streamFile = setup_atx_streamfile(streamFile); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0}; + char filename[PATH_LIMIT]; + size_t filename_len; + int i, num_segments = 0; + size_t riff_size; +VGM_LOG("1\n"); + + if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */ + if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail; + num_segments = read_16bitLE(0x1e,streamFile); + + /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */ + get_streamfile_name(streamFile, filename,PATH_LIMIT); + filename_len = strlen(filename); + if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail; + + /* setup segments (could avoid reopening first segment but meh) */ + for (i = 0; i < num_segments; i++) { + off_t subfile_offset; + size_t subfile_size; +VGM_LOG("loop\n"); + filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ + new_streamFile = open_stream_name(streamFile, filename); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + + if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */ + goto fail; + + /* parse block/segment header (other Media.Vision's files use it too) */ + subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */ + subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */ +VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size); + if (read_16bitLE(0x1c,segment_streamFiles[i]) != i) + goto fail; /* segment sequence */ + /* 0x04: block size (should match subfile_size in .ATX) */ + /* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */ + + /* clamp to ignore header/footer during next reads */ + new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + } + + /* setup with all segments and clamp further using riff_size (last segment has padding) */ + riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08; + + new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + if (!temp_streamFile) { + for (i = 0; i < num_segments; i++) + close_streamfile(segment_streamFiles[i]); + } else { + close_streamfile(temp_streamFile); /* closes all segments */ + } + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index ab2a605bd..a102d1b47 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -7,7 +7,7 @@ typedef struct { int is_encrypted; int is_music; - int total_streams; + int total_subsongs; int channel_count; int sample_rate; @@ -47,7 +47,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { vgmstream->sample_rate = awc.sample_rate; vgmstream->num_samples = awc.num_samples; - vgmstream->num_streams = awc.total_streams; + vgmstream->num_streams = awc.total_subsongs; + vgmstream->stream_size = awc.stream_size; vgmstream->meta_type = meta_AWC; @@ -113,7 +114,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { int i, ch, entries; uint32_t flags, info_header, tag_count = 0, tags_skip = 0; off_t off; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; memset(awc,0,sizeof(awc_header)); @@ -161,13 +162,13 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000; if (awc->is_music) { /* all streams except id 0 is a channel */ - awc->total_streams = 1; - target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */ + awc->total_subsongs = 1; + target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */ } else { /* each stream is a single sound */ - awc->total_streams = entries; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail; + awc->total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail; } @@ -176,7 +177,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { info_header = read_32bit(off + 0x04*i, streamFile); tag_count = (info_header >> 29) & 0x7; /* 3b */ //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ - if (target_stream-1 == i) + if (target_subsong-1 == i) break; tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h index 8305020c5..81d43befc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h @@ -62,15 +62,6 @@ static void close_bar(BARSTREAMFILE *streamFile) { return; } -#ifdef PROFILE_STREAMFILE -size_t get_bytes_read_bar(BARSTREAMFILE *streamFile) { - return streamFile->real_file->get_bytes_read(streamFile->real_file); -} - -int (*get_error_count)(BARSTREAMFILE *streamFile) { - return streamFile->real_file->get_error_count(streamFile->real_file); -} -#endif /*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file) { BARSTREAMFILE *streamfile = malloc(sizeof(BARSTREAMFILE)); @@ -87,10 +78,6 @@ int (*get_error_count)(BARSTREAMFILE *streamFile) { streamfile->sf.get_realname = (void*)get_realname_bar; streamfile->sf.open = (void*)open_bar; streamfile->sf.close = (void*)close_bar; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = get_bytes_read_bar; - streamfile->sf.get_error_count = get_error_count_bar; -#endif streamfile->real_file = file; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c index f95ac9940..0be6ebabc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c @@ -1,9 +1,14 @@ #include "meta.h" #include "../coding/coding.h" + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels); + + /* BGW - from Final Fantasy XI (PC) music files */ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; uint32_t codec, file_size, block_size, sample_rate, block_align; int32_t loop_start; off_t start_offset; @@ -32,18 +37,15 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { channel_count = read_8bit(0x2e,streamFile); block_align = read_8bit(0x2f,streamFile); - - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_BGW; vgmstream->sample_rate = sample_rate; @@ -65,7 +67,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { case 3: { /* ATRAC3 (encrypted) */ uint8_t buf[0x100]; int bytes, joint_stereo, skip_samples; - ffmpeg_custom_config cfg; + size_t data_size = file_size - start_offset; vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ if (loop_flag) { @@ -77,18 +79,18 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { joint_stereo = 0; skip_samples = 0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, file_size - start_offset, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); if (bytes <= 0) goto fail; - memset(&cfg, 0, sizeof(ffmpeg_custom_config)); - cfg.type = FFMPEG_BGW_ATRAC3; - cfg.channels = vgmstream->channels; + temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); + if (!temp_streamFile) goto fail; - vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,file_size - start_offset, &cfg); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + close_streamfile(temp_streamFile); break; } #endif @@ -98,17 +100,17 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { } - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } + /* SPW (SEWave) - from PlayOnline viewer for Final Fantasy XI (PC) */ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -118,8 +120,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { int channel_count, loop_flag = 0; - /* check extensions */ - if ( !check_extensions(streamFile, "spw") ) + /* check extensions */ + if ( !check_extensions(streamFile, "spw") ) goto fail; /* check header */ @@ -127,10 +129,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { read_32bitBE(4,streamFile) != 0x76650000) /* "ve\0\0" */ goto fail; - /* check file size with header value */ - if (read_32bitLE(0x8,streamFile) != get_streamfile_size(streamFile)) - goto fail; - file_size = read_32bitLE(0x08,streamFile); codec = read_32bitLE(0x0c,streamFile); /*file_id = read_32bitLE(0x10,streamFile);*/ @@ -144,17 +142,15 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { block_align = read_8bit(0x2b,streamFile); /*0x2c: unk (0x01 when PCM, 0x10 when VAG?) */ - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_SPW; vgmstream->sample_rate = sample_rate; @@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { } break; - + case 1: /* PCM */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; @@ -182,8 +178,9 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { vgmstream->loop_start_sample = (loop_start-1); vgmstream->loop_end_sample = vgmstream->num_samples; } - + break; + default: goto fail; } @@ -199,3 +196,61 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +#define BGW_KEY_MAX (0xC0*2) + +typedef struct { + uint8_t key[BGW_KEY_MAX]; + size_t key_size; +} bgw_decryption_data; + +/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */ +static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + dest[i] ^= data->key[(offset + i) % data->key_size]; + } + + return bytes_read; +} + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bgw_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bgw_decryption_data); + int ch; + + /* setup decryption with key (first frame + modified channel header) */ + if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; + + io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); + for (ch = 0; ch < channels; ch++) { + uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); + put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); + } + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c index f703b2a6a..3e3595762 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bik.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -2,13 +2,14 @@ #include "../coding/coding.h" #include "../util.h" -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples); +static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); /* BINK 1/2 - RAD Game Tools movies (audio/video format) */ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0; - int stream_index = streamFile->stream_index; + int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; + int total_subsongs = 0, stream_index = streamFile->stream_index; + size_t stream_size; /* check extension, case insensitive (bika = manually demuxed audio) */ @@ -19,7 +20,7 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; /* find target stream info and samples */ - if (!bink_get_info(streamFile, &total_streams, &channel_count, &sample_rate, &num_samples)) + if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) goto fail; /* build the VGMSTREAM */ @@ -29,7 +30,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_BINK; #ifdef VGM_USE_FFMPEG @@ -58,12 +60,13 @@ fail: * as they are not in the main header. The header for BINK1 and 2 is the same. * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) */ -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { +static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { uint32_t *offsets = NULL; uint32_t num_frames, num_samples_b = 0; off_t cur_offset; int i, j, sample_rate, channel_count; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; + size_t stream_size = 0; size_t filesize = get_streamfile_size(streamFile); uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00); @@ -76,20 +79,20 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */ /* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */ - total_streams = read_32bitLE(0x28,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail; + total_subsongs = read_32bitLE(0x28,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail; /* find stream info and position in offset table */ cur_offset = 0x2c; if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */ (signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */ cur_offset += 0x04; /* unknown v2 header field */ - cur_offset += 0x04*total_streams; /* skip streams max packet bytes */ - sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile); - channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ - cur_offset += 0x04*total_streams; /* skip streams info */ - cur_offset += 0x04*total_streams; /* skip streams ids */ + cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */ + sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile); + channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ + cur_offset += 0x04*total_subsongs; /* skip streams info */ + cur_offset += 0x04*total_subsongs; /* skip streams ids */ /* read frame offsets in a buffer, to avoid fseeking to the table back and forth */ @@ -111,10 +114,11 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * cur_offset = offsets[i]; /* read audio packet headers per stream */ - for (j=0; j < total_streams; j++) { + for (j=0; j < total_subsongs; j++) { uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ - if (j == target_stream-1) { + if (j == target_subsong-1) { + stream_size += 0x04 + ap_size; if (ap_size > 0) num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ break; /* next frame */ @@ -128,7 +132,8 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * free(offsets); - if (out_total_streams) *out_total_streams = total_streams; + if (out_total_subsongs) *out_total_subsongs = total_subsongs; + if (out_stream_size) *out_stream_size = stream_size; if (out_sample_rate) *out_sample_rate = sample_rate; if (out_channel_count) *out_channel_count = channel_count; //todo returns a few more samples (~48) than binkconv.exe? diff --git a/Frameworks/vgmstream/vgmstream/src/meta/flx.c b/Frameworks/vgmstream/vgmstream/src/meta/flx.c index ab28a3221..d37762e86 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/flx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/flx.c @@ -7,7 +7,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { off_t start_offset, stream_offset = 0; size_t data_size; int loop_flag, channel_count, codec; - int total_streams = 0, target_stream = streamFile->stream_index; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + size_t stream_size = 0; /* check extensions (.flx: name of archive, files inside don't have extensions) */ @@ -24,23 +25,26 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { || read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile)) goto fail; - if (target_stream == 0) target_stream = 1; + if (target_subsong == 0) target_subsong = 1; for (i = 0; i < entries; i++) { off_t entry_offset = read_32bitLE(offset + 0x00, streamFile); - /* 0x04: stream size */ + size_t entry_size = read_32bitLE(offset + 0x04, streamFile); offset += 0x08; if (entry_offset != 0x00) - total_streams++; /* many entries are empty */ - if (total_streams == target_stream && stream_offset == 0) + total_subsongs++; /* many entries are empty */ + if (total_subsongs == target_subsong && stream_offset == 0) { stream_offset = entry_offset; /* found but let's keep adding total_streams */ + stream_size = entry_size; + } } - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (stream_offset == 0x00) goto fail; } else { stream_offset = 0x00; + stream_size = get_streamfile_size(streamFile); } if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10) @@ -57,7 +61,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile); - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PC_FLX; switch(codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 5b0e12e7e..6a6bbf53b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -1,9 +1,6 @@ #include "meta.h" #include "../coding/coding.h" -#define FAKE_RIFF_BUFFER_SIZE 100 - -static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset); /* ************************************************************************************************************ * FSB defines, copied from the public spec (https://www.fmod.org/questions/question/forum-4928/) @@ -65,9 +62,7 @@ static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offse #define FSOUND_CHANNELMODE_MASK (FSOUND_CHANNELMODE_ALLMONO | FSOUND_CHANNELMODE_ALLSTEREO | FSOUND_CHANNELMODE_PROTOOLS) #define FSOUND_CHANNELMODE_DEFAULT 0x00000000 /* Determine channel assignment automatically from channel count. */ #define FSOUND_CHANNELMODE_RESERVED 0x00000C00 - #define FSOUND_NORMAL (FSOUND_16BITS | FSOUND_SIGNED | FSOUND_MONO) - #define FSB_SAMPLE_DATA_ALIGN 32 @@ -75,23 +70,22 @@ static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offse typedef struct { /* main header */ uint32_t id; - int32_t numsamples; /* number of samples(streams) in the file */ - uint32_t shdrsize; /* size in bytes of all of the sample headers including extended information */ - uint32_t datasize; /* size in bytes of compressed sample data */ - /* main header: FSB 3/3.1/4 */ - uint32_t version; /* extended fsb version */ - uint32_t flags; /* flags that apply to all samples(streams) in the fsb */ + int32_t total_subsongs; + uint32_t sample_header_size; /* all of the sample headers including extended information */ + uint32_t data_size; + uint32_t version; /* extended fsb version (in FSB 3/3.1/4) */ + uint32_t flags; /* flags common to all streams (in FSB 3/3.1/4)*/ /* sample header */ - uint32_t lengthsamples; - uint32_t lengthcompressedbytes; - uint32_t loopstart; - uint32_t loopend; + uint32_t num_samples; + uint32_t stream_size; + uint32_t loop_start; + uint32_t loop_end; uint32_t mode; - int32_t deffreq; - uint16_t numchannels; + int32_t sample_rate; + uint16_t channels; /* extra */ - uint32_t hdrsize; - uint32_t shdrsize_min; + uint32_t base_header_size; + uint32_t sample_header_min; meta_t meta_type; off_t name_offset; @@ -102,294 +96,328 @@ typedef struct { /* FSB4 */ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { - return init_vgmstream_fsb_offset(streamFile, 0x0); -} - -/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii) - * 16 byte header which holds the filesize - * (unsure if this is from a proper rip) */ -VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { - if (read_32bitBE(0x00,streamFile) != 0x00574156) /* "\0WAV" */ - return NULL; - return init_vgmstream_fsb_offset(streamFile, 0x10); -} - -VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t custom_data_offset; int loop_flag = 0; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; + fsb_header fsb = {0}; - fsb_header fsbh; /* check extensions (.bnk = Hard Corps Uprising PS3) */ - if ( !check_extensions(streamFile, "fsb,wii,bnk") ) + if ( !check_extensions(streamFile, "fsb,bnk") ) goto fail; /* check header */ - fsbh.id = read_32bitBE(offset+0x00,streamFile); - if (fsbh.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */ - fsbh.meta_type = meta_FSB1; - fsbh.hdrsize = 0x10; - fsbh.shdrsize_min = 0x40; + fsb.id = read_32bitBE(0x00,streamFile); + if (fsb.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */ + fsb.meta_type = meta_FSB1; + fsb.base_header_size = 0x10; + fsb.sample_header_min = 0x40; /* main header */ - fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); - fsbh.datasize = read_32bitLE(offset+0x08,streamFile); - fsbh.shdrsize = 0x40; - fsbh.version = 0; - fsbh.flags = 0; + fsb.total_subsongs = read_32bitLE(0x04,streamFile); + fsb.data_size = read_32bitLE(0x08,streamFile); + fsb.sample_header_size = 0x40; + fsb.version = 0; + fsb.flags = 0; - if (fsbh.numsamples > 1) goto fail; + if (fsb.total_subsongs > 1) goto fail; /* sample header (first stream only, not sure if there are multi-FSB1) */ { - off_t s_off = offset+fsbh.hdrsize; + off_t s_off = fsb.base_header_size; - fsbh.name_offset = s_off; - fsbh.name_size = 0x20; - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile); + fsb.name_offset = s_off; + fsb.name_size = 0x20; + fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); + fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); + fsb.sample_rate = read_32bitLE(s_off+0x28,streamFile); /* 0x2c:? 0x2e:? 0x30:? 0x32:? */ - fsbh.mode = read_32bitLE(s_off+0x34,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x38,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x3c,streamFile); + fsb.mode = read_32bitLE(s_off+0x34,streamFile); + fsb.loop_start = read_32bitLE(s_off+0x38,streamFile); + fsb.loop_end = read_32bitLE(s_off+0x3c,streamFile); - fsbh.numchannels = (fsbh.mode & FSOUND_STEREO) ? 2 : 1; - if (fsbh.loopend > fsbh.lengthsamples) /* this seems common... */ - fsbh.lengthsamples = fsbh.loopend; + fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1; + if (fsb.loop_end > fsb.num_samples) /* this seems common... */ + fsb.num_samples = fsb.loop_end; - start_offset = offset + fsbh.hdrsize + fsbh.shdrsize; - custom_data_offset = offset + fsbh.hdrsize + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + start_offset = fsb.base_header_size + fsb.sample_header_size; + custom_data_offset = fsb.base_header_size + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ } } else { /* other FSBs (common/extended format) */ - if (fsbh.id == 0x46534232) { /* "FSB2" */ - fsbh.meta_type = meta_FSB2; - fsbh.hdrsize = 0x10; - fsbh.shdrsize_min = 0x40; /* guessed */ - } else if (fsbh.id == 0x46534233) { /* "FSB3" */ - fsbh.meta_type = meta_FSB3; - fsbh.hdrsize = 0x18; - fsbh.shdrsize_min = 0x40; - } else if (fsbh.id == 0x46534234) { /* "FSB4" */ - fsbh.meta_type = meta_FSB4; - fsbh.hdrsize = 0x30; - fsbh.shdrsize_min = 0x50; + if (fsb.id == 0x46534232) { /* "FSB2" */ + fsb.meta_type = meta_FSB2; + fsb.base_header_size = 0x10; + fsb.sample_header_min = 0x40; /* guessed */ + } else if (fsb.id == 0x46534233) { /* "FSB3" */ + fsb.meta_type = meta_FSB3; + fsb.base_header_size = 0x18; + fsb.sample_header_min = 0x40; + } else if (fsb.id == 0x46534234) { /* "FSB4" */ + fsb.meta_type = meta_FSB4; + fsb.base_header_size = 0x30; + fsb.sample_header_min = 0x50; } else { goto fail; } /* main header */ - fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); - fsbh.shdrsize = read_32bitLE(offset+0x08,streamFile); - fsbh.datasize = read_32bitLE(offset+0x0c,streamFile); - if (fsbh.hdrsize > 0x10) { - fsbh.version = read_32bitLE(offset+0x10,streamFile); - fsbh.flags = read_32bitLE(offset+0x14,streamFile); + fsb.total_subsongs = read_32bitLE(0x04,streamFile); + fsb.sample_header_size = read_32bitLE(0x08,streamFile); + fsb.data_size = read_32bitLE(0x0c,streamFile); + if (fsb.base_header_size > 0x10) { + fsb.version = read_32bitLE(0x10,streamFile); + fsb.flags = read_32bitLE(0x14,streamFile); /* FSB4: 0x18:hash 0x20:guid */ } else { - fsbh.version = 0; - fsbh.flags = 0; + fsb.version = 0; + fsb.flags = 0; } - if (fsbh.version == FMOD_FSB_VERSION_3_1) { - fsbh.shdrsize_min = 0x50; - } else if (fsbh.version != 0 /* FSB2 */ - && fsbh.version != FMOD_FSB_VERSION_3_0 - && fsbh.version != FMOD_FSB_VERSION_4_0) { + if (fsb.version == FMOD_FSB_VERSION_3_1) { + fsb.sample_header_min = 0x50; + } else if (fsb.version != 0 /* FSB2 */ + && fsb.version != FMOD_FSB_VERSION_3_0 + && fsb.version != FMOD_FSB_VERSION_4_0) { goto fail; } - if (fsbh.shdrsize < fsbh.shdrsize_min) goto fail; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > fsbh.numsamples || fsbh.numsamples < 1) goto fail; + if (fsb.sample_header_size < fsb.sample_header_min) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail; /* sample header (N-stream) */ { int i; - off_t s_off = offset + fsbh.hdrsize; - off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize; + off_t s_off = fsb.base_header_size; + off_t d_off = fsb.base_header_size + fsb.sample_header_size; /* find target_stream header (variable sized) */ - for(i = 0; i < fsbh.numsamples; i++) { + for (i = 0; i < fsb.total_subsongs; i++) { size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile); - fsbh.name_offset = s_off+0x02; - fsbh.name_size = 0x20-0x02; - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x2c,streamFile); - fsbh.mode = read_32bitLE(s_off+0x30,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x34,streamFile); + fsb.name_offset = s_off+0x02; + fsb.name_size = 0x20-0x02; + fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); + fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); + fsb.loop_start = read_32bitLE(s_off+0x28,streamFile); + fsb.loop_end = read_32bitLE(s_off+0x2c,streamFile); + fsb.mode = read_32bitLE(s_off+0x30,streamFile); + fsb.sample_rate = read_32bitLE(s_off+0x34,streamFile); /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ - fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile); - /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */ + fsb.channels = read_16bitLE(s_off+0x3e,streamFile); + /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ - if (i+1 == target_stream) /* d_off found */ + if (i+1 == target_subsong) /* d_off found */ break; s_off += stream_header_size; - d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */ + d_off += fsb.stream_size; /* there is no offset so manually count */ /* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */ - if ((fsbh.mode & FSOUND_IMAADPCM) && (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { + if ((fsb.mode & FSOUND_IMAADPCM) && (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { if (d_off % 0x20) d_off += 0x20 - (d_off % 0x20); } } - if (i > fsbh.numsamples) goto fail; /* not found */ + if (i > fsb.total_subsongs) goto fail; /* not found */ start_offset = d_off; - custom_data_offset = s_off + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + custom_data_offset = s_off + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ } } /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ - //VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); + //VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); /* sometimes there is garbage at the end or missing bytes due to improper demuxing */ - VGM_ASSERT(fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset, - "FSB wrong head/datasize found (expected 0x%x vs 0x%lx)\n", - fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize, streamFile->get_size(streamFile) - offset); + VGM_ASSERT(fsb.base_header_size + fsb.sample_header_size + fsb.data_size != streamFile->get_size(streamFile), + "FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n", + fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile)); /* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks - * that should loop and jingles/sfx that shouldn't. We'll try to disable looping is it looks jingly enough. */ - loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF); - if(!(fsbh.mode & FSOUND_LOOP_NORMAL) /* rarely set */ - && fsbh.loopstart+fsbh.loopend+1 == fsbh.lengthsamples /* full loop */ - && fsbh.lengthsamples < 20*fsbh.deffreq) /* seconds, lame but no other way to know */ + * that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */ + loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); + if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */ + && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ + && fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */ loop_flag = 0; /* ping-pong looping = no looping? (forward > reverse > forward) */ - VGM_ASSERT(fsbh.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); + VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(fsbh.numchannels,loop_flag); + vgmstream = allocate_vgmstream(fsb.channels,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = fsbh.deffreq; - vgmstream->num_samples = fsbh.lengthsamples; - vgmstream->loop_start_sample = fsbh.loopstart; - vgmstream->loop_end_sample = fsbh.loopend; - vgmstream->num_streams = fsbh.numsamples; - vgmstream->meta_type = fsbh.meta_type; - if (fsbh.name_offset) - read_string(vgmstream->stream_name,fsbh.name_size+1, fsbh.name_offset,streamFile); + vgmstream->sample_rate = fsb.sample_rate; + vgmstream->num_samples = fsb.num_samples; + vgmstream->loop_start_sample = fsb.loop_start; + vgmstream->loop_end_sample = fsb.loop_end; + vgmstream->num_streams = fsb.total_subsongs; + vgmstream->stream_size = fsb.stream_size; + vgmstream->meta_type = fsb.meta_type; + if (fsb.name_offset) + read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile); - /* parse format */ - if (fsbh.mode & FSOUND_MPEG) { - /* FSB3: ?; FSB4: Shatter, Way of the Samurai 3/4, Forza Horizon 1/2, Dragon Age Origins */ + /* parse codec */ + if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */ #if defined(VGM_USE_MPEG) mpeg_custom_config cfg = {0}; cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : - (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : - (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); - //VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ - VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); + //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ + VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - #else goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ #endif } - else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */ + else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; - /* "interleaved header" IMA, which seems only used with >2ch (ex. Blade Kitten 5.1) */ - if (vgmstream->channels > 2) + /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) + * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */ + if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) vgmstream->coding_type = coding_FSB_IMA; } - else if (fsbh.mode & FSOUND_VAG) { - /* FSB1: Jurassic Park Operation Genesis - * FSB3: ?; FSB4: Spider Man Web of Shadows, Speed Racer, Silent Hill: Shattered Memories (PS2) */ + else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; } - else if (fsbh.mode & FSOUND_XMA) { + else if (fsb.mode & FSOUND_XMA) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */ #if defined(VGM_USE_FFMPEG) - /* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[FAKE_RIFF_BUFFER_SIZE]; + uint8_t buf[0x100]; size_t bytes, block_size, block_count; - /* not accurate but not needed by FFmpeg */ + block_size = 0x8000; /* FSB default */ - block_count = fsbh.datasize / block_size; /* read_32bitLE(custom_data_offset +0x14) -1? */ + block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ - /* make a fake riff so FFmpeg can parse the XMA2 */ - bytes = ffmpeg_make_riff_xma2(buf, FAKE_RIFF_BUFFER_SIZE, fsbh.lengthsamples, fsbh.datasize, fsbh.numchannels, fsbh.deffreq, block_count, block_size); - if (bytes <= 0) - goto fail; + bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsbh.datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - #else goto fail; #endif } - else if (fsbh.mode & FSOUND_GCADPCM) { + else if (fsb.mode & FSOUND_GCADPCM) { /* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */ vgmstream->coding_type = coding_NGC_DSP_subint; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x2; dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e); } - else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag, unused) */ - /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ + else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ VGM_LOG("FSB4 FSOUND_CELT found\n"); goto fail; } else { /* PCM */ - if (fsbh.mode & FSOUND_8BITS) { - vgmstream->coding_type = (fsbh.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; + if (fsb.mode & FSOUND_8BITS) { + vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x1; } - else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ + else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ /* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS), * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ - vgmstream->coding_type = (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; + vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x2; } } /* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */ - if (fsbh.numchannels > 1 && (fsbh.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { + if (fsb.channels > 1 && (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { if (vgmstream->coding_type == coding_NGC_DSP_subint) vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = fsbh.lengthcompressedbytes / fsbh.numchannels; + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; } /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } + + +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size); + +/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii). + * Has a 0x10 BE header that holds the filesize (unsure if this is from a proper rip). */ +VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *test_streamFile = NULL; + off_t subfile_start = 0x10; + size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo + + /* check extensions */ + if ( !check_extensions(streamFile, "fsb,wii") ) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x00574156) /* "\0WAV" */ + goto fail; + + /* parse FSB subfile */ + test_streamFile = setup_fsb4_wav_streamfile(streamFile, subfile_start,subfile_size); + if (!test_streamFile) goto fail; + + vgmstream = init_vgmstream_fsb(test_streamFile); + if (!vgmstream) goto fail; + + /* init the VGMSTREAM */ + close_streamfile(test_streamFile); + return vgmstream; + +fail: + close_streamfile(test_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"fsb"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index a29fa70e2..0d79502b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; - int TotalStreams, TargetStream = streamFile->stream_index; + int TotalSubsongs, TargetSubsong = streamFile->stream_index; int i; /* check extension, case insensitive */ @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { Version = read_32bitLE(0x04,streamFile); if (Version != 0x00 && Version != 0x01) goto fail; - TotalStreams = read_32bitLE(0x08,streamFile); + TotalSubsongs = read_32bitLE(0x08,streamFile); SampleHeaderLength = read_32bitLE(0x0C,streamFile); NameTableLength = read_32bitLE(0x10,streamFile); SampleDataLength = read_32bitLE(0x14,streamFile); @@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) goto fail; - if (TargetStream == 0) TargetStream = 1; /* default to 1 */ - if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail; + if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ + if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; SampleHeaderStart = BaseHeaderLength; /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ - for (i = 1; i <= TotalStreams; i++) { + for (i = 1; i <= TotalSubsongs; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; uint32_t SampleMode1, SampleMode2; @@ -156,11 +156,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* stream found */ - if (i == TargetStream) { + if (i == TargetSubsong) { StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; /* get stream size from next stream or datasize if there is only one */ - if (i == TotalStreams) { + if (i == TotalSubsongs) { StreamSize = SampleDataLength - DataStart; } else { uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); @@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* get stream name */ if (NameTableLength) { - NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile); + NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile); } @@ -187,12 +187,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = SampleRate; - vgmstream->num_streams = TotalStreams; vgmstream->num_samples = NumSamples; if (LoopFlag) { vgmstream->loop_start_sample = LoopStart; vgmstream->loop_end_sample = LoopEnd; } + vgmstream->num_streams = TotalSubsongs; + vgmstream->stream_size = StreamSize; vgmstream->meta_type = meta_FSB5; if (NameOffset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c new file mode 100644 index 000000000..98af7df96 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c @@ -0,0 +1,158 @@ +#include "meta.h" +#include "fsb_keys.h" + +#define FSB_KEY_MAX 128 /* probably 32 */ + +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt); + + +/* fully encrypted FSBs */ +VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { + VGMSTREAM * vgmstream = NULL; + + /* check extensions */ + if ( !check_extensions(streamFile, "fsb") ) + goto fail; + + /* ignore non-encrypted FSB */ + if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */ + goto fail; + + + /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */ + { + STREAMFILE *temp_streamFile = NULL; + uint8_t key[FSB_KEY_MAX]; + size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile); + + if (key_size) { + { + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0); + if (!temp_streamFile) goto fail; + + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); + + close_streamfile(temp_streamFile); + } + + if (!vgmstream) { + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1); + if (!temp_streamFile) goto fail; + + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); + + close_streamfile(temp_streamFile); + } + } + } + + + /* try all keys until one works */ + if (!vgmstream) { + int i; + STREAMFILE *temp_streamFile = NULL; + + for (i = 0; i < fsbkey_list_count; i++) { + fsbkey_info entry = fsbkey_list[i]; + ;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); + + temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt); + if (!temp_streamFile) goto fail; + + if (fsbkey_list[i].is_fsb5) { + vgmstream = init_vgmstream_fsb5(temp_streamFile); + } else { + vgmstream = init_vgmstream_fsb(temp_streamFile); + } + + close_streamfile(temp_streamFile); + if (vgmstream) break; + } + } + + if (!vgmstream) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + uint8_t key[FSB_KEY_MAX]; + size_t key_size; + int is_alt; +} fsb_decryption_data; + +/* Encrypted FSB info from guessfsb and fsbext */ +static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) { + static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */ + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF + }; + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (inverted bits and xor) */ + for (i = 0; i < bytes_read; i++) { + uint8_t xor = data->key[(offset + i) % data->key_size]; + uint8_t val = dest[i]; + if (data->is_alt) { + dest[i] = reverse_bits_table[val ^ xor]; + } + else { + dest[i] = reverse_bits_table[val] ^ xor; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + fsb_decryption_data io_data = {0}; + size_t io_data_size = sizeof(fsb_decryption_data); + + /* setup decryption with key (external) */ + if (!key_size || key_size > FSB_KEY_MAX) goto fail; + + memcpy(io_data.key, key, key_size); + io_data.key_size = key_size; + io_data.is_alt = is_alt; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h new file mode 100644 index 000000000..1b3a6081d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -0,0 +1,126 @@ +#ifndef _FSB_KEYS_H_ +#define _FSB_KEYS_H_ + +typedef struct { + int is_fsb5; + int is_alt; + size_t fsbkey_size; + const uint8_t *fsbkey; +} fsbkey_info; + +/** + * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums and with guessfsb.exe + */ + +/* DJ Hero 2 (X360) */ //"nos71RiT" +static const uint8_t key_dj2[] = { 0x6E,0x6F,0x73,0x37,0x31,0x52,0x69,0x54 }; + +/* Double Fine Productions: Brutal Legend, Massive Chalice, etc (multi) */ //"DFm3t4lFTW" +static const uint8_t key_dfp[] = { 0x44,0x46,0x6D,0x33,0x74,0x34,0x6C,0x46,0x54,0x57 }; + +/* N++ (PC?) */ //"H$#FJa%7gRZZOlxLiN50&g5Q" +static const uint8_t key_npp[] = { 0x48,0x24,0x23,0x46,0x4A,0x61,0x25,0x37,0x67,0x52,0x5A,0x5A,0x4F,0x6C,0x78,0x4C,0x69,0x4E,0x35,0x30,0x26,0x67,0x35,0x51 }; + +/* Slightly Mad Studios: Project CARS (PC?), World of Speed (PC) */ //"sTOoeJXI2LjK8jBMOk8h5IDRNZl3jq3I" +static const uint8_t key_sms[] = { 0x73,0x54,0x4F,0x6F,0x65,0x4A,0x58,0x49,0x32,0x4C,0x6A,0x4B,0x38,0x6A,0x42,0x4D,0x4F,0x6B,0x38,0x68,0x35,0x49,0x44,0x52,0x4E,0x5A,0x6C,0x33,0x6A,0x71,0x33,0x49 }; + +/* Ghost in the Shell: First Assault (PC) */ //"%lAn2{Pi*Lhw3T}@7*!kV=?qS$@iNlJ" +static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,0x4C,0x68,0x77,0x33,0x54,0x7D,0x40,0x37,0x2A,0x21,0x6B,0x56,0x3D,0x3F,0x71,0x53,0x24,0x40,0x69,0x4E,0x6C,0x4A }; + +/* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz" +static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A }; + +/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" +static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; + +/* Mortal Kombat X */ +static const uint8_t key_mkx[] = { 0x99,0x61,0x64,0xB5,0xFC,0x0F,0x40,0x29,0x83,0xF6,0x1F,0x22,0x0B,0xB5,0x1D,0xC6 }; + +/* Xian Xia Chuan (PC) */ //"gat@tcqs2010" +static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 }; + +/* Mirror War Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev" +static const uint8_t key_mwr[] = { 0x6C,0x6F,0x67,0x69,0x63,0x73,0x6F,0x75,0x6E,0x64,0x64,0x65,0x73,0x69,0x67,0x6E,0x6D,0x77,0x73,0x64,0x65,0x76 }; + +/* Need for Speed Shift 2 Unleashed (PC demo?) */ //"p&oACY^c4LK5C2v^x5nIO6kg5vNH$tlj" +static const uint8_t key_n2u[] = { 0x70,0x26,0x6F,0x41,0x43,0x59,0x5E,0x63,0x34,0x4C,0x4B,0x35,0x43,0x32,0x76,0x5E,0x78,0x35,0x6E,0x49,0x4F,0x36,0x6B,0x67,0x35,0x76,0x4E,0x48,0x24,0x74,0x6C,0x6A }; + +/* Critter Crunch, Superbrothers: Sword & Sworcery */ //"j1$Mk0Libg3#apEr42mo" +static const uint8_t key_ccr[] = { 0x6A,0x31,0x24,0x4D,0x6B,0x30,0x4C,0x69,0x62,0x67,0x33,0x23,0x61,0x70,0x45,0x72,0x34,0x32,0x6D,0x6F }; + +/* Cyphers */ //"@kdj43nKDN^k*kj3ndf02hd95nsl(NJG" +static const uint8_t key_cyp[] = { 0x40,0x6B,0x64,0x6A,0x34,0x33,0x6E,0x4B,0x44,0x4E,0x5E,0x6B,0x2A,0x6B,0x6A,0x33,0x6E,0x64,0x66,0x30,0x32,0x68,0x64,0x39,0x35,0x6E,0x73,0x6C,0x28,0x4E,0x4A,0x47 }; + +/* Xuan Dou Zhi Wang / King of Combat */ //"Xiayuwu69252.Sonicli81223#$*@*0" +static const uint8_t key_xdz[] = { 0x58,0x69,0x61,0x79,0x75,0x77,0x75,0x36,0x39,0x32,0x35,0x32,0x2E,0x53,0x6F,0x6E,0x69,0x63,0x6C,0x69,0x38,0x31,0x32,0x32,0x33,0x23,0x24,0x2A,0x40,0x2A,0x30 }; + +/* Ji Feng Zhi Ren / Kritika Online */ //"kri_tika_5050_" +static const uint8_t key_jzz[] = { 0x6B,0x72,0x69,0x5F,0x74,0x69,0x6B,0x61,0x5F,0x35,0x30,0x35,0x30,0x5F }; + +/* Invisible Inc. */ //"mint78run52" +static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,0x35,0x32 }; + +/* Guitar Hero 3 */ //"5atu6w4zaw" +static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; + +// Unknown: +// - Battle: Los Angeles +// - Guitar Hero: Warriors of Rock, DJ hero FSB +// - Longmenkezhan +// - Gas Guzzlers: Combat Carnage (PC?) "C5FA83EA64B34EC2BFE" hex or text? [FSB5] + +static const fsbkey_info fsbkey_list[] = { + { 0,0, sizeof(key_dj2),key_dj2 }, + { 0,0, sizeof(key_dfp),key_dfp }, + { 1,0, sizeof(key_dfp),key_dfp },//untested + { 1,1, sizeof(key_dfp),key_dfp },//untested + { 1,0, sizeof(key_npp),key_npp }, + { 1,0, sizeof(key_sms),key_sms }, + { 1,0, sizeof(key_gfs),key_gfs }, + { 1,0, sizeof(key_rev),key_rev }, + { 1,0, sizeof(key_ds3),key_ds3 },//untested + { 1,1, sizeof(key_ds3),key_ds3 }, + { 1,0, sizeof(key_mkx),key_mkx },//untested + { 1,1, sizeof(key_mkx),key_mkx },//untested + { 0,0, sizeof(key_xxc),key_xxc },//untested + { 0,1, sizeof(key_xxc),key_xxc },//untested + { 1,0, sizeof(key_xxc),key_xxc },//untested + { 1,1, sizeof(key_xxc),key_xxc },//untested + { 0,0, sizeof(key_mwr),key_mwr },//untested + { 0,1, sizeof(key_mwr),key_mwr },//untested + { 1,0, sizeof(key_mwr),key_mwr },//untested + { 1,1, sizeof(key_mwr),key_mwr },//untested + { 0,0, sizeof(key_n2u),key_n2u },//untested + { 0,1, sizeof(key_n2u),key_n2u },//untested + { 1,0, sizeof(key_n2u),key_n2u },//untested + { 1,1, sizeof(key_n2u),key_n2u },//untested + { 0,0, sizeof(key_ccr),key_ccr },//untested + { 0,1, sizeof(key_ccr),key_ccr },//untested + { 1,0, sizeof(key_ccr),key_ccr },//untested + { 1,1, sizeof(key_ccr),key_ccr },//untested + { 0,0, sizeof(key_cyp),key_cyp },//untested + { 0,1, sizeof(key_cyp),key_cyp },//untested + { 1,0, sizeof(key_cyp),key_cyp },//untested + { 1,1, sizeof(key_cyp),key_cyp },//untested + { 0,0, sizeof(key_xdz),key_xdz },//untested + { 0,1, sizeof(key_xdz),key_xdz },//untested + { 1,0, sizeof(key_xdz),key_xdz },//untested + { 1,1, sizeof(key_xdz),key_xdz },//untested + { 0,0, sizeof(key_jzz),key_jzz },//untested + { 0,1, sizeof(key_jzz),key_jzz },//untested + { 1,0, sizeof(key_jzz),key_jzz },//untested + { 1,1, sizeof(key_jzz),key_jzz },//untested + { 0,0, sizeof(key_inv),key_inv },//untested + { 0,1, sizeof(key_inv),key_inv },//untested + { 1,0, sizeof(key_inv),key_inv },//untested + { 1,1, sizeof(key_inv),key_inv },//untested + { 0,0, sizeof(key_gh3),key_gh3 },//untested + { 0,1, sizeof(key_gh3),key_gh3 },//untested + { 1,0, sizeof(key_gh3),key_gh3 },//untested + { 1,1, sizeof(key_gh3),key_gh3 },//untested + +}; +static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); + + +#endif /* _FSB_KEYS_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c index c334ad5cd..9002fdd20 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c @@ -6,7 +6,7 @@ typedef enum { XMA2, ATRAC9 } gtd_codec; /* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, chunk_offset, stpr_offset, name_offset = 0; + off_t start_offset, chunk_offset, stpr_offset, name_offset = 0, loop_start_offset, loop_end_offset; size_t data_size, chunk_size; int loop_flag, channel_count, sample_rate; int num_samples, loop_start_sample, loop_end_sample; @@ -48,6 +48,9 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { start_offset = 0x34 + read_32bitLE(0x30,streamFile); channel_count = read_32bitLE(0x10,streamFile); sample_rate = read_32bitLE(0x14,streamFile); + loop_start_offset = read_32bitLE(0x1c, streamFile); + loop_end_offset = read_32bitLE(0x20, streamFile); + loop_flag = loop_end_offset > loop_start_offset; at9_config_data = read_32bitBE(0x28,streamFile); /* 0x18-0x28: fixed/unknown values */ @@ -105,7 +108,10 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - + if (loop_flag) { + vgmstream->loop_start_sample = atrac9_bytes_to_samples(loop_start_offset - start_offset, vgmstream->codec_data); + vgmstream->loop_end_sample = atrac9_bytes_to_samples(loop_end_offset - start_offset, vgmstream->codec_data); + } vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data); break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 102614604..fcd599be5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -54,7 +54,7 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { /* find decryption key in external file or preloaded list */ { uint8_t keybuf[8]; - if ( read_key_file(keybuf, 8, streamFile) ) { + if (read_key_file(keybuf, 8, streamFile) == 8) { ciphKey2 = get_32bitBE(keybuf+0); ciphKey1 = get_32bitBE(keybuf+4); } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index d83949070..231307cda 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -222,6 +222,12 @@ static const hcakey_info hcakey_list[] = { // Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) {4902201417679}, // 0000047561F95FCF + // Kai-ri-Sei Million Arthur (Vita) + {1782351729464341796}, // 18BC2F7463867524 + + // Dx2 Shin Megami Tensei Liberation (iOS/Android) + {118714477}, // 000000000713706D + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kma9.c b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c new file mode 100644 index 000000000..16f2e7835 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c @@ -0,0 +1,79 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* KMA9 - Koei Tecmo's custom ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ +VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t stream_size; + int loop_flag, channel_count; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + + + /* check extension */ + if ( !check_extensions(streamFile,"km9") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x4B4D4139) /* "KMA9" */ + goto fail; + + start_offset = read_32bitLE(0x04,streamFile); + channel_count = read_16bitLE(0x32,streamFile); + loop_flag = (read_32bitLE(0x28,streamFile) != 0); + + total_subsongs = read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* 0x0c: unknown */ + stream_size = read_32bitLE(0x14,streamFile); /* per subsong */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x34,streamFile); + vgmstream->num_samples = read_32bitLE(0x18,streamFile); /* without skip_samples? */ + vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */ + vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */ + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_KMA9; + +#ifdef VGM_USE_ATRAC9 + { + atrac9_config cfg = {0}; + + cfg.type = ATRAC9_KMA9; + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(0x5c,streamFile); + cfg.encoder_delay = read_32bitLE(0x20,streamFile); + + cfg.interleave_skip = read_32bitLE(0x10,streamFile); /* 1 superframe */ + cfg.subsong_skip = total_subsongs; + start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + if (loop_flag) { /* seems correct but must double check */ + vgmstream->loop_start_sample -= cfg.encoder_delay; + //vgmstream->loop_end_sample -= cfg.encoder_delay; + } + } +#else + goto fail; +#endif + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 632285945..9884b3b42 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -6,7 +6,8 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int loop_flag, channel_count; - int32_t loop_length; + int8_t version; + int32_t loop_length, coef_start_offset, coef_spacing; off_t start_offset; if (!check_extensions(streamFile, "ktss")) @@ -15,6 +16,19 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { if (read_32bitBE(0, streamFile) != 0x4B545353) /* "KTSS" */ goto fail; + /* check type details */ + version = read_8bit(0x22, streamFile); + if (version == 1) { + coef_start_offset = 0x40; + coef_spacing = 0x2e; + } + else if (version == 3) { // Fire Emblem Warriors (Switch) + coef_start_offset = 0x5c; + coef_spacing = 0x60; + } + else + goto fail; + loop_length = read_32bitLE(0x38, streamFile); loop_flag = loop_length > 0; channel_count = read_8bit(0x29, streamFile); @@ -35,7 +49,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x8; - dsp_read_coefs_le(vgmstream, streamFile, 0x40, 0x2e); + dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing); start_offset = read_32bitLE(0x24, streamFile) + 0x20; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) @@ -46,4 +60,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index cd7a78c7a..8088dc7c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -99,11 +99,14 @@ typedef struct { meta_t meta_type; layout_t layout_type; - /* XOR setup (SCD) */ - int decryption_enabled; - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); + off_t stream_size; + int total_subsongs; + + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; + uint32_t sngw_xor; } vgm_vorbis_info_t; @@ -690,4 +693,17 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mogg(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile); #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c b/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c index a95a18f1d..9b6bd83fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c @@ -8,10 +8,11 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { off_t start_offset; int loop_flag = 0, channel_count; int num_samples = 0, loop_start = 0, loop_end = 0; - off_t offset = 0; + off_t offset = 0, data_offset; + size_t data_size; /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"opus,lopus")) /* no relation to Ogg Opus */ + if ( !check_extensions(streamFile,"opus,lopus,nop")) /* no relation to Ogg Opus */ goto fail; /* variations, maybe custom */ @@ -32,17 +33,33 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { loop_start = read_32bitLE(0x08,streamFile); loop_end = read_32bitLE(0x0c,streamFile); } + else if (read_32bitBE(0x00, streamFile) == 0x73616466 && read_32bitBE(0x08, streamFile) == 0x6f707573) { /* Xenoblade Chronicles 2 */ + offset = read_32bitLE(0x1c, streamFile); + + num_samples = read_32bitLE(0x28, streamFile); + loop_flag = read_8bit(0x19, streamFile); + if (loop_flag) { + loop_start = read_32bitLE(0x2c, streamFile); + loop_end = read_32bitLE(0x30, streamFile); + } + } else { offset = 0x00; } - if (read_32bitBE(offset + 0x00,streamFile) != 0x01000080) + if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) goto fail; + + channel_count = read_8bit(offset + 0x09, streamFile); + /* 0x0a: packet size if CBR, 0 if VBR */ + data_offset = offset + read_32bitLE(offset + 0x10, streamFile); - start_offset = offset + 0x28; - channel_count = read_8bit(offset + 0x09,streamFile); /* assumed */ - /* 0x0a: packet size if CBR?, other values: no idea */ + if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) + goto fail; + + data_size = read_32bitLE(data_offset + 0x04, streamFile); + start_offset = data_offset + 0x08; loop_flag = (loop_end > 0); /* -1 when not set */ @@ -60,10 +77,9 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG { uint8_t buf[0x100]; - size_t bytes, skip, data_size; + size_t bytes, skip; ffmpeg_custom_config cfg; - data_size = get_streamfile_size(streamFile) - start_offset; skip = 0; //todo bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c new file mode 100644 index 000000000..a0a72ff22 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -0,0 +1,453 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_VORBIS +#include +#include +#include "meta.h" +#include + +#define OGG_DEFAULT_BITSTREAM 0 + +static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void * datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + size_t bytes_read, items_read; + + off_t real_offset = ov_streamfile->start + ov_streamfile->offset; + size_t max_bytes = size * nmemb; + + /* clamp for virtual filesize */ + if (max_bytes > ov_streamfile->size - ov_streamfile->offset) + max_bytes = ov_streamfile->size - ov_streamfile->offset; + + bytes_read = read_streamfile(ptr, real_offset, max_bytes, ov_streamfile->streamfile); + items_read = bytes_read / size; + + /* may be encrypted */ + if (ov_streamfile->decryption_callback) { + ov_streamfile->decryption_callback(ptr, size, items_read, ov_streamfile); + } + + ov_streamfile->offset += items_read * size; + + return items_read; +} + +static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + ogg_int64_t base_offset, new_offset; + + switch (whence) { + case SEEK_SET: + base_offset = 0; + break; + case SEEK_CUR: + base_offset = ov_streamfile->offset; + break; + case SEEK_END: + base_offset = ov_streamfile->size; + break; + default: + return -1; + break; + } + + + new_offset = base_offset + offset; + if (new_offset < 0 || new_offset > ov_streamfile->size) { + return -1; /* *must* return -1 if stream is unseekable */ + } else { + ov_streamfile->offset = new_offset; + return 0; + } +} + +static long ov_tell_func(void * datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + return ov_streamfile->offset; +} + +static int ov_close_func(void * datasource) { + /* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work + * (closing the streamfile is done in free_ogg_vorbis) */ + return 0; +} + + +static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* first 0x800 bytes are xor'd with 0xff */ + if (ov_streamfile->offset < 0x800) { + int num_crypt = 0x800 - ov_streamfile->offset; + if (num_crypt > bytes_read) + num_crypt = bytes_read; + + for (i = 0; i < num_crypt; i++) + ((uint8_t*)ptr)[i] ^= 0xff; + } +} + +static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* first 0x100 bytes are xor'd with offset */ + if (ov_streamfile->offset < 0x100) { + int max_offset = ov_streamfile->offset + bytes_read; + if (max_offset > 0x100) + max_offset = 0x100; + + for (i = ov_streamfile->offset; i < max_offset; i++) { + ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; + } + } +} + +static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + int i; + + /* add 0x23 ('#') */ + { + for (i = 0; i < bytes_read; i++) + ((uint8_t*)ptr)[i] += 0x23; + } +} + +static void sngw_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + char *header_id = "OggS"; + uint8_t key[4]; + + put_32bitBE(key, ov_streamfile->sngw_xor); + + /* bytes are xor'd with key and nibble-swapped */ + { + for (i = 0; i < bytes_read; i++) { + if (ov_streamfile->offset+i < 0x04) { + /* replace key in the first 4 bytes with "OggS" */ + ((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4]; + } + else { + uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4]; + ((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f); + } + } + } +} + +static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + static const uint8_t key[16] = { + 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00 + }; + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* bytes are xor'd with key */ + { + for (i = 0; i < bytes_read; i++) + ((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % 16]; + } +} + + +/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ +VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { + char filename[PATH_LIMIT]; + vgm_vorbis_info_t inf = {0}; + off_t start_offset = 0; + + int is_ogg = 0; + int is_um3 = 0; + int is_kovs = 0; + int is_psychic = 0; + int is_sngw = 0; + int is_isd = 0; + + + /* check extension */ + if (check_extensions(streamFile,"ogg,logg")) { /* .ogg: standard/psychic, .logg: renamed for plugins */ + is_ogg = 1; + } else if (check_extensions(streamFile,"um3")) { + is_um3 = 1; + } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ + is_kovs = 1; + } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Devil May Cry 4 SE (PC), Biohazard 6 (PC) */ + is_sngw = 1; + } else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */ + is_isd = 1; + } else { + goto fail; + } + streamFile->get_name(streamFile,filename,sizeof(filename)); + + /* check standard Ogg Vorbis */ + if (is_ogg) { + + /* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */ + if (read_32bitBE(0x00,streamFile) == 0x2c444430) { + is_psychic = 1; + inf.decryption_callback = psychic_ogg_decryption_callback; + } + else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + goto fail; /* not known (ex. Wwise) */ + } + } + + /* check "Ultramarine3" (???), may be encrypted */ + if (is_um3) { + if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + inf.decryption_callback = um3_ogg_decryption_callback; + } + } + + /* check KOVS (Koei Tecmo games), encrypted and has an actual header */ + if (is_kovs) { + if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ + goto fail; + } + inf.loop_start = read_32bitLE(0x08,streamFile); + inf.loop_flag = (inf.loop_start != 0); + inf.decryption_callback = kovs_ogg_decryption_callback; + + start_offset = 0x20; + } + + /* check SNGW (Capcom's MT Framework PC games), may be encrypted */ + if (is_sngw) { + if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + inf.sngw_xor = read_32bitBE(0x00,streamFile); + inf.decryption_callback = sngw_ogg_decryption_callback; + } + } + + /* check ISD (Gunvolt PC) */ + if (is_isd) { + inf.decryption_callback = isd_ogg_decryption_callback; + + //todo looping unknown, not in Ogg comments + // game has sound/GV_steam.* files with info about sound/stream/*.isd + //- .ish: constant id/names + //- .isl: unknown table, maybe looping? + //- .isf: format table, ordered like file numbers, 0x18 header with: + // 0x00(2): ?, 0x02(2): channels, 0x04: sample rate, 0x08: skip samples (in PCM bytes), always 32000 + // 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) + } + + + if (is_um3) { + inf.meta_type = meta_OGG_UM3; + } else if (is_kovs) { + inf.meta_type = meta_OGG_KOVS; + } else if (is_psychic) { + inf.meta_type = meta_OGG_PSYCHIC; + } else if (is_sngw) { + inf.meta_type = meta_OGG_SNGW; + } else if (is_isd) { + inf.meta_type = meta_OGG_ISD; + } else { + inf.meta_type = meta_OGG_VORBIS; + } + inf.layout_type = layout_ogg_vorbis; + + return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + +fail: + return NULL; +} + +VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t start, const vgm_vorbis_info_t *vgm_inf) { + VGMSTREAM * vgmstream = NULL; + ogg_vorbis_codec_data * data = NULL; + OggVorbis_File *ovf = NULL; + vorbis_info *vi; + + int loop_flag = vgm_inf->loop_flag; + int32_t loop_start = vgm_inf->loop_start; + int loop_length_found = vgm_inf->loop_length_found; + int32_t loop_length = vgm_inf->loop_length; + int loop_end_found = vgm_inf->loop_end_found; + int32_t loop_end = vgm_inf->loop_end; + size_t stream_size = vgm_inf->stream_size ? + vgm_inf->stream_size : + get_streamfile_size(streamFile) - start; + + ov_callbacks default_callbacks; + + if (!callbacks_p) { + default_callbacks.read_func = ov_read_func; + default_callbacks.seek_func = ov_seek_func; + default_callbacks.close_func = ov_close_func; + default_callbacks.tell_func = ov_tell_func; + + callbacks_p = &default_callbacks; + } + + /* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */ + { + OggVorbis_File temp_ovf; + ogg_vorbis_streamfile temp_streamfile; + + temp_streamfile.streamfile = streamFile; + + temp_streamfile.start = start; + temp_streamfile.offset = 0; + temp_streamfile.size = stream_size; + + temp_streamfile.decryption_callback = vgm_inf->decryption_callback; + temp_streamfile.scd_xor = vgm_inf->scd_xor; + temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; + temp_streamfile.sngw_xor = vgm_inf->sngw_xor; + + /* open the ogg vorbis file for testing */ + memset(&temp_ovf, 0, sizeof(temp_ovf)); + if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p)) + goto fail; + + /* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */ + ov_clear(&temp_ovf); + } + + + /* proceed to init codec_data and reopen a STREAMFILE for this stream */ + { + data = calloc(1,sizeof(ogg_vorbis_codec_data)); + if (!data) goto fail; + + data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!data->ov_streamfile.streamfile) goto fail; + + data->ov_streamfile.start = start; + data->ov_streamfile.offset = 0; + data->ov_streamfile.size = stream_size; + + data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; + data->ov_streamfile.scd_xor = vgm_inf->scd_xor; + data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; + data->ov_streamfile.sngw_xor = vgm_inf->sngw_xor; + + /* open the ogg vorbis file for real */ + if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p)) + goto fail; + ovf = &data->ogg_vorbis_file; + } + + /* get info from bitstream 0 */ + data->bitstream = OGG_DEFAULT_BITSTREAM; + vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM); + + /* search for loop comments */ + { + int i; + vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM); + + for (i = 0; i < comment->comments; i++) { + const char * user_comment = comment->user_comments[i]; + if (strstr(user_comment,"loop_start=")==user_comment || /* PSO4 */ + strstr(user_comment,"LOOP_START=")==user_comment || /* PSO4 */ + strstr(user_comment,"COMMENT=LOOPPOINT=")==user_comment || + strstr(user_comment,"LOOPSTART=")==user_comment || + strstr(user_comment,"um3.stream.looppoint.start=")==user_comment || + strstr(user_comment,"LOOP_BEGIN=")==user_comment || /* Hatsune Miku: Project Diva F (PS3) */ + strstr(user_comment,"LoopStart=")==user_comment) { /* Devil May Cry 4 (PC) */ + loop_start = atol(strrchr(user_comment,'=')+1); + loop_flag = (loop_start >= 0); + } + else if (strstr(user_comment,"LOOPLENGTH=")==user_comment) {/* (LOOPSTART pair) */ + loop_length = atol(strrchr(user_comment,'=')+1); + loop_length_found = 1; + } + else if (strstr(user_comment,"title=-lps")==user_comment) { /* Memories Off #5 (PC) */ + loop_start = atol(user_comment+10); + loop_flag = (loop_start >= 0); + } + else if (strstr(user_comment,"album=-lpe")==user_comment) { /* (title=-lps pair) */ + loop_end = atol(user_comment+10); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"LoopEnd=")==user_comment) { /* (LoopStart pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } + } + else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* (LOOP_BEGIN pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } + } + else if (strstr(user_comment,"lp=")==user_comment) { + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"LOOPDEFS=")==user_comment) { /* Fairy Fencer F: Advent Dark Force */ + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"COMMENT=loop(")==user_comment) { /* Zero Time Dilemma (PC) */ + sscanf(strrchr(user_comment,'(')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(vi->channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->codec_data = data; /* store our fun extra datas */ + vgmstream->channels = vi->channels; + vgmstream->sample_rate = vi->rate; + vgmstream->num_streams = vgm_inf->total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + if (loop_length_found) + vgmstream->loop_end_sample = loop_start+loop_length; + else if (loop_end_found) + vgmstream->loop_end_sample = loop_end; + else + vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->loop_flag = loop_flag; + + if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->coding_type = coding_ogg_vorbis; + vgmstream->layout_type = vgm_inf->layout_type; + vgmstream->meta_type = vgm_inf->meta_type; + + return vgmstream; + +fail: + /* clean up anything we may have opened */ + if (data) { + if (ovf) + ov_clear(&data->ogg_vorbis_file);//same as ovf + if (data->ov_streamfile.streamfile) + close_streamfile(data->ov_streamfile.streamfile); + free(data); + } + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } + return NULL; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c deleted file mode 100644 index 02aaade46..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c +++ /dev/null @@ -1,461 +0,0 @@ -#include "../vgmstream.h" - -#ifdef VGM_USE_VORBIS - -#include -#include -#include "meta.h" -#include "../util.h" -#include - - -#define DEFAULT_BITSTREAM 0 - -static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_um3(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x800 bytes of um3 are xor'd with 0xff */ - if (ov_streamfile->offset < 0x800) { - int num_crypt = 0x800-ov_streamfile->offset; - int i; - - if (num_crypt > bytes_read) num_crypt=bytes_read; - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static size_t read_func_kovs(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset+ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x100 bytes of KOVS are xor'd with offset */ - if (ov_streamfile->offset < 0x100) { - int i; - - for (i=ov_streamfile->offset;i<0x100;i++) - ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; - } - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* may be encrypted */ - if (ov_streamfile->decryption_enabled) { - ov_streamfile->decryption_callback(ptr, size, nmemb, ov_streamfile, bytes_read); - } - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_psych(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - size_t i; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - /* add 0x23 ('#') */ - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static int seek_func(void *datasource, ogg_int64_t offset, int whence) { - ogg_vorbis_streamfile * const ov_streamfile = datasource; - ogg_int64_t base_offset; - ogg_int64_t new_offset; - - switch (whence) { - case SEEK_SET: - base_offset = 0; - break; - case SEEK_CUR: - base_offset = ov_streamfile->offset; - break; - case SEEK_END: - base_offset = ov_streamfile->size - ov_streamfile->other_header_bytes; - break; - default: - return -1; - break; - } - - new_offset = base_offset + offset; - if (new_offset < 0 || new_offset > (ov_streamfile->size - ov_streamfile->other_header_bytes)) { - return -1; - } else { - ov_streamfile->offset = new_offset; - return 0; - } -} - -static long tell_func(void * datasource) { - ogg_vorbis_streamfile * const ov_streamfile = datasource; - return ov_streamfile->offset; -} - -/* setting close_func in ov_callbacks to NULL doesn't seem to work */ -static int close_func(void * datasource) { - return 0; -} - -/* Ogg Vorbis, by way of libvorbisfile */ - -VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; - - ov_callbacks callbacks; - - off_t other_header_bytes = 0; - int um3_ogg = 0; - int kovs_ogg = 0; - int psych_ogg = 0; - - vgm_vorbis_info_t inf; - memset(&inf, 0, sizeof(inf)); - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - - /* It is only interesting to use oggs with vgmstream if they are looped. - To prevent such files from being played by other plugins and such they - may be renamed to .logg. This meta reader should still support .ogg, - though. */ - if (strcasecmp("logg",filename_extension(filename)) && - strcasecmp("ogg",filename_extension(filename))) { - if (!strcasecmp("um3",filename_extension(filename))) { - um3_ogg = 1; - } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie, kovs: header id only? */ - kovs_ogg = 1; - } else { - goto fail; - } - } - - /* not all um3-ogg are crypted */ - if (um3_ogg && read_32bitBE(0x0,streamFile)==0x4f676753) { - um3_ogg = 0; - } - - /* use KOVS header */ - if (kovs_ogg) { - if (read_32bitBE(0x0,streamFile)!=0x4b4f5653) { /* "KOVS" */ - goto fail; - } - if (read_32bitLE(0x8,streamFile)!=0) { - inf.loop_start = read_32bitLE(0x8,streamFile); - inf.loop_flag = 1; - } - - other_header_bytes = 0x20; - } - - /* detect Psychic Software obfuscation (as seen in "Darkwind") */ - if (read_32bitBE(0x0,streamFile)==0x2c444430) { - psych_ogg = 1; - } - - if (um3_ogg) { - callbacks.read_func = read_func_um3; - } else if (kovs_ogg) { - callbacks.read_func = read_func_kovs; - } else if (psych_ogg) { - callbacks.read_func = read_func_psych; - } else { - callbacks.read_func = read_func; - } - callbacks.seek_func = seek_func; - callbacks.close_func = close_func; - callbacks.tell_func = tell_func; - - if (um3_ogg) { - inf.meta_type = meta_OGG_UM3; - } else if (kovs_ogg) { - inf.meta_type = meta_OGG_KOVS; - } else if (psych_ogg) { - inf.meta_type = meta_OGG_PSYCH; - } else { - inf.meta_type = meta_OGG_VORBIS; - } - - inf.layout_type = layout_ogg_vorbis; - - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, &callbacks, other_header_bytes, &inf); - -fail: - return NULL; -} - -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) { - VGMSTREAM * vgmstream = NULL; - - OggVorbis_File temp_ovf; - ogg_vorbis_streamfile temp_streamfile; - - ogg_vorbis_codec_data * data = NULL; - OggVorbis_File *ovf; - int inited_ovf = 0; - vorbis_info *info; - - int loop_flag = vgm_inf->loop_flag; - int32_t loop_start = vgm_inf->loop_start; - int loop_length_found = vgm_inf->loop_length_found; - int32_t loop_length = vgm_inf->loop_length; - int loop_end_found = vgm_inf->loop_end_found; - int32_t loop_end = vgm_inf->loop_end; - - ov_callbacks default_callbacks; - - if (!callbacks_p) { - default_callbacks.read_func = read_func; - default_callbacks.seek_func = seek_func; - default_callbacks.close_func = close_func; - default_callbacks.tell_func = tell_func; - - if (vgm_inf->decryption_enabled) { - default_callbacks.read_func = read_func_scd; - } - - callbacks_p = &default_callbacks; - } - - temp_streamfile.streamfile = streamFile; - temp_streamfile.offset = 0; - temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); - temp_streamfile.other_header_bytes = other_header_bytes; - temp_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - temp_streamfile.decryption_callback = vgm_inf->decryption_callback; - temp_streamfile.scd_xor = vgm_inf->scd_xor; - temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - - /* can we open this as a proper ogg vorbis file? */ - memset(&temp_ovf, 0, sizeof(temp_ovf)); - if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, - 0, *callbacks_p)) goto fail; - - /* we have to close this as it has the init_vgmstream meta-reading - STREAMFILE */ - ov_clear(&temp_ovf); - - /* proceed to open a STREAMFILE just for this stream */ - data = calloc(1,sizeof(ogg_vorbis_codec_data)); - if (!data) goto fail; - - data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!data->ov_streamfile.streamfile) goto fail; - data->ov_streamfile.offset = 0; - data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); - data->ov_streamfile.other_header_bytes = other_header_bytes; - data->ov_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; - data->ov_streamfile.scd_xor = vgm_inf->scd_xor; - data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - - /* open the ogg vorbis file for real */ - if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, - 0, *callbacks_p)) goto fail; - ovf = &data->ogg_vorbis_file; - inited_ovf = 1; - - data->bitstream = DEFAULT_BITSTREAM; - - info = ov_info(ovf,DEFAULT_BITSTREAM); - - /* grab the comments */ - { - int i; - vorbis_comment *comment; - - comment = ov_comment(ovf,DEFAULT_BITSTREAM); - - /* search for a "loop_start" comment */ - for (i=0;icomments;i++) { - if (strstr(comment->user_comments[i],"loop_start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_START=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOPSTART=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"um3.stream.looppoint.start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_BEGIN=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LoopStart=")== - comment->user_comments[i] - ) { - loop_start=atol(strrchr(comment->user_comments[i],'=')+1); - if (loop_start >= 0) - loop_flag=1; - } - else if (strstr(comment->user_comments[i],"LOOPLENGTH=")== - comment->user_comments[i]) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1); - loop_length_found=1; - } - else if (strstr(comment->user_comments[i],"title=-lps")== - comment->user_comments[i]) { - loop_start=atol(comment->user_comments[i]+10); - if (loop_start >= 0) - loop_flag=1; - } - else if (strstr(comment->user_comments[i],"album=-lpe")== - comment->user_comments[i]) { - loop_end=atol(comment->user_comments[i]+10); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"LoopEnd=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } - } - else if (strstr(comment->user_comments[i],"LOOP_END=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } - } - else if (strstr(comment->user_comments[i],"lp=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"LOOPDEFS=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"COMMENT=loop(")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'(')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - } - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(info->channels,loop_flag); - if (!vgmstream) goto fail; - - /* store our fun extra datas */ - vgmstream->codec_data = data; - - /* fill in the vital statistics */ - vgmstream->channels = info->channels; - vgmstream->sample_rate = info->rate; - - /* let's play the whole file */ - vgmstream->num_samples = ov_pcm_total(ovf,-1); - - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - if (loop_length_found) - vgmstream->loop_end_sample = loop_start+loop_length; - else if (loop_end_found) - vgmstream->loop_end_sample = loop_end; - else - vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->loop_flag = loop_flag; - - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - } - vgmstream->coding_type = coding_ogg_vorbis; - vgmstream->layout_type = vgm_inf->layout_type; - vgmstream->meta_type = vgm_inf->meta_type; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (data) { - if (inited_ovf) - ov_clear(&data->ogg_vorbis_file); - if (data->ov_streamfile.streamfile) - close_streamfile(data->ov_streamfile.streamfile); - free(data); - } - if (vgmstream) { - vgmstream->codec_data = NULL; - close_vgmstream(vgmstream); - } - return NULL; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c index d59062431..d32b69e03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c @@ -7,10 +7,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, name_offset = 0; - size_t data_size, chunk_size; + size_t stream_size, chunk_size; int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate; int32_t loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ /* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ @@ -46,14 +46,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { /* check multi-streams */ - total_streams = read_32bitLE(chunk_offset+0x00,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { - off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_stream-1); /* position in FORM */ + off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */ off_t stream_offset, next_stream_offset, data_offset = 0; type = read_8bit(header_offset+0x00, streamHeader); @@ -83,22 +83,22 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!data_offset) goto fail; } - if (target_stream == total_streams) { + if (target_subsong == total_subsongs) { next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader); } else { - off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_stream); + off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong); next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader); } - data_size = next_stream_offset - stream_offset; + stream_size = next_stream_offset - stream_offset; start_offset = data_offset + stream_offset; } /* get stream name (always follows FORM) */ if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */ chunk_offset = 0x10+0x10 + chunk_size + 0x10; - if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) { - name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader); + if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) { + name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader); } } @@ -108,7 +108,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PS2_RXWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -143,10 +144,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { joint_stereo = 0; encoder_delay = 0x0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); if (bytes <= 0) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c index 6e612c09a..1dd04d61d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c @@ -39,20 +39,27 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { } } - /* test first block (except when RIFF) */ + /* test some blocks (except when RIFF) since other .XA/STR may start blank */ if (start_offset == 0) { - int i, j; + int i, j, block; + off_t test_offset = start_offset; + size_t sector_size = (is_blocked ? 0x900 : 0x800); + size_t block_size = 0x80; - /* 0x80 frames for 1 sector (max ~0x800 for ISO mode) */ - for (i = 0; i < (0x800/0x80); i++) { - off_t test_offset = start_offset + (is_blocked ? 0x18 : 0x00) + 0x80*i; + for (block = 0; block < 3; block++) { + test_offset += (is_blocked ? 0x18 : 0x00); /* header */ - /* ADPCM predictors should be 0..3 index */ - for (j = 0; j < 16; j++) { - uint8_t header = read_8bit(test_offset + i, streamFile); - if (((header >> 4) & 0xF) > 3) - goto fail; + for (i = 0; i < (sector_size/block_size); i++) { + /* first 0x10 ADPCM predictors should be 0..3 index */ + for (j = 0; j < 16; j++) { + uint8_t header = read_8bit(test_offset + i, streamFile); + if (((header >> 4) & 0xF) > 3) + goto fail; + } + test_offset += 0x80; } + + test_offset += (is_blocked ? 0x18 : 0x00); /* footer */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 31412d5ec..00520b3f5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -6,6 +6,10 @@ /* RIFF - Resource Interchange File Format, standard container used in many games */ +#ifdef VGM_USE_VORBIS +static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size); +#endif + /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; @@ -76,16 +80,17 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream typedef struct { off_t offset; off_t size; + uint32_t codec; int sample_rate; int channel_count; uint32_t block_size; + int bps; int coding_type; int interleave; } riff_fmt_chunk; static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) { - int codec, bps; int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -97,12 +102,12 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->block_size = read_16bit(current_chunk+0x14,streamFile); fmt->interleave = 0; - bps = read_16bit(current_chunk+0x16,streamFile); - codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); + fmt->bps = read_16bit(current_chunk+0x16,streamFile); + fmt->codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); - switch (codec) { + switch (fmt->codec) { case 0x01: /* PCM */ - switch (bps) { + switch (fmt->bps) { case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; fmt->interleave = 2; @@ -117,17 +122,17 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk break; case 0x02: /* MS ADPCM */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MSADPCM; break; case 0x11: /* MS IMA ADPCM */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; case 0x69: /* MS IMA ADPCM (XBOX) - Rayman Raving Rabbids 2 (PC) */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; @@ -136,9 +141,9 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk if (!check_extensions(streamFile,"med")) goto fail; - if (bps == 4) /* normal MS IMA */ + if (fmt->bps == 4) /* normal MS IMA */ fmt->coding_type = coding_MS_IMA; - else if (bps == 3) /* 3-bit MS IMA, used in a very few files */ + else if (fmt->bps == 3) /* 3-bit MS IMA, used in a very few files */ goto fail; //fmt->coding_type = coding_MS_IMA_3BIT; else goto fail; @@ -150,12 +155,20 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->interleave = 0x12; break; - case 0x5050: /* Ubisoft .sns uses this for DSP */ + case 0x5050: /* Ubisoft LyN engine's DSP */ if (!sns) goto fail; fmt->coding_type = coding_NGC_DSP; fmt->interleave = 0x08; break; +#ifdef VGM_USE_VORBIS + case 0x6771: /* Ogg Vorbis (mode 3+) */ + fmt->coding_type = coding_ogg_vorbis; + break; +#else + goto fail; +#endif + case 0x270: /* ATRAC3 */ #ifdef VGM_USE_FFMPEG fmt->coding_type = coding_FFmpeg; @@ -264,8 +277,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { riff_size = read_32bitLE(0x04,streamFile); file_size = get_streamfile_size(streamFile); + /* for some of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ + if (riff_size+0x08+0x01 == file_size) + riff_size += 0x01; + /* check for truncated RIFF */ - if (file_size < riff_size+8) goto fail; + if (file_size < riff_size+0x08) goto fail; /* read through chunks to verify format and find metadata */ { @@ -273,7 +290,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { while (current_chunk < file_size && current_chunk < riff_size+8) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + size_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + + if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ + chunk_size += (chunk_size%2) ? 0x01 : 0x00; if (current_chunk+8+chunk_size > file_size) goto fail; @@ -370,6 +390,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { goto fail; +#ifdef VGM_USE_VORBIS + /* special case using init_vgmstream_ogg_vorbis */ + if (fmt.coding_type == coding_ogg_vorbis) { + return parse_riff_ogg(streamFile, start_offset, data_size); + } +#endif + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); if (!vgmstream) goto fail; @@ -385,10 +413,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; case coding_L5_555: + if (!mwv) goto fail; vgmstream->num_samples = data_size / 0x12 / fmt.channel_count * 32; /* coefs */ - if (mwv) { + { int i, ch; const int filter_order = 3; int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, streamFile); @@ -415,10 +444,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); break; case coding_NGC_DSP: - //sample_count = dsp_bytes_to_samples(data_size, fmt.channel_count); /* expected from the "fact" chunk */ + if (!sns) goto fail; + if (fact_sample_count <= 0) goto fail; + vgmstream->num_samples = fact_sample_count; + //vgmstream->num_samples = dsp_bytes_to_samples(data_size, fmt.channel_count); /* coefs */ - if (sns) { + { int i, ch; static const int16_t coef[16] = { /* common codebook? */ 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, @@ -433,6 +465,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } break; + #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); @@ -492,11 +525,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { default: goto fail; } - /* .sns uses fact chunk */ - if (sns) { - if (-1 == fact_sample_count) goto fail; - vgmstream->num_samples = fact_sample_count; - } /* coding, layout, interleave */ vgmstream->coding_type = fmt.coding_type; @@ -688,3 +716,98 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +#ifdef VGM_USE_VORBIS +typedef struct { + off_t patch_offset; +} riff_ogg_io_data; + +static size_t riff_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) { + size_t bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* has garbage init Oggs pages, patch bad flag */ + if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes_read) { + VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset\n"); + dest[data->patch_offset - offset] = 0x00; + } + + return bytes_read; +} + +/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ +static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size) { + off_t patch_offset = 0; + size_t real_size = data_size; + + /* initial page flag is repeated and causes glitches in decoders, find bad offset */ + { + off_t offset = start_offset + 0x04+0x02; + off_t offset_limit = start_offset + data_size; /* usually in the first 0x3000 but can be +0x100000 */ + + while (offset < offset_limit) { + if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753 && /* "OggS" */ + read_16bitBE(offset+0x04, streamFile) == 0x0002) { /* start page flag */ + + //todo callback should patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets + if (patch_offset) { + VGM_LOG("RIFF Ogg: found multiple repeated start pages\n"); + return NULL; + } + + patch_offset = offset /*- start_offset*/ + 0x04+0x01; + } + offset++; //todo could be optimized to do OggS page sizes + } + } + + /* last pages don't have the proper flag and confuse decoders, find actual end */ + { + size_t max_size = data_size; + off_t offset_limit = start_offset + data_size - 0x1000; /* not worth checking more, let decoder try */ + off_t offset = start_offset + data_size - 0x1a; + + while (offset > offset_limit) { + if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753) { /* "OggS" */ + if (read_16bitBE(offset+0x04, streamFile) == 0x0004) { /* last page flag */ + real_size = max_size; + break; + } else { + max_size = offset - start_offset; /* ignore bad pages */ + } + } + offset--; + } + } + + /* Soundforge includes fact_samples but should be equal to Ogg samples */ + + /* actual Ogg init with custom callback to patch weirdness */ + { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *custom_streamFile = NULL; + char filename[PATH_LIMIT]; + vgm_vorbis_info_t inf = {0}; + riff_ogg_io_data io_data = {0}; + size_t io_data_size = sizeof(riff_ogg_io_data); + + + inf.layout_type = layout_ogg_vorbis; + inf.meta_type = meta_RIFF_WAVE; + inf.stream_size = real_size; + //inf.loop_flag = 0; /* not observed */ + + io_data.patch_offset = patch_offset; + + custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read); + if (!custom_streamFile) return NULL; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, filename, NULL, start_offset, &inf); + + close_streamfile(custom_streamFile); + + return vgmstream; + } +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index fee8a1709..3594eb45f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { size_t block_size = 0, block_size_total = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int i, total_segments; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; if (!check_extensions(streamFile,"rws")) @@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { * 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */ read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ total_segments = read_32bit(off+0x20,streamFile); - total_streams = read_32bit(off+0x28,streamFile); + total_subsongs = read_32bit(off+0x28,streamFile); /* skip audio file name */ off += 0x50 + get_rws_string_size(off+0x50, streamFile); @@ -58,8 +58,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams" * of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams. * ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */ - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* get segment info, for all streams */ /* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */ @@ -70,9 +70,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get usable segment sizes (usually ok but sometimes > stream_size), per stream */ for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */ - stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile); + stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile); } - off += 0x04 * (total_segments * total_streams); + off += 0x04 * (total_segments * total_subsongs); /* skip segment uuids */ off += 0x10 * total_segments; @@ -85,21 +85,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get stream layout */ /* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) * 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */ - for (i = 0; i < total_streams; i++) { /* get block_sizes */ + for (i = 0; i < total_subsongs; i++) { /* get block_sizes */ block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */ block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */ stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */ } } - off += 0x28 * total_streams; + off += 0x28 * total_subsongs; /* get stream config */ /* 0x04: command?, 0x0c(1): bits per sample, others: null? */ - for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */ + for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */ int prev_codec = 0; - if (i+1 == target_stream) { + if (i+1 == target_subsong) { sample_rate = read_32bit(off+0x00, streamFile); //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ channel_count = read_8bit(off+0x0d, streamFile); @@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */ /* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { coefs_offset = off + 0x1c; } off += 0x60; @@ -120,11 +120,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { } /* skip stream uuids */ - off += 0x10 * total_streams; + off += 0x10 * total_subsongs; /* get stream name */ - for (i = 0; i < total_streams; i++) { - if (i+1 == target_stream) { + for (i = 0; i < total_subsongs; i++) { + if (i+1 == target_subsong) { name_offset = off; } off += get_rws_string_size(off, streamFile); @@ -137,7 +137,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* sometimes it's wrong for no apparent reason (probably a bug in RWS) */ - stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams; + stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs; if (stream_size > stream_size_expected) { VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); stream_size = stream_size_expected; @@ -149,7 +149,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_RWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); @@ -159,10 +160,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->full_block_size = block_size_total; switch(codec) { + case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */ case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ - /* ex. The Legend of Spyro (X360) */ + /* ex. D.i.R.T. - Origin of the Species (PC), The Legend of Spyro (X360) */ vgmstream->coding_type = coding_PCM16_int; - vgmstream->codec_endian = 1; /* big */ + vgmstream->codec_endian = (codec == 0xD01BD217); vgmstream->interleave_block_size = 0x02; /* only used to setup channels */ vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab.c b/Frameworks/vgmstream/vgmstream/src/meta/sab.c index 450fd8d4b..8532e4787 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab.c @@ -8,8 +8,8 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, data_size, loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end; + int total_subsongs, target_subsong = streamFile->stream_index; /* .sab: main, .sob: config/names */ if (!check_extensions(streamFile,"sab")) @@ -20,29 +20,29 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */ - total_streams = is_stream ? 1 : read_32bitLE(0x08,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */ /* stream config */ - codec = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x00,streamFile); - channel_count = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x04,streamFile); - sample_rate = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x08,streamFile); - data_size = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x0c,streamFile); - loop_start = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x10,streamFile); - loop_end = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x14,streamFile); + codec = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile); + channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile); + sample_rate = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile); + stream_size = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile); + loop_start = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile); + loop_end = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile); loop_flag = (loop_end > 0); - start_offset = 0x18 + 0x1c*total_streams; + start_offset = 0x18 + 0x1c*total_subsongs; if (start_offset % align) start_offset += align - (start_offset % align); - start_offset += read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x18,streamFile); + start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile); if (is_stream) { channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */ - data_size *= channel_count; + stream_size *= channel_count; } /* build the VGMSTREAM */ @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SAB; switch(codec) { @@ -60,7 +60,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16); vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16); @@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x10; - vgmstream->num_samples = ps_bytes_to_samples(data_size, vgmstream->channels); + vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); break; @@ -81,7 +81,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = is_stream ? layout_interleave : layout_none; vgmstream->interleave_block_size = is_stream ? align : 0x00; - vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, 0x24*vgmstream->channels, vgmstream->channels); + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_start_sample = ms_ima_bytes_to_samples(loop_start, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_end_sample = ms_ima_bytes_to_samples(loop_end, 0x24*vgmstream->channels, vgmstream->channels); break; @@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; } - get_stream_name(vgmstream->stream_name, streamFile, target_stream); + get_stream_name(vgmstream->stream_name, streamFile, target_subsong); if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 07cfba50d..33c04628c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -2,19 +2,17 @@ #include "../coding/coding.h" -/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in: - * PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle - * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */ +/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; - size_t data_size; + size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, type; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -59,14 +57,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t stream_offset; - chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/ + chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ @@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); - data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; @@ -107,7 +105,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -118,23 +117,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->loop_end_sample -= 1; switch (type) { - case 0x03: /* PS-ADPCM */ + case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; if (is_sgx || is_sgb) { vgmstream->interleave_block_size = 0x10; } else { /* this only seems to happen with SFX */ - vgmstream->interleave_block_size = data_size; + vgmstream->interleave_block_size = stream_size; } break; #ifdef VGM_USE_FFMPEG - case 0x04: { /* ATRAC3plus */ + case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ ffmpeg_codec_data *ffmpeg_data; /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -158,7 +157,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; } #endif - case 0x05: /* Short PS-ADPCM */ + case 0x05: /* Short PS-ADPCM [Afrika (PS3)] */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4; @@ -166,10 +165,10 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG - case 0x06: { /* AC3 */ + case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c new file mode 100644 index 000000000..5ac38d25f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -0,0 +1,82 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension); + +/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */ +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int type, sample_rate; + off_t subfile_offset; + size_t subfile_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"sps")) + goto fail; + + /* mini header */ + type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo + subfile_size = read_32bitLE(0x04,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + /* 0x0a/0b: stereo+loop flags? */ + //num_samples = read_32bitLE(0x0c,streamFile); + subfile_offset = 0x10; + + /* init the VGMSTREAM */ + switch(type) { + case 1: /* .vag */ + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ps2_vag(temp_streamFile); + if (!vgmstream) goto fail; + break; + + case 2: /* .at3 */ + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + //VGM_LOG(vgmstream->num_samples != num_samples, + // "SPS: sps num_samples and subfile num_samples don't match\n"); + //vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3 + vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 2dfa87052..203fbd9d5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -4,20 +4,19 @@ #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); #endif -/* SCD - Square-Enix console games (FF XIII, XIV) */ +/* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - off_t start_offset, tables_offset, headers_offset, meta_offset, post_meta_offset; - int headers_entries; - int32_t stream_size, loop_start, loop_end; + off_t start_offset, tables_offset, meta_offset, post_meta_offset; + int32_t stream_size, subheader_size, loop_start, loop_end; - int target_stream = streamFile->stream_index; - int loop_flag = 0, channel_count, codec_id, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + int loop_flag = 0, channel_count, codec, sample_rate; int aux_chunk_count; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; @@ -25,32 +24,34 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "scd") ) goto fail; + if ( !check_extensions(streamFile, "scd") ) + goto fail; streamFile->get_name(streamFile,filename,sizeof(filename)); - /* SEDB */ - if (read_32bitBE(0x00,streamFile) != 0x53454442) goto fail; - /* SSCF */ - if (read_32bitBE(0x04,streamFile) != 0x53534346) goto fail; + /** main header **/ + if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ + read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */ + goto fail; - /** main header section **/ if (read_32bitBE(0x08,streamFile) == 2 || /* version 2 BE, as seen in FFXIII demo for PS3 */ read_32bitBE(0x08,streamFile) == 3) { /* version 3 BE, as seen in FFXIII for PS3 */ - + //size_offset = 0x14; read_32bit = read_32bitBE; read_16bit = read_16bitBE; - //size_offset = 0x14; - } else if (read_32bitLE(0x08,streamFile) == 3 || /* version 2/3 LE, as seen in FFXIV for PC (and others?) */ - read_32bitLE(0x08,streamFile) == 2) { - + } + else if (read_32bitLE(0x08,streamFile) == 2 || /* version 2/3 LE, as seen in FFXIV for PC (and others) */ + read_32bitLE(0x08,streamFile) == 3) { + //size_offset = 0x10; read_32bit = read_32bitLE; read_16bit = read_16bitLE; - //size_offset = 0x10; - } else goto fail; + } + else { + goto fail; + } - /* 0x0c: probably 0=LE, 1=BE */ - /* 0x0d: unk (always 0x04) */ - tables_offset = read_16bit(0xe,streamFile); + /* 0x0c: probably 0=LE, 1=BE */ + /* 0x0d: unknown (always 0x04) */ + tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */ #if 0 /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */ @@ -59,43 +60,66 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { goto fail; #endif - /** offset tables **/ - /* 0x00: table1_unknown entries */ - /* 0x02: table2_unknown entries */ - /* 0x04: table_headers entries */ - /* 0x06: unknown (varies) */ - /* 0x08: table1_unknown start offset */ - /* 0x0c: table_headers start offset */ - /* 0x10: table2_unknown start offset */ - /* 0x14: unknown (0x0) */ - /* 0x18: unknown offset */ - /* 0x1c: unknown (0x0) */ - headers_entries = read_16bit(tables_offset+0x04,streamFile); - if (target_stream == 0) target_stream = 1; /* auto: default to 1 */ - if (target_stream < 0 || target_stream > headers_entries || headers_entries < 1) goto fail; - headers_offset = read_32bit(tables_offset+0x0c,streamFile); + /** offset tables **/ + /* 0x00(2): table1/4 (unknown) entries */ + /* 0x02(2): table2 (unknown) entries */ + /* 0x04(2): table3 (headers) entries */ + /* 0x06(2): unknown, varies even for clone files */ - /** header table entries (each is an uint32_t offset to stream header) **/ - meta_offset = read_32bit(headers_offset + (target_stream-1)*4,streamFile); + /* (implicit: table1 starts at 0x20) */ + /* 0x08: table2 (unknown) start offset */ + /* 0x0c: table3 (headers) start offset */ + /* 0x10: table4 (unknown) start offset */ + /* 0x14: always null? */ + /* 0x18: table5? (unknown) start offset? */ + /* 0x1c: unknown, often null */ + /* each table entry is an uint32_t offset */ + /* if a table isn't present entries is 0 and offset points to next table */ + + /* find meta_offset in table3 (headers) and total subsongs */ + { + int i; + int headers_entries = read_16bit(tables_offset+0x04,streamFile); + off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile); + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */ + for (i = 0; i < headers_entries; i++) { + off_t header_offset = read_32bit(headers_offset + i*0x04,streamFile); + + if (read_32bit(header_offset+0x0c,streamFile) == -1) + continue; /* codec -1 when dummy */ + + total_subsongs++; + if (!meta_offset && total_subsongs == target_subsong) + meta_offset = header_offset; + } + if (meta_offset == 0) goto fail; + /* SCD can contain 0 entries too */ + } /** stream header **/ - stream_size = read_32bit(meta_offset+0x00,streamFile); - channel_count = read_32bit(meta_offset+0x04,streamFile); - sample_rate = read_32bit(meta_offset+0x08,streamFile); - codec_id = read_32bit(meta_offset+0x0c,streamFile); + stream_size = read_32bit(meta_offset+0x00,streamFile); + channel_count = read_32bit(meta_offset+0x04,streamFile); + sample_rate = read_32bit(meta_offset+0x08,streamFile); + codec = read_32bit(meta_offset+0x0c,streamFile); - loop_start = read_32bit(meta_offset+0x10,streamFile); - loop_end = read_32bit(meta_offset+0x14,streamFile); - loop_flag = (loop_end > 0); - - post_meta_offset = meta_offset + 0x20; - start_offset = post_meta_offset + read_32bit(meta_offset+0x18,streamFile); + loop_start = read_32bit(meta_offset+0x10,streamFile); + loop_end = read_32bit(meta_offset+0x14,streamFile); + subheader_size = read_32bit(meta_offset+0x18,streamFile); aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); - /* 0x01e(e): unknown, seen in some FF XIV sfx (IMA) */ + /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */ + + loop_flag = (loop_end > 0); + post_meta_offset = meta_offset + 0x20; + start_offset = post_meta_offset + subheader_size; /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */ - if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx has 0x01000000 */ + if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */ VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count); goto fail; } @@ -107,73 +131,52 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS - /* special case using init_vgmstream_ogg_vorbis with callbacks */ - if (codec_id == 0x06) { - VGMSTREAM * result = NULL; - uint32_t seek_table_size, vorb_header_size; - uint8_t xor_version, xor_byte; + /* special case using init_vgmstream_ogg_vorbis */ + if (codec == 0x06) { + uint8_t ogg_version, ogg_byte; vgm_vorbis_info_t inf = {0}; - inf.loop_start = loop_start; - inf.loop_end = loop_end; - inf.loop_flag = loop_flag; - inf.loop_end_found = loop_flag; - inf.loop_length_found = 0; inf.layout_type = layout_ogg_vorbis; inf.meta_type = meta_SQEX_SCD; + inf.total_subsongs = total_subsongs; + /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ - /* the following could be simplified but it's not clear what field signals that seek table exists - * (seems that encrypted = always seek table, but maybe post_meta_offset+0x01 = 0x20) */ + ogg_version = read_8bit(post_meta_offset + 0x00, streamFile); + /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */ + ogg_byte = read_8bit(post_meta_offset + 0x02, streamFile); + /* 0x03(1): ? in v3 */ - /* try regular Ogg with default values */ - { - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); - if (result != NULL) - return result; + if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */ + inf.stream_size = stream_size; } + else { /* 0x20 header, then seek table */ + size_t seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); + size_t vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); + /* 0x18(4): ? (can be 0) */ - /* skip seek table and try regular Ogg again */ - { - seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); - vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); - if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != read_32bit(meta_offset+0x18, streamFile)) { - return NULL; - } + if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != subheader_size) + goto fail; - start_offset = post_meta_offset + 0x20 + seek_table_size; + inf.stream_size = vorb_header_size + stream_size; + start_offset = post_meta_offset + 0x20 + seek_table_size; /* subheader_size skips vorb_header */ - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); - if (result != NULL) - return result; - } - - /* try encrypted Ogg (with seek table already skipped) */ - { - xor_version = read_8bit(post_meta_offset + 0x00, streamFile); - xor_byte = read_8bit(post_meta_offset + 0x02, streamFile); - if (xor_byte == 0) - return NULL; /* not actually encrypted, happens but should be handled above */ - - if (xor_version == 2) { /* header is XOR'ed using byte */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v2_callback; - inf.scd_xor = xor_byte; + if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */ + inf.decryption_callback = scd_ogg_v2_decryption_callback; + inf.scd_xor = ogg_byte; inf.scd_xor_length = vorb_header_size; } - else if (xor_version == 3) { /* full file is XOR'ed using table */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v3_callback; - inf.scd_xor = stream_size & 0xFF; /* xor_byte is not used? (also there is data at +0x03) */ - inf.scd_xor_length = stream_size; + else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC) */ + inf.decryption_callback = scd_ogg_v3_decryption_callback; + inf.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ + inf.scd_xor_length = vorb_header_size + stream_size; } else { - VGM_LOG("SCD: unknown encryption 0x%x\n", xor_version); - return NULL; + VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version); } - - /* hope this works */ - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); } + + /* actual Ogg init */ + return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); } #endif @@ -182,21 +185,33 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = headers_entries; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; - switch (codec_id) { + switch (codec) { case 0x01: /* PCM */ - vgmstream->coding_type = coding_PCM16_int; - vgmstream->layout_type = layout_none; - vgmstream->num_samples = stream_size / 2 / channel_count; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); if (loop_flag) { - vgmstream->loop_start_sample = loop_start / 2 / channel_count; - vgmstream->loop_end_sample = loop_end / 2 / channel_count; + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16); + } + break; + + case 0x03: /* PS-ADPCM [Final Fantasy Type-0] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + if (loop_flag) { + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); } break; @@ -234,6 +249,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x0c,streamFile); + /* in post_meta_offset is a WAVEFORMATEX (including coefs and all) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { @@ -324,7 +340,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { break; } - case 0x0E: { /* ATRAC3plus [Lord of Arcana (PSP)] */ + case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ ffmpeg_codec_data *ffmpeg_data = NULL; /* full RIFF header at start_offset/post_meta_offset (same) */ @@ -357,8 +373,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } #endif + case -1: /* used for dummy entries */ default: - VGM_LOG("SCD: unknown codec_id 0x%x\n", codec_id); + VGM_LOG("SCD: unknown codec 0x%x\n", codec); goto fail; } @@ -375,9 +392,14 @@ fail: #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile * ov_streamfile = (ogg_vorbis_streamfile*)datasource; + /* no encryption, sometimes happens */ + if (ov_streamfile->scd_xor == 0x00) + return; + /* header is XOR'd with a constant byte */ if (ov_streamfile->offset < ov_streamfile->scd_xor_length) { int i, num_crypt; @@ -392,9 +414,9 @@ static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, vo } } -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { - /* V3 decryption table found in the .exe */ - static const uint8_t scd_ogg_v3_lookuptable[256] = { /* FF XIV Heavensward */ +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + /* V3 decryption table found in the .exe of FF XIV Heavensward */ + static const uint8_t scd_ogg_v3_lookuptable[256] = { 0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F 0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F 0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F @@ -412,23 +434,25 @@ static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, vo 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF 0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF }; + + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile *ov_streamfile = (ogg_vorbis_streamfile*)datasource; /* file is XOR'd with a table (algorithm and table by Ioncannon) */ - if (ov_streamfile->offset < ov_streamfile->scd_xor_length) { + { //if (ov_streamfile->offset < ov_streamfile->scd_xor_length) int i, num_crypt; - uint8_t byte1, byte2, xorByte; + uint8_t byte1, byte2, xor_byte; num_crypt = bytes_read; byte1 = ov_streamfile->scd_xor & 0x7F; byte2 = ov_streamfile->scd_xor & 0x3F; for (i = 0; i < num_crypt; i++) { - xorByte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; - xorByte &= 0xFF; - xorByte ^= ((uint8_t*)ptr)[i]; - xorByte ^= byte1; - ((uint8_t*)ptr)[i] = (uint8_t)xorByte; + xor_byte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; + xor_byte &= 0xFF; + xor_byte ^= ((uint8_t*)ptr)[i]; + xor_byte ^= byte1; + ((uint8_t*)ptr)[i] = (uint8_t)xor_byte; } } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c new file mode 100644 index 000000000..2c98eaa2b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -0,0 +1,273 @@ +#include "meta.h" +#include "../coding/coding.h" + + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; + size_t stream_size, subheader_size; //, name_size = 0; + + int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; + int is_sab = 0, is_mab = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */ + if ( !check_extensions(streamFile,"sab,mab,sbin")) + goto fail; + + + /** main header **/ + if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */ + is_sab = 1; + } else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */ + is_mab = 1; + } else { + goto fail; + } + + //if (read_8bit(0x04,streamFile) != 0x02) /* version? */ + // goto fail; + /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */ + /* 0x05(1): 0x00/01? */ + /* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */ + if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + /* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */ + if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile)) + goto fail; + /* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */ + + + /** offset tables **/ + if (is_sab) { + if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ + if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ + if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //seq_offset = read_32bit(0x38,streamFile); + //trk_offset = read_32bit(0x48,streamFile); + mtrl_offset = read_32bit(0x58,streamFile); + } + else if (is_mab) { + if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ + if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //inst_offset = read_32bit(0x38,streamFile); + mtrl_offset = read_32bit(0x48,streamFile); + } + else { + goto fail; + } + /* each section starts with: + * 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10 + * 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */ + + /* find meta_offset in mtrl and total subsongs */ + { + int i; + int entries = read_16bit(mtrl_offset+0x04,streamFile); + off_t entries_offset = mtrl_offset + 0x10; + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */ + for (i = 0; i < entries; i++) { + off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile); + + if (read_8bit(entry_offset+0x05,streamFile) == 0) + continue; /* codec 0 when dummy */ + + total_subsongs++; + if (!meta_offset && total_subsongs == target_subsong) + meta_offset = entry_offset; + } + if (meta_offset == 0) goto fail; + /* SAB can contain 0 entries too */ + } + + + /** stream header **/ + /* 0x00(2): 0x00/01? */ + /* 0x02(2): base entry size? (0x20) */ + channel_count = read_8bit(meta_offset+0x04,streamFile); + codec = read_8bit(meta_offset+0x05,streamFile); + //entry_id = read_16bit(meta_offset+0x06,streamFile); + sample_rate = read_32bit(meta_offset+0x08,streamFile); + loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */ + + loop_end = read_32bit(meta_offset+0x10,streamFile); + subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */ + stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ + /* 0x1c: null? */ + + loop_flag = (loop_end > 0); + post_meta_offset = meta_offset + 0x20; + + + /** info section (get stream name) **/ + //if (is_sab) { //todo load name based on entry id + /* "snd ": unknown flags/sizes and name */ + /* 0x08(2): file number within descriptor */ + /* 0x1a(2): base_entry size (-0x10?) */ + //name_size = read_32bit(snd_offset+0x20,streamFile); + //name_offset = snd_offset+0x70; + /* 0x24(4): unique id? (referenced in "seq" section) */ + //} + //else if (is_mab) { + /* "musc": unknown flags sizes and names, another format */ + //} + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; + + switch(codec) { + + case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */ + start_offset = post_meta_offset + subheader_size; + + /* 0x00 (2): null?, 0x02(2): entry size? */ + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile); + + /* much like AKBs, there are slightly different loop values here, probably more accurate + * (if no loop, loop_end doubles as num_samples) */ + vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end + break; + } + +#ifdef VGM_USE_ATRAC9 + case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ + atrac9_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header has various typical ATRAC9 values */ + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); +VGM_LOG("1\n"); + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; +VGM_LOG("2\n"); + vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ + vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */ + mpeg_codec_data *mpeg_data = NULL; + mpeg_custom_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */ + + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } +#endif + + case 0x07: { /* HCA subfile [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */ + //todo there is no easy way to use the HCA decoder; try subfile hack for now + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = post_meta_offset + 0x10; + size_t subfile_size = stream_size + subheader_size - 0x10; + /* post header has 0x10 unknown + HCA header */ + + + temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_hca(temp_streamFile); + if (temp_vgmstream) { + /* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */ + temp_vgmstream->num_streams = vgmstream->num_streams; + temp_vgmstream->stream_size = vgmstream->stream_size; + temp_vgmstream->meta_type = vgmstream->meta_type; + + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return temp_vgmstream; + } + else { + close_streamfile(temp_streamFile); + goto fail; + } + } + + case 0x00: /* dummy entry */ + default: + VGM_LOG("SQEX SEAD: unknown codec %x\n", codec); + goto fail; + } + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index a42ff2876..13801cad6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -7,13 +7,13 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; - size_t chunk_size; + size_t chunk_size, stream_size = 0; int is_separate; int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; uint32_t at9_config_data = 0; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -38,16 +38,16 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */ /* check multi-streams (usually only in SFX containers) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t table_offset, header_offset, stream_offset; /* get target offset using table of relative offsets within WAVE */ - table_offset = chunk_offset + 0x08 + 4*(target_stream-1); + table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); header_offset = table_offset + read_32bitLE(table_offset,streamHeader); /* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */ @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); - /* 0x20(4): data size */ + stream_size = read_32bitLE(header_offset+0x20,streamHeader); stream_offset = read_32bitLE(header_offset+0x24,streamHeader); /* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller). @@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ for (i = 0; i < num_entries; i++) { uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); - if (index+1 == target_stream) { + if (index+1 == target_subsong) { name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader); break; } @@ -116,20 +116,27 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); switch (codec) { - case 0x21: /* HEVAG */ + case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + break; + + case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; #ifdef VGM_USE_ATRAC9 - case 0x42: { /* ATRAC9 */ + case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita)] */ atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; @@ -142,9 +149,9 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { break; } #endif - + //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ default: - VGM_LOG("SXD: unknown codec 0x%x", codec); + VGM_LOG("SXD: unknown codec 0x%x\n", codec); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 27495f354..d0ad0272c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -127,6 +127,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { vgmstream->sample_rate = sb.sample_rate; vgmstream->num_streams = sb.total_streams; + vgmstream->stream_size = sb.stream_size; vgmstream->meta_type = meta_UBI_SB; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vxn.c b/Frameworks/vgmstream/vgmstream/src/meta/vxn.c index 238d3e24f..27aacce09 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vxn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vxn.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples; off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00; size_t stream_size; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ if (!check_extensions(streamFile,"vxn")) @@ -31,13 +31,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { * (the "Plst" and "Rule" chunks may have order info) */ if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */ goto fail; - total_streams = read_32bitLE(chunk_offset+0x00, streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00, streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile); - stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile); - num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile); + stream_offset = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x00, streamFile); + stream_size = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x04, streamFile); + num_samples = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x08, streamFile); if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */ goto fail; @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; - + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_VXN; switch (codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index e4619791a..bf5e97bc2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -233,25 +233,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */ data_offsets = 0x18; block_offsets = 0; /* no need, full headers are present */ - cfg.header_type = TYPE_8; - cfg.packet_type = STANDARD; - cfg.setup_type = HEADER_TRIAD; + cfg.header_type = WWV_TYPE_8; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_HEADER_TRIAD; break; //case 0x32: /* ? */ case 0x34: /* common (2010~2011) */ data_offsets = 0x18; block_offsets = 0x30; - cfg.header_type = TYPE_6; - cfg.packet_type = STANDARD; - cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ + cfg.header_type = WWV_TYPE_6; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */ data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; - cfg.setup_type = EXTERNAL_CODEBOOKS; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; break; default: VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); @@ -277,11 +277,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ - cfg.setup_type = FULL_SETUP; + cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */ - cfg.setup_type = INLINE_CODEBOOKS; + cfg.setup_type = WWV_INLINE_CODEBOOKS; } } @@ -297,13 +297,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x30: data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks * - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too) * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */ - cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ + cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ break; //case 0x2a: /* Rocksmith 2011 X360? */ @@ -326,14 +326,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09 * (maybe lower spec machines = needs simpler packets) */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) - cfg.packet_type = STANDARD; + cfg.packet_type = WWV_STANDARD; } /* try with the selected codebooks */ vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) { /* codebooks failed: try again with the other type */ - cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; + cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS; vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c index 0a84c1e81..382a4e0ad 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" +#include "../layout/layout.h" + static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); @@ -10,12 +11,12 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int loop_flag = 0, channel_count, codec; int big_endian; - int sample_rate, num_samples, multiplier, multistreams = 0; + int sample_rate, num_samples, interleave_factor, multistreams = 0; int total_subsongs = 0, target_subsong = streamFile->stream_index; - off_t start_offset, loop_start, loop_end, chunk_offset; + off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset; off_t first_offset = 0x20; - size_t chunk_size; + size_t chunk_size, stream_size; /* check extension, case insensitive */ if (!check_extensions(streamFile,"xvag")) @@ -37,7 +38,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* 0x08: flags? (&0x01=big endian, 0x02=?, 0x06=full RIFF AT9?) * 0x09: flags2? (0x00/0x01/0x04, speaker mode?) * 0x0a: always 0? - * 0x0b: version-flag? (0x5f/0x60/0x61, last has extra data) */ + * 0x0b: version-flag? (0x5f/0x60/0x61/0x62/etc) */ /* "fmat": base format (always first) */ if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/ @@ -48,14 +49,20 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* 0x0c: samples again? playable section? */ VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); - multiplier = read_32bit(chunk_offset+0x10,streamFile); /* 'interleave factor' */ + interleave_factor = read_32bit(chunk_offset+0x10,streamFile); sample_rate = read_32bit(chunk_offset+0x14,streamFile); - /* 0x18: datasize */ + stream_size = read_32bit(chunk_offset+0x18,streamFile); - /* extra data, seen in MPEG/ATRAC9 */ + /* extra data, seen in versions 0x61+ */ if (chunk_size > 0x1c) { - total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); /* number of interleaved layers */ - multistreams = read_32bit(chunk_offset+0x20,streamFile); /* number of bitstreams per layer (for multistream Nch MPEG/ATRAC9) */ + /* number of interleaved subsong layers */ + total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); + /* number of interleaved bitstreams per layer (multistreams * channels_per_stream = channels) */ + multistreams = read_32bit(chunk_offset+0x20,streamFile); + } + else { + total_subsongs = 1; + multistreams = 1; } if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -66,9 +73,9 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* "cues": cue/labels (rare) */ /* "0000": end chunk before start_offset */ - /* some XVAG seem to do full loops, this should detect them as looping */ + /* some XVAG seem to do full loops, this should detect them as looping (basically tests is last frame is empty) */ //todo remove, looping seems external and specified in Scream Tool's bank formats - if (codec == 0x06) { + if (codec == 0x06 && total_subsongs == 1) { loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); } @@ -79,30 +86,45 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = (stream_size / total_subsongs); vgmstream->meta_type = meta_XVAG; switch (codec) { - //case 0x??: /* PCM? */ case 0x06: /* VAG (PS ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ case 0x07: /* SVAG? (PS ADPCM with extended table): inFamous 1 (PS3) */ - if (total_subsongs > 1 || multistreams > 1) goto fail; - if (multiplier > 1) goto fail; + if (multistreams > 1 && multistreams != vgmstream->channels) goto fail; + if (total_subsongs > 1 && multistreams > 1) goto fail; + if (total_subsongs > 1 && vgmstream->channels > 1) goto fail; /* unknown layout */ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; vgmstream->coding_type = coding_PSX; + if (total_subsongs > 1) { /* God of War 3 (PS4) */ + vgmstream->layout_type = layout_blocked_xvag_subsong; + vgmstream->interleave_block_size = 0x10; + vgmstream->full_block_size = 0x10 * interleave_factor * total_subsongs; + vgmstream->current_block_size = 0x10 * interleave_factor; + start_offset += 0x10 * interleave_factor * (target_subsong-1); + } + else { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10 * interleave_factor; /* usually 1, bigger in GoW3 PS4 */ + } + if (loop_flag) { vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); } + break; #ifdef VGM_USE_MPEG case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ mpeg_custom_config cfg = {0}; - if (total_subsongs > 1 || (multistreams > 1 && multistreams == vgmstream->channels)) goto fail; + /* often 2ch per MPEG and rarely 1ch (GoW3 PS4) */ + if (multistreams > 1 && !(multistreams*1 == vgmstream->channels || multistreams*2 == vgmstream->channels)) goto fail; + if (total_subsongs > 1) goto fail; + //todo rare test file in The Last of Us PS4 uses 6ch with 1 2ch stream, surround MPEG/mp3pro? /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ @@ -110,7 +132,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { goto fail; cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ - cfg.interleave = cfg.chunk_size * multiplier; + cfg.interleave = cfg.chunk_size * interleave_factor; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); if (!vgmstream->codec_data) goto fail; @@ -138,7 +160,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { } else if (total_subsongs > 1) { /* interleaves 'multiplier' superframes per subsong (all share config_data) */ - cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * multiplier; + cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * interleave_factor; cfg.subsong_skip = total_subsongs; /* start in subsong's first superframe */ start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); @@ -158,6 +180,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { } #endif + //case 0x??: /* PCM? */ default: goto fail; } @@ -167,6 +190,9 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; + if (vgmstream->layout_type == layout_blocked_xvag_subsong) + block_update_xvag_subsong(start_offset, vgmstream); + return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 403f0d98d..88335d956 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -9,7 +9,7 @@ /* the x.x version is just to make it clearer, MS only classifies XACT as 1/2/3 */ #define XACT1_0_MAX 1 /* Project Gotham Racing 2 (v1), Silent Hill 4 (v1) */ -#define XACT1_1_MAX 3 /* The King of Fighters 2003 (v3) */ +#define XACT1_1_MAX 3 /* Unreal Championship (v2), The King of Fighters 2003 (v3) */ #define XACT2_0_MAX 34 /* Dead or Alive 4 (v17), Kameo (v23), Table Tennis (v34) */ // v35/36/37 too? #define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too? #define XACT2_2_MAX 41 /* Blue Dragon (v40) */ @@ -354,47 +354,45 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->loop_start_sample = xwb.loop_start_sample; vgmstream->loop_end_sample = xwb.loop_end_sample; vgmstream->num_streams = xwb.streams; + vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile); switch(xwb.codec) { - case PCM: - vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 : + case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ + vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8_U : (xwb.little_endian ? coding_PCM16LE : coding_PCM16BE); vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none; vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02; break; - case XBOX_ADPCM: + case XBOX_ADPCM: /* Silent Hill 4 (Xbox) */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; break; - case MS_ADPCM: + case MS_ADPCM: /* Persona 4 Ultimax (AC) */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/ break; #ifdef VGM_USE_FFMPEG - case XMA1: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA1: { /* Kameo (X360) */ uint8_t buf[100]; int bytes; bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case XMA2: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA2: { /* Blue Dragon (X360) */ uint8_t buf[100]; int bytes, block_size, block_count; @@ -404,15 +402,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case WMA: { /* WMAudio1 (WMA v1) */ + case WMA: { /* WMAudio1 (WMA v1): Prince of Persia 2 port (Xbox) */ ffmpeg_codec_data *ffmpeg_data = NULL; ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); @@ -427,8 +424,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */ - ffmpeg_codec_data *ffmpeg_data = NULL; + case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */ uint8_t buf[100]; int bytes, bps_index, block_align, block_index, avg_bps, wma_codec; @@ -444,15 +440,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case ATRAC3: { /* Techland PS3 extension */ + case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */ uint8_t buf[200]; int bytes; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c new file mode 100644 index 000000000..a9f7b4c54 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c @@ -0,0 +1,108 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */ +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count, codec; + + /* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */ + if ( !check_extensions(streamFile,"xwc")) + goto fail; + + if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */ + read_32bitBE(0x04,streamFile) != 0x00900000) + goto fail; + + data_size = read_32bitLE(0x08, streamFile); /* including subheader */ + channel_count = read_32bitLE(0x0c, streamFile); + /* 0x10: num_samples */ + /* 0x14: 0x8000? */ + codec = read_32bitBE(0x24, streamFile); + /* 0x28: num_samples */ + /* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */ + /* 0x30+: codec dependant */ + loop_flag = 0; /* seemingly not in the file */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = read_32bitLE(0x28, streamFile); + vgmstream->meta_type = meta_XWC; + + switch(codec) { +#ifdef VGM_USE_MPEG + case 0x4D504547: { /* "MPEG" (PS3) */ + mpeg_custom_config cfg = {0}; + + start_offset = 0x800; + vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve + cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28; + + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame; + break; + } +#endif +#ifdef VGM_USE_FFMPEG + case 0x584D4100: { /* "XMA\0" (X360) */ + uint8_t buf[0x100]; + int32_t bytes, seek_size, block_size, block_count, sample_rate; + + seek_size = read_32bitLE(0x30, streamFile); + start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08; + start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */ + + sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile); + block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile); + block_count = read_32bitBE(0x34+seek_size+0x28, streamFile); + /* others: scrambled RIFF fmt BE values */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size - start_offset - 0x28); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = sample_rate; + break; + } + + case 0x564F5242: { /* "VORB" (PC) */ + start_offset = 0x30; + + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, data_size - start_offset - 0x28); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile); + break; + } +#endif + default: + goto fail; + } + + if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */ + VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate); + goto fail; + } + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 97e549927..f62b78422 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -22,10 +22,6 @@ typedef struct { uint8_t * buffer; /* data buffer */ size_t buffersize; /* max buffer size */ size_t filesize; /* cached file size (max offset) */ -#ifdef PROFILE_STREAMFILE - size_t bytes_read; - int error_count; -#endif } STDIOSTREAMFILE; static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); @@ -77,9 +73,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST /* position to new offset */ if (fseeko(streamfile->infile,offset,SEEK_SET)) { streamfile->offset = streamfile->filesize; -#ifdef PROFILE_STREAMFILE - streamfile->error_count++; -#endif return 0; /* fail miserably (fseek shouldn't fail and reach this) */ } streamfile->offset = offset; @@ -94,15 +87,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST length_read = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); streamfile->validsize = length_read; -#ifdef PROFILE_STREAMFILE - if (ferror(streamfile->infile)) { - clearerr(streamfile->infile); - streamfile->error_count++; - } - - streamfile->bytes_read += length_read; -#endif - /* if we can't get enough to satisfy the request (EOF) we give up */ if (length_read < length_to_read) { memcpy(dest,streamfile->buffer,length_read); @@ -154,10 +138,6 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse /* request outside buffer: new fread */ { size_t length_read = read_the_rest(dest,offset,length,streamfile); -#ifdef PROFILE_STREAMFILE - if (length_read < length) - streamfile->error_count++; -#endif return length_read; } } @@ -181,15 +161,6 @@ static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t lengt buffer[length-1]='\0'; } -#ifdef PROFILE_STREAMFILE -static size_t get_bytes_read_stdio(STDIOSTREAMFILE *streamFile) { - return streamFile->bytes_read; -} -static size_t get_error_count_stdio(STDIOSTREAMFILE *streamFile) { - return streamFile->error_count; -} -#endif - static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) { int newfd; FILE *newfile; @@ -237,10 +208,6 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char streamfile->sf.get_realname = (void*)get_name_stdio; streamfile->sf.open = (void*)open_stdio; streamfile->sf.close = (void*)close_stdio; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = (void*)get_bytes_read_stdio; - streamfile->sf.get_error_count = (void*)get_error_count_stdio; -#endif streamfile->infile = infile; streamfile->buffersize = buffersize; @@ -280,6 +247,439 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) { return open_stdio_streamfile_buffer_by_file(file,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } +/* **************************************************** */ + +//todo stream_index: copy? pass? funtion? external? +//todo use realnames on reopen? simplify? +//todo use safe string ops, this ain't easy + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; +} WRAP_STREAMFILE; + +static size_t wrap_read(WRAP_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */ +} +static size_t wrap_get_size(WRAP_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static size_t wrap_get_offset(WRAP_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void wrap_get_realname(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static void wrap_open(WRAP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); /* default (don't wrap) */ +} +static void wrap_close(WRAP_STREAMFILE *streamfile) { + //streamfile->inner_sf->close(streamfile->inner_sf); /* don't close */ + free(streamfile); +} + +STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) { + WRAP_STREAMFILE *this_sf; + + if (!streamfile) return NULL; + + this_sf = calloc(1,sizeof(WRAP_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)wrap_read; + this_sf->sf.get_size = (void*)wrap_get_size; + this_sf->sf.get_offset = (void*)wrap_get_offset; + this_sf->sf.get_name = (void*)wrap_get_name; + this_sf->sf.get_realname = (void*)wrap_get_realname; + this_sf->sf.open = (void*)wrap_open; + this_sf->sf.close = (void*)wrap_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + off_t start; + size_t size; +} CLAMP_STREAMFILE; + +static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { + off_t inner_offset = streamfile->start + offset; + size_t clamp_length = length > (streamfile->size - offset) ? (streamfile->size - offset) : length; + return streamfile->inner_sf->read(streamfile->inner_sf, dest, inner_offset, clamp_length); +} +static size_t clamp_get_size(CLAMP_STREAMFILE *streamfile) { + return streamfile->size; +} +static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf) - streamfile->start; +} +static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + char original_filename[PATH_LIMIT]; + STREAMFILE *new_inner_sf; + + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); + + /* detect re-opening the file */ + if (strcmp(filename, original_filename) == 0) { + return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */ + } else { + return new_inner_sf; /**/ + } +} +static void clamp_close(CLAMP_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile); +} + +STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) { + CLAMP_STREAMFILE *this_sf; + + if (!streamfile || !size) return NULL; + if (start + size > get_streamfile_size(streamfile)) return NULL; + + this_sf = calloc(1,sizeof(CLAMP_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)clamp_read; + this_sf->sf.get_size = (void*)clamp_get_size; + this_sf->sf.get_offset = (void*)clamp_get_offset; + this_sf->sf.get_name = (void*)clamp_get_name; + this_sf->sf.get_realname = (void*)clamp_get_realname; + this_sf->sf.open = (void*)clamp_open; + this_sf->sf.close = (void*)clamp_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + this_sf->start = start; + this_sf->size = size; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + void* data; + size_t data_size; + size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); +} IO_STREAMFILE; + +static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { + return streamfile->read_callback(streamfile->inner_sf, dest, offset, length, streamfile->data); +} +static size_t io_get_size(IO_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static off_t io_get_offset(IO_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + //todo should have some flag to decide if opening other files with IO + STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback); +} +static void io_close(IO_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile->data); + free(streamfile); +} + +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback) { + IO_STREAMFILE *this_sf; + + if (!streamfile) return NULL; + if ((data && !data_size) || (!data && data_size)) return NULL; + + this_sf = calloc(1,sizeof(IO_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)io_read; + this_sf->sf.get_size = (void*)io_get_size; + this_sf->sf.get_offset = (void*)io_get_offset; + this_sf->sf.get_name = (void*)io_get_name; + this_sf->sf.get_realname = (void*)io_get_realname; + this_sf->sf.open = (void*)io_open; + this_sf->sf.close = (void*)io_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + if (data) { + this_sf->data = malloc(data_size); + if (!this_sf->data) { + free(this_sf); + return NULL; + } + memcpy(this_sf->data, data, data_size); + } + this_sf->data_size = data_size; + this_sf->read_callback = read_callback; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + char fakename[PATH_LIMIT]; +} FAKENAME_STREAMFILE; + +static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */ +} +static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + strncpy(buffer,streamfile->fakename,length); + buffer[length-1]='\0'; +} +static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + fakename_get_name(streamfile, buffer, length); +} +static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + /* detect re-opening the file */ + if (strcmp(filename, streamfile->fakename) == 0) { + STREAMFILE *new_inner_sf; + char original_filename[PATH_LIMIT]; + + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize); + return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL); + } + else { + return streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); + } +} +static void fakename_close(FAKENAME_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile); +} + +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) { + FAKENAME_STREAMFILE *this_sf; + + if (!streamfile || (!fakename && !fakeext)) return NULL; + + this_sf = calloc(1,sizeof(FAKENAME_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)fakename_read; + this_sf->sf.get_size = (void*)fakename_get_size; + this_sf->sf.get_offset = (void*)fakename_get_offset; + this_sf->sf.get_name = (void*)fakename_get_name; + this_sf->sf.get_realname = (void*)fakename_get_realname; + this_sf->sf.open = (void*)fakename_open; + this_sf->sf.close = (void*)fakename_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + /* copy passed name or retain current, and swap extension if expected */ + if (fakename) { + strcpy(this_sf->fakename,fakename); + } else { + streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT); + } + if (fakeext) { + char * ext = strrchr(this_sf->fakename,'.'); + if (ext != NULL) + ext[1] = '\0'; /* truncate past dot */ + strcat(this_sf->fakename, fakeext); + } + + return &this_sf->sf; +} + +/* **************************************************** */ + + +typedef struct { + STREAMFILE sf; + + STREAMFILE **inner_sfs; + size_t inner_sfs_size; + size_t *sizes; + off_t size; + off_t offset; +} MULTIFILE_STREAMFILE; + +static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + int i, segment = 0; + off_t segment_offset = 0; + size_t done = 0; + + if (offset > streamfile->size) { + streamfile->offset = streamfile->size; + return 0; + } + + /* map external offset to multifile offset */ + for (i = 0; i < streamfile->inner_sfs_size; i++) { + size_t segment_size = streamfile->sizes[i]; + /* check if offset falls in this segment */ + if (offset >= segment_offset && offset < segment_offset + segment_size) { + segment = i; + segment_offset = offset - segment_offset; + break; + } + + segment_offset += segment_size; + } + + /* reads can span multiple segments */ + while(done < length) { + if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */ + break; + /* reads over segment size are ok, will return smaller value and continue next segment */ + done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done); + segment++; + segment_offset = 0; + } + + streamfile->offset = offset + done; + return done; +} +static size_t multifile_get_size(MULTIFILE_STREAMFILE *streamfile) { + return streamfile->size; +} +static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) { + return streamfile->offset; +} +static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length); +} +static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + multifile_get_name(streamfile, buffer, length); +} +static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + char original_filename[PATH_LIMIT]; + STREAMFILE *new_sf = NULL; + STREAMFILE **new_inner_sfs = NULL; + int i; + + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT); + + /* detect re-opening the file */ + if (strcmp(filename, original_filename) == 0) { /* same multifile */ + new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*)); + if (!new_inner_sfs) goto fail; + + for (i = 0; i < streamfile->inner_sfs_size; i++) { + streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT); + new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize); + if (!new_inner_sfs[i]) goto fail; + } + + new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size); + if (!new_sf) goto fail; + + return new_sf; + } + else { + return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */ + } + +fail: + if (new_inner_sfs) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(new_inner_sfs[i]); + } + free(new_inner_sfs); + return NULL; +} +static void multifile_close(MULTIFILE_STREAMFILE *streamfile) { + int i; + for (i = 0; i < streamfile->inner_sfs_size; i++) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(streamfile->inner_sfs[i]); + } + free(streamfile->inner_sfs); + free(streamfile->sizes); + free(streamfile); +} + +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) { + MULTIFILE_STREAMFILE *this_sf; + int i; + + if (!streamfiles || !streamfiles_size) return NULL; + for (i = 0; i < streamfiles_size; i++) { + if (!streamfiles[i]) return NULL; + } + + this_sf = calloc(1,sizeof(MULTIFILE_STREAMFILE)); + if (!this_sf) goto fail; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)multifile_read; + this_sf->sf.get_size = (void*)multifile_get_size; + this_sf->sf.get_offset = (void*)multifile_get_offset; + this_sf->sf.get_name = (void*)multifile_get_name; + this_sf->sf.get_realname = (void*)multifile_get_realname; + this_sf->sf.open = (void*)multifile_open; + this_sf->sf.close = (void*)multifile_close; + this_sf->sf.stream_index = streamfiles[0]->stream_index; + + this_sf->inner_sfs_size = streamfiles_size; + this_sf->inner_sfs = calloc(streamfiles_size, sizeof(STREAMFILE*)); + if (!this_sf->inner_sfs) goto fail; + this_sf->sizes = calloc(streamfiles_size, sizeof(size_t)); + if (!this_sf->sizes) goto fail; + + for (i = 0; i < this_sf->inner_sfs_size; i++) { + this_sf->inner_sfs[i] = streamfiles[i]; + this_sf->sizes[i] = streamfiles[i]->get_size(streamfiles[i]); + this_sf->size += this_sf->sizes[i]; + } + + return &this_sf->sf; + +fail: + if (this_sf) { + free(this_sf->inner_sfs); + free(this_sf->sizes); + } + free(this_sf); + return NULL; +} /* **************************************************** */ @@ -393,17 +793,15 @@ STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * name) { return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } -/** - * open file containing decryption keys and copy to buffer - * tries combinations of keynames based on the original filename - * - * returns true if found and copied - */ -int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { +/* Opens a file containing decryption keys and copies to buffer. + * Tries combinations of keynames based on the original filename. + * returns size of key if found and copied */ +size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { char keyname[PATH_LIMIT]; char filename[PATH_LIMIT]; const char *path, *ext; STREAMFILE * streamFileKey = NULL; + size_t keysize; streamFile->get_name(streamFile,filename,sizeof(filename)); @@ -456,17 +854,17 @@ int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { } found: - if (get_streamfile_size(streamFileKey) != bufsize) goto fail; + keysize = get_streamfile_size(streamFileKey); + if (keysize > bufsize) goto fail; - if (read_streamfile(buf, 0, bufsize, streamFileKey)!=bufsize) goto fail; + if (read_streamfile(buf, 0, keysize, streamFileKey) != keysize) + goto fail; close_streamfile(streamFileKey); - - return 1; + return keysize; fail: - if (streamFileKey) close_streamfile(streamFileKey); - + close_streamfile(streamFileKey); return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 0398b5ae0..4d0aba655 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -55,13 +55,8 @@ typedef struct _STREAMFILE { struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize); void (*close)(struct _STREAMFILE *); -#ifdef PROFILE_STREAMFILE - size_t (*get_bytes_read)(struct _STREAMFILE *); - int (*get_error_count)(struct _STREAMFILE *); -#endif - - /* Substream selection for files with multiple streams. Manually used in metas if supported. + /* Substream selection for files with subsongs. Manually used in metas if supported. * Not ideal here, but it's the simplest way to pass to all init_vgmstream_x functions. */ int stream_index; /* 0=default/auto (first), 1=first, N=Nth */ @@ -73,9 +68,34 @@ STREAMFILE * open_stdio_streamfile(const char * filename); /* create a STREAMFILE from pre-opened file path */ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename); +/* A STREAMFILE that doesn't close the underlying stream. + * Calls to open won't wrap the new SF (assumes it needs to be closed). + * Can be used in metas to test custom IO without closing the external SF. */ +STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile); + +/* A STREAMFILE that clamps IO to a section of a larger stream. + * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); + +/* A STREAMFILE with custom IO, that clamps IO to a section of a larger stream. + * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback); + +/* A STREAMFILE that reports a fake name, but still re-opens itself properly. + * Can be used to trick a meta's extension check (to call from another, with a modified SF). + * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. + * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext); + +/* A streamfile formed from multiple streamfiles, their data joined during reads. + * Can be used when data is segmented in multiple separate files. + * The first streamfile is used to get names, stream index and so on. */ +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { + if (streamfile==NULL) return; streamfile->close(streamfile); } @@ -89,23 +109,6 @@ static inline size_t get_streamfile_size(STREAMFILE * streamfile) { return streamfile->get_size(streamfile); } -#ifdef PROFILE_STREAMFILE -/* return how many bytes we read into buffers */ -static inline size_t get_streamfile_bytes_read(STREAMFILE * streamfile) { - if (streamfile->get_bytes_read) - return streamfile->get_bytes_read(streamfile); - else - return 0; -} - -/* return how many times we encountered a read error */ -static inline int get_streamfile_error_count(STREAMFILE * streamfile) { - if (streamfile->get_error_count) - return streamfile->get_error_count(streamfile); - else - return 0; -} -#endif /* Sometimes you just need an int, and we're doing the buffering. * Note, however, that if these fail to read they'll return -1, @@ -163,7 +166,7 @@ STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * ext); int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); -int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); +size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 68f8290e3..3677f6fc3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -373,6 +373,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_vid1, init_vgmstream_flx, init_vgmstream_mogg, + init_vgmstream_kma9, + init_vgmstream_fsb_encrypted, + init_vgmstream_xwc, + init_vgmstream_atsl3, + init_vgmstream_sps_n1, + init_vgmstream_atx, + init_vgmstream_sqex_sead, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -973,6 +980,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_awc: case layout_blocked_vgs: case layout_blocked_vawx: + case layout_blocked_xvag_subsong: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_acm: @@ -2192,6 +2200,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { } concatn(length,desc,temp); + snprintf(temp,TEMPSIZE, + "\nbitrate: %d kbps", + get_vgmstream_average_bitrate(vgmstream) / 1000); + concatn(length,desc,temp); + /* only interesting if more than one */ if (vgmstream->num_streams > 1) { snprintf(temp,TEMPSIZE, @@ -2440,9 +2453,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * return vgmstream->ch[channel].streamfile; } +static int get_vgmstream_average_bitrate_from_size(size_t size, int sample_rate, int length_samples) { + return (int)((int64_t)size * 8 * sample_rate / length_samples); +} static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) { - // todo: not correct in subsongs or formats which only use part of the data - return (int)((int64_t)get_streamfile_size(streamfile) * 8 * sample_rate / length_samples); + return get_vgmstream_average_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples); } /* Return the average bitrate in bps of all unique files contained within this stream. */ @@ -2460,6 +2475,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { if (!sample_rate || !channels || !length_samples) return 0; + /* subsongs need to report this to properly calculate */ + if (vgmstream->stream_size) { + return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples); + } + if (channels >= 1) { streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0); if (streamFile) { diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 3cbe097b3..c8131ad90 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -258,6 +258,7 @@ typedef enum { layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ + layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ /* otherwise odd */ layout_acm, /* libacm layout */ @@ -652,16 +653,20 @@ typedef enum { meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */ meta_PC_FLX, /* Ultima IX PC */ meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */ - -#ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ meta_OGG_SLI, /* Ogg Vorbis file w/ companion .sli for looping */ meta_OGG_SLI2, /* Ogg Vorbis file w/ different styled .sli for looping */ meta_OGG_SFL, /* Ogg Vorbis file w/ .sfl (RIFF SFPL) for looping */ - meta_OGG_UM3, /* Ogg Vorbis with first 0x800 bytes XOR 0xFF */ - meta_OGG_KOVS, /* Ogg Vorbis with exta header and 0x100 bytes XOR */ - meta_OGG_PSYCH, /* Ogg Vorbis with all bytes -0x23*/ -#endif + meta_OGG_UM3, /* Ogg Vorbis with optional encryption */ + meta_OGG_KOVS, /* Ogg Vorbis with encryption (Koei Tecmo Games) */ + meta_OGG_PSYCHIC, /* Ogg Vorbis with encryption */ + meta_OGG_SNGW, /* Ogg Vorbis with optional encryption (Capcom PC games) */ + meta_OGG_ISD, /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */ + meta_KMA9, /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */ + meta_XWC, /* Starbreeze games */ + meta_SQEX_SAB, /* Square-Enix newest middleware (sound) */ + meta_SQEX_MAB, /* Square-Enix newest middleware (music) */ + #ifdef VGM_USE_MP4V2 meta_MP4, /* AAC (iOS) */ #endif @@ -739,6 +744,7 @@ typedef struct { int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ + size_t stream_size; /* info to properly calculate bitrate */ /* looping */ int loop_flag; /* is this stream looped? */ @@ -801,15 +807,16 @@ typedef struct { /* Ogg with Vorbis */ typedef struct { STREAMFILE *streamfile; - ogg_int64_t offset; - ogg_int64_t size; - ogg_int64_t other_header_bytes; + ogg_int64_t start; /* file offset where the Ogg starts */ + ogg_int64_t offset; /* virtual offset, from 0 to size */ + ogg_int64_t size; /* virtual size of the Ogg */ - /* XOR setup (SCD) */ - int decryption_enabled; - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; + uint32_t sngw_xor; + } ogg_vorbis_streamfile; typedef struct { @@ -831,9 +838,9 @@ typedef enum { } vorbis_custom_t; /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ -typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */ -typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */ -typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */ +typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t; +typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t; +typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t; typedef struct { /* to reconstruct init packets */ @@ -1001,6 +1008,7 @@ typedef struct { typedef enum { ATRAC9_DEFAULT = 0, /* ATRAC9 standard */ ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */ + ATRAC9_KMA9, /* Koei Tecmo KMA9: interleaved subsongs */ //ATRAC9_FSB, /* FMOD FSB: Vita multichannel interleaves 2ch xN superframes */ //ATRAC9_EATRAX, /* EA EATrax: buffered ATRAC9 in SPS blocks (superframes can be split between blocks) */ } atrac9_custom_t; @@ -1100,7 +1108,6 @@ typedef enum { FFMPEG_STANDARD, /* default FFmpeg */ FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */ FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */ - FFMPEG_BGW_ATRAC3, /* Encrypted raw ATRAC3 */ //FFMPEG_EA_SCHL, /* Normal header+data (ex. ATRAC3) in SCxx blocks */ //FFMPEG_SFH, /* ATRAC3plus header+data in SFH blocks */ //FFMPEG_AWC_XMA, /* XMA data in AWC blocks, 1 streams per channel */ @@ -1118,7 +1125,6 @@ typedef struct { /* internal sequences, when needed */ int sequence; int samples_done; - uint8_t * key; } ffmpeg_custom_config; typedef struct { From 8591c29eb82beef40ce2cbde95beb315d17f77c3 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 18 Feb 2018 20:19:18 -0800 Subject: [PATCH 020/104] Fix playlist info loading threading magic. --- Playlist/PlaylistLoader.m | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Playlist/PlaylistLoader.m b/Playlist/PlaylistLoader.m index 5ca519166..346fb39e2 100755 --- a/Playlist/PlaylistLoader.m +++ b/Playlist/PlaylistLoader.m @@ -464,7 +464,7 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL { long batchCount = ([entries count] / 16) + ([entries count] % 16 ? 1 : 0); NSMutableArray *array = [[NSMutableArray alloc] init]; - long i; + long i, j; for (i = 0; i < batchCount; ++i) { @@ -482,10 +482,11 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL if (!i) return; - semaphore_t info_sem; - semaphore_create( mach_task_self(), &info_sem, SYNC_POLICY_FIFO, (int) -i + 1 ); - - __block semaphore_t weak_sem = info_sem; + NSLock *outLock = [[NSLock alloc] init]; + NSMutableArray *outArray = [[NSMutableArray alloc] init]; + + __block NSLock *weakLock = outLock; + __block NSMutableArray *weakArray = outArray; for (NSArray *a in array) { @@ -497,8 +498,7 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL [op addExecutionBlock:^{ for (PlaylistEntry *pe in weakA) { - __block PlaylistEntry *weakPe = pe; - __block NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20]; + NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20]; NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:pe.URL]; if (entryProperties == nil) @@ -506,11 +506,11 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL [entryInfo addEntriesFromDictionary:entryProperties]; [entryInfo addEntriesFromDictionary:[AudioMetadataReader metadataForURL:pe.URL]]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [weakPe setMetadata:entryInfo]; - semaphore_signal(weak_sem); - }); + + [weakLock lock]; + [weakArray addObject:pe]; + [weakArray addObject:entryInfo]; + [weakLock unlock]; } }]; @@ -518,7 +518,21 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL } [queue waitUntilAllOperationsAreFinished]; + + semaphore_t info_sem; + semaphore_create( mach_task_self(), &info_sem, SYNC_POLICY_FIFO, (int) -i + 1 ); + __block semaphore_t weak_sem = info_sem; + + for (i = 0, j = [outArray count]; i < j; i += 2) { + __block PlaylistEntry *weakPe = [outArray objectAtIndex:i]; + __block NSDictionary *entryInfo = [outArray objectAtIndex:i + 1]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakPe setMetadata:entryInfo]; + semaphore_signal(weak_sem); + }); + } + semaphore_wait(info_sem); semaphore_destroy(mach_task_self(), info_sem); From 6cd51431bdf44c905e0ed3b2bf56a618d8cc034e Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 18 Feb 2018 20:25:43 -0800 Subject: [PATCH 021/104] Updated libopenmpt to version 0.3.6. --- .gitmodules | 3 - Frameworks/OpenMPT/OpenMPT | 1 - Frameworks/OpenMPT/OpenMPT/LICENSE | 27 + Frameworks/OpenMPT/OpenMPT/Makefile | 1375 +++ Frameworks/OpenMPT/OpenMPT/README.md | 370 + .../OpenMPT/OpenMPT/common/BuildSettings.h | 736 ++ .../OpenMPT/OpenMPT/common/CompilerDetect.h | 367 + .../OpenMPT/common/ComponentManager.cpp | 465 + .../OpenMPT/OpenMPT/common/ComponentManager.h | 507 ++ .../OpenMPT/OpenMPT/common/Endianness.h | 1061 +++ .../OpenMPT/OpenMPT/common/FileReader.cpp | 154 + .../OpenMPT/OpenMPT/common/FileReader.h | 1167 +++ .../OpenMPT/OpenMPT/common/FileReaderFwd.h | 42 + Frameworks/OpenMPT/OpenMPT/common/FlagSet.h | 412 + Frameworks/OpenMPT/OpenMPT/common/Logging.cpp | 423 + Frameworks/OpenMPT/OpenMPT/common/Logging.h | 264 + .../OpenMPT/OpenMPT/common/Profiler.cpp | 221 + Frameworks/OpenMPT/OpenMPT/common/Profiler.h | 125 + .../OpenMPT/OpenMPT/common/StringFixer.h | 410 + .../OpenMPT/OpenMPT/common/WriteMemoryDump.h | 71 + .../OpenMPT/OpenMPT/common/misc_util.cpp | 135 + Frameworks/OpenMPT/OpenMPT/common/misc_util.h | 1134 +++ .../OpenMPT/OpenMPT/common/mptBufferIO.h | 175 + Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp | 376 + Frameworks/OpenMPT/OpenMPT/common/mptCPU.h | 72 + Frameworks/OpenMPT/OpenMPT/common/mptCRC.h | 245 + .../OpenMPT/OpenMPT/common/mptFileIO.cpp | 383 + Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h | 589 ++ Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp | 827 ++ Frameworks/OpenMPT/OpenMPT/common/mptIO.h | 926 ++ .../OpenMPT/OpenMPT/common/mptLibrary.cpp | 550 ++ .../OpenMPT/OpenMPT/common/mptLibrary.h | 118 + Frameworks/OpenMPT/OpenMPT/common/mptMutex.h | 233 + Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp | 564 ++ Frameworks/OpenMPT/OpenMPT/common/mptOS.h | 163 + .../OpenMPT/OpenMPT/common/mptPathString.cpp | 889 ++ .../OpenMPT/OpenMPT/common/mptPathString.h | 503 ++ .../OpenMPT/OpenMPT/common/mptRandom.cpp | 318 + Frameworks/OpenMPT/OpenMPT/common/mptRandom.h | 643 ++ .../OpenMPT/OpenMPT/common/mptString.cpp | 1691 ++++ Frameworks/OpenMPT/OpenMPT/common/mptString.h | 668 ++ .../OpenMPT/common/mptStringFormat.cpp | 498 ++ .../OpenMPT/OpenMPT/common/mptStringFormat.h | 782 ++ .../OpenMPT/OpenMPT/common/mptStringParse.cpp | 120 + .../OpenMPT/OpenMPT/common/mptStringParse.h | 196 + Frameworks/OpenMPT/OpenMPT/common/mptThread.h | 460 + Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp | 307 + Frameworks/OpenMPT/OpenMPT/common/mptTime.h | 114 + .../OpenMPT/OpenMPT/common/mptTypeTraits.h | 142 + Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp | 756 ++ Frameworks/OpenMPT/OpenMPT/common/mptUUID.h | 146 + Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp | 766 ++ Frameworks/OpenMPT/OpenMPT/common/mptWine.h | 127 + .../OpenMPT/common/serialization_utils.cpp | 735 ++ .../OpenMPT/common/serialization_utils.h | 552 ++ Frameworks/OpenMPT/OpenMPT/common/stdafx.cpp | 20 + Frameworks/OpenMPT/OpenMPT/common/stdafx.h | 124 + .../OpenMPT/OpenMPT/common/typedefs.cpp | 49 + Frameworks/OpenMPT/OpenMPT/common/typedefs.h | 544 ++ Frameworks/OpenMPT/OpenMPT/common/version.cpp | 794 ++ Frameworks/OpenMPT/OpenMPT/common/version.h | 120 + .../OpenMPT/OpenMPT/common/versionNumber.h | 30 + .../OpenMPT/contrib/fuzzing/all_formats.dict | 304 + .../OpenMPT/OpenMPT/contrib/fuzzing/build.sh | 4 + .../OpenMPT/contrib/fuzzing/fuzz-master.sh | 15 + .../OpenMPT/contrib/fuzzing/fuzz-settings.sh | 18 + .../OpenMPT/contrib/fuzzing/fuzz-slave.sh | 5 + .../OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c | 54 + .../OpenMPT/contrib/fuzzing/get-afl.sh | 16 + .../OpenMPT/OpenMPT/contrib/fuzzing/readme.md | 46 + .../contrib/libopenmpt/libopenmpt_bass.c | 109 + .../OpenMPT/OpenMPT/examples/.clang-format | 63 + .../OpenMPT/examples/libopenmpt_example_c.c | 203 + .../examples/libopenmpt_example_c_mem.c | 292 + .../examples/libopenmpt_example_c_pipe.c | 152 + .../examples/libopenmpt_example_c_probe.c | 181 + .../examples/libopenmpt_example_c_stdout.c | 176 + .../examples/libopenmpt_example_c_unsafe.c | 65 + .../examples/libopenmpt_example_cxx.cpp | 78 + .../OpenMPT/OpenMPT/include/minimp3/LGPL.txt | 504 ++ .../OpenMPT/include/minimp3/OpenMPT.txt | 10 + .../OpenMPT/OpenMPT/include/minimp3/libc.h | 184 + .../OpenMPT/OpenMPT/include/minimp3/minimp3.c | 2692 ++++++ .../OpenMPT/OpenMPT/include/minimp3/minimp3.h | 23 + .../OpenMPT/OpenMPT/include/miniz/OpenMPT.txt | 17 + .../OpenMPT/OpenMPT/include/miniz/miniz.c | 7579 +++++++++++++++++ .../OpenMPT/OpenMPT/include/miniz/miniz.h | 1349 +++ .../include/libmodplug/libmodplug.pc.in | 13 + .../modplug/include/libmodplug/modplug.h | 171 + .../include/libmodplug/modplug_0.8.7.h | 168 + .../modplug/include/libmodplug/sndfile.h | 1017 +++ .../modplug/include/libmodplug/stdafx.h | 127 + .../OpenMPT/include/stb_vorbis/OpenMPT.txt | 17 + .../OpenMPT/include/stb_vorbis/stb_vorbis.c | 5463 ++++++++++++ .../OpenMPT/OpenMPT/libopenmpt/Doxyfile | 2466 ++++++ .../bindings/freebasic/libopenmpt.bi | 1615 ++++ .../bindings/freebasic/libopenmpt_ext.bi | 332 + .../OpenMPT/libopenmpt/doc/foo_openmpt.txt | 1 + .../OpenMPT/libopenmpt/doc/in_openmpt.txt | 1 + .../OpenMPT/libopenmpt/doc/xmp-openmpt.txt | 1 + .../OpenMPT/libopenmpt/dox/changelog.md | 676 ++ .../OpenMPT/libopenmpt/dox/dependencies.md | 105 + .../OpenMPT/OpenMPT/libopenmpt/dox/index.dox | 39 + .../OpenMPT/libopenmpt/dox/packaging.md | 38 + .../OpenMPT/libopenmpt/dox/quickstart.md | 70 + .../OpenMPT/OpenMPT/libopenmpt/dox/tests.md | 44 + .../OpenMPT/OpenMPT/libopenmpt/dox/todo.md | 8 + .../OpenMPT/libopenmpt/foo_openmpt.cpp | 345 + .../OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp | 513 ++ .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.h | 1439 ++++ .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp | 1008 +++ .../OpenMPT/libopenmpt/libopenmpt.pc.in | 14 + .../OpenMPT/libopenmpt/libopenmpt_c.cpp | 1666 ++++ .../OpenMPT/libopenmpt/libopenmpt_config.h | 204 + .../OpenMPT/libopenmpt/libopenmpt_cxx.cpp | 436 + .../OpenMPT/libopenmpt/libopenmpt_ext.h | 318 + .../OpenMPT/libopenmpt/libopenmpt_ext.hpp | 306 + .../libopenmpt/libopenmpt_ext_impl.cpp | 296 + .../libopenmpt/libopenmpt_ext_impl.hpp | 112 + .../OpenMPT/libopenmpt/libopenmpt_impl.cpp | 1613 ++++ .../OpenMPT/libopenmpt/libopenmpt_impl.hpp | 212 + .../OpenMPT/libopenmpt/libopenmpt_internal.h | 36 + .../OpenMPT/libopenmpt/libopenmpt_modplug.c | 693 ++ .../libopenmpt/libopenmpt_modplug_cpp.cpp | 890 ++ .../libopenmpt/libopenmpt_plugin_gui.cpp | 333 + .../libopenmpt/libopenmpt_plugin_gui.hpp | 37 + .../libopenmpt/libopenmpt_plugin_gui.rc | 138 + .../libopenmpt/libopenmpt_plugin_settings.hpp | 127 + .../libopenmpt_stream_callbacks_buffer.h | 179 + .../libopenmpt_stream_callbacks_fd.h | 82 + .../libopenmpt_stream_callbacks_file.h | 113 + .../OpenMPT/libopenmpt/libopenmpt_test.cpp | 67 + .../OpenMPT/libopenmpt/libopenmpt_version.h | 75 + .../OpenMPT/libopenmpt/libopenmpt_version.mk | 8 + .../OpenMPT/libopenmpt/libopenmpt_version.rc | 216 + .../OpenMPT/OpenMPT/libopenmpt/resource.h | 26 + .../OpenMPT/libopenmpt/xmp-openmpt.cpp | 1735 ++++ .../OpenMPT/OpenMPT/openmpt123/openmpt123.cpp | 2520 ++++++ .../OpenMPT/OpenMPT/openmpt123/openmpt123.h2m | 2 + .../OpenMPT/OpenMPT/openmpt123/openmpt123.hpp | 661 ++ .../OpenMPT/openmpt123/openmpt123_config.hpp | 68 + .../OpenMPT/openmpt123/openmpt123_flac.hpp | 135 + .../OpenMPT/openmpt123/openmpt123_mmio.hpp | 138 + .../openmpt123/openmpt123_portaudio.hpp | 288 + .../openmpt123/openmpt123_pulseaudio.hpp | 166 + .../OpenMPT/openmpt123/openmpt123_raw.hpp | 58 + .../OpenMPT/openmpt123/openmpt123_sdl.hpp | 133 + .../OpenMPT/openmpt123/openmpt123_sdl2.hpp | 192 + .../OpenMPT/openmpt123/openmpt123_sndfile.hpp | 208 + .../OpenMPT/openmpt123/openmpt123_stdout.hpp | 49 + .../OpenMPT/openmpt123/openmpt123_waveout.hpp | 198 + .../OpenMPT/OpenMPT/soundbase/SampleFormat.h | 93 + .../soundbase/SampleFormatConverters.h | 1199 +++ .../OpenMPT/soundbase/SampleFormatCopy.h | 135 + Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp | 142 + Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h | 36 + Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp | 431 + Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h | 120 + Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp | 557 ++ Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h | 67 + .../OpenMPT/OpenMPT/sounddsp/Reverb.cpp | 1262 +++ Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h | 280 + .../OpenMPT/soundlib/AudioCriticalSection.cpp | 74 + .../OpenMPT/soundlib/AudioCriticalSection.h | 70 + .../OpenMPT/soundlib/AudioReadTarget.h | 208 + .../OpenMPT/OpenMPT/soundlib/ChunkReader.h | 141 + .../OpenMPT/OpenMPT/soundlib/Container.h | 41 + .../OpenMPT/soundlib/ContainerMMCMP.cpp | 436 + .../OpenMPT/soundlib/ContainerPP20.cpp | 219 + .../OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp | 205 + .../OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp | 440 + .../OpenMPT/OpenMPT/soundlib/Dither.cpp | 273 + Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h | 90 + .../OpenMPT/OpenMPT/soundlib/Dlsbank.cpp | 1985 +++++ Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h | 157 + .../OpenMPT/OpenMPT/soundlib/Fastmix.cpp | 693 ++ .../OpenMPT/OpenMPT/soundlib/FloatMixer.h | 312 + .../OpenMPT/soundlib/ITCompression.cpp | 443 + .../OpenMPT/OpenMPT/soundlib/ITCompression.h | 102 + .../OpenMPT/OpenMPT/soundlib/ITTools.cpp | 652 ++ Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h | 301 + .../OpenMPT/soundlib/InstrumentExtensions.cpp | 717 ++ .../OpenMPT/OpenMPT/soundlib/IntMixer.h | 388 + .../OpenMPT/OpenMPT/soundlib/Load_669.cpp | 333 + .../OpenMPT/OpenMPT/soundlib/Load_amf.cpp | 653 ++ .../OpenMPT/OpenMPT/soundlib/Load_ams.cpp | 1117 +++ .../OpenMPT/OpenMPT/soundlib/Load_dbm.cpp | 700 ++ .../OpenMPT/OpenMPT/soundlib/Load_digi.cpp | 228 + .../OpenMPT/OpenMPT/soundlib/Load_dmf.cpp | 1171 +++ .../OpenMPT/OpenMPT/soundlib/Load_dsm.cpp | 327 + .../OpenMPT/OpenMPT/soundlib/Load_dtm.cpp | 594 ++ .../OpenMPT/OpenMPT/soundlib/Load_far.cpp | 335 + .../OpenMPT/OpenMPT/soundlib/Load_gdm.cpp | 523 ++ .../OpenMPT/OpenMPT/soundlib/Load_imf.cpp | 653 ++ .../OpenMPT/OpenMPT/soundlib/Load_it.cpp | 2473 ++++++ .../OpenMPT/OpenMPT/soundlib/Load_itp.cpp | 398 + .../OpenMPT/OpenMPT/soundlib/Load_mdl.cpp | 853 ++ .../OpenMPT/OpenMPT/soundlib/Load_med.cpp | 932 ++ .../OpenMPT/OpenMPT/soundlib/Load_mid.cpp | 1400 +++ .../OpenMPT/OpenMPT/soundlib/Load_mo3.cpp | 1852 ++++ .../OpenMPT/OpenMPT/soundlib/Load_mod.cpp | 2372 ++++++ .../OpenMPT/OpenMPT/soundlib/Load_mt2.cpp | 1162 +++ .../OpenMPT/OpenMPT/soundlib/Load_mtm.cpp | 244 + .../OpenMPT/OpenMPT/soundlib/Load_okt.cpp | 451 + .../OpenMPT/OpenMPT/soundlib/Load_plm.cpp | 398 + .../OpenMPT/OpenMPT/soundlib/Load_psm.cpp | 1419 +++ .../OpenMPT/OpenMPT/soundlib/Load_ptm.cpp | 288 + .../OpenMPT/OpenMPT/soundlib/Load_s3m.cpp | 965 +++ .../OpenMPT/OpenMPT/soundlib/Load_sfx.cpp | 466 + .../OpenMPT/OpenMPT/soundlib/Load_stm.cpp | 360 + .../OpenMPT/OpenMPT/soundlib/Load_stp.cpp | 882 ++ .../OpenMPT/OpenMPT/soundlib/Load_uax.cpp | 183 + .../OpenMPT/OpenMPT/soundlib/Load_ult.cpp | 487 ++ .../OpenMPT/OpenMPT/soundlib/Load_wav.cpp | 180 + .../OpenMPT/OpenMPT/soundlib/Load_xm.cpp | 1143 +++ Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h | 72 + .../OpenMPT/OpenMPT/soundlib/MIDIEvents.cpp | 263 + .../OpenMPT/OpenMPT/soundlib/MIDIEvents.h | 168 + .../OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp | 480 ++ .../OpenMPT/OpenMPT/soundlib/MIDIMacros.h | 168 + .../OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp | 113 + .../OpenMPT/OpenMPT/soundlib/MPEGFrame.h | 29 + .../OpenMPT/OpenMPT/soundlib/Message.cpp | 230 + Frameworks/OpenMPT/OpenMPT/soundlib/Message.h | 67 + .../OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp | 93 + .../OpenMPT/OpenMPT/soundlib/MixFuncTable.h | 50 + Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h | 49 + .../OpenMPT/OpenMPT/soundlib/MixerInterface.h | 107 + .../OpenMPT/OpenMPT/soundlib/MixerLoops.cpp | 911 ++ .../OpenMPT/OpenMPT/soundlib/MixerLoops.h | 44 + .../OpenMPT/soundlib/MixerSettings.cpp | 57 + .../OpenMPT/OpenMPT/soundlib/MixerSettings.h | 52 + .../OpenMPT/OpenMPT/soundlib/ModChannel.cpp | 134 + .../OpenMPT/OpenMPT/soundlib/ModChannel.h | 216 + .../OpenMPT/soundlib/ModInstrument.cpp | 330 + .../OpenMPT/OpenMPT/soundlib/ModInstrument.h | 189 + .../OpenMPT/OpenMPT/soundlib/ModSample.cpp | 362 + .../OpenMPT/OpenMPT/soundlib/ModSample.h | 110 + .../OpenMPT/OpenMPT/soundlib/ModSampleCopy.h | 155 + .../OpenMPT/OpenMPT/soundlib/ModSequence.cpp | 633 ++ .../OpenMPT/OpenMPT/soundlib/ModSequence.h | 198 + .../OpenMPT/OpenMPT/soundlib/OggStream.cpp | 173 + .../OpenMPT/OpenMPT/soundlib/OggStream.h | 87 + Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp | 341 + Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h | 53 + .../OpenMPT/OpenMPT/soundlib/Resampler.h | 139 + .../OpenMPT/OpenMPT/soundlib/RowVisitor.cpp | 236 + .../OpenMPT/OpenMPT/soundlib/RowVisitor.h | 88 + .../OpenMPT/OpenMPT/soundlib/S3MTools.cpp | 120 + .../OpenMPT/OpenMPT/soundlib/S3MTools.h | 140 + .../OpenMPT/soundlib/SampleFormatFLAC.cpp | 671 ++ .../OpenMPT/soundlib/SampleFormatMP3.cpp | 384 + .../soundlib/SampleFormatMediaFoundation.cpp | 492 ++ .../OpenMPT/soundlib/SampleFormatOpus.cpp | 183 + .../OpenMPT/soundlib/SampleFormatVorbis.cpp | 355 + .../OpenMPT/soundlib/SampleFormats.cpp | 2545 ++++++ .../OpenMPT/OpenMPT/soundlib/SampleIO.cpp | 1082 +++ .../OpenMPT/OpenMPT/soundlib/SampleIO.h | 314 + .../OpenMPT/OpenMPT/soundlib/Snd_defs.h | 614 ++ .../OpenMPT/OpenMPT/soundlib/Snd_flt.cpp | 152 + .../OpenMPT/OpenMPT/soundlib/Snd_fx.cpp | 6008 +++++++++++++ .../OpenMPT/OpenMPT/soundlib/Sndfile.cpp | 1933 +++++ Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h | 1104 +++ .../OpenMPT/OpenMPT/soundlib/Sndmix.cpp | 2586 ++++++ .../OpenMPT/soundlib/SoundFilePlayConfig.cpp | 122 + .../OpenMPT/soundlib/SoundFilePlayConfig.h | 113 + .../OpenMPT/OpenMPT/soundlib/Tables.cpp | 1003 +++ Frameworks/OpenMPT/OpenMPT/soundlib/Tables.h | 42 + .../OpenMPT/OpenMPT/soundlib/Tagging.cpp | 38 + Frameworks/OpenMPT/OpenMPT/soundlib/Tagging.h | 44 + .../OpenMPT/OpenMPT/soundlib/UMXTools.cpp | 213 + .../OpenMPT/OpenMPT/soundlib/UMXTools.h | 57 + .../OpenMPT/soundlib/UpgradeModule.cpp | 603 ++ .../OpenMPT/OpenMPT/soundlib/WAVTools.cpp | 592 ++ .../OpenMPT/OpenMPT/soundlib/WAVTools.h | 414 + .../OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp | 102 + .../OpenMPT/OpenMPT/soundlib/WindowedFIR.h | 86 + .../OpenMPT/OpenMPT/soundlib/XMTools.cpp | 436 + Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h | 189 + .../OpenMPT/OpenMPT/soundlib/load_j2b.cpp | 1048 +++ .../OpenMPT/soundlib/mod_specifications.cpp | 500 ++ .../OpenMPT/soundlib/mod_specifications.h | 100 + .../OpenMPT/OpenMPT/soundlib/modcommand.cpp | 1149 +++ .../OpenMPT/OpenMPT/soundlib/modcommand.h | 220 + .../OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp | 973 +++ .../OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h | 127 + .../OpenMPT/OpenMPT/soundlib/pattern.cpp | 626 ++ Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h | 229 + .../OpenMPT/soundlib/patternContainer.cpp | 216 + .../OpenMPT/soundlib/patternContainer.h | 113 + .../soundlib/plugins/DigiBoosterEcho.cpp | 230 + .../soundlib/plugins/DigiBoosterEcho.h | 121 + .../OpenMPT/soundlib/plugins/LFOPlugin.cpp | 522 ++ .../OpenMPT/soundlib/plugins/LFOPlugin.h | 151 + .../OpenMPT/soundlib/plugins/OpCodes.h | 101 + .../soundlib/plugins/PlugInterface.cpp | 1017 +++ .../OpenMPT/soundlib/plugins/PlugInterface.h | 284 + .../soundlib/plugins/PluginEventQueue.h | 159 + .../soundlib/plugins/PluginManager.cpp | 661 ++ .../OpenMPT/soundlib/plugins/PluginManager.h | 174 + .../soundlib/plugins/PluginMixBuffer.h | 122 + .../OpenMPT/soundlib/plugins/PluginStructs.h | 138 + .../OpenMPT/soundlib/plugins/dmo/Chorus.cpp | 277 + .../OpenMPT/soundlib/plugins/dmo/Chorus.h | 117 + .../soundlib/plugins/dmo/Compressor.cpp | 238 + .../OpenMPT/soundlib/plugins/dmo/Compressor.h | 109 + .../soundlib/plugins/dmo/DMOPlugin.cpp | 562 ++ .../OpenMPT/soundlib/plugins/dmo/DMOPlugin.h | 100 + .../soundlib/plugins/dmo/Distortion.cpp | 250 + .../OpenMPT/soundlib/plugins/dmo/Distortion.h | 97 + .../OpenMPT/soundlib/plugins/dmo/Echo.cpp | 198 + .../OpenMPT/soundlib/plugins/dmo/Echo.h | 99 + .../OpenMPT/soundlib/plugins/dmo/Flanger.cpp | 145 + .../OpenMPT/soundlib/plugins/dmo/Flanger.h | 69 + .../OpenMPT/soundlib/plugins/dmo/Gargle.cpp | 202 + .../OpenMPT/soundlib/plugins/dmo/Gargle.h | 90 + .../soundlib/plugins/dmo/I3DL2Reverb.cpp | 590 ++ .../soundlib/plugins/dmo/I3DL2Reverb.h | 165 + .../OpenMPT/soundlib/plugins/dmo/ParamEq.cpp | 200 + .../OpenMPT/soundlib/plugins/dmo/ParamEq.h | 98 + .../soundlib/plugins/dmo/WavesReverb.cpp | 261 + .../soundlib/plugins/dmo/WavesReverb.h | 107 + .../OpenMPT/OpenMPT/soundlib/tuning.cpp | 878 ++ Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h | 253 + .../OpenMPT/soundlib/tuningCollection.cpp | 302 + .../OpenMPT/OpenMPT/soundlib/tuningbase.cpp | 160 + .../OpenMPT/OpenMPT/soundlib/tuningbase.h | 73 + .../OpenMPT/soundlib/tuningcollection.h | 85 + Frameworks/OpenMPT/OpenMPT/test/TestTools.h | 30 + .../OpenMPT/OpenMPT/test/TestToolsLib.cpp | 224 + .../OpenMPT/OpenMPT/test/TestToolsLib.h | 362 + .../OpenMPT/OpenMPT/test/TestToolsTracker.h | 74 + Frameworks/OpenMPT/OpenMPT/test/test.cpp | 4024 +++++++++ Frameworks/OpenMPT/OpenMPT/test/test.flac | Bin 0 -> 319 bytes Frameworks/OpenMPT/OpenMPT/test/test.h | 22 + Frameworks/OpenMPT/OpenMPT/test/test.mptm | Bin 0 -> 11931 bytes Frameworks/OpenMPT/OpenMPT/test/test.s3m | Bin 0 -> 784 bytes Frameworks/OpenMPT/OpenMPT/test/test.xm | Bin 0 -> 6308 bytes 338 files changed, 157732 insertions(+), 4 deletions(-) delete mode 160000 Frameworks/OpenMPT/OpenMPT create mode 100644 Frameworks/OpenMPT/OpenMPT/LICENSE create mode 100644 Frameworks/OpenMPT/OpenMPT/Makefile create mode 100644 Frameworks/OpenMPT/OpenMPT/README.md create mode 100644 Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/Endianness.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/FileReader.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/FlagSet.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/Logging.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/Logging.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/Profiler.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/StringFixer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/misc_util.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptCPU.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptCRC.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptIO.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptMutex.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptOS.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptPathString.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptRandom.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptString.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptString.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptStringParse.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptThread.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptTime.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptUUID.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptWine.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/stdafx.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/stdafx.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/typedefs.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/version.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/common/version.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/versionNumber.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/build.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-settings.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-slave.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/readme.md create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/.clang-format create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_pipe.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_probe.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_stdout.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c create mode 100644 Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c create mode 100644 Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c create mode 100644 Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in create mode 100644 Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h create mode 100644 Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/Doxyfile create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt_ext.bi create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/in_openmpt.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/xmp-openmpt.txt create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/index.dox create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/packaging.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/tests.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/todo.md create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.pc.in create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_internal.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_buffer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_fd.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_file.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_test.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.h2m create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_pulseaudio.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_raw.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sndfile.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_stdout.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ChunkReader.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Container.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/FloatMixer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Message.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Tables.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Tagging.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Tagging.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginEventQueue.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h create mode 100644 Frameworks/OpenMPT/OpenMPT/test/TestTools.h create mode 100644 Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h create mode 100644 Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.flac create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.h create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.mptm create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.s3m create mode 100644 Frameworks/OpenMPT/OpenMPT/test/test.xm diff --git a/.gitmodules b/.gitmodules index f4ceabbed..c413cbd16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "Frameworks/mGBA/mGBA/mgba"] path = Frameworks/mGBA/mGBA/mgba url = https://github.com/kode54/mgba.git -[submodule "Frameworks/OpenMPT/OpenMPT"] - path = Frameworks/OpenMPT/OpenMPT - url = https://gitlab.kode54.net/kode54/OpenMPT.git [submodule "Frameworks/AdPlug/AdPlug/adplug"] path = Frameworks/AdPlug/AdPlug/adplug url = https://github.com/adplug/adplug.git diff --git a/Frameworks/OpenMPT/OpenMPT b/Frameworks/OpenMPT/OpenMPT deleted file mode 160000 index bf3d5ea38..000000000 --- a/Frameworks/OpenMPT/OpenMPT +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf3d5ea380157da7aa0b7bc58c3d889ac7e31b37 diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE new file mode 100644 index 000000000..ad8c18f5d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -0,0 +1,27 @@ +The OpenMPT code is licensed under the BSD license. + +Copyright (c) 2004-2018, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile new file mode 100644 index 000000000..401dfdd08 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -0,0 +1,1375 @@ +# +# libopenmpt and openmpt123 GNU Makefile +# +# Authors: Joern Heusipp +# OpenMPT Devs +# +# The OpenMPT source code is released under the BSD license. +# Read LICENSE for more details. +# + +# +# Supported parameters: +# +# +# Build configuration (provide on each `make` invocation): +# +# CONFIG=[gcc|clang|mingw64-win32|mingw64-win64|emscripten|emscripten-old] (default: CONFIG=) +# +# Build configurations can override or change defaults of other build options. +# See below and in `build/make/` for details. +# +# +# Compiler options (environment variables): +# +# CC +# CXX +# LD +# AR +# CPPFLAGS +# CXXFLAGS +# CFLAGS +# LDFLAGS +# LDLIBS +# ARFLAGS +# +# CXXSTDLIB_PCLIBSPRIVATE C++ standard library (or libraries) required for +# static linking. This will be put in the pkg-config file +# libopenmpt.pc Libs.private field and used for nothing else. +# +# +# +# Build flags (provide on each `make` invocation) (defaults are shown): +# +# DYNLINK=1 Dynamically link examples and openmpt123 against libopenmpt +# SHARED_LIB=1 Build shared library +# STATIC_LIB=1 Build static library +# EXAMPLES=1 Build examples +# OPENMPT123=1 Build openmpt123 +# SHARED_SONAME=1 Set SONAME of shared library +# DEBUG=0 Build debug binaries without optimization and with symbols +# OPTIMIZE=1 Build optimized binaries +# OPTIMIZE_SIZE=0 Build size-optimized binaries +# TEST=1 Include test suite in default target. +# ONLY_TEST=0 Only build the test suite. +# STRICT=0 Treat warnings as errors. +# STDCXX=c++11 C++ standard version (only for GCC and clang) +# CHECKED=0 Enable run-time assertions. +# CHECKED_ADDRESS=0 Enable address sanitizer +# CHECKED_UNDEFINED=0 Enable undefined behaviour sanitizer +# +# +# Build flags for libopenmpt (provide on each `make` invocation) +# (defaults are 0): +# +# NO_ZLIB=1 Avoid using zlib, even if found +# NO_MPG123=1 Avoid using libmpg123, even if found +# NO_OGG=1 Avoid using libogg, even if found +# NO_VORBIS=1 Avoid using libvorbis, even if found +# NO_VORBISFILE=1 Avoid using libvorbisfile, even if found +# +# NO_STBVORBIS=1 Do not fallback to stb_vorbis +# +# USE_MINIMP3=1 Use minimp3. +# Beware that minimp3 is LGPL 2.1 licensed. +# +# Build flags for libopenmpt examples and openmpt123 +# (provide on each `make` invocation) +# (defaults are 0): +# +# NO_PORTAUDIO=1 Avoid using PortAudio, even if found +# NO_PORTAUDIOCPP=1 Avoid using PortAudio C++, even if found +# +# Build flags for openmpt123 (provide on each `make` invocation) +# (defaults are 0): +# +# NO_PULSEAUDIO=1 Avoid using PulseAudio, even if found +# NO_SDL=1 Avoid using SDL, even if found +# NO_SDL2=1 Avoid using SDL2, even if found +# NO_FLAC=1 Avoid using FLAC, even if found +# NO_SNDFILE=1 Avoid using libsndfile, even if found +# +# +# Install options (provide on each `make install` invocation) +# +# PREFIX (e.g.: PREFIX=$HOME/opt, default: PREFIX=/usr/local) +# DESTDIR (e.g.: DESTDIR=bin/dest, default: DESTDIR=) +# +# +# Verbosity: +# +# QUIET=[0,1] (default: QUIET=0) +# VERBOSE=[0,1,2] (default: VERBOSE=0) +# +# +# Supported targets: +# +# make clean +# make [all] +# make doc +# make check +# make dist +# make dist-doc +# make install +# make install-doc +# + + + +INFO = @echo +SILENT = @ +VERYSILENT = @ + + +ifeq ($(VERBOSE),2) +INFO = @true +SILENT = +VERYSILENT = +endif + +ifeq ($(VERBOSE),1) +INFO = @true +SILENT = +VERYSILENT = @ +endif + + +ifeq ($(QUIET),1) +INFO = @true +SILENT = @ +VERYSILENT = @ +endif + + +# general settings + +DYNLINK=1 +SHARED_LIB=1 +STATIC_LIB=1 +EXAMPLES=1 +FUZZ=0 +SHARED_SONAME=1 +DEBUG=0 +OPTIMIZE=1 +OPTIMIZE_SIZE=0 +TEST=1 +ONLY_TEST=0 +SOSUFFIX=.so +SOSUFFIXWINDOWS=0 +OPENMPT123=1 +STRICT=0 + +CHECKED=0 +CHECKED_ADDRESS=0 +CHECKED_UNDEFINED=0 + +REQUIRES_RUNPREFIX=0 + + +# get commandline or defaults + +CPPFLAGS := $(CPPFLAGS) +CXXFLAGS := $(CXXFLAGS) +CFLAGS := $(CFLAGS) +LDFLAGS := $(LDFLAGS) +LDLIBS := $(LDLIBS) +ARFLAGS := $(ARFLAGS) + +PREFIX := $(PREFIX) +ifeq ($(PREFIX),) +PREFIX := /usr/local +endif + +MANDIR ?= $(PREFIX)/share/man +#DESTDIR := $(DESTDIR) +#ifeq ($(DESTDIR),) +#DESTDIR := bin/dest +#endif + + +# version + +include libopenmpt/libopenmpt_version.mk + +LIBOPENMPT_SO_VERSION=$(LIBOPENMPT_LTVER_CURRENT) + + +# host setup + +ifeq ($(OS),Windows_NT) + +HOST=windows +HOST_FLAVOUR= + +RM = del /q /f +RMTREE = del /q /f /s +INSTALL = echo install +INSTALL_MAKE_DIR = echo install +INSTALL_DIR = echo install +FIXPATH = $(subst /,\,$1) + +else + +HOST=unix +HOST_FLAVOUR= + +RM = rm -f +RMTREE = rm -rf +INSTALL = install +INSTALL_MAKE_DIR = install -d +INSTALL_DIR = cp -r -v +FIXPATH = $1 + +UNAME_S:=$(shell uname -s) +ifeq ($(UNAME_S),Darwin) +HOST_FLAVOUR=MACOSX +endif +ifeq ($(UNAME_S),Linux) +HOST_FLAVOUR=LINUX +endif +ifeq ($(UNAME_S),FreeBSD) +HOST_FLAVOUR=FREEBSD +endif + +endif + + +# compiler setup + +ifeq ($(CONFIG)x,x) + +include build/make/config-defaults.mk + +else + +include build/make/config-$(CONFIG).mk + +endif + + +# build setup + +BINDIR_MADE:=$(shell mkdir -p bin) + +ifeq ($(SOSUFFIXWINDOWS),1) +LIBOPENMPT_SONAME=libopenmpt-$(LIBOPENMPT_SO_VERSION)$(SOSUFFIX) +else +LIBOPENMPT_SONAME=libopenmpt$(SOSUFFIX).$(LIBOPENMPT_SO_VERSION) +endif + +INSTALL_PROGRAM = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 0644 +INSTALL_LIB = $(INSTALL) -m 0644 +INSTALL_DATA_DIR = $(INSTALL_DIR) +INSTALL_MAKE_DIR += -m 0755 + +CPPFLAGS += -Icommon -I. -Iinclude/modplug/include -Iinclude + +ifeq ($(MPT_COMPILER_GENERIC),1) + +CXXFLAGS += +CFLAGS += +LDFLAGS += +LDLIBS += +ARFLAGS += + +ifeq ($(DEBUG),1) +CPPFLAGS += -DMPT_BUILD_DEBUG +CXXFLAGS += -g +CFLAGS += -g +else +ifeq ($(OPTIMIZE),1) +CXXFLAGS += -O +CFLAGS += -O +endif +endif + +ifeq ($(CHECKED),1) +CPPFLAGS += -DMPT_BUILD_CHECKED +CXXFLAGS += -g +CFLAGS += -g +endif + +CXXFLAGS += -Wall +CFLAGS += -Wall + +else + +CXXFLAGS += -fvisibility=hidden +CFLAGS += -fvisibility=hidden +LDFLAGS += +LDLIBS += +ARFLAGS += + +ifeq ($(DEBUG),1) +CPPFLAGS += -DMPT_BUILD_DEBUG +CXXFLAGS += -O0 -g -fno-omit-frame-pointer +CFLAGS += -O0 -g -fno-omit-frame-pointer +else +ifeq ($(OPTIMIZE_SIZE),1) +CXXFLAGS += -ffunction-sections -fdata-sections -Os -ffast-math +CFLAGS += -ffunction-sections -fdata-sections -Os -ffast-math -fno-strict-aliasing +LDFLAGS += -Wl,--gc-sections +else +ifeq ($(OPTIMIZE),1) +CXXFLAGS += -O3 -ffast-math +CFLAGS += -O3 -ffast-math -fno-strict-aliasing +endif +endif +endif + +ifeq ($(CHECKED),1) +CPPFLAGS += -DMPT_BUILD_CHECKED +CXXFLAGS += -g -fno-omit-frame-pointer +CFLAGS += -g -fno-omit-frame-pointer +endif + +ifeq ($(FUZZ),1) +CPPFLAGS += +CXXFLAGS += -fno-omit-frame-pointer +CFLAGS += -fno-omit-frame-pointer +endif + +CXXFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CXXFLAGS_WARNINGS) +CFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CFLAGS_WARNINGS) + +endif + +ifeq ($(STRICT),1) +CXXFLAGS += -Werror +CFLAGS += -Werror +endif + +ifeq ($(DYNLINK),1) +LDFLAGS_RPATH += -Wl,-rpath,./bin +LDFLAGS_LIBOPENMPT += -Lbin +LDLIBS_LIBOPENMPT += -lopenmpt +endif + +ifeq ($(HOST),unix) + +ifeq ($(shell help2man --version > /dev/null 2>&1 && echo yes ),yes) +MPT_WITH_HELP2MAN := 1 +endif + +ifeq ($(shell doxygen --version > /dev/null 2>&1 && echo yes ),yes) +MPT_WITH_DOXYGEN := 1 +endif + +endif + +PC_LIBS_PRIVATE := +PC_LIBS_PRIVATE += $(CXXSTDLIB_PCLIBSPRIVATE) + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +NO_ZLIB:=1 +endif + +ifeq ($(NO_ZLIB),1) +else +#LDLIBS += -lz +ifeq ($(shell pkg-config --exists zlib && echo yes),yes) +CPPFLAGS_ZLIB := $(shell pkg-config --cflags-only-I zlib ) -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := $(shell pkg-config --libs-only-L zlib ) $(shell pkg-config --libs-only-other zlib ) +LDLIBS_ZLIB := $(shell pkg-config --libs-only-l zlib ) +PC_REQUIRES_ZLIB := zlib +else +ifeq ($(FORCE_DEPS),1) +$(error zlib not found) +else +$(warning warning: zlib not found) +endif +NO_ZLIB:=1 +endif +endif + +ifeq ($(NO_MPG123),1) +else +#LDLIBS += -lmpg123 +ifeq ($(shell pkg-config --exists 'libmpg123 >= 1.13.0' && echo yes),yes) +CPPFLAGS_MPG123 := $(shell pkg-config --cflags-only-I 'libmpg123 >= 1.13.0' ) -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := $(shell pkg-config --libs-only-L 'libmpg123 >= 1.13.0' ) $(shell pkg-config --libs-only-other 'libmpg123 >= 1.13.0' ) +LDLIBS_MPG123 := $(shell pkg-config --libs-only-l 'libmpg123 >= 1.13.0' ) +PC_REQUIRES_MPG123 := libmpg123 +else +ifeq ($(FORCE_DEPS),1) +$(error mpg123 not found) +else +$(warning warning: mpg123 not found) +endif +NO_MPG123:=1 +endif +endif + +ifeq ($(NO_OGG),1) +else +#LDLIBS += -logg +ifeq ($(shell pkg-config --exists ogg && echo yes),yes) +CPPFLAGS_OGG := $(shell pkg-config --cflags-only-I ogg ) -DMPT_WITH_OGG +LDFLAGS_OGG := $(shell pkg-config --libs-only-L ogg ) $(shell pkg-config --libs-only-other ogg ) +LDLIBS_OGG := $(shell pkg-config --libs-only-l ogg ) +PC_REQUIRES_OGG := ogg +else +ifeq ($(FORCE_DEPS),1) +$(error ogg not found) +else +$(warning warning: ogg not found) +endif +NO_OGG:=1 +endif +endif + +ifeq ($(NO_VORBIS),1) +else +#LDLIBS += -lvorbis +ifeq ($(shell pkg-config --exists vorbis && echo yes),yes) +CPPFLAGS_VORBIS := $(shell pkg-config --cflags-only-I vorbis ) -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := $(shell pkg-config --libs-only-L vorbis ) $(shell pkg-config --libs-only-other vorbis ) +LDLIBS_VORBIS := $(shell pkg-config --libs-only-l vorbis ) +PC_REQUIRES_VORBIS := vorbis +else +ifeq ($(FORCE_DEPS),1) +$(error vorbis not found) +else +$(warning warning: vorbis not found) +endif +NO_VORBIS:=1 +endif +endif + +ifeq ($(NO_VORBISFILE),1) +else +#LDLIBS += -lvorbisfile +ifeq ($(shell pkg-config --exists vorbisfile && echo yes),yes) +CPPFLAGS_VORBISFILE := $(shell pkg-config --cflags-only-I vorbisfile ) -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := $(shell pkg-config --libs-only-L vorbisfile ) $(shell pkg-config --libs-only-other vorbisfile ) +LDLIBS_VORBISFILE := $(shell pkg-config --libs-only-l vorbisfile ) +PC_REQUIRES_VORBISFILE := vorbisfile +else +ifeq ($(FORCE_DEPS),1) +$(error vorbisfile not found) +else +$(warning warning: vorbisfile not found) +endif +NO_VORBISFILE:=1 +endif +endif + +ifeq ($(NO_SDL2),1) +else +#LDLIBS += -lsdl2 +ifeq ($(shell pkg-config --exists sdl2 && echo yes),yes) +CPPFLAGS_SDL := $(shell pkg-config --cflags-only-I sdl2 ) -DMPT_WITH_SDL2 +LDFLAGS_SDL := $(shell pkg-config --libs-only-L sdl2 ) $(shell pkg-config --libs-only-other sdl2 ) +LDLIBS_SDL := $(shell pkg-config --libs-only-l sdl2 ) +NO_SDL:=1 +else +ifeq ($(FORCE_DEPS),1) +$(error sdl2 not found) +else +$(warning warning: sdl2 not found) +endif +NO_SDL2:=1 +endif +endif + +ifeq ($(NO_SDL),1) +else +#LDLIBS += -lsdl +ifeq ($(shell pkg-config --exists sdl && echo yes),yes) +CPPFLAGS_SDL := $(shell pkg-config --cflags-only-I sdl ) -DMPT_WITH_SDL +LDFLAGS_SDL := $(shell pkg-config --libs-only-L sdl ) $(shell pkg-config --libs-only-other sdl ) +LDLIBS_SDL := $(shell pkg-config --libs-only-l sdl ) +else +ifeq ($(FORCE_DEPS),1) +$(error sdl not found) +else +$(warning warning: sdl not found) +endif +NO_SDL:=1 +endif +endif + +ifeq ($(NO_PORTAUDIO),1) +else +#LDLIBS += -lportaudio +ifeq ($(shell pkg-config --exists portaudio-2.0 && echo yes),yes) +CPPFLAGS_PORTAUDIO := $(shell pkg-config --cflags-only-I portaudio-2.0 ) -DMPT_WITH_PORTAUDIO +LDFLAGS_PORTAUDIO := $(shell pkg-config --libs-only-L portaudio-2.0 ) $(shell pkg-config --libs-only-other portaudio-2.0 ) +LDLIBS_PORTAUDIO := $(shell pkg-config --libs-only-l portaudio-2.0 ) +else +ifeq ($(FORCE_DEPS),1) +$(error portaudio not found) +else +$(warning warning: portaudio not found) +endif +NO_PORTAUDIO:=1 +endif +endif + +ifeq ($(NO_PORTAUDIOCPP),1) +else +#LDLIBS += -lportaudiocpp +ifeq ($(shell pkg-config --exists portaudiocpp && echo yes),yes) +CPPFLAGS_PORTAUDIOCPP := $(shell pkg-config --cflags-only-I portaudiocpp ) -DMPT_WITH_PORTAUDIOCPP +LDFLAGS_PORTAUDIOCPP := $(shell pkg-config --libs-only-L portaudiocpp ) $(shell pkg-config --libs-only-other portaudiocpp ) +LDLIBS_PORTAUDIOCPP := $(shell pkg-config --libs-only-l portaudiocpp ) +else +ifeq ($(FORCE_DEPS),1) +$(error portaudiocpp not found) +else +$(warning warning: portaudiocpp not found) +endif +NO_PORTAUDIOCPP:=1 +endif +endif + +ifeq ($(NO_PULSEAUDIO),1) +else +#LDLIBS += -lpulse-simple +ifeq ($(shell pkg-config --exists libpulse libpulse-simple && echo yes),yes) +CPPFLAGS_PULSEAUDIO := $(shell pkg-config --cflags-only-I libpulse libpulse-simple ) -DMPT_WITH_PULSEAUDIO +LDFLAGS_PULSEAUDIO := $(shell pkg-config --libs-only-L libpulse libpulse-simple ) $(shell pkg-config --libs-only-other libpulse libpulse-simple ) +LDLIBS_PULSEAUDIO := $(shell pkg-config --libs-only-l libpulse libpulse-simple ) +else +ifeq ($(FORCE_DEPS),1) +$(error pulseaudio not found) +else +$(warning warning: pulseaudio not found) +endif +NO_PULSEAUDIO:=1 +endif +endif + +ifeq ($(NO_FLAC),1) +else +#LDLIBS += -lFLAC +ifeq ($(shell pkg-config --exists 'flac >= 1.3.0' && echo yes),yes) +CPPFLAGS_FLAC := $(shell pkg-config --cflags-only-I 'flac >= 1.3.0' ) -DMPT_WITH_FLAC +LDFLAGS_FLAC := $(shell pkg-config --libs-only-L 'flac >= 1.3.0' ) $(shell pkg-config --libs-only-other 'flac >= 1.3.0' ) +LDLIBS_FLAC := $(shell pkg-config --libs-only-l 'flac >= 1.3.0' ) +else +ifeq ($(FORCE_DEPS),1) +$(error flac not found) +else +$(warning warning: flac not found) +endif +NO_FLAC:=1 +endif +endif + +ifeq ($(NO_SNDFILE),1) +else +#LDLIBS += -lsndfile +ifeq ($(shell pkg-config --exists sndfile && echo yes),yes) +CPPFLAGS_SNDFILE := $(shell pkg-config --cflags-only-I sndfile ) -DMPT_WITH_SNDFILE +LDFLAGS_SNDFILE := $(shell pkg-config --libs-only-L sndfile ) $(shell pkg-config --libs-only-other sndfile ) +LDLIBS_SNDFILE := $(shell pkg-config --libs-only-l sndfile ) +else +ifeq ($(FORCE_DEPS),1) +$(error sndfile not found) +else +$(warning warning: sndfile not found) +endif +NO_SNDFILE:=1 +endif +endif + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +CPPFLAGS += -DMPT_BUILD_HACK_ARCHIVE_SUPPORT +endif + +CPPFLAGS += $(CPPFLAGS_ZLIB) $(CPPFLAGS_MPG123) $(CPPFLAGS_OGG) $(CPPFLAGS_VORBIS) $(CPPFLAGS_VORBISFILE) +LDFLAGS += $(LDFLAGS_ZLIB) $(LDFLAGS_MPG123) $(LDFLAGS_OGG) $(LDFLAGS_VORBIS) $(LDFLAGS_VORBISFILE) +LDLIBS += $(LDLIBS_ZLIB) $(LDLIBS_MPG123) $(LDLIBS_OGG) $(LDLIBS_VORBIS) $(LDLIBS_VORBISFILE) + +CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_SDL) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) +LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_SDL) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) +LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_SDL) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) + + +%: %.o + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +%.o: %.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(OUTPUT_OPTION) $< + +%.o: %.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< + +%.test.o: %.cpp + $(INFO) [CXX-TEST] $< + $(VERYSILENT)$(CXX) -DLIBOPENMPT_BUILD_TEST $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.test.d + $(SILENT)$(COMPILE.cc) -DLIBOPENMPT_BUILD_TEST $(OUTPUT_OPTION) $< + +%.test.o: %.c + $(INFO) [CC-TEST] $< + $(VERYSILENT)$(CC) -DLIBOPENMPT_BUILD_TEST $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.test.d + $(SILENT)$(COMPILE.c) -DLIBOPENMPT_BUILD_TEST $(OUTPUT_OPTION) $< + +%.tar.gz: %.tar + $(INFO) [GZIP] $< + $(SILENT)gzip --rsyncable --no-name --best > $@ < $< + + +-include build/dist.mk +DIST_LIBOPENMPT_VERSION_PURE:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL) +CPPFLAGS += -Ibuild/svn_version +ifeq ($(MPT_SVNVERSION),) +SVN_INFO:=$(shell svn info . > /dev/null 2>&1 ; echo $$? ) +ifeq ($(SVN_INFO),0) +# in svn checkout +MPT_SVNVERSION := $(shell svnversion -n . | tr ':' '-' ) +MPT_SVNURL := $(shell svn info --xml | grep '^' | sed 's///g' | sed 's/<\/url>//g' ) +MPT_SVNDATE := $(shell svn info --xml | grep '^' | sed 's///g' | sed 's/<\/date>//g' ) +CPPFLAGS += -D MPT_SVNURL=\"$(MPT_SVNURL)\" -D MPT_SVNVERSION=\"$(MPT_SVNVERSION)\" -D MPT_SVNDATE=\"$(MPT_SVNDATE)\" +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +else +GIT_STATUS:=$(shell git status > /dev/null 2>&1 ; echo $$? ) +ifeq ($(GIT_STATUS),0) +# in git chechout +MPT_SVNVERSION := $(shell git log --grep=git-svn-id -n 1 | grep git-svn-id | tail -n 1 | tr ' ' '\n' | tail -n 2 | head -n 1 | sed 's/@/ /g' | awk '{print $$2;}' )$(shell if [ $$(git rev-list $$(git log --grep=git-svn-id -n 1 --format=format:'%H') ^$$(git log -n 1 --format=format:'%H') --count ) -ne 0 ] ; then echo M ; fi ) +MPT_SVNURL := $(shell git log --grep=git-svn-id -n 1 | grep git-svn-id | tail -n 1 | tr ' ' '\n' | tail -n 2 | head -n 1 | sed 's/@/ /g' | awk '{print $$1;}' ) +MPT_SVNDATE := $(shell git log -n 1 --date=iso --format=format:'%cd' | sed 's/ +0000/Z/g' | tr ' ' 'T' ) +CPPFLAGS += -D MPT_SVNURL=\"$(MPT_SVNURL)\" -D MPT_SVNVERSION=\"$(MPT_SVNVERSION)\" -D MPT_SVNDATE=\"$(MPT_SVNDATE)\" +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +else +# not in svn checkout +DIST_OPENMPT_VERSION:=rUNKNOWN +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+rUNKNOWN +endif +endif +endif +else +# in dist package +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +endif +DIST_LIBOPENMPT_TARBALL_VERSION:=$(DIST_LIBOPENMPT_VERSION_PURE) + +ifeq ($(MPT_SVNVERSION),) +else +MPT_SVNREVISION := $(shell echo $(MPT_SVNVERSION) | sed 's/M//g' | sed 's/S//g' | sed 's/P//g' | sed -E 's/([0-9]+)-//g' ) +MPT_SVNDIRTY := $(shell echo $(MPT_SVNVERSION) | grep -v 'M\|S\|P' >/dev/null 2>&1 ; echo $$? ) +MPT_SVNMIXED := $(shell echo $(MPT_SVNVERSION) | grep -v '-' >/dev/null 2>&1; echo $$? ) +endif + + +CPPFLAGS += -DLIBOPENMPT_BUILD + + +COMMON_CXX_SOURCES += \ + $(sort $(wildcard common/*.cpp)) \ + +SOUNDLIB_CXX_SOURCES += \ + $(COMMON_CXX_SOURCES) \ + $(sort $(wildcard soundbase/*.cpp)) \ + $(sort $(wildcard soundlib/*.cpp)) \ + $(sort $(wildcard soundlib/plugins/*.cpp)) \ + $(sort $(wildcard soundlib/plugins/dmo/*.cpp)) \ + $(sort $(wildcard sounddsp/*.cpp)) \ + + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +SOUNDLIB_CXX_SOURCES += $(sort $(wildcard unarchiver/*.cpp)) +endif + +LIBOPENMPT_CXX_SOURCES += \ + $(SOUNDLIB_CXX_SOURCES) \ + libopenmpt/libopenmpt_c.cpp \ + libopenmpt/libopenmpt_cxx.cpp \ + libopenmpt/libopenmpt_impl.cpp \ + libopenmpt/libopenmpt_ext_impl.cpp \ + +include/miniz/miniz.o : CFLAGS+=$(CFLAGS_SILENT) +include/miniz/miniz.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(NO_ZLIB),1) +LIBOPENMPT_C_SOURCES += include/miniz/miniz.c +LIBOPENMPTTEST_C_SOURCES += include/miniz/miniz.c +CPPFLAGS += -DMPT_WITH_MINIZ +endif + +include/minimp3/minimp3.o : CFLAGS+=$(CFLAGS_SILENT) +include/minimp3/minimp3.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(NO_MPG123),1) +ifeq ($(USE_MINIMP3),1) +LIBOPENMPT_C_SOURCES += include/minimp3/minimp3.c +LIBOPENMPTTEST_C_SOURCES += include/minimp3/minimp3.c +CPPFLAGS += -DMPT_WITH_MINIMP3 +endif +endif + +include/stb_vorbis/stb_vorbis.o : CFLAGS+=$(CFLAGS_SILENT) +include/stb_vorbis/stb_vorbis.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(NO_OGG),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +ifeq ($(NO_VORBIS),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +ifeq ($(NO_VORBISFILE),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +endif +endif +endif + +LIBOPENMPT_OBJECTS += $(LIBOPENMPT_CXX_SOURCES:.cpp=.o) $(LIBOPENMPT_C_SOURCES:.c=.o) +LIBOPENMPT_DEPENDS = $(LIBOPENMPT_OBJECTS:.o=.d) +ALL_OBJECTS += $(LIBOPENMPT_OBJECTS) +ALL_DEPENDS += $(LIBOPENMPT_DEPENDS) + +ifeq ($(DYNLINK),1) +OUTPUT_LIBOPENMPT += bin/libopenmpt$(SOSUFFIX) +else +OBJECTS_LIBOPENMPT += $(LIBOPENMPT_OBJECTS) +endif + + +LIBOPENMPT_MODPLUG_C_SOURCES += \ + libopenmpt/libopenmpt_modplug.c \ + +LIBOPENMPT_MODPLUG_CPP_SOURCES += \ + libopenmpt/libopenmpt_modplug_cpp.cpp \ + +LIBOPENMPT_MODPLUG_OBJECTS = $(LIBOPENMPT_MODPLUG_C_SOURCES:.c=.o) $(LIBOPENMPT_MODPLUG_CPP_SOURCES:.cpp=.o) +LIBOPENMPT_MODPLUG_DEPENDS = $(LIBOPENMPT_MODPLUG_OBJECTS:.o=.d) +ALL_OBJECTS += $(LIBOPENMPT_MODPLUG_OBJECTS) +ALL_DEPENDS += $(LIBOPENMPT_MODPLUG_DEPENDS) + + +OPENMPT123_CXX_SOURCES += \ + $(sort $(wildcard openmpt123/*.cpp)) \ + +OPENMPT123_OBJECTS += $(OPENMPT123_CXX_SOURCES:.cpp=.o) +OPENMPT123_DEPENDS = $(OPENMPT123_OBJECTS:.o=.d) +ALL_OBJECTS += $(OPENMPT123_OBJECTS) +ALL_DEPENDS += $(OPENMPT123_DEPENDS) + + +LIBOPENMPTTEST_CXX_SOURCES += \ + libopenmpt/libopenmpt_test.cpp \ + $(SOUNDLIB_CXX_SOURCES) \ + $(sort $(wildcard test/*.cpp)) \ + +LIBOPENMPTTEST_OBJECTS = $(LIBOPENMPTTEST_CXX_SOURCES:.cpp=.test.o) $(LIBOPENMPTTEST_C_SOURCES:.c=.test.o) +LIBOPENMPTTEST_DEPENDS = $(LIBOPENMPTTEST_CXX_SOURCES:.cpp=.test.d) $(LIBOPENMPTTEST_C_SOURCES:.c=.test.d) +ALL_OBJECTS += $(LIBOPENMPTTEST_OBJECTS) +ALL_DEPENDS += $(LIBOPENMPTTEST_DEPENDS) + + +EXAMPLES_CXX_SOURCES += $(sort $(wildcard examples/*.cpp)) +EXAMPLES_C_SOURCES += $(sort $(wildcard examples/*.c)) + +EXAMPLES_OBJECTS += $(EXAMPLES_CXX_SOURCES:.cpp=.o) +EXAMPLES_OBJECTS += $(EXAMPLES_C_SOURCES:.c=.o) +EXAMPLES_DEPENDS = $(EXAMPLES_OBJECTS:.o=.d) +ALL_OBJECTS += $(EXAMPLES_OBJECTS) +ALL_DEPENDS += $(EXAMPLES_DEPENDS) + + +FUZZ_CXX_SOURCES += $(sort $(wildcard contrib/fuzzing/*.cpp)) +FUZZ_C_SOURCES += $(sort $(wildcard contrib/fuzzing/*.c)) + +FUZZ_OBJECTS += $(FUZZ_CXX_SOURCES:.cpp=.o) +FUZZ_OBJECTS += $(FUZZ_C_SOURCES:.c=.o) +FUZZ_DEPENDS = $(FUZZ_OBJECTS:.o=.d) +ALL_OBJECTS += $(FUZZ_OBJECTS) +ALL_DEPENDS += $(FUZZ_DEPENDS) + + +.PHONY: all +all: + +-include $(ALL_DEPENDS) + +ifeq ($(DYNLINK),1) +OUTPUTS += bin/libopenmpt$(SOSUFFIX) +OUTPUTS += bin/libopenmpt_modplug$(SOSUFFIX) +endif +ifeq ($(SHARED_LIB),1) +OUTPUTS += bin/libopenmpt$(SOSUFFIX) +endif +ifeq ($(STATIC_LIB),1) +OUTPUTS += bin/libopenmpt.a +endif +ifeq ($(OPENMPT123),1) +OUTPUTS += bin/openmpt123$(EXESUFFIX) +endif +ifeq ($(EXAMPLES),1) +ifeq ($(NO_PORTAUDIO),1) +else +OUTPUTS += bin/libopenmpt_example_c$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_mem$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_unsafe$(EXESUFFIX) +endif +ifeq ($(NO_PORTAUDIOCPP),1) +else +OUTPUTS += bin/libopenmpt_example_cxx$(EXESUFFIX) +endif +OUTPUTS += bin/libopenmpt_example_c_pipe$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_stdout$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_probe$(EXESUFFIX) +endif +ifeq ($(FUZZ),1) +OUTPUTS += bin/fuzz$(EXESUFFIX) +endif +ifeq ($(TEST),1) +OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) +endif +ifeq ($(HOST),unix) +OUTPUTS += bin/libopenmpt.pc +endif +ifeq ($(OPENMPT123),1) +ifeq ($(MPT_WITH_HELP2MAN),1) +OUTPUTS += bin/openmpt123.1 +endif +endif +ifeq ($(SHARED_SONAME),1) +LIBOPENMPT_LDFLAGS += -Wl,-soname,$(LIBOPENMPT_SONAME) +endif + +MISC_OUTPUTS += bin/openmpt123$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_mem$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_probe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_cxx$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout$(EXESUFFIX).norpath +MISC_OUTPUTS += libopenmpt$(SOSUFFIX) +MISC_OUTPUTS += bin/.docs +MISC_OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) +MISC_OUTPUTS += bin/libopenmpt_test.js.mem +MISC_OUTPUTS += bin/made.docs +MISC_OUTPUTS += bin/$(LIBOPENMPT_SONAME) +MISC_OUTPUTS += bin/libopenmpt.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.js.mem +MISC_OUTPUTS += bin/openmpt.a +#old +MISC_OUTPUTS += bin/libopenmpt_example_c_safe$(EXESUFFIX) +MISC_OUTPUTS += bin/libopenmpt_example_c_safe$(EXESUFFIX).norpath + +MISC_OUTPUTDIRS += bin/dest +MISC_OUTPUTDIRS += bin/docs + +DIST_OUTPUTS += bin/dist.mk +DIST_OUTPUTS += bin/svn_version_dist.h +DIST_OUTPUTS += bin/dist.tar +DIST_OUTPUTS += bin/dist-tar.tar +DIST_OUTPUTS += bin/dist-zip.tar +DIST_OUTPUTS += bin/dist-doc.tar +DIST_OUTPUTS += bin/dist-autotools.tar +DIST_OUTPUTS += bin/dist-js.tar +DIST_OUTPUTS += bin/made.docs + +DIST_OUTPUTDIRS += bin/dist +DIST_OUTPUTDIRS += bin/dist-doc +DIST_OUTPUTDIRS += bin/dist-tar +DIST_OUTPUTDIRS += bin/dist-zip +DIST_OUTPUTDIRS += bin/dist-autotools +DIST_OUTPUTDIRS += bin/dist-js +DIST_OUTPUTDIRS += bin/docs + + + +ifeq ($(ONLY_TEST),1) +all: bin/libopenmpt_test$(EXESUFFIX) +else +all: $(OUTPUTS) +endif + +.PHONY: docs +docs: bin/made.docs + +.PHONY: doc +doc: bin/made.docs + +bin/made.docs: + $(VERYSILENT)mkdir -p bin/docs + $(INFO) [DOXYGEN] libopenmpt +ifeq ($(SILENT_DOCS),1) + $(SILENT) ( cat libopenmpt/Doxyfile ; echo 'PROJECT_NUMBER = "$(DIST_LIBOPENMPT_VERSION)"' ; echo 'WARN_IF_DOC_ERROR = NO' ) | doxygen - +else + $(SILENT) ( cat libopenmpt/Doxyfile ; echo 'PROJECT_NUMBER = "$(DIST_LIBOPENMPT_VERSION)"' ) | doxygen - +endif + $(VERYSILENT)touch $@ + +.PHONY: check +check: test + +.PHONY: test +test: bin/libopenmpt_test$(EXESUFFIX) +ifeq ($(REQUIRES_RUNPREFIX),1) + cd bin && $(RUNPREFIX) libopenmpt_test$(EXESUFFIX) +else + bin/libopenmpt_test$(EXESUFFIX) +endif + +bin/libopenmpt_test$(EXESUFFIX): $(LIBOPENMPTTEST_OBJECTS) + $(INFO) [LD-TEST] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(TEST_LDFLAGS) $(LIBOPENMPTTEST_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ + +bin/libopenmpt.pc: + $(INFO) [GEN] $@ + $(VERYSILENT)rm -rf $@ + $(VERYSILENT)echo > $@.tmp + $(VERYSILENT)echo 'prefix=$(PREFIX)' >> $@.tmp + $(VERYSILENT)echo 'exec_prefix=$${prefix}' >> $@.tmp + $(VERYSILENT)echo 'libdir=$${exec_prefix}/lib' >> $@.tmp + $(VERYSILENT)echo 'includedir=$${prefix}/include' >> $@.tmp + $(VERYSILENT)echo '' >> $@.tmp + $(VERYSILENT)echo 'Name: libopenmpt' >> $@.tmp + $(VERYSILENT)echo 'Description: Tracker module player based on OpenMPT' >> $@.tmp + $(VERYSILENT)echo 'Version: $(DIST_LIBOPENMPT_VERSION)' >> $@.tmp + $(VERYSILENT)echo 'Requires.private: $(PC_REQUIRES_ZLIB) $(PC_REQUIRES_MPG123) $(PC_REQUIRES_OGG) $(PC_REQUIRES_VORBIS) $(PC_REQUIRES_VORBISFILE)' >> $@.tmp + $(VERYSILENT)echo 'Libs: -L$${libdir} -lopenmpt' >> $@.tmp + $(VERYSILENT)echo 'Libs.private: $(PC_LIBS_PRIVATE)' >> $@.tmp + $(VERYSILENT)echo 'Cflags: -I$${includedir}' >> $@.tmp + $(VERYSILENT)mv $@.tmp $@ + +.PHONY: install +install: $(OUTPUTS) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/include/libopenmpt + $(INSTALL_DATA) libopenmpt/libopenmpt_config.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_config.h + $(INSTALL_DATA) libopenmpt/libopenmpt_version.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_version.h + $(INSTALL_DATA) libopenmpt/libopenmpt.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_buffer.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_buffer.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_fd.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_fd.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_file.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_file.h + $(INSTALL_DATA) libopenmpt/libopenmpt.hpp $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt.hpp + $(INSTALL_DATA) libopenmpt/libopenmpt_ext.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_ext.h + $(INSTALL_DATA) libopenmpt/libopenmpt_ext.hpp $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_ext.hpp + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib/pkgconfig + $(INSTALL_DATA) bin/libopenmpt.pc $(DESTDIR)$(PREFIX)/lib/pkgconfig/libopenmpt.pc +ifeq ($(SHARED_LIB),1) +ifeq ($(SHARED_SONAME),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/$(LIBOPENMPT_SONAME) $(DESTDIR)$(PREFIX)/lib/$(LIBOPENMPT_SONAME) + ln -sf $(LIBOPENMPT_SONAME) $(DESTDIR)$(PREFIX)/lib/libopenmpt$(SOSUFFIX) +else + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/libopenmpt$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt$(SOSUFFIX) +endif + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib +endif +ifeq ($(STATIC_LIB),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_DATA) bin/libopenmpt.a $(DESTDIR)$(PREFIX)/lib/libopenmpt.a +endif +ifeq ($(OPENMPT123),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/bin + $(INSTALL_PROGRAM) bin/openmpt123$(EXESUFFIX).norpath $(DESTDIR)$(PREFIX)/bin/openmpt123$(EXESUFFIX) +ifeq ($(MPT_WITH_HELP2MAN),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(MANDIR)/man1 + $(INSTALL_DATA) bin/openmpt123.1 $(DESTDIR)$(MANDIR)/man1/openmpt123.1 +endif +endif + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt + $(INSTALL_DATA) LICENSE $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/LICENSE + $(INSTALL_DATA) README.md $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/README.md + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples + $(INSTALL_DATA) examples/libopenmpt_example_c.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c.c + $(INSTALL_DATA) examples/libopenmpt_example_c_mem.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_mem.c + $(INSTALL_DATA) examples/libopenmpt_example_c_probe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_probe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_unsafe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_unsafe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_pipe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_pipe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_stdout.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_stdout.c + $(INSTALL_DATA) examples/libopenmpt_example_cxx.cpp $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_cxx.cpp + +.PHONY: install-doc +install-doc: bin/made.docs +ifeq ($(MPT_WITH_DOXYGEN),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/html/ + $(INSTALL_DATA_DIR) bin/docs/html $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/html +endif + +.PHONY: install-openmpt-modplug +install-openmpt-modplug: $(OUTPUTS) +ifeq ($(SHARED_LIB),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX) + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX).1 + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX).1.0.0 +endif + +.PHONY: install-modplug +install-modplug: $(OUTPUTS) +ifeq ($(SHARED_LIB),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX) + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX).1 + $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX).1.0.0 +endif + +.PHONY: dist +dist: bin/dist-tar.tar bin/dist-zip.tar bin/dist-doc.tar + +.PHONY: dist-tar +dist-tar: bin/dist-tar.tar + +bin/dist-tar.tar: bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar.gz + rm -rf bin/dist-tar.tar + cd bin/dist-tar/ && rm -rf libopenmpt + cd bin/dist-tar/ && mkdir -p libopenmpt/src.makefile/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-tar/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar.gz libopenmpt/src.makefile/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-tar/ && tar cvf ../dist-tar.tar libopenmpt + +.PHONY: dist-zip +dist-zip: bin/dist-zip.tar + +bin/dist-zip.tar: bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip + rm -rf bin/dist-zip.tar + cd bin/dist-zip/ && rm -rf libopenmpt + cd bin/dist-zip/ && mkdir -p libopenmpt/src.msvc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-zip/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip libopenmpt/src.msvc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-zip/ && tar cvf ../dist-zip.tar libopenmpt + +.PHONY: dist-doc +dist-doc: bin/dist-doc.tar + +bin/dist-doc.tar: bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar.gz + rm -rf bin/dist-doc.tar + cd bin/dist-doc/ && rm -rf libopenmpt + cd bin/dist-doc/ && mkdir -p libopenmpt/doc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-doc/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar.gz libopenmpt/doc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-doc/ && tar cvf ../dist-doc.tar libopenmpt + +.PHONY: dist-js +dist-js: bin/dist-js.tar + +bin/dist-js.tar: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz + rm -rf bin/dist-js.tar + cd bin/dist-js/ && rm -rf libopenmpt + cd bin/dist-js/ && mkdir -p libopenmpt/dev.js/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-js/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz libopenmpt/dev.js/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-js/ && tar cvf ../dist-js.tar libopenmpt + +.PHONY: bin/dist.mk +bin/dist.mk: + rm -rf $@ + echo > $@.tmp + echo 'MPT_SVNVERSION=$(MPT_SVNVERSION)' >> $@.tmp + echo 'MPT_SVNURL=$(MPT_SVNURL)' >> $@.tmp + echo 'MPT_SVNDATE=$(MPT_SVNDATE)' >> $@.tmp + mv $@.tmp $@ + +.PHONY: bin/svn_version_dist.h +bin/svn_version_dist.h: + rm -rf $@ + echo > $@.tmp + echo '#pragma once' >> $@.tmp + echo '#define OPENMPT_VERSION_SVNVERSION "$(MPT_SVNVERSION)"' >> $@.tmp + echo '#define OPENMPT_VERSION_REVISION $(MPT_SVNREVISION)' >> $@.tmp + echo '#define OPENMPT_VERSION_DIRTY $(MPT_SVNDIRTY)' >> $@.tmp + echo '#define OPENMPT_VERSION_MIXEDREVISIONS $(MPT_SVNMIXED)' >> $@.tmp + echo '#define OPENMPT_VERSION_URL "$(MPT_SVNURL)"' >> $@.tmp + echo '#define OPENMPT_VERSION_DATE "$(MPT_SVNDATE)"' >> $@.tmp + echo '#define OPENMPT_VERSION_IS_PACKAGE 1' >> $@.tmp + echo >> $@.tmp + mv $@.tmp $@ + +.PHONY: bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar +bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar: docs + mkdir -p bin/dist-doc + rm -rf bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc + mkdir -p bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc + cp -Rv bin/docs/html bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc/docs + cd bin/dist-doc/ && tar cv libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc > libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar + +.PHONY: bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar +bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin/svn_version_dist.h + mkdir -p bin/dist-tar + rm -rf bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include + svn export ./LICENSE bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE + svn export ./README.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md + svn export ./Makefile bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile + svn export ./bin bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + svn export ./build/android_ndk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/android_ndk + svn export ./build/make bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/make + svn export ./build/svn_version bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version + svn export ./common bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common + svn export ./soundbase bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase + svn export ./soundlib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib + svn export ./sounddsp bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp + svn export ./test bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/test + svn export ./libopenmpt bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt + svn export ./examples bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/examples + svn export ./openmpt123 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/openmpt123 + svn export ./contrib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/contrib + svn export ./include/minimp3 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/minimp3 + svn export ./include/miniz bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/miniz + svn export ./include/modplug bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/modplug + svn export ./include/stb_vorbis bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis + cp bin/dist.mk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/dist.mk + cp bin/svn_version_dist.h bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version/svn_version.h + cd bin/dist-tar/ && tar cv libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar + +.PHONY: bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip +bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn_version_dist.h + mkdir -p bin/dist-zip + rm -rf bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include + svn export ./LICENSE bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE --native-eol CRLF + svn export ./README.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md --native-eol CRLF + svn export ./Makefile bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile --native-eol CRLF + svn export ./bin bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin --native-eol CRLF + svn export ./build/svn_version bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version --native-eol CRLF + svn export ./build/vcpkg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vcpkg --native-eol CRLF + svn export ./build/vs bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs --native-eol CRLF + svn export ./build/vs2015 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2015 --native-eol CRLF + svn export ./build/vs2015xp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2015xp --native-eol CRLF + svn export ./build/vs2017 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017 --native-eol CRLF + svn export ./build/vs2017xp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017xp --native-eol CRLF + svn export ./build/windesktop81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/windesktop81 --native-eol CRLF + svn export ./build/winstore82 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/winstore82 --native-eol CRLF + svn export ./build/download_externals.cmd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/download_externals.cmd --native-eol CRLF + svn export ./common bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common --native-eol CRLF + svn export ./soundbase bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase --native-eol CRLF + svn export ./soundlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib --native-eol CRLF + svn export ./sounddsp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp --native-eol CRLF + svn export ./test bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/test --native-eol CRLF + svn export ./libopenmpt bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt --native-eol CRLF + svn export ./examples bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/examples --native-eol CRLF + svn export ./openmpt123 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/openmpt123 --native-eol CRLF + svn export ./contrib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/contrib --native-eol CRLF + svn export ./include/minimp3 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/minimp3 --native-eol CRLF + svn export ./include/miniz bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/miniz --native-eol CRLF + svn export ./include/mpg123 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/mpg123 --native-eol CRLF + svn export ./include/flac bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/flac --native-eol CRLF + svn export ./include/portaudio bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/portaudio --native-eol CRLF + svn export ./include/modplug bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/modplug --native-eol CRLF + svn export ./include/foobar2000sdk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/foobar2000sdk --native-eol CRLF + svn export ./include/ogg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/ogg --native-eol CRLF + svn export ./include/pugixml bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/pugixml --native-eol CRLF + svn export ./include/stb_vorbis bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis --native-eol CRLF + svn export ./include/vorbis bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/vorbis --native-eol CRLF + svn export ./include/winamp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/winamp --native-eol CRLF + svn export ./include/xmplay bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/xmplay --native-eol CRLF + svn export ./include/zlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/zlib --native-eol CRLF + cp bin/dist.mk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/dist.mk + cp bin/svn_version_dist.h bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version/svn_version.h + cd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/ && zip -r ../libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip --compression-method deflate -9 * + +.PHONY: bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION).zip +bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION).zip: bin/svn_version_dist.h + mkdir -p bin/dist-zip + rm -rf bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION) + svn export ./ bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/ --native-eol CRLF + cp bin/svn_version_dist.h bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/common/svn_version_default/svn_version.h + cd bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/ && zip -r ../OpenMPT-src-$(DIST_OPENMPT_VERSION).zip --compression-method deflate -9 * + +.PHONY: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar +bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: bin/libopenmpt.js + mkdir -p bin/dist-js + rm -rf bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses + svn export ./LICENSE bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/license.txt + svn export ./include/miniz/miniz.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.miniz.txt + svn export ./include/stb_vorbis/stb_vorbis.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.stb_vorbis.txt + cp bin/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt.js + cp bin/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt.js.mem + cd bin/dist-js/ && tar cv libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar + +bin/libopenmpt.a: $(LIBOPENMPT_OBJECTS) + $(INFO) [AR] $@ + $(SILENT)$(AR) $(ARFLAGS) $@ $^ + +bin/libopenmpt$(SOSUFFIX): $(LIBOPENMPT_OBJECTS) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +ifeq ($(SHARED_SONAME),1) + $(SILENT)mv bin/libopenmpt$(SOSUFFIX) bin/$(LIBOPENMPT_SONAME) + $(SILENT)ln -sf $(LIBOPENMPT_SONAME) bin/libopenmpt$(SOSUFFIX) +endif + +bin/libopenmpt_modplug$(SOSUFFIX): $(LIBOPENMPT_MODPLUG_OBJECTS) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) -shared $(LDFLAGS_LIBOPENMPT) $(LIBOPENMPT_MODPLUG_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + +bin/openmpt123.1: bin/openmpt123$(EXESUFFIX) openmpt123/openmpt123.h2m + $(INFO) [HELP2MAN] $@ + $(SILENT)help2man --no-discard-stderr --no-info --version-option=--man-version --help-option=--man-help --include=openmpt123/openmpt123.h2m $< > $@ + +openmpt123/openmpt123.o: openmpt123/openmpt123.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CXXFLAGS_OPENMPT123) $(CPPFLAGS) $(CPPFLAGS_OPENMPT123) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(CXXFLAGS_OPENMPT123) $(CPPFLAGS_OPENMPT123) $(OUTPUT_OPTION) $< +bin/openmpt123$(EXESUFFIX): $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ +endif + +contrib/fuzzing/fuzz.o: contrib/fuzzing/fuzz.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +bin/fuzz$(EXESUFFIX): contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif + +examples/libopenmpt_example_c.o: examples/libopenmpt_example_c.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_mem.o: examples/libopenmpt_example_c_mem.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_unsafe.o: examples/libopenmpt_example_c_unsafe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_pipe.o: examples/libopenmpt_example_c_pipe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_stdout.o: examples/libopenmpt_example_c_stdout.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_probe.o: examples/libopenmpt_example_c_probe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_cxx.o: examples/libopenmpt_example_cxx.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CXXFLAGS_PORTAUDIOCPP) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIOCPP) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(CXXFLAGS_PORTAUDIOCPP) $(CPPFLAGS_PORTAUDIOCPP) $(OUTPUT_OPTION) $< +bin/libopenmpt_example_c$(EXESUFFIX): examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_mem$(EXESUFFIX): examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_unsafe$(EXESUFFIX): examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_pipe$(EXESUFFIX): examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_c_stdout$(EXESUFFIX): examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_c_probe$(EXESUFFIX): examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_cxx$(EXESUFFIX): examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ +endif + +.PHONY: clean +clean: + $(INFO) clean ... + $(SILENT)$(RM) $(call FIXPATH,$(OUTPUTS) $(ALL_OBJECTS) $(ALL_DEPENDS) $(MISC_OUTPUTS)) + $(SILENT)$(RMTREE) $(call FIXPATH,$(MISC_OUTPUTDIRS)) + +.PHONY: clean-dist +clean-dist: + $(INFO) clean-dist ... + $(SILENT)$(RM) $(call FIXPATH,$(DIST_OUTPUTS)) + $(SILENT)$(RMTREE) $(call FIXPATH,$(DIST_OUTPUTDIRS)) + +.PHONY: distversion +distversion: + $(SILENT)echo "$(DIST_LIBOPENMPT_VERSION)" + +.PHONY: distversion-pure +distversion-pure: + $(SILENT)echo "$(DIST_LIBOPENMPT_VERSION_PURE)" + +.PHONY: distversion-tarball +distversion-tarball: + $(SILENT)echo "$(DIST_LIBOPENMPT_TARBALL_VERSION)" diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md new file mode 100644 index 000000000..2ded2f96f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -0,0 +1,370 @@ + +README +====== + +OpenMPT and libopenmpt +====================== + + +How to compile +-------------- + + +### OpenMPT + + - Supported Visual Studio versions: + + - Visual Studio 2015 Update 3 Community/Professional/Enterprise + + To compile the project, open `build/vs2015/OpenMPT.sln` and hit the + compile button. + + - Visual Studio 2017 Community/Professional/Enterprise + + To compile the project, open `build/vs2017/OpenMPT.sln` and hit the + compile button. + + - The Windows 8.1 SDK and Microsoft Foundation Classes (MFC) are required to + build OpenMPT (both are included with Visual Studio, however may need to be + selected explicitly during setup). In order to build OpenMPT for Windows XP, + the XP targetting toolset also needs to be installed. + + - The VST and ASIO SDKs are needed for compiling with VST and ASIO support. + + If you don't want this, uncomment `#define NO_VST` and comment out + `#define MPT_WITH_ASIO` in the file `common/BuildSettings.h`. + + The ASIO and VST SDKs can be downloaded automatically on Windows 7 or later + with 7-Zip installed by just running the `build/download_externals.cmd` + script. + + If you do not want to or cannot use this script, you may follow these manual + steps instead: + + - ASIO: + + If you use `#define MPT_WITH_ASIO`, you will need to put the ASIO SDK in + the `include/ASIOSDK2` folder. The top level directory of the SDK is + already named `ASIOSDK2`, so simply move that directory in the include + folder. + + Please visit + [steinberg.net](http://www.steinberg.net/en/company/developers.html) to + download the SDK. + + - VST: + + If you don't use `#define NO_VST`, you will need to put the VST SDK in + the `include/vstsdk2.4` folder. + + Simply copy all files from the `VST3 SDK` folder in the SDK .zip file to + `include/vstsdk2.4/`. + + Note: OpenMPT makes use of the VST 2.4 specification only. The VST3 SDK + still contains all necessary files in the right locations. If you still + have the old VST 2.4 SDK laying around, this should also work fine. + + Please visit + [steinberg.net](http://www.steinberg.net/en/company/developers.html) to + download the SDK. + + If you need further help with the VST and ASIO SDKs, get in touch with the + main OpenMPT developers. + + - 7-Zip is required to be installed in the default path in order to build the + required files for OpenMPT Wine integration. + + Please visit [7-zip.org](http://www.7-zip.org/) to download 7-Zip. + + +### libopenmpt and openmpt123 + +For detailed requirements, see `libopenmpt/dox/quickstart.md`. + + - Autotools + + Grab a `libopenmpt-VERSION-autotools.tar.gz` tarball. + + ./configure + make + make check + sudo make install + + - Visual Studio: + + - You will find solutions for Visual Studio 2015 to 2017 in the + corresponding `build/vsVERSION/` folder. + Projects that target Windows versions before Windows 7 are available in + `build/vsVERSIONxp/` + Most projects are supported with any of the mentioned Visual Studio + verions, with the following exceptions: + + - in_openmpt: Requires Visual Studio with MFC. + + - xmp-openmpt: Requires Visual Studio with MFC. + + - You will need the Winamp 5 SDK and the XMPlay SDK if you want to + compile the plugins for these 2 players. They can be downloaded + automatically on Windows 7 or later with 7-Zip installed by just running + the `build/download_externals.cmd` script. + + If you do not want to or cannot use this script, you may follow these + manual steps instead: + + - Winamp 5 SDK: + + To build libopenmpt as a winamp input plugin, copy the contents of + `WA5.55_SDK.exe` to include/winamp/. + + Please visit + [winamp.com](http://wiki.winamp.com/wiki/Plug-in_Developer) to + download the SDK. + You can disable in_openmpt in the solution configuration. + + - XMPlay SDK: + + To build libopenmpt with XMPlay input plugin support, copy the + contents of xmp-sdk.zip into include/xmplay/. + + Please visit [un4seen.com](http://www.un4seen.com/xmplay.html) to + download the SDK. + You can disable xmp-openmpt in the solution configuration. + + - Makefile + + The makefile supports different build environments and targets via the + `CONFIG=` parameter directly to the make invocation. + Use `make CONFIG=$newconfig clean` when switching between different configs + because the makefile cleans only intermediates and target that are active + for the current config and no configuration state is kept around across + invocations. + + - mingw-w64: + + The required version is at least 4.8. + + make CONFIG=mingw64-win32 # for win32 + + make CONFIG=mingw64-win64 # for win64 + + - gcc or clang (on Unix-like systems, including Mac OS X with MacPorts): + + The minimum required compiler versions are: + + - gcc 4.8 + + - clang 3.4 + + The Makefile requires pkg-config for native builds. + For sound output in openmpt123, PortAudio or SDL is required. + openmpt123 can optionally use libflac and libsndfile to render PCM + files to disk. + + When using gcc, run: + + make CONFIG=gcc + + When using clang, it is recommended to do: + + make CONFIG=clang + + Otherwise, simply run + + make + + which will try to guess the compiler based on your operating system. + + - emscripten (on Unix-like systems): + + libopenmpt has been tested and verified to work with emscripten 1.31 or + later (earlier versions might or might not work). + + Run: + + make CONFIG=emscripten + + Running the test suite on the command line is also supported by using + node.js. Version 0.10.25 or greater has been tested. Earlier versions + might or might not work. Depending on how your distribution calls the + `node.js` binary, you might have to edit + `build/make/config-emscripten.mk`. + + - Haiku: + + To compile libopenmpt on Haiku (using the 32-bit gcc2h), run: + + make CONFIG=haiku + + - American Fuzzy Lop: + + To compile libopenmpt with fuzzing instrumentation for afl-fuzz, run: + + make CONFIG=afl + + For more detailed instructions, read contrib/fuzzing/readme.md + + - other compilers: + + To compiler libopenmpt with other C++11 compliant compilers, run: + + make CONFIG=generic + + + The `Makefile` supports some customizations. You might want to read the top + which should get you some possible make settings, like e.g. + `make DYNLINK=0` or similar. Cross compiling or different compiler would + best be implemented via new `config-*.mk` files. + + The `Makefile` also supports building doxygen documentation by using + + make doc + + Binaries and documentation can be installed systen-wide with + + make PREFIX=/yourprefix install + make PREFIX=/yourprefix install-doc + + Some systems (i.e. Linux) require running + + sudo ldconfig + + in order for the system linker to be able to pick up newly installed + libraries. + + `PREFIX` defaults to `/usr/local`. A `DESTDIR=` parameter is also + supported. + + - Android NDK + + See `build/android_ndk/README.AndroidNDK.txt`. + + + +Coding conventions +------------------ + + +### OpenMPT + +(see below for an example) + +- Place curly braces at the beginning of the line, not at the end +- Generally make use of the custom index types like `SAMPLEINDEX` or + `ORDERINDEX` when referring to samples, orders, etc. +- When changing playback behaviour, make sure that you use the function + `CSoundFile::IsCompatibleMode()` so that modules made with previous versions + of MPT still sound correct (if the change is extremely small, this might be + unnecessary) +- `CamelCase` function and variable names are preferred. + +#### OpenMPT code example + +~~~~{.cpp} +void Foo::Bar(int foobar) +{ + while(true) + { + // some code + } +} +~~~~ + + +### libopenmpt + +**Note:** +**This applies to `libopenmpt/` and `openmpt123/` directories only.** +**Use OpenMPT style (see above) otherwise.** + +The code generally tries to follow these conventions, but they are not +strictly enforced and there are valid reasons to diverge from these +conventions. Using common sense is recommended. + + - In general, the most important thing is to keep style consistent with + directly surrounding code. + - Use C++ std types when possible, prefer `std::size_t` and `std::int32_t` + over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`) + - Qualify namespaces explicitly, do not use `using`. + Members of `namespace openmpt` can be named without full namespace + qualification. + - Prefer the C++ version in `namespace std` if the same functionality is + provided by the C standard library as well. Also, include the C++ + version of C standard library headers (e.g. use `` instead of + ``. + - Do not use ANY locale-dependant C functions. For locale-dependant C++ + functionaly (especially iostream), always imbue the + `std::locale::classic()` locale. + - Prefer kernel_style_names over CamelCaseNames. + - If a folder (or one of its parent folders) contains .clang-format, + use clang-format v3.5 for indenting C++ and C files, otherwise: + - `{` are placed at the end of the opening line. + - Enclose even single statements in curly braces. + - Avoid placing single statements on the same line as the `if`. + - Opening parentheses are separated from keywords with a space. + - Opening parentheses are not separated from function names. + - Place spaces around operators and inside parentheses. + - Align `:` and `,` when inheriting or initializing members in a + constructor. + - The pointer `*` is separated from both the type and the variable name. + - Use tabs for identation, spaces for formatting. + Tabs should only appear at the very beginning of a line. + Do not assume any particular width of the TAB character. If width is + important for formatting reasons, use spaces. + - Use empty lines at will. + - API documentation is done with doxygen. + Use general C doxygen for the C API. + Use QT-style doxygen for the C++ API. + +#### libopenmpt indentation example + +~~~~{.cpp} +namespace openmpt { + +// This is totally meaningless code and just illustrates indentation. + +class foo + : public base + , public otherbase +{ + +private: + + std::int32_t x; + std::int16_t y; + +public: + + foo() + : x(0) + , y(-1) + { + return; + } + + int bar() const; + +}; // class foo + +int foo::bar() const { + + for ( int i = 0; i < 23; ++i ) { + switch ( x ) { + case 2: + something( y ); + break; + default: + something( ( y - 1 ) * 2 ); + break; + } + } + if ( x == 12 ) { + return -1; + } else if ( x == 42 ) { + return 1; + } + return 42; + +} + +} // namespace openmpt +~~~~ diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h new file mode 100644 index 000000000..82631e0b3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h @@ -0,0 +1,736 @@ +/* + * BuildSettings.h + * --------------- + * Purpose: Global, user settable compile time flags (and some global system header configuration) + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + + +#include "CompilerDetect.h" + + + +// set windows version early so that we can deduce dependencies from SDK version + +#if MPT_OS_WINDOWS + +#if defined(MPT_BUILD_MSVC) + +#if defined(MPT_BUILD_TARGET_XP) + +#if defined(_M_X64) +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0502 // _WIN32_WINNT_WS03 +#endif +#else // !_M_X64 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP +#endif +#endif // _M_X64 + +#else // MPT_BUILD_TARGET + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7 +#endif + +#endif // MPT_BUILD_TARGET + +#else // !MPT_BUILD_MSVC + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP +#endif + +#endif // MPT_BUILD_MSVC + +#ifndef WINVER +#define WINVER _WIN32_WINNT +#endif + +#endif // MPT_OS_WINDOWS + + + +#if defined(MODPLUG_TRACKER) && defined(LIBOPENMPT_BUILD) +#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" +#elif defined(MODPLUG_TRACKER) +// nothing +#elif defined(LIBOPENMPT_BUILD) +// nothing +#else +#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" +#endif // MODPLUG_TRACKER || LIBOPENMPT_BUILD + + + +// wrapper for autoconf macros + +#if defined(HAVE_CONFIG_H) + +#include "config.h" + +// Fixup dependencies which are currently not used in libopenmpt itself +#ifdef MPT_WITH_FLAC +#undef MPT_WITH_FLAC +#endif + +#endif // HAVE_CONFIG_H + + + +// Dependencies from the MSVC build system +#if defined(MPT_BUILD_MSVC) + +// This section defines which dependencies are available when building with +// MSVC. Other build systems provide MPT_WITH_* macros via command-line or other +// means. +// OpenMPT and libopenmpt should compile and run successfully (albeit with +// reduced functionality) with any or all dependencies missing/disabled. +// The defaults match the bundled third-party libraries with the addition of +// ASIO and VST SDKs. + +#if defined(MODPLUG_TRACKER) + +// OpenMPT-only dependencies +#define MPT_WITH_ASIO +#define MPT_WITH_DSOUND +#define MPT_WITH_LHASA +#define MPT_WITH_MINIZIP +#define MPT_WITH_OPUS +#define MPT_WITH_OPUSENC +#define MPT_WITH_OPUSFILE +#define MPT_WITH_PICOJSON +#define MPT_WITH_PORTAUDIO +//#define MPT_WITH_PULSEAUDIO +//#define MPT_WITH_PULSEAUDIOSIMPLE +#define MPT_WITH_SMBPITCHSHIFT +#define MPT_WITH_UNRAR +#define MPT_WITH_VORBISENC + +// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) +//#define MPT_WITH_DL +#define MPT_WITH_FLAC +//#define MPT_WITH_ICONV +//#define MPT_WITH_LTDL +#if MPT_OS_WINDOWS +#if (_WIN32_WINNT >= 0x0601) +#define MPT_WITH_MEDIAFOUNDATION +#endif +#endif +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#define MPT_WITH_ZLIB + +#endif // MODPLUG_TRACKER + +#if defined(LIBOPENMPT_BUILD) + +// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) +#if defined(LIBOPENMPT_BUILD_FULL) && defined(LIBOPENMPT_BUILD_SMALL) +#error "only one of LIBOPENMPT_BUILD_FULL or LIBOPENMPT_BUILD_SMALL can be defined" +#endif // LIBOPENMPT_BUILD_FULL && LIBOPENMPT_BUILD_SMALL + +#if defined(LIBOPENMPT_BUILD_FULL) + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_ICONV +//#define MPT_WITH_LTDL +#if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT +#if (_WIN32_WINNT >= 0x0601) +#define MPT_WITH_MEDIAFOUNDATION +#endif +#endif +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#define MPT_WITH_ZLIB + +#elif defined(LIBOPENMPT_BUILD_SMALL) + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_ICONV +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +#define MPT_WITH_MINIMP3 +#define MPT_WITH_MINIZ +//#define MPT_WITH_MPG123 +//#define MPT_WITH_OGG +#define MPT_WITH_STBVORBIS +//#define MPT_WITH_VORBIS +//#define MPT_WITH_VORBISFILE +//#define MPT_WITH_ZLIB + +#else // !LIBOPENMPT_BUILD_SMALL + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_ICONV +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#define MPT_WITH_ZLIB + +#endif // LIBOPENMPT_BUILD_SMALL + +#endif // LIBOPENMPT_BUILD + +#endif // MPT_BUILD_MSVC + + + +#if defined(MODPLUG_TRACKER) + +// Enable built-in test suite. +#ifdef _DEBUG +#define ENABLE_TESTS +#endif + +// Disable any file saving functionality (not really useful except for the player library) +//#define MODPLUG_NO_FILESAVE + +// Disable any debug logging +//#define NO_LOGGING +#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT) +#define MPT_LOG_GLOBAL_LEVEL_STATIC +#define MPT_LOG_GLOBAL_LEVEL 0 +#endif + +// Disable all runtime asserts +#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT) +#define NO_ASSERTS +#endif + +// Enable std::istream support in class FileReader (this is generally not needed for the tracker, local files can easily be mmapped as they have been before introducing std::istream support) +//#define MPT_FILEREADER_STD_ISTREAM + +// Enable callback stream wrapper for FileReader (required by libopenmpt C API). +//#define MPT_FILEREADER_CALLBACK_STREAM + +// Support for externally linked samples e.g. in MPTM files +#define MPT_EXTERNAL_SAMPLES + +// Support mpt::ChartsetLocale +#define MPT_ENABLE_CHARSET_LOCALE + +// Use inline assembly +#define ENABLE_ASM + +// Disable unarchiving support +//#define NO_ARCHIVE_SUPPORT + +// Disable the built-in reverb effect +//#define NO_REVERB + +// Disable built-in miscellaneous DSP effects (surround, mega bass, noise reduction) +//#define NO_DSP + +// Disable the built-in equalizer. +//#define NO_EQ + +// Disable the built-in automatic gain control +//#define NO_AGC + +// Define to build without VST plugin support; makes build possible without VST SDK. +//#define NO_VST + +// Define to build without DMO plugin support +//#define NO_DMO + +// (HACK) Define to build without any plugin support +//#define NO_PLUGINS + +// Do not build libopenmpt C api +#define NO_LIBOPENMPT_C + +// Do not build libopenmpt C++ api +#define NO_LIBOPENMPT_CXX + +#endif // MODPLUG_TRACKER + + + +#if defined(LIBOPENMPT_BUILD) + +#if (defined(_DEBUG) || defined(DEBUG)) && !defined(MPT_BUILD_DEBUG) +#define MPT_BUILD_DEBUG +#endif + +#if defined(LIBOPENMPT_BUILD_TEST) +#define ENABLE_TESTS +#else +#define MODPLUG_NO_FILESAVE +#endif +#if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS) +// enable asserts +#else +#define NO_ASSERTS +#endif +//#define NO_LOGGING +#define MPT_FILEREADER_STD_ISTREAM +#define MPT_FILEREADER_CALLBACK_STREAM +//#define MPT_EXTERNAL_SAMPLES +#if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) +#define MPT_ENABLE_CHARSET_LOCALE +#else +//#define MPT_ENABLE_CHARSET_LOCALE +#endif +// Do not use inline asm in library builds. There is just about no codepath which would use it anyway. +//#define ENABLE_ASM +#if defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) +//#define NO_ARCHIVE_SUPPORT +#else +#define NO_ARCHIVE_SUPPORT +#endif +//#define NO_REVERB +#define NO_DSP +#define NO_EQ +#define NO_AGC +#define NO_VST +//#if !MPT_OS_WINDOWS || MPT_OS_WINDOWS_WINRT || !MPT_COMPILER_MSVC || !defined(LIBOPENMPT_BUILD_FULL) +#define NO_DMO +//#endif +//#define NO_PLUGINS +//#define NO_LIBOPENMPT_C +//#define NO_LIBOPENMPT_CXX + +#endif // LIBOPENMPT_BUILD + + + +#if MPT_OS_WINDOWS + + #define MPT_CHARSET_WIN32 + +#elif MPT_OS_LINUX + + #define MPT_CHARSET_ICONV + +#elif MPT_OS_ANDROID + + #define MPT_CHARSET_INTERNAL + +#elif MPT_OS_EMSCRIPTEN + + #define MPT_CHARSET_INTERNAL + #ifndef MPT_LOCALE_ASSUME_CHARSET + #define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8 + #endif + +#elif MPT_OS_MACOSX_OR_IOS + + #if defined(MPT_WITH_ICONV) + #define MPT_CHARSET_ICONV + #ifndef MPT_ICONV_NO_WCHAR + #define MPT_ICONV_NO_WCHAR + #endif + #else + #define MPT_CHARSET_INTERNAL + #endif + //#ifndef MPT_LOCALE_ASSUME_CHARSET + //#define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8 + //#endif + +#elif defined(MPT_WITH_ICONV) + + #define MPT_CHARSET_ICONV + +#endif + + + +#if MPT_COMPILER_MSVC + + // Use wide strings for MSVC because this is the native encoding on + // microsoft platforms. + #define MPT_USTRING_MODE_WIDE 1 + #define MPT_USTRING_MODE_UTF8 0 + +#else // !MPT_COMPILER_MSVC + + #define MPT_USTRING_MODE_WIDE 0 + #define MPT_USTRING_MODE_UTF8 1 + +#endif // MPT_COMPILER_MSVC + +#if MPT_USTRING_MODE_UTF8 + + // MPT_USTRING_MODE_UTF8 mpt::ustring is implemented via mpt::u8string + #define MPT_ENABLE_U8STRING 1 + +#else + + #define MPT_ENABLE_U8STRING 0 + +#endif + +#if defined(MODPLUG_TRACKER) || MPT_USTRING_MODE_WIDE + + // mpt::ToWString, mpt::wfmt, ConvertStrTo + // Required by the tracker to ease interfacing with WinAPI. + // Required by MPT_USTRING_MODE_WIDE to ease type tunneling in mpt::format. + #define MPT_WSTRING_FORMAT 1 + +#else + + #define MPT_WSTRING_FORMAT 0 + +#endif + +#if MPT_OS_WINDOWS || MPT_USTRING_MODE_WIDE || MPT_WSTRING_FORMAT + + // mpt::ToWide + // Required on Windows by mpt::PathString. + // Required by MPT_USTRING_MODE_WIDE as they share the conversion functions. + // Required by MPT_WSTRING_FORMAT because of std::string<->std::wstring conversion in mpt::ToString and mpt::ToWString. + #define MPT_WSTRING_CONVERT 1 + +#else + + #define MPT_WSTRING_CONVERT 0 + +#endif + + + +// fixing stuff up + +#if defined(MPT_BUILD_TARGET_XP) +// Also support Wine 1.6 in addition to Windows XP +#ifndef MPT_QUIRK_NO_CPP_THREAD +#define MPT_QUIRK_NO_CPP_THREAD +#endif +#endif + +#if defined(MPT_BUILD_ANALYZED) || defined(MPT_BUILD_CHECKED) +#ifdef NO_ASSERTS +#undef NO_ASSERTS // static or dynamic analyzers want assertions on +#endif +#endif + +#if defined(MPT_BUILD_FUZZER) +#ifndef MPT_FUZZ_TRACKER +#define MPT_FUZZ_TRACKER +#endif +#endif + +#if !MPT_COMPILER_MSVC && defined(ENABLE_ASM) +#undef ENABLE_ASM // inline assembly requires MSVC compiler +#endif + +#if defined(ENABLE_ASM) +#if MPT_COMPILER_MSVC && defined(_M_IX86) + +// Generate general x86 inline assembly / intrinsics. +#define ENABLE_X86 +// Generate inline assembly using MMX instructions (only used when the CPU supports it). +#define ENABLE_MMX +// Generate inline assembly using SSE instructions (only used when the CPU supports it). +#define ENABLE_SSE +// Generate inline assembly using SSE2 instructions (only used when the CPU supports it). +#define ENABLE_SSE2 +// Generate inline assembly using SSE3 instructions (only used when the CPU supports it). +#define ENABLE_SSE3 +// Generate inline assembly using SSE4 instructions (only used when the CPU supports it). +#define ENABLE_SSE4 +// Generate inline assembly using AMD specific instruction set extensions (only used when the CPU supports it). +#define ENABLE_X86_AMD + +#elif MPT_COMPILER_MSVC && defined(_M_X64) + +// Generate general x64 inline assembly / intrinsics. +#define ENABLE_X64 +// Generate inline assembly using SSE2 instructions (only used when the CPU supports it). +#define ENABLE_SSE2 +// Generate inline assembly using SSE3 instructions (only used when the CPU supports it). +#define ENABLE_SSE3 +// Generate inline assembly using SSE4 instructions (only used when the CPU supports it). +#define ENABLE_SSE4 + +#endif // arch +#endif // ENABLE_ASM + +#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !MPT_OS_WINDOWS_WINRT +#define MPT_ENABLE_MPG123_DELAYLOAD +#endif + +#if defined(ENABLE_TESTS) && defined(MODPLUG_NO_FILESAVE) +#undef MODPLUG_NO_FILESAVE // tests recommend file saving +#endif + +#if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZ) +// Only one deflate implementation should be used. Prefer zlib. +#undef MPT_WITH_MINIZ +#endif + +#if !MPT_OS_WINDOWS && defined(MPT_WITH_MEDIAFOUNDATION) +#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires Windows +#endif + +#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_TEMPFILE) +#define MPT_ENABLE_TEMPFILE +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_TEMPFILE) +#define MPT_ENABLE_TEMPFILE +#endif + +#if !defined(MPT_CHARSET_WIN32) && !defined(MPT_CHARSET_ICONV) && !defined(MPT_CHARSET_CODECVTUTF8) && !defined(MPT_CHARSET_INTERNAL) +#define MPT_CHARSET_INTERNAL +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // Tracker requires dynamic library loading for export codecs +#endif + +#if defined(MPT_ENABLE_MPG123_DELAYLOAD) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // static MSVC builds require dynbind to load delay-loaded DLLs +#endif + +#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // MediaFoundation needs dynamic loading in order to test availability of delay loaded libs +#endif + +#if (defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3)) && !defined(MPT_ENABLE_MP3_SAMPLES) +#define MPT_ENABLE_MP3_SAMPLES +#endif + +#if defined(ENABLE_TESTS) +#define MPT_ENABLE_FILEIO // Test suite requires PathString for file loading. +#endif + +#if !MPT_OS_WINDOWS && !defined(MPT_FILEREADER_STD_ISTREAM) +#define MPT_FILEREADER_STD_ISTREAM // MMAP is only supported on Windows +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_FILEIO) +#define MPT_ENABLE_FILEIO // Tracker requires disk file io +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_THREAD) +#define MPT_ENABLE_THREAD // Tracker requires threads +#endif + +#if defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_ENABLE_FILEIO) +#define MPT_ENABLE_FILEIO // External samples require disk file io +#endif + +#if !defined(MODPLUG_NO_FILESAVE) && !defined(MPT_ENABLE_FILEIO_STDIO) +#define MPT_ENABLE_FILEIO_STDIO // file saving requires FILE* +#endif + +#if defined(NO_PLUGINS) +// Any plugin type requires NO_PLUGINS to not be defined. +#define NO_VST +#define NO_DMO +#endif + + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) && !defined(MPT_BUILD_WINESUPPORT_WRAPPER) +#ifndef MPT_NO_NAMESPACE +#define MPT_NO_NAMESPACE +#endif +#endif + +#if defined(MPT_NO_NAMESPACE) + +#ifdef OPENMPT_NAMESPACE +#undef OPENMPT_NAMESPACE +#endif +#define OPENMPT_NAMESPACE + +#ifdef OPENMPT_NAMESPACE_BEGIN +#undef OPENMPT_NAMESPACE_BEGIN +#endif +#define OPENMPT_NAMESPACE_BEGIN + +#ifdef OPENMPT_NAMESPACE_END +#undef OPENMPT_NAMESPACE_END +#endif +#define OPENMPT_NAMESPACE_END + +#else + +#ifndef OPENMPT_NAMESPACE +#define OPENMPT_NAMESPACE OpenMPT +#endif + +#ifndef OPENMPT_NAMESPACE_BEGIN +#define OPENMPT_NAMESPACE_BEGIN namespace OPENMPT_NAMESPACE { +#endif +#ifndef OPENMPT_NAMESPACE_END +#define OPENMPT_NAMESPACE_END } +#endif + +#endif + + + +// platform configuration + +#if MPT_OS_WINDOWS + +#define WIN32_LEAN_AND_MEAN + +// windows.h excludes +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOPROFILER // Profiler interface. +#define NOMCX // Modem Configuration Extensions + +// mmsystem.h excludes +#define MMNODRV +//#define MMNOSOUND +//#define MMNOWAVE +//#define MMNOMIDI +#define MMNOAUX +#define MMNOMIXER +//#define MMNOTIMER +#define MMNOJOY +#define MMNOMCI +//#define MMNOMMIO +//#define MMNOMMSYSTEM + +// mmreg.h excludes +#define NOMMIDS +//#define NONEWWAVE +#define NONEWRIFF +#define NOJPEGDIB +#define NONEWIC +#define NOBITMAP + +#endif // MPT_OS_WINDOWS + + + +// stdlib configuration + +#define __STDC_CONSTANT_MACROS +#define __STDC_LIMIT_MACROS + +#define _USE_MATH_DEFINES + +#if !MPT_OS_ANDROID +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#endif // !MPT_OS_ANDROID + + + +// compiler configuration + +#if MPT_COMPILER_MSVC + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS // Define to disable the "This function or variable may be unsafe" warnings. +#endif +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1 +#ifndef _SCL_SECURE_NO_WARNINGS +#define _SCL_SECURE_NO_WARNINGS +#endif + +#ifndef NO_WARN_MBCS_MFC_DEPRECATION +#define NO_WARN_MBCS_MFC_DEPRECATION +#endif + +#pragma warning(disable:4355) // 'this' : used in base member initializer list + +// happens for immutable classes (i.e. classes containing const members) +#pragma warning(disable:4512) // assignment operator could not be generated + +#pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. + +#ifdef MPT_BUILD_ANALYZED +// Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. +//#pragma warning(disable:6246) +//#pragma warning(disable:6262) +#pragma warning(disable:6326) // Potential comparison of a constant with another constant +//#pragma warning(disable:6385) +//#pragma warning(disable:6386) +#endif // MPT_BUILD_ANALYZED + +#endif // MPT_COMPILER_MSVC + +#if MPT_COMPILER_MSVCCLANGC2 + +#if MPT_OS_WINDOWS +// As Clang defines __STDC__ 1, Windows headers will use named union fields. The MediaFoundation headers do not support this, though. +// Clang supports nameless union fields just fine, and luckily there is a way to override the Windows headers behaviour. +#define _FORCENAMELESSUNION +#endif // MPT_OS_WINDOWS + +#endif // MPT_COMPILER_MSVCCLANGC2 + + + +// third-party library configuration + +#ifdef MPT_WITH_FLAC +#ifdef MPT_BUILD_MSVC_STATIC +#define FLAC__NO_DLL +#endif +#endif + +#ifdef MPT_WITH_PICOJSON +#define PICOJSON_USE_INT64 +#endif + +#ifdef MPT_WITH_SMBPITCHSHIFT +#ifdef MPT_BUILD_MSVC_SHARED +#define SMBPITCHSHIFT_USE_DLL +#endif +#endif + +#ifdef MPT_WITH_STBVORBIS +#define STB_VORBIS_HEADER_ONLY +#ifndef STB_VORBIS_NO_PULLDATA_API +#define STB_VORBIS_NO_PULLDATA_API +#endif +#ifndef STB_VORBIS_NO_STDIO +#define STB_VORBIS_NO_STDIO +#endif +#endif + +#ifdef MPT_WITH_VORBISFILE +#ifndef OV_EXCLUDE_STATIC_CALLBACKS +#define OV_EXCLUDE_STATIC_CALLBACKS +#endif +#endif + +#ifdef MPT_WITH_ZLIB +#ifdef MPT_BUILD_MSVC_SHARED +#define ZLIB_DLL +#endif +#endif + diff --git a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h new file mode 100644 index 000000000..78c180116 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h @@ -0,0 +1,367 @@ +/* + * CompilerDetect.h + * ---------------- + * Purpose: Detect current compiler and provide readable version test macros. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + + +#define MPT_COMPILER_MAKE_VERSION2(version,sp) ((version) * 100 + (sp)) +#define MPT_COMPILER_MAKE_VERSION3(major,minor,patch) ((major) * 10000 + (minor) * 100 + (patch)) +#define MPT_COMPILER_MAKE_VERSION3_BUILD(major,minor,build) ((major) * 10000000 + (minor) * 100000 + (patch)) + + + +#if defined(MPT_COMPILER_GENERIC) + +#undef MPT_COMPILER_GENERIC +#define MPT_COMPILER_GENERIC 1 + +#elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__) + +#define MPT_COMPILER_MSVCCLANGC2 1 +#define MPT_COMPILER_MSVCCLANGC2_VERSION (__c2_version__) +#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION >= MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build))) +#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION < MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build))) + +#elif defined(__clang__) + +#define MPT_COMPILER_CLANG 1 +#define MPT_COMPILER_CLANG_VERSION MPT_COMPILER_MAKE_VERSION3(__clang_major__,__clang_minor__,__clang_patchlevel__) +#define MPT_CLANG_AT_LEAST(major,minor,patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) +#define MPT_CLANG_BEFORE(major,minor,patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) + +#if MPT_CLANG_BEFORE(3,4,0) +#error "clang version 3.4 required" +#endif + +#if defined(__clang_analyzer__) +#ifndef MPT_BUILD_ANALYZED +#define MPT_BUILD_ANALYZED +#endif +#endif + +#elif defined(__GNUC__) + +#define MPT_COMPILER_GCC 1 +#define MPT_COMPILER_GCC_VERSION MPT_COMPILER_MAKE_VERSION3(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#define MPT_GCC_AT_LEAST(major,minor,patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) +#define MPT_GCC_BEFORE(major,minor,patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) + +#if MPT_GCC_BEFORE(4,8,0) +#error "GCC version 4.8 required" +#endif + +#elif defined(_MSC_VER) + +#define MPT_COMPILER_MSVC 1 +#if (_MSC_VER >= 1912) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,5) +#elif (_MSC_VER >= 1911) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,3) +#elif (_MSC_VER >= 1910) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,0) +#elif (_MSC_VER >= 1900) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015,0) +#elif (_MSC_VER >= 1800) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2013,0) +#elif (_MSC_VER >= 1700) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2012,0) +#elif (_MSC_VER >= 1600) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2010,0) +#elif (_MSC_VER >= 1500) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2008,0) +#else +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2005,0) +#endif +#define MPT_MSVC_AT_LEAST(version,sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version),(sp))) +#define MPT_MSVC_BEFORE(version,sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version),(sp))) + +#if MPT_MSVC_BEFORE(2015,0) +#error "MSVC version 2015 required" +#endif + +#if defined(_PREFAST_) +#ifndef MPT_BUILD_ANALYZED +#define MPT_BUILD_ANALYZED +#endif +#endif + +#else + +#define MPT_COMPILER_GENERIC 1 + +#endif + + + +#ifndef MPT_COMPILER_GENERIC +#define MPT_COMPILER_GENERIC 0 +#endif +#ifndef MPT_COMPILER_MSVCCLANGC2 +#define MPT_COMPILER_MSVCCLANGC2 0 +#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) 0 +#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) 0 +#endif +#ifndef MPT_COMPILER_CLANG +#define MPT_COMPILER_CLANG 0 +#define MPT_CLANG_AT_LEAST(major,minor,patch) 0 +#define MPT_CLANG_BEFORE(major,minor,patch) 0 +#endif +#ifndef MPT_COMPILER_GCC +#define MPT_COMPILER_GCC 0 +#define MPT_GCC_AT_LEAST(major,minor,patch) 0 +#define MPT_GCC_BEFORE(major,minor,patch) 0 +#endif +#ifndef MPT_COMPILER_MSVC +#define MPT_COMPILER_MSVC 0 +#define MPT_MSVC_AT_LEAST(version,sp) 0 +#define MPT_MSVC_BEFORE(version,sp) 0 +#endif + + + +#if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG + +#if (__cplusplus >= 201703) +#define MPT_CXX 17 +#elif (__cplusplus >= 201402) +#define MPT_CXX 14 +#else +#define MPT_CXX 11 +#endif + +#elif MPT_COMPILER_MSVC + +#if MPT_MSVC_AT_LEAST(2017,0) +#if (_MSVC_LANG >= 201402) +#define MPT_CXX 14 +#else +#define MPT_CXX 11 +#endif +#else +#define MPT_CXX 11 +#endif + +#else + +#define MPT_CXX 11 + +#endif + +// MPT_CXX is stricter than just using __cplusplus directly. +// We will only claim a language version as supported IFF all core language and +// library fatures that we need are actually supported AND working correctly +// (to our needs). + +#define MPT_CXX_AT_LEAST(version) (MPT_CXX >= (version)) +#define MPT_CXX_BEFORE(version) (MPT_CXX < (version)) + + + +#if MPT_COMPILER_MSVC + #define MPT_PLATFORM_LITTLE_ENDIAN +#elif MPT_COMPILER_GCC + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define MPT_PLATFORM_BIG_ENDIAN + #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define MPT_PLATFORM_LITTLE_ENDIAN + #endif +#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define MPT_PLATFORM_BIG_ENDIAN + #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define MPT_PLATFORM_LITTLE_ENDIAN + #endif +#endif + +// fallback: +#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) + // taken from boost/detail/endian.hpp + #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ + || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ + || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) + #define MPT_PLATFORM_BIG_ENDIAN + #elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \ + || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ + || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) + #define MPT_PLATFORM_LITTLE_ENDIAN + #elif defined(__sparc) || defined(__sparc__) \ + || defined(_POWER) || defined(__powerpc__) \ + || defined(__ppc__) || defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) || defined(_POWER) \ + || defined(__s390__) + #define MPT_PLATFORM_BIG_ENDIAN + #elif defined(__i386__) || defined(__alpha__) \ + || defined(__ia64) || defined(__ia64__) \ + || defined(_M_IX86) || defined(_M_IA64) \ + || defined(_M_ALPHA) || defined(__amd64) \ + || defined(__amd64__) || defined(_M_AMD64) \ + || defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_X64) || defined(__bfin__) + #define MPT_PLATFORM_LITTLE_ENDIAN + #endif +#endif + +#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) +#define MPT_PLATFORM_ENDIAN_KNOWN 1 +#else +#define MPT_PLATFORM_ENDIAN_KNOWN 0 +#endif + + + +// This should really be based on __STDCPP_THREADS__, but that is not defined by +// GCC or clang. Stupid. +// Just assume multithreaded and disable for platforms we know are +// singlethreaded later on. +#define MPT_PLATFORM_MULTITHREADED 1 + + + +// specific C++ features + + + +// C++11 constexpr + +#if MPT_COMPILER_MSVC +#define MPT_COMPILER_QUIRK_CONSTEXPR_NO_STRING_LITERALS +#endif + + + +#if MPT_COMPILER_MSVC +// Compiler has multiplication/division semantics when shifting signed integers. +#define MPT_COMPILER_SHIFT_SIGNED 1 +#endif + +#ifndef MPT_COMPILER_SHIFT_SIGNED +#define MPT_COMPILER_SHIFT_SIGNED 0 +#endif + + + +#if MPT_COMPILER_GCC || MPT_COMPILER_MSVC +// Compiler supports type-punning through unions. This is not stricly standard-conforming. +// For GCC, this is documented, for MSVC this is apparently not documented, but we assume it. +#define MPT_COMPILER_UNION_TYPE_ALIASES 1 +#endif + +#ifndef MPT_COMPILER_UNION_TYPE_ALIASES +// Compiler does not support type-punning through unions. std::memcpy is used instead. +// This is the safe fallback and strictly standard-conforming. +// Another standard-compliant alternative would be casting pointers to a character type pointer. +// This results in rather unreadable code and, +// in most cases, compilers generate better code by just inlining the memcpy anyway. +// (see ). +#define MPT_COMPILER_UNION_TYPE_ALIASES 0 +#endif + + + +// The order of the checks matters! +#if defined(__EMSCRIPTEN__) + #define MPT_OS_EMSCRIPTEN 1 + #if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__) + #if (__EMSCRIPTEN_major__ > 1) + #define MPT_OS_EMSCRIPTEN_ANCIENT 0 + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 36) + #define MPT_OS_EMSCRIPTEN_ANCIENT 0 + #else + #define MPT_OS_EMSCRIPTEN_ANCIENT 1 + #endif + #else + #define MPT_OS_EMSCRIPTEN_ANCIENT 1 + #endif +#elif defined(_WIN32) + #define MPT_OS_WINDOWS 1 + #if defined(WINAPI_FAMILY) + #include + #if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + #define MPT_OS_WINDOWS_WINRT 0 + #else + #define MPT_OS_WINDOWS_WINRT 1 + #endif + #else // !WINAPI_FAMILY + #define MPT_OS_WINDOWS_WINRT 0 + #endif // WINAPI_FAMILY +#elif defined(__APPLE__) + #define MPT_OS_MACOSX_OR_IOS 1 + //#include "TargetConditionals.h" + //#if TARGET_IPHONE_SIMULATOR + //#elif TARGET_OS_IPHONE + //#elif TARGET_OS_MAC + //#else + //#endif +#elif defined(__ANDROID__) || defined(ANDROID) + #define MPT_OS_ANDROID 1 +#elif defined(__linux__) + #define MPT_OS_LINUX 1 +#elif defined(__DragonFly__) + #define MPT_OS_DRAGONFLYBSD 1 +#elif defined(__FreeBSD__) + #define MPT_OS_FREEBSD 1 +#elif defined(__OpenBSD__) + #define MPT_OS_OPENBSD 1 +#elif defined(__NetBSD__) + #define MPT_OS_NETBSD 1 +#elif defined(__unix__) + #define MPT_OS_GENERIC_UNIX 1 +#else + #define MPT_OS_UNKNOWN 1 +#endif + +#ifndef MPT_OS_EMSCRIPTEN +#define MPT_OS_EMSCRIPTEN 0 +#endif +#ifndef MPT_OS_WINDOWS +#define MPT_OS_WINDOWS 0 +#endif +#ifndef MPT_OS_WINDOWS_WINRT +#define MPT_OS_WINDOWS_WINRT 0 +#endif +#ifndef MPT_OS_MACOSX_OR_IOS +#define MPT_OS_MACOSX_OR_IOS 0 +#endif +#ifndef MPT_OS_ANDROID +#define MPT_OS_ANDROID 0 +#endif +#ifndef MPT_OS_LINUX +#define MPT_OS_LINUX 0 +#endif +#ifndef MPT_OS_DRAGONFLYBSD +#define MPT_OS_DRAGONFLYBSD 0 +#endif +#ifndef MPT_OS_FREEBSD +#define MPT_OS_FREEBSD 0 +#endif +#ifndef MPT_OS_OPENBSD +#define MPT_OS_OPENBSD 0 +#endif +#ifndef MPT_OS_NETBSD +#define MPT_OS_NETBSD 0 +#endif +#ifndef MPT_OS_GENERIC_UNIX +#define MPT_OS_GENERIC_UNIX 0 +#endif +#ifndef MPT_OS_UNKNOWN +#define MPT_OS_UNKNOWN 0 +#endif + +#ifndef MPT_OS_EMSCRIPTEN_ANCIENT +#define MPT_OS_EMSCRIPTEN_ANCIENT 0 +#endif + + + +#if MPT_OS_EMSCRIPTEN +#undef MPT_PLATFORM_MULTITHREADED +#define MPT_PLATFORM_MULTITHREADED 0 +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp new file mode 100644 index 000000000..ebe158bac --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp @@ -0,0 +1,465 @@ +/* + * ComponentManager.cpp + * -------------------- + * Purpose: Manages loading of optional components. + * Notes : (currently none) + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "ComponentManager.h" + +#include "Logging.h" + +#include "mptMutex.h" + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_COMPONENTS) + + +ComponentBase::ComponentBase(ComponentType type) + : m_Type(type) + , m_Initialized(false) + , m_Available(false) +{ + return; +} + + +ComponentBase::~ComponentBase() +{ + return; +} + + +void ComponentBase::SetInitialized() +{ + m_Initialized = true; +} + + +void ComponentBase::SetAvailable() +{ + m_Available = true; +} + + +ComponentType ComponentBase::GetType() const +{ + return m_Type; +} + + +bool ComponentBase::IsInitialized() const +{ + return m_Initialized; +} + + +bool ComponentBase::IsAvailable() const +{ + return m_Initialized && m_Available; +} + + +mpt::ustring ComponentBase::GetVersion() const +{ + return mpt::ustring(); +} + + +void ComponentBase::Initialize() +{ + if(IsInitialized()) + { + return; + } + if(DoInitialize()) + { + SetAvailable(); + } + SetInitialized(); +} + + +#if defined(MPT_ENABLE_DYNBIND) + + +ComponentLibrary::ComponentLibrary(ComponentType type) + : ComponentBase(type) + , m_BindFailed(false) +{ + return; +} + + +ComponentLibrary::~ComponentLibrary() +{ + return; +} + + +bool ComponentLibrary::AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath) +{ + if(m_Libraries[libName].IsValid()) + { + // prefer previous + return true; + } + mpt::Library lib(libPath); + if(!lib.IsValid()) + { + return false; + } + m_Libraries[libName] = lib; + return true; +} + + +void ComponentLibrary::ClearLibraries() +{ + m_Libraries.clear(); +} + + +void ComponentLibrary::SetBindFailed() +{ + m_BindFailed = true; +} + + +void ComponentLibrary::ClearBindFailed() +{ + m_BindFailed = false; +} + + +bool ComponentLibrary::HasBindFailed() const +{ + return m_BindFailed; +} + + +mpt::Library ComponentLibrary::GetLibrary(const std::string &libName) const +{ + const auto it = m_Libraries.find(libName); + if(it == m_Libraries.end()) + { + return mpt::Library(); + } + return it->second; +} + + +#endif // MPT_ENABLE_DYNBIND + + +#if MPT_COMPONENT_MANAGER + + +ComponentFactoryBase::ComponentFactoryBase(const std::string &id, const std::string &settingsKey) + : m_ID(id) + , m_SettingsKey(settingsKey) +{ + return; +} + + +ComponentFactoryBase::~ComponentFactoryBase() +{ + return; +} + + +std::string ComponentFactoryBase::GetID() const +{ + return m_ID; +} + + +std::string ComponentFactoryBase::GetSettingsKey() const +{ + return m_SettingsKey; +} + + +void ComponentFactoryBase::PreConstruct() const +{ + MPT_LOG(LogInformation, "Components", + mpt::format(MPT_USTRING("Constructing Component %1")) + ( mpt::ToUnicode(mpt::CharsetASCII, m_ID) + ) + ); +} + + +void ComponentFactoryBase::Initialize(ComponentManager &componentManager, std::shared_ptr component) const +{ + if(componentManager.IsComponentBlocked(GetSettingsKey())) + { + return; + } + componentManager.InitializeComponent(component); +} + + +// Global list of component register functions. +// We do not use a global scope static list head because the corresponding +// mutex would be no POD type and would thus not be safe to be usable in +// zero-initialized state. +// Function scope static initialization is guaranteed to be thread safe +// in C++11. +// We use this implementation to be future-proof. +// MSVC currently does not exploit the possibility of using multiple threads +// for global lifetime object's initialization. +// An implementation with a simple global list head and no mutex at all would +// thus work fine for MSVC (currently). + +static mpt::mutex & ComponentListMutex() +{ + static mpt::mutex g_ComponentListMutex; + return g_ComponentListMutex; +} + +static ComponentListEntry * & ComponentListHead() +{ + static ComponentListEntry *g_ComponentListHead = nullptr; + return g_ComponentListHead; +} + +bool ComponentListPush(ComponentListEntry *entry) +{ + MPT_LOCK_GUARD guard(ComponentListMutex()); + entry->next = ComponentListHead(); + ComponentListHead() = entry; + return true; +} + + +static std::shared_ptr g_ComponentManager; + + +void ComponentManager::Init(const IComponentManagerSettings &settings) +{ + MPT_LOG(LogInformation, "Components", MPT_USTRING("Init")); + // cannot use make_shared because the constructor is private + g_ComponentManager = std::shared_ptr(new ComponentManager(settings)); +} + + +void ComponentManager::Release() +{ + MPT_LOG(LogInformation, "Components", MPT_USTRING("Release")); + g_ComponentManager = nullptr; +} + + +std::shared_ptr ComponentManager::Instance() +{ + return g_ComponentManager; +} + + +ComponentManager::ComponentManager(const IComponentManagerSettings &settings) + : m_Settings(settings) +{ + MPT_LOCK_GUARD guard(ComponentListMutex()); + for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) + { + entry->reg(*this); + } +} + + +void ComponentManager::Register(const IComponentFactory &componentFactory) +{ + if(m_Components.find(componentFactory.GetID()) != m_Components.end()) + { + return; + } + RegisteredComponent registeredComponent; + registeredComponent.settingsKey = componentFactory.GetSettingsKey(); + registeredComponent.factoryMethod = componentFactory.GetStaticConstructor(); + registeredComponent.instance = nullptr; + registeredComponent.weakInstance = std::weak_ptr(); + m_Components.insert(std::make_pair(componentFactory.GetID(), registeredComponent)); +} + + +void ComponentManager::Startup() +{ + MPT_LOG(LogDebug, "Components", MPT_USTRING("Startup")); + if(m_Settings.LoadOnStartup()) + { + for(auto &it : m_Components) + { + it.second.instance = it.second.factoryMethod(*this); + it.second.weakInstance = it.second.instance; + } + } + if(!m_Settings.KeepLoaded()) + { + for(auto &it : m_Components) + { + it.second.instance = nullptr; + } + } +} + + +bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const +{ + return m_Settings.IsBlocked(settingsKey); +} + + +void ComponentManager::InitializeComponent(std::shared_ptr component) const +{ + if(!component) + { + return; + } + if(component->IsInitialized()) + { + return; + } + component->Initialize(); +} + + +std::shared_ptr ComponentManager::GetComponent(const IComponentFactory &componentFactory) +{ + std::shared_ptr component = nullptr; + auto it = m_Components.find(componentFactory.GetID()); + if(it != m_Components.end()) + { // registered component + if((*it).second.instance) + { // loaded + component = (*it).second.instance; + } else + { // not loaded + component = (*it).second.weakInstance.lock(); + if(!component) + { + component = (*it).second.factoryMethod(*this); + } + if(m_Settings.KeepLoaded()) + { // keep the component loaded + (*it).second.instance = component; + } + (*it).second.weakInstance = component; + } + } else + { // unregistered component + component = componentFactory.Construct(*this); + } + MPT_ASSERT(component); + return component; +} + + +std::shared_ptr ComponentManager::ReloadComponent(const IComponentFactory &componentFactory) +{ + std::shared_ptr component = nullptr; + auto it = m_Components.find(componentFactory.GetID()); + if(it != m_Components.end()) + { // registered component + if((*it).second.instance) + { // loaded + (*it).second.instance = nullptr; + if(!(*it).second.weakInstance.expired()) + { + throw std::runtime_error("Component not completely unloaded. Cannot reload."); + } + (*it).second.weakInstance = std::weak_ptr(); + } + // not loaded + component = (*it).second.factoryMethod(*this); + if(m_Settings.KeepLoaded()) + { // keep the component loaded + (*it).second.instance = component; + } + (*it).second.weakInstance = component; + } else + { // unregistered component + component = componentFactory.Construct(*this); + } + MPT_ASSERT(component); + return component; +} + + +std::vector ComponentManager::GetRegisteredComponents() const +{ + std::vector result; + result.reserve(m_Components.size()); + for(const auto &it : m_Components) + { + result.push_back(it.first); + } + return result; +} + + +ComponentInfo ComponentManager::GetComponentInfo(std::string name) const +{ + ComponentInfo result; + result.name = name; + result.state = ComponentStateUnregistered; + result.settingsKey = ""; + result.type = ComponentTypeUnknown; + const auto it = m_Components.find(name); + if(it == m_Components.end()) + { + result.state = ComponentStateUnregistered; + return result; + } + result.settingsKey = it->second.settingsKey; + if(IsComponentBlocked(it->second.settingsKey)) + { + result.state = ComponentStateBlocked; + return result; + } + std::shared_ptr component = it->second.instance; + if(!component) + { + component = it->second.weakInstance.lock(); + } + if(!component) + { + result.state = ComponentStateUnintialized; + return result; + } + result.type = component->GetType(); + if(!component->IsInitialized()) + { + result.state = ComponentStateUnintialized; + return result; + } + if(!component->IsAvailable()) + { + result.state = ComponentStateUnavailable; + return result; + } + result.state = ComponentStateAvailable; + return result; +} + + +mpt::PathString ComponentManager::GetComponentPath() const +{ + return m_Settings.Path(); +} + + +#endif // MPT_COMPONENT_MANAGER + + +#endif // MPT_ENABLE_COMPONENTS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h new file mode 100644 index 000000000..2a2b79903 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h @@ -0,0 +1,507 @@ +/* + * ComponentManager.h + * ------------------ + * Purpose: Manages loading of optional components. + * Notes : (currently none) + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include +#include +#include "../common/misc_util.h" +#include "../common/mptMutex.h" + + +OPENMPT_NAMESPACE_BEGIN + + +#define MPT_ENABLE_COMPONENTS + + +#if defined(MPT_ENABLE_COMPONENTS) + + +#if defined(MODPLUG_TRACKER) +#define MPT_COMPONENT_MANAGER 1 +#else +#define MPT_COMPONENT_MANAGER 0 +#endif + + +enum ComponentType +{ + ComponentTypeUnknown = 0, + ComponentTypeBuiltin, // PortAudio + ComponentTypeSystem, // mf.dll + ComponentTypeSystemInstallable, // acm mp3 codec + ComponentTypeBundled, // libsoundtouch + ComponentTypeForeign, // libmp3lame +}; + + +class ComponentFactoryBase; + + +class IComponent +{ + + friend class ComponentFactoryBase; + +protected: + + IComponent() { } + +public: + + virtual ~IComponent() { } + +public: + + virtual ComponentType GetType() const = 0; + + virtual bool IsInitialized() const = 0; // Initialize() has been called + virtual bool IsAvailable() const = 0; // Initialize() has been successfull + virtual mpt::ustring GetVersion() const = 0; + + virtual void Initialize() = 0; // try to load the component + +}; + + +class ComponentBase + : public IComponent +{ + +private: + + ComponentType m_Type; + + bool m_Initialized; + bool m_Available; + +protected: + + ComponentBase(ComponentType type); + +public: + + virtual ~ComponentBase(); + +protected: + + void SetInitialized(); + void SetAvailable(); + +public: + + virtual ComponentType GetType() const; + virtual bool IsInitialized() const; + virtual bool IsAvailable() const; + + virtual mpt::ustring GetVersion() const; + +public: + + virtual void Initialize(); + +protected: + + virtual bool DoInitialize() = 0; + +}; + + +class ComponentBuiltin : public ComponentBase +{ +public: + ComponentBuiltin() + : ComponentBase(ComponentTypeBuiltin) + { + return; + } + virtual bool DoInitialize() + { + return true; + } +}; + + +#define MPT_GLOBAL_BIND(lib, name) name = &::name; + + +#if defined(MPT_ENABLE_DYNBIND) + + +class ComponentLibrary + : public ComponentBase +{ + +private: + + typedef std::map TLibraryMap; + TLibraryMap m_Libraries; + + bool m_BindFailed; + +protected: + + ComponentLibrary(ComponentType type); + +public: + + virtual ~ComponentLibrary(); + +protected: + + bool AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath); + void ClearLibraries(); + void SetBindFailed(); + void ClearBindFailed(); + bool HasBindFailed() const; + +public: + + virtual mpt::Library GetLibrary(const std::string &libName) const; + + template + bool Bind(Tfunc * & f, const std::string &libName, const std::string &symbol) const + { + return GetLibrary(libName).Bind(f, symbol); + } + +protected: + + virtual bool DoInitialize() = 0; + +}; + + +#define MPT_COMPONENT_BIND(libName, func) MPT_DO { if(!Bind( func , libName , #func )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func ) +#define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol ) + + +class ComponentSystemDLL : public ComponentLibrary +{ +private: + mpt::PathString m_BaseName; +public: + ComponentSystemDLL(const mpt::PathString &baseName) + : ComponentLibrary(ComponentTypeSystem) + , m_BaseName(baseName) + { + return; + } + virtual bool DoInitialize() + { + AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName)); + return GetLibrary(m_BaseName.ToUTF8()).IsValid(); + } +}; + + +class ComponentBundledDLL : public ComponentLibrary +{ +private: + mpt::PathString m_FullName; +public: + ComponentBundledDLL(const mpt::PathString &fullName) + : ComponentLibrary(ComponentTypeBundled) + , m_FullName(fullName) + { + return; + } + virtual bool DoInitialize() + { + AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName)); + return GetLibrary(m_FullName.ToUTF8()).IsValid(); + } +}; + + +#endif // MPT_ENABLE_DYNBIND + + +#if MPT_COMPONENT_MANAGER + + +class ComponentManager; + +typedef std::shared_ptr (*ComponentFactoryMethod)(ComponentManager &componentManager); + + +class IComponentFactory +{ +protected: + IComponentFactory() { } +public: + virtual ~IComponentFactory() { } +public: + virtual std::string GetID() const = 0; + virtual std::string GetSettingsKey() const = 0; + virtual std::shared_ptr Construct(ComponentManager &componentManager) const = 0; + virtual ComponentFactoryMethod GetStaticConstructor() const = 0; +}; + + +class ComponentFactoryBase + : public IComponentFactory +{ +private: + std::string m_ID; + std::string m_SettingsKey; +protected: + ComponentFactoryBase(const std::string &id, const std::string &settingsKey); + void PreConstruct() const; + void Initialize(ComponentManager &componentManager, std::shared_ptr component) const; +public: + virtual ~ComponentFactoryBase(); + virtual std::string GetID() const; + virtual std::string GetSettingsKey() const; + virtual std::shared_ptr Construct(ComponentManager &componentManager) const = 0; + virtual ComponentFactoryMethod GetStaticConstructor() const = 0; +}; + + +template +class ComponentFactory + : public ComponentFactoryBase +{ +public: + ComponentFactory() + : ComponentFactoryBase(T::g_ID, T::g_SettingsKey) + { + return; + } + virtual ~ComponentFactory() + { + return; + } +public: + virtual std::shared_ptr Construct(ComponentManager &componentManager) const + { + PreConstruct(); + std::shared_ptr component = std::make_shared(); + Initialize(componentManager, component); + return component; + } + static std::shared_ptr StaticConstruct(ComponentManager &componentManager) + { + return ComponentFactory().Construct(componentManager); + } + virtual ComponentFactoryMethod GetStaticConstructor() const + { + return &StaticConstruct; + } +}; + + +class IComponentManagerSettings +{ +public: + virtual bool LoadOnStartup() const = 0; + virtual bool KeepLoaded() const = 0; + virtual bool IsBlocked(const std::string &key) const = 0; + virtual mpt::PathString Path() const = 0; +}; + + +class ComponentManagerSettingsDefault + : public IComponentManagerSettings +{ +public: + virtual bool LoadOnStartup() const { return false; } + virtual bool KeepLoaded() const { return true; } + virtual bool IsBlocked(const std::string & /*key*/ ) const { return false; } + virtual mpt::PathString Path() const { return mpt::PathString(); } +}; + + +enum ComponentState +{ + ComponentStateUnregistered, + ComponentStateBlocked, + ComponentStateUnintialized, + ComponentStateUnavailable, + ComponentStateAvailable, +}; + + +struct ComponentInfo +{ + std::string name; + ComponentState state; + std::string settingsKey; + ComponentType type; +}; + + +class ComponentManager +{ + friend class ComponentFactoryBase; +public: + static void Init(const IComponentManagerSettings &settings); + static void Release(); + static std::shared_ptr Instance(); +private: + ComponentManager(const IComponentManagerSettings &settings); +private: + struct RegisteredComponent + { + std::string settingsKey; + ComponentFactoryMethod factoryMethod; + std::shared_ptr instance; + std::weak_ptr weakInstance; + }; + typedef std::map TComponentMap; + const IComponentManagerSettings &m_Settings; + TComponentMap m_Components; +private: + bool IsComponentBlocked(const std::string &settingsKey) const; + void InitializeComponent(std::shared_ptr component) const; +public: + void Register(const IComponentFactory &componentFactory); + void Startup(); + std::shared_ptr GetComponent(const IComponentFactory &componentFactory); + std::shared_ptr ReloadComponent(const IComponentFactory &componentFactory); + std::vector GetRegisteredComponents() const; + ComponentInfo GetComponentInfo(std::string name) const; + mpt::PathString GetComponentPath() const; +}; + + +struct ComponentListEntry +{ + ComponentListEntry *next; + void (*reg)(ComponentManager &componentManager); +}; + +bool ComponentListPush(ComponentListEntry *entry); + +#define MPT_DECLARE_COMPONENT_MEMBERS public: static const char * const g_ID; static const char * const g_SettingsKey; + +#define MPT_REGISTERED_COMPONENT(name, settingsKey) \ + static void RegisterComponent ## name (ComponentManager &componentManager) \ + { \ + componentManager.Register(ComponentFactory< name >()); \ + } \ + static ComponentListEntry Component ## name ## ListEntry = { nullptr, & RegisterComponent ## name }; \ + bool Component ## name ## Registered = ComponentListPush(& Component ## name ## ListEntry ); \ + const char * const name :: g_ID = #name ; \ + const char * const name :: g_SettingsKey = settingsKey ; \ +/**/ + + +template +std::shared_ptr GetComponent() +{ + return std::dynamic_pointer_cast(ComponentManager::Instance()->GetComponent(ComponentFactory())); +} + + +template +std::shared_ptr ReloadComponent() +{ + return std::dynamic_pointer_cast(ComponentManager::Instance()->ReloadComponent(ComponentFactory())); +} + + +static inline mpt::PathString GetComponentPath() +{ + return ComponentManager::Instance()->GetComponentPath(); +} + + +#else // !MPT_COMPONENT_MANAGER + + +#define MPT_DECLARE_COMPONENT_MEMBERS + +#define MPT_REGISTERED_COMPONENT(name, settingsKey) + + +template +std::shared_ptr GetComponent() +{ + static std::weak_ptr cache; + static mpt::mutex m; + MPT_LOCK_GUARD l(m); + std::shared_ptr component = cache.lock(); + if(!component) + { + component = std::make_shared(); + component->Initialize(); + cache = component; + } + return component; +} + + +static inline mpt::PathString GetComponentPath() +{ + return mpt::PathString(); +} + + +#endif // MPT_COMPONENT_MANAGER + + +// Simple wrapper around std::shared_ptr which automatically +// gets a reference to the component (or constructs it) on initialization. +template +class ComponentHandle +{ +private: + std::shared_ptr component; +public: + ComponentHandle() + : component(GetComponent()) + { + return; + } + ~ComponentHandle() + { + return; + } + bool IsAvailable() const + { + return component && component->IsAvailable(); + } + const T *get() const + { + return component.get(); + } + const T &operator*() const + { + return *component; + } + const T *operator->() const + { + return &*component; + } +#if MPT_COMPONENT_MANAGER + void Reload() + { + component = nullptr; + component = ReloadComponent(); + } +#endif +}; + + +template +bool IsComponentAvailable(const ComponentHandle &handle) +{ + return handle.IsAvailable(); +} + + +#endif // MPT_ENABLE_COMPONENTS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h new file mode 100644 index 000000000..52ef2df5c --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h @@ -0,0 +1,1061 @@ +/* + * Endianness.h + * ------------ + * Purpose: Code for deadling with endianness. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include +#include +#include +#include +#if MPT_COMPILER_MSVC +#include +#endif + + + +// Platform has native IEEE floating point representation _AND_ floating point +// endianess is the same as integer endianness. +// We just test __STDC_IEC_559__ for now. +#if MPT_COMPILER_GENERIC + #define MPT_PLATFORM_IEEE_FLOAT 0 +#elif MPT_COMPILER_MSVC + #define MPT_PLATFORM_IEEE_FLOAT 1 +#else // MPT_COMPILER + #if MPT_PLATFORM_ENDIAN_KNOWN + #if defined(__STDC_IEC_559__) + #if (__STDC_IEC_559__) + #define MPT_PLATFORM_IEEE_FLOAT 1 + #else + #define MPT_PLATFORM_IEEE_FLOAT 0 + #endif + #else + #define MPT_PLATFORM_IEEE_FLOAT 0 + #endif + #else + #define MPT_PLATFORM_IEEE_FLOAT 0 + #endif +#endif // MPT_COMPILER + +#if !MPT_PLATFORM_IEEE_FLOAT +#include +#endif + + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt { + +struct endian_type { uint16 value; }; +static MPT_FORCEINLINE bool operator == (const endian_type & a, const endian_type & b) { return a.value == b.value; } +static MPT_FORCEINLINE bool operator != (const endian_type & a, const endian_type & b) { return a.value != b.value; } + +static const endian_type endian_big = { 0x1234u }; +static const endian_type endian_little = { 0x3412u }; + +namespace detail { + static MPT_FORCEINLINE endian_type endian_probe() + { + STATIC_ASSERT(sizeof(endian_type) == 2); + const mpt::byte probe[2] = { 0x12, 0x34 }; + endian_type test; + std::memcpy(&test, probe, 2); + return test; + } +} + +static MPT_FORCEINLINE endian_type endian() +{ + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + return endian_little; + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + return endian_big; + #else + return detail::endian_probe(); + #endif +} + +static MPT_FORCEINLINE bool endian_is_little() +{ + return endian() == endian_little; +} + +static MPT_FORCEINLINE bool endian_is_big() +{ + return endian() == endian_big; +} + +} // namespace mpt + + + +namespace mpt { namespace detail { +enum Endianness +{ + BigEndian, + LittleEndian, +#if MPT_PLATFORM_ENDIAN_KNOWN +#if defined(MPT_PLATFORM_BIG_ENDIAN) + NativeEndian = BigEndian, +#else + NativeEndian = LittleEndian, +#endif +#endif +}; +} } // namespace mpt::detail + +struct BigEndian_tag +{ + static const mpt::detail::Endianness Endianness = mpt::detail::BigEndian; +}; + +struct LittleEndian_tag +{ + static const mpt::detail::Endianness Endianness = mpt::detail::LittleEndian; +}; + + + +// Ending swaps: +// SwapBytesBE(x) and variants may be used either to: +// -Convert integer x, which is in big endian format (for example read from file), +// to endian format of current architecture. +// -Convert value x from endian format of current architecture to big endian format. +// Similarly SwapBytesLE(x) converts known little endian format to format of current +// endian architecture or value x in format of current architecture to little endian +// format. + +#if MPT_COMPILER_GCC +#define MPT_bswap16 __builtin_bswap16 +#define MPT_bswap32 __builtin_bswap32 +#define MPT_bswap64 __builtin_bswap64 +#elif MPT_COMPILER_MSVC +#define MPT_bswap16 _byteswap_ushort +#define MPT_bswap32 _byteswap_ulong +#define MPT_bswap64 _byteswap_uint64 +#endif + +namespace mpt { namespace detail { +// catch system macros +#ifndef MPT_bswap16 +#ifdef bswap16 +static MPT_FORCEINLINE uint16 mpt_bswap16(uint16 x) { return bswap16(x); } +#define MPT_bswap16 mpt::detail::mpt_bswap16 +#endif +#endif +#ifndef MPT_bswap32 +#ifdef bswap32 +static MPT_FORCEINLINE uint32 mpt_bswap32(uint32 x) { return bswap32(x); } +#define MPT_bswap32 mpt::detail::mpt_bswap32 +#endif +#endif +#ifndef MPT_bswap64 +#ifdef bswap64 +static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); } +#define MPT_bswap64 mpt::detail::mpt_bswap64 +#endif +#endif +} } // namespace mpt::detail + + +// No intrinsics available +#ifndef MPT_bswap16 +#define MPT_bswap16(x) \ + ( uint16(0) \ + | ((static_cast(x) >> 8) & 0x00FFu) \ + | ((static_cast(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#endif +#ifndef MPT_bswap32 +#define MPT_bswap32(x) \ + ( uint32(0) \ + | ((static_cast(x) & 0x000000FFu) << 24) \ + | ((static_cast(x) & 0x0000FF00u) << 8) \ + | ((static_cast(x) & 0x00FF0000u) >> 8) \ + | ((static_cast(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#endif +#ifndef MPT_bswap64 +#define MPT_bswap64(x) \ + ( uint64(0) \ + | (((static_cast(x) >> 0) & 0xffull) << 56) \ + | (((static_cast(x) >> 8) & 0xffull) << 48) \ + | (((static_cast(x) >> 16) & 0xffull) << 40) \ + | (((static_cast(x) >> 24) & 0xffull) << 32) \ + | (((static_cast(x) >> 32) & 0xffull) << 24) \ + | (((static_cast(x) >> 40) & 0xffull) << 16) \ + | (((static_cast(x) >> 48) & 0xffull) << 8) \ + | (((static_cast(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ +#endif + + +#if MPT_PLATFORM_ENDIAN_KNOWN + +#if defined(MPT_PLATFORM_BIG_ENDIAN) + +#define MPT_bswap64le(x) MPT_bswap64(x) +#define MPT_bswap32le(x) MPT_bswap32(x) +#define MPT_bswap16le(x) MPT_bswap16(x) +#define MPT_bswap64be(x) (x) +#define MPT_bswap32be(x) (x) +#define MPT_bswap16be(x) (x) + +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + +#define MPT_bswap64be(x) MPT_bswap64(x) +#define MPT_bswap32be(x) MPT_bswap32(x) +#define MPT_bswap16be(x) MPT_bswap16(x) +#define MPT_bswap64le(x) (x) +#define MPT_bswap32le(x) (x) +#define MPT_bswap16le(x) (x) + +#endif + +#else // !MPT_PLATFORM_ENDIAN_KNOWN + +template +static MPT_FORCEINLINE std::array EndianEncode(T val) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + STATIC_ASSERT(sizeof(T) == size); + typedef T base_type; + typedef typename std::make_unsigned::type unsigned_base_type; + typedef Tendian endian_type; + unsigned_base_type uval = static_cast(val); + std::array data; + MPT_CONSTANT_IF(endian_type::Endianness == mpt::detail::LittleEndian) + { + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + data[i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); + } + } else + { + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + data[(sizeof(base_type)-1) - i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); + } + } + return data; +} + +template +static MPT_FORCEINLINE T EndianDecode(std::array data) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + STATIC_ASSERT(sizeof(T) == size); + typedef T base_type; + typedef typename std::make_unsigned::type unsigned_base_type; + typedef Tendian endian_type; + base_type val = base_type(); + unsigned_base_type uval = unsigned_base_type(); + MPT_CONSTANT_IF(endian_type::Endianness == mpt::detail::LittleEndian) + { + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + uval |= static_cast(static_cast(data[i])) << (i*8); + } + } else + { + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + uval |= static_cast(static_cast(data[(sizeof(base_type)-1) - i])) << (i*8); + } + } + val = static_cast(uval); + return val; +} + +template +static MPT_FORCEINLINE T MPT_bswap_impl(T val) +{ + typedef typename std::make_unsigned::type Tu; + std::array data = EndianEncode(val); + std::memcpy(&val, data.data(), sizeof(T)); + return val; +} + +#define MPT_bswap64be(x) MPT_bswap_impl(x) +#define MPT_bswap32be(x) MPT_bswap_impl(x) +#define MPT_bswap16be(x) MPT_bswap_impl(x) +#define MPT_bswap64le(x) MPT_bswap_impl(x) +#define MPT_bswap32le(x) MPT_bswap_impl(x) +#define MPT_bswap16le(x) MPT_bswap_impl(x) + +#endif // MPT_PLATFORM_ENDIAN_KNOWN + +static MPT_FORCEINLINE uint64 SwapBytesBE(uint64 value) { return MPT_bswap64be(value); } +static MPT_FORCEINLINE uint32 SwapBytesBE(uint32 value) { return MPT_bswap32be(value); } +static MPT_FORCEINLINE uint16 SwapBytesBE(uint16 value) { return MPT_bswap16be(value); } +static MPT_FORCEINLINE uint64 SwapBytesLE(uint64 value) { return MPT_bswap64le(value); } +static MPT_FORCEINLINE uint32 SwapBytesLE(uint32 value) { return MPT_bswap32le(value); } +static MPT_FORCEINLINE uint16 SwapBytesLE(uint16 value) { return MPT_bswap16le(value); } +static MPT_FORCEINLINE int64 SwapBytesBE(int64 value) { return MPT_bswap64be(value); } +static MPT_FORCEINLINE int32 SwapBytesBE(int32 value) { return MPT_bswap32be(value); } +static MPT_FORCEINLINE int16 SwapBytesBE(int16 value) { return MPT_bswap16be(value); } +static MPT_FORCEINLINE int64 SwapBytesLE(int64 value) { return MPT_bswap64le(value); } +static MPT_FORCEINLINE int32 SwapBytesLE(int32 value) { return MPT_bswap32le(value); } +static MPT_FORCEINLINE int16 SwapBytesLE(int16 value) { return MPT_bswap16le(value); } + +// Do NOT remove these overloads, even if they seem useless. +// We do not want risking to extend 8bit integers to int and then +// endian-converting and casting back to int. +// Thus these overloads. +static MPT_FORCEINLINE uint8 SwapBytesLE(uint8 value) { return value; } +static MPT_FORCEINLINE int8 SwapBytesLE(int8 value) { return value; } +static MPT_FORCEINLINE char SwapBytesLE(char value) { return value; } +static MPT_FORCEINLINE uint8 SwapBytesBE(uint8 value) { return value; } +static MPT_FORCEINLINE int8 SwapBytesBE(int8 value) { return value; } +static MPT_FORCEINLINE char SwapBytesBE(char value) { return value; } + +static MPT_FORCEINLINE uint64 SwapBytes(uint64 value) { return MPT_bswap64(value); } +static MPT_FORCEINLINE uint32 SwapBytes(uint32 value) { return MPT_bswap32(value); } +static MPT_FORCEINLINE uint16 SwapBytes(uint16 value) { return MPT_bswap16(value); } +static MPT_FORCEINLINE int64 SwapBytes(int64 value) { return MPT_bswap64(value); } +static MPT_FORCEINLINE int32 SwapBytes(int32 value) { return MPT_bswap32(value); } +static MPT_FORCEINLINE int16 SwapBytes(int16 value) { return MPT_bswap16(value); } +static MPT_FORCEINLINE uint8 SwapBytes(uint8 value) { return value; } +static MPT_FORCEINLINE int8 SwapBytes(int8 value) { return value; } +static MPT_FORCEINLINE char SwapBytes(char value) { return value; } + +#undef MPT_bswap16le +#undef MPT_bswap32le +#undef MPT_bswap64le +#undef MPT_bswap16be +#undef MPT_bswap32be +#undef MPT_bswap64be +#undef MPT_bswap16 +#undef MPT_bswap32 +#undef MPT_bswap64 + + +// 1.0f --> 0x3f800000u +static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) +{ +#if MPT_PLATFORM_IEEE_FLOAT + STATIC_ASSERT(sizeof(uint32) == sizeof(float32)); + #if MPT_COMPILER_UNION_TYPE_ALIASES + union { + float32 f; + uint32 i; + } conv; + conv.f = f; + return conv.i; + #else + uint32 i = 0; + std::memcpy(&i, &f, sizeof(float32)); + return i; + #endif +#else + int e = 0; + float m = std::frexp(f, &e); + if(e == 0 && std::fabs(m) == 0.0f) + { + uint32 expo = 0u; + uint32 sign = std::signbit(m) ? 0x01u : 0x00u; + uint32 mant = 0u; + uint32 i = 0u; + i |= (mant << 0) & 0x007fffffu; + i |= (expo << 23) & 0x7f800000u; + i |= (sign << 31) & 0x80000000u; + return i; + } else + { + uint32 expo = e + 127 - 1; + uint32 sign = std::signbit(m) ? 0x01u : 0x00u; + uint32 mant = static_cast(std::fabs(std::ldexp(m, 24))); + uint32 i = 0u; + i |= (mant << 0) & 0x007fffffu; + i |= (expo << 23) & 0x7f800000u; + i |= (sign << 31) & 0x80000000u; + return i; + } +#endif +} +static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) +{ +#if MPT_PLATFORM_IEEE_FLOAT + STATIC_ASSERT(sizeof(uint64) == sizeof(float64)); + #if MPT_COMPILER_UNION_TYPE_ALIASES + union { + float64 f; + uint64 i; + } conv; + conv.f = f; + return conv.i; + #else + uint64 i = 0; + std::memcpy(&i, &f, sizeof(float64)); + return i; + #endif +#else + int e = 0; + double m = std::frexp(f, &e); + if(e == 0 && std::fabs(m) == 0.0) + { + uint64 expo = 0u; + uint64 sign = std::signbit(m) ? 0x01u : 0x00u; + uint64 mant = 0u; + uint64 i = 0u; + i |= (mant << 0) & 0x000fffffffffffffull; + i |= (expo << 52) & 0x7ff0000000000000ull; + i |= (sign << 63) & 0x8000000000000000ull; + return i; + } else + { + uint64 expo = e + 1023 - 1; + uint64 sign = std::signbit(m) ? 0x01u : 0x00u; + uint64 mant = static_cast(std::fabs(std::ldexp(m, 53))); + uint64 i = 0u; + i |= (mant << 0) & 0x000fffffffffffffull; + i |= (expo << 52) & 0x7ff0000000000000ull; + i |= (sign << 63) & 0x8000000000000000ull; + return i; + } +#endif +} + +// 0x3f800000u --> 1.0f +static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) +{ +#if MPT_PLATFORM_IEEE_FLOAT + STATIC_ASSERT(sizeof(uint32) == sizeof(float32)); + #if MPT_COMPILER_UNION_TYPE_ALIASES + union { + uint32 i; + float32 f; + } conv; + conv.i = i; + return conv.f; + #else + float32 f = 0.0f; + std::memcpy(&f, &i, sizeof(float32)); + return f; + #endif +#else + uint32 mant = (i & 0x007fffffu) >> 0; + uint32 expo = (i & 0x7f800000u) >> 23; + uint32 sign = (i & 0x80000000u) >> 31; + if(expo == 0) + { + float m = sign ? -static_cast(mant) : static_cast(mant); + int e = expo - 127 + 1 - 24; + float f = std::ldexp(m, e); + return static_cast(f); + } else + { + mant |= 0x00800000u; + float m = sign ? -static_cast(mant) : static_cast(mant); + int e = expo - 127 + 1 - 24; + float f = std::ldexp(m, e); + return static_cast(f); + } +#endif +} +static MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) +{ +#if MPT_PLATFORM_IEEE_FLOAT + STATIC_ASSERT(sizeof(uint64) == sizeof(float64)); + #if MPT_COMPILER_UNION_TYPE_ALIASES + union { + uint64 i; + float64 f; + } conv; + conv.i = i; + return conv.f; + #else + float64 f = 0.0; + std::memcpy(&f, &i, sizeof(float64)); + return f; + #endif +#else + uint64 mant = (i & 0x000fffffffffffffull) >> 0; + uint64 expo = (i & 0x7ff0000000000000ull) >> 52; + uint64 sign = (i & 0x8000000000000000ull) >> 63; + if(expo == 0) + { + double m = sign ? -static_cast(mant) : static_cast(mant); + int e = expo - 1023 + 1 - 53; + double f = std::ldexp(m, e); + return static_cast(f); + } else + { + mant |= 0x0010000000000000ull; + double m = sign ? -static_cast(mant) : static_cast(mant); + int e = expo - 1023 + 1 - 53; + double f = std::ldexp(m, e); + return static_cast(f); + } +#endif +} + + +// template parameters are byte indices corresponding to the individual bytes of iee754 in memory +template +struct IEEE754binary32Emulated +{ +private: + typedef IEEE754binary32Emulated self_t; + mpt::byte bytes[4]; +public: + MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + { + return bytes[i]; + } + MPT_FORCEINLINE IEEE754binary32Emulated() { } + MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) + { + SetInt32(EncodeIEEE754binary32(f)); + } + // b0...b3 are in memory order, i.e. depend on the endianness of this type + // little endian: (0x00,0x00,0x80,0x3f) + // big endian: (0x3f,0x80,0x00,0x00) + MPT_FORCEINLINE explicit IEEE754binary32Emulated(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3) + { + bytes[0] = b0; + bytes[1] = b1; + bytes[2] = b2; + bytes[3] = b3; + } + MPT_FORCEINLINE operator float32 () const + { + return DecodeIEEE754binary32(GetInt32()); + } + MPT_FORCEINLINE self_t & SetInt32(uint32 i) + { + bytes[hihi] = static_cast(i >> 24); + bytes[hilo] = static_cast(i >> 16); + bytes[lohi] = static_cast(i >> 8); + bytes[lolo] = static_cast(i >> 0); + return *this; + } + MPT_FORCEINLINE uint32 GetInt32() const + { + return 0u + | (static_cast(bytes[hihi]) << 24) + | (static_cast(bytes[hilo]) << 16) + | (static_cast(bytes[lohi]) << 8) + | (static_cast(bytes[lolo]) << 0) + ; + } + MPT_FORCEINLINE bool operator == (const self_t &cmp) const + { + return true + && bytes[0] == cmp.bytes[0] + && bytes[1] == cmp.bytes[1] + && bytes[2] == cmp.bytes[2] + && bytes[3] == cmp.bytes[3] + ; + } + MPT_FORCEINLINE bool operator != (const self_t &cmp) const + { + return !(*this == cmp); + } +}; +template +struct IEEE754binary64Emulated +{ +private: + typedef IEEE754binary64Emulated self_t; + mpt::byte bytes[8]; +public: + MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + { + return bytes[i]; + } + MPT_FORCEINLINE IEEE754binary64Emulated() { } + MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) + { + SetInt64(EncodeIEEE754binary64(f)); + } + MPT_FORCEINLINE explicit IEEE754binary64Emulated(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3, mpt::byte b4, mpt::byte b5, mpt::byte b6, mpt::byte b7) + { + bytes[0] = b0; + bytes[1] = b1; + bytes[2] = b2; + bytes[3] = b3; + bytes[4] = b4; + bytes[5] = b5; + bytes[6] = b6; + bytes[7] = b7; + } + MPT_FORCEINLINE operator float64 () const + { + return DecodeIEEE754binary64(GetInt64()); + } + MPT_FORCEINLINE self_t & SetInt64(uint64 i) + { + bytes[hihihi] = static_cast(i >> 56); + bytes[hihilo] = static_cast(i >> 48); + bytes[hilohi] = static_cast(i >> 40); + bytes[hilolo] = static_cast(i >> 32); + bytes[lohihi] = static_cast(i >> 24); + bytes[lohilo] = static_cast(i >> 16); + bytes[lolohi] = static_cast(i >> 8); + bytes[lololo] = static_cast(i >> 0); + return *this; + } + MPT_FORCEINLINE uint64 GetInt64() const + { + return 0u + | (static_cast(bytes[hihihi]) << 56) + | (static_cast(bytes[hihilo]) << 48) + | (static_cast(bytes[hilohi]) << 40) + | (static_cast(bytes[hilolo]) << 32) + | (static_cast(bytes[lohihi]) << 24) + | (static_cast(bytes[lohilo]) << 16) + | (static_cast(bytes[lolohi]) << 8) + | (static_cast(bytes[lololo]) << 0) + ; + } + MPT_FORCEINLINE bool operator == (const self_t &cmp) const + { + return true + && bytes[0] == cmp.bytes[0] + && bytes[1] == cmp.bytes[1] + && bytes[2] == cmp.bytes[2] + && bytes[3] == cmp.bytes[3] + && bytes[4] == cmp.bytes[4] + && bytes[5] == cmp.bytes[5] + && bytes[6] == cmp.bytes[6] + && bytes[7] == cmp.bytes[7] + ; + } + MPT_FORCEINLINE bool operator != (const self_t &cmp) const + { + return !(*this == cmp); + } +}; + +typedef IEEE754binary32Emulated<0,1,2,3> IEEE754binary32EmulatedBE; +typedef IEEE754binary32Emulated<3,2,1,0> IEEE754binary32EmulatedLE; +typedef IEEE754binary64Emulated<0,1,2,3,4,5,6,7> IEEE754binary64EmulatedBE; +typedef IEEE754binary64Emulated<7,6,5,4,3,2,1,0> IEEE754binary64EmulatedLE; + +MPT_BINARY_STRUCT(IEEE754binary32EmulatedBE, 4) +MPT_BINARY_STRUCT(IEEE754binary32EmulatedLE, 4) +MPT_BINARY_STRUCT(IEEE754binary64EmulatedBE, 8) +MPT_BINARY_STRUCT(IEEE754binary64EmulatedLE, 8) + +#if MPT_PLATFORM_IEEE_FLOAT + +struct IEEE754binary32Native +{ +private: + float32 value; +public: + MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + { + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + return static_cast(EncodeIEEE754binary32(value) >> (i*8)); + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); + #else + STATIC_ASSERT(false); + #endif + } + MPT_FORCEINLINE IEEE754binary32Native() { } + MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) + { + value = f; + } + // b0...b3 are in memory order, i.e. depend on the endianness of this type + // little endian: (0x00,0x00,0x80,0x3f) + // big endian: (0x3f,0x80,0x00,0x00) + MPT_FORCEINLINE explicit IEEE754binary32Native(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3) + { + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + value = DecodeIEEE754binary32(0u + | (static_cast(b0) << 0) + | (static_cast(b1) << 8) + | (static_cast(b2) << 16) + | (static_cast(b3) << 24) + ); + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + value = DecodeIEEE754binary32(0u + | (static_cast(b0) << 24) + | (static_cast(b1) << 16) + | (static_cast(b2) << 8) + | (static_cast(b3) << 0) + ); + #else + STATIC_ASSERT(false); + #endif + } + MPT_FORCEINLINE operator float32 () const + { + return value; + } + MPT_FORCEINLINE IEEE754binary32Native & SetInt32(uint32 i) + { + value = DecodeIEEE754binary32(i); + return *this; + } + MPT_FORCEINLINE uint32 GetInt32() const + { + return EncodeIEEE754binary32(value); + } + MPT_FORCEINLINE bool operator == (const IEEE754binary32Native &cmp) const + { + return value == cmp.value; + } + MPT_FORCEINLINE bool operator != (const IEEE754binary32Native &cmp) const + { + return value != cmp.value; + } +}; + +struct IEEE754binary64Native +{ +private: + float64 value; +public: + MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + { + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + return static_cast(EncodeIEEE754binary64(value) >> (i*8)); + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + return static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8)); + #else + STATIC_ASSERT(false); + #endif + } + MPT_FORCEINLINE IEEE754binary64Native() { } + MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) + { + value = f; + } + MPT_FORCEINLINE explicit IEEE754binary64Native(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3, mpt::byte b4, mpt::byte b5, mpt::byte b6, mpt::byte b7) + { + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + value = DecodeIEEE754binary64(0ull + | (static_cast(b0) << 0) + | (static_cast(b1) << 8) + | (static_cast(b2) << 16) + | (static_cast(b3) << 24) + | (static_cast(b4) << 32) + | (static_cast(b5) << 40) + | (static_cast(b6) << 48) + | (static_cast(b7) << 56) + ); + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + value = DecodeIEEE754binary64(0ull + | (static_cast(b0) << 56) + | (static_cast(b1) << 48) + | (static_cast(b2) << 40) + | (static_cast(b3) << 32) + | (static_cast(b4) << 24) + | (static_cast(b5) << 16) + | (static_cast(b6) << 8) + | (static_cast(b7) << 0) + ); + #else + STATIC_ASSERT(false); + #endif + } + MPT_FORCEINLINE operator float64 () const + { + return value; + } + MPT_FORCEINLINE IEEE754binary64Native & SetInt64(uint64 i) + { + value = DecodeIEEE754binary64(i); + return *this; + } + MPT_FORCEINLINE uint64 GetInt64() const + { + return EncodeIEEE754binary64(value); + } + MPT_FORCEINLINE bool operator == (const IEEE754binary64Native &cmp) const + { + return value == cmp.value; + } + MPT_FORCEINLINE bool operator != (const IEEE754binary64Native &cmp) const + { + return value != cmp.value; + } +}; + +STATIC_ASSERT(sizeof(IEEE754binary32Native) == 4); +STATIC_ASSERT(sizeof(IEEE754binary64Native) == 8); + +#if MPT_PLATFORM_IEEE_FLOAT +namespace mpt { +template <> struct is_binary_safe< IEEE754binary32Native > : public std::true_type { }; +template <> struct is_binary_safe< IEEE754binary64Native > : public std::true_type { }; +} +#endif // MPT_PLATFORM_IEEE_FLOAT + +#if defined(MPT_PLATFORM_LITTLE_ENDIAN) +typedef IEEE754binary32Native IEEE754binary32LE; +typedef IEEE754binary32EmulatedBE IEEE754binary32BE; +typedef IEEE754binary64Native IEEE754binary64LE; +typedef IEEE754binary64EmulatedBE IEEE754binary64BE; +#elif defined(MPT_PLATFORM_BIG_ENDIAN) +typedef IEEE754binary32EmulatedLE IEEE754binary32LE; +typedef IEEE754binary32Native IEEE754binary32BE; +typedef IEEE754binary64EmulatedLE IEEE754binary64LE; +typedef IEEE754binary64Native IEEE754binary64BE; +#endif + +#else // !MPT_PLATFORM_IEEE_FLOAT + +typedef IEEE754binary32EmulatedLE IEEE754binary32LE; +typedef IEEE754binary32EmulatedBE IEEE754binary32BE; +typedef IEEE754binary64EmulatedLE IEEE754binary64LE; +typedef IEEE754binary64EmulatedBE IEEE754binary64BE; + +#endif // MPT_PLATFORM_IEEE_FLOAT + +STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); +STATIC_ASSERT(sizeof(IEEE754binary32BE) == 4); +STATIC_ASSERT(sizeof(IEEE754binary64LE) == 8); +STATIC_ASSERT(sizeof(IEEE754binary64BE) == 8); + +typedef IEEE754binary32LE float32le; +typedef IEEE754binary32BE float32be; +typedef IEEE754binary64LE float64le; +typedef IEEE754binary64BE float64be; + +STATIC_ASSERT(sizeof(float32le) == 4); +STATIC_ASSERT(sizeof(float32be) == 4); +STATIC_ASSERT(sizeof(float64le) == 8); +STATIC_ASSERT(sizeof(float64be) == 8); + + +// On-disk integer types with defined endianness and no alignemnt requirements +// Note: To easily debug module loaders (and anything else that uses this +// wrapper struct), you can use the Debugger Visualizers available in +// build/vs/debug/ to conveniently view the wrapped contents. + +template +struct packed +{ +public: + typedef T base_type; + typedef Tendian endian_type; +private: +#if MPT_PLATFORM_ENDIAN_KNOWN + mpt::byte data[sizeof(base_type)]; +#else // !MPT_PLATFORM_ENDIAN_KNOWN + std::array data; +#endif // MPT_PLATFORM_ENDIAN_KNOWN +public: + MPT_FORCEINLINE void set(base_type val) + { + STATIC_ASSERT(std::numeric_limits::is_integer); + #if MPT_PLATFORM_ENDIAN_KNOWN + MPT_CONSTANT_IF(mpt::detail::NativeEndian != endian_type::Endianness) + { + val = SwapBytes(val); + } + std::memcpy(data, &val, sizeof(val)); + #else // !MPT_PLATFORM_ENDIAN_KNOWN + typedef typename std::make_unsigned::type unsigned_base_type; + data = EndianEncode(val); + #endif // MPT_PLATFORM_ENDIAN_KNOWN + } + MPT_FORCEINLINE base_type get() const + { + STATIC_ASSERT(std::numeric_limits::is_integer); + #if MPT_PLATFORM_ENDIAN_KNOWN + base_type val = base_type(); + std::memcpy(&val, data, sizeof(val)); + MPT_CONSTANT_IF(mpt::detail::NativeEndian != endian_type::Endianness) + { + val = SwapBytes(val); + } + return val; + #else // !MPT_PLATFORM_ENDIAN_KNOWN + typedef typename std::make_unsigned::type unsigned_base_type; + return EndianDecode(data); + #endif // MPT_PLATFORM_ENDIAN_KNOWN + } + MPT_FORCEINLINE packed & operator = (const base_type & val) { set(val); return *this; } + MPT_FORCEINLINE operator base_type () const { return get(); } +public: + packed & operator &= (base_type val) { set(get() & val); return *this; } + packed & operator |= (base_type val) { set(get() | val); return *this; } + packed & operator ^= (base_type val) { set(get() ^ val); return *this; } + packed & operator += (base_type val) { set(get() + val); return *this; } + packed & operator -= (base_type val) { set(get() - val); return *this; } + packed & operator *= (base_type val) { set(get() * val); return *this; } + packed & operator /= (base_type val) { set(get() / val); return *this; } + packed & operator %= (base_type val) { set(get() % val); return *this; } + packed & operator ++ () { set(get() + 1); return *this; } // prefix + packed & operator -- () { set(get() - 1); return *this; } // prefix + base_type operator ++ (int) { base_type old = get(); set(old + 1); return old; } // postfix + base_type operator -- (int) { base_type old = get(); set(old - 1); return old; } // postfix +}; + +typedef packed< int64, LittleEndian_tag> int64le; +typedef packed< int32, LittleEndian_tag> int32le; +typedef packed< int16, LittleEndian_tag> int16le; +typedef packed< int8 , LittleEndian_tag> int8le; +typedef packed uint64le; +typedef packed uint32le; +typedef packed uint16le; +typedef packed uint8le; + +typedef packed< int64, BigEndian_tag> int64be; +typedef packed< int32, BigEndian_tag> int32be; +typedef packed< int16, BigEndian_tag> int16be; +typedef packed< int8 , BigEndian_tag> int8be; +typedef packed uint64be; +typedef packed uint32be; +typedef packed uint16be; +typedef packed uint8be; + +MPT_BINARY_STRUCT(int64le, 8) +MPT_BINARY_STRUCT(int32le, 4) +MPT_BINARY_STRUCT(int16le, 2) +MPT_BINARY_STRUCT(int8le , 1) +MPT_BINARY_STRUCT(uint64le, 8) +MPT_BINARY_STRUCT(uint32le, 4) +MPT_BINARY_STRUCT(uint16le, 2) +MPT_BINARY_STRUCT(uint8le , 1) + +MPT_BINARY_STRUCT(int64be, 8) +MPT_BINARY_STRUCT(int32be, 4) +MPT_BINARY_STRUCT(int16be, 2) +MPT_BINARY_STRUCT(int8be , 1) +MPT_BINARY_STRUCT(uint64be, 8) +MPT_BINARY_STRUCT(uint32be, 4) +MPT_BINARY_STRUCT(uint16be, 2) +MPT_BINARY_STRUCT(uint8be , 1) + +namespace mpt { + +template struct make_le { typedef packed type; }; +template struct make_be { typedef packed type; }; + +} // namespace mpt + + + +// Small helper class to support unaligned memory access on all platforms. +// This is only used to make old module loaders work everywhere. +// Do not use in new code. +template +class const_unaligned_ptr_le +{ +public: + typedef T value_type; +private: + const mpt::byte *mem; + value_type Read() const + { + mpt::byte bytes[sizeof(value_type)]; + std::memcpy(bytes, mem, sizeof(value_type)); + #if defined(MPT_PLATFORM_BIG_ENDIAN) + std::reverse(bytes, bytes + sizeof(value_type)); + #endif + value_type val = value_type(); + std::memcpy(&val, bytes, sizeof(value_type)); + return val; + } +public: + const_unaligned_ptr_le() : mem(nullptr) {} + const_unaligned_ptr_le(const const_unaligned_ptr_le & other) : mem(other.mem) {} + const_unaligned_ptr_le & operator = (const const_unaligned_ptr_le & other) { mem = other.mem; return *this; } + explicit const_unaligned_ptr_le(const uint8 *mem) : mem(mem) {} + explicit const_unaligned_ptr_le(const char *mem) : mem(mpt::byte_cast(mem)) {} + const_unaligned_ptr_le & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } + const_unaligned_ptr_le & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } + const_unaligned_ptr_le & operator ++ () { mem += sizeof(value_type); return *this; } + const_unaligned_ptr_le & operator -- () { mem -= sizeof(value_type); return *this; } + const_unaligned_ptr_le operator ++ (int) { const_unaligned_ptr_le result = *this; ++result; return result; } + const_unaligned_ptr_le operator -- (int) { const_unaligned_ptr_le result = *this; --result; return result; } + const_unaligned_ptr_le operator + (std::size_t count) const { const_unaligned_ptr_le result = *this; result += count; return result; } + const_unaligned_ptr_le operator - (std::size_t count) const { const_unaligned_ptr_le result = *this; result -= count; return result; } + const value_type operator * () const { return Read(); } + const value_type operator [] (std::size_t i) const { return *((*this) + i); } + operator bool () const { return mem != nullptr; } +}; + +template +class const_unaligned_ptr_be +{ +public: + typedef T value_type; +private: + const mpt::byte *mem; + value_type Read() const + { + mpt::byte bytes[sizeof(value_type)]; + std::memcpy(bytes, mem, sizeof(value_type)); + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + std::reverse(bytes, bytes + sizeof(value_type)); + #endif + value_type val = value_type(); + std::memcpy(&val, bytes, sizeof(value_type)); + return val; + } +public: + const_unaligned_ptr_be() : mem(nullptr) {} + const_unaligned_ptr_be(const const_unaligned_ptr_be & other) : mem(other.mem) {} + const_unaligned_ptr_be & operator = (const const_unaligned_ptr_be & other) { mem = other.mem; return *this; } + explicit const_unaligned_ptr_be(const uint8 *mem) : mem(mem) {} + explicit const_unaligned_ptr_be(const char *mem) : mem(mpt::byte_cast(mem)) {} + const_unaligned_ptr_be & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } + const_unaligned_ptr_be & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } + const_unaligned_ptr_be & operator ++ () { mem += sizeof(value_type); return *this; } + const_unaligned_ptr_be & operator -- () { mem -= sizeof(value_type); return *this; } + const_unaligned_ptr_be operator ++ (int) { const_unaligned_ptr_be result = *this; ++result; return result; } + const_unaligned_ptr_be operator -- (int) { const_unaligned_ptr_be result = *this; --result; return result; } + const_unaligned_ptr_be operator + (std::size_t count) const { const_unaligned_ptr_be result = *this; result += count; return result; } + const_unaligned_ptr_be operator - (std::size_t count) const { const_unaligned_ptr_be result = *this; result -= count; return result; } + const value_type operator * () const { return Read(); } + const value_type operator [] (std::size_t i) const { return *((*this) + i); } + operator bool () const { return mem != nullptr; } +}; + +template +class const_unaligned_ptr +{ +public: + typedef T value_type; +private: + const mpt::byte *mem; + value_type Read() const + { + value_type val = value_type(); + std::memcpy(&val, mem, sizeof(value_type)); + return val; + } +public: + const_unaligned_ptr() : mem(nullptr) {} + const_unaligned_ptr(const const_unaligned_ptr & other) : mem(other.mem) {} + const_unaligned_ptr & operator = (const const_unaligned_ptr & other) { mem = other.mem; return *this; } + explicit const_unaligned_ptr(const uint8 *mem) : mem(mem) {} + explicit const_unaligned_ptr(const char *mem) : mem(mpt::byte_cast(mem)) {} + const_unaligned_ptr & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } + const_unaligned_ptr & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } + const_unaligned_ptr & operator ++ () { mem += sizeof(value_type); return *this; } + const_unaligned_ptr & operator -- () { mem -= sizeof(value_type); return *this; } + const_unaligned_ptr operator ++ (int) { const_unaligned_ptr result = *this; ++result; return result; } + const_unaligned_ptr operator -- (int) { const_unaligned_ptr result = *this; --result; return result; } + const_unaligned_ptr operator + (std::size_t count) const { const_unaligned_ptr result = *this; result += count; return result; } + const_unaligned_ptr operator - (std::size_t count) const { const_unaligned_ptr result = *this; result -= count; return result; } + const value_type operator * () const { return Read(); } + const value_type operator [] (std::size_t i) const { return *((*this) + i); } + operator bool () const { return mem != nullptr; } +}; + + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp b/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp new file mode 100644 index 000000000..6f7b33f91 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp @@ -0,0 +1,154 @@ +/* + * FileReader.cpp + * -------------- + * Purpose: A basic class for transparent reading of memory-based files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "FileReader.h" + +#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS +#include +#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS + + +OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension) + : m_IsTempFile(false) +{ + try + { + file.Rewind(); + if(file.GetFileName().empty()) + { + const mpt::PathString tempName = mpt::CreateTempFileName(MPT_PATHSTRING("OpenMPT"), fileNameExtension); + +#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT +#if (_WIN32_WINNT < 0x0602) +#define MPT_ONDISKFILEWRAPPER_NO_CREATEFILE +#endif +#endif + +#ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + FILE * f = _wfopen(tempName.AsNative().c_str(), L"wb"); + if(!f) + { + throw std::runtime_error(""); + } + while(!file.EndOfFile()) + { + FileReader::PinnedRawDataView view = file.ReadPinnedRawDataView(mpt::IO::BUFFERSIZE_NORMAL); + std::size_t towrite = view.size(); + std::size_t written = 0; + do + { + std::size_t chunkSize = mpt::saturate_cast(towrite); + std::size_t chunkDone = 0; + chunkDone = fwrite(view.data() + written, 1, chunkSize, f); + if(chunkDone != chunkSize) + { + fclose(f); + f = NULL; + throw std::runtime_error(""); + } + towrite -= chunkDone; + written += chunkDone; + } while(towrite > 0); + } + fclose(f); + f = NULL; + +#else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + HANDLE hFile = NULL; + #if MPT_OS_WINDOWS_WINRT + hFile = CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL); + #else + hFile = CreateFileW(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); + #endif + if(hFile == NULL || hFile == INVALID_HANDLE_VALUE) + { + throw std::runtime_error(""); + } + while(!file.EndOfFile()) + { + FileReader::PinnedRawDataView view = file.ReadPinnedRawDataView(mpt::IO::BUFFERSIZE_NORMAL); + std::size_t towrite = view.size(); + std::size_t written = 0; + do + { + DWORD chunkSize = mpt::saturate_cast(towrite); + DWORD chunkDone = 0; + WriteFile(hFile, view.data() + written, chunkSize, &chunkDone, NULL); + if(chunkDone != chunkSize) + { + CloseHandle(hFile); + hFile = NULL; + throw std::runtime_error(""); + } + towrite -= chunkDone; + written += chunkDone; + } while(towrite > 0); + } + CloseHandle(hFile); + hFile = NULL; + +#endif // MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + m_Filename = tempName; + m_IsTempFile = true; + } else + { + m_Filename = file.GetFileName(); + } + } catch (const std::runtime_error &) + { + m_IsTempFile = false; + m_Filename = mpt::PathString(); + } +} + + +OnDiskFileWrapper::~OnDiskFileWrapper() +{ + if(m_IsTempFile) + { + DeleteFileW(m_Filename.AsNative().c_str()); + m_IsTempFile = false; + } + m_Filename = mpt::PathString(); +} + + +bool OnDiskFileWrapper::IsValid() const +{ + return !m_Filename.empty(); +} + + +mpt::PathString OnDiskFileWrapper::GetFilename() const +{ + return m_Filename; +} + + +#else + + +MPT_MSVC_WORKAROUND_LNK4221(FileReader) + + +#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h new file mode 100644 index 000000000..200fcd21c --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h @@ -0,0 +1,1167 @@ +/* + * FileReader.h + * ------------ + * Purpose: A basic class for transparent reading of memory-based files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "typedefs.h" +#include "mptTypeTraits.h" +#include "StringFixer.h" +#include "misc_util.h" +#include "Endianness.h" +#include "mptIO.h" +#include +#include +#include +#include + +#include "FileReaderFwd.h" + + +OPENMPT_NAMESPACE_BEGIN + + +// change to show warnings for functions which trigger pre-caching the whole file for unseekable streams +//#define FILEREADER_DEPRECATED MPT_DEPRECATED +#define FILEREADER_DEPRECATED + + +class FileReaderTraitsMemory +{ + +public: + + typedef FileDataContainerMemory::off_t off_t; + + typedef FileDataContainerMemory data_type; + typedef const FileDataContainerMemory & ref_data_type; + typedef const FileDataContainerMemory & shared_data_type; + typedef FileDataContainerMemory value_data_type; + + static shared_data_type get_shared(const data_type & data) { return data; } + static ref_data_type get_ref(const data_type & data) { return data; } + + static value_data_type make_data() { return mpt::const_byte_span(); } + static value_data_type make_data(mpt::const_byte_span data) { return data; } + + static value_data_type make_chunk(shared_data_type data, off_t position, off_t size) + { + return mpt::as_span(data.GetRawData() + position, size); + } + +}; + +#if defined(MPT_FILEREADER_STD_ISTREAM) + +class FileReaderTraitsStdStream +{ + +public: + + typedef IFileDataContainer::off_t off_t; + + typedef std::shared_ptr data_type; + typedef const IFileDataContainer & ref_data_type; + typedef std::shared_ptr shared_data_type; + typedef std::shared_ptr value_data_type; + + static shared_data_type get_shared(const data_type & data) { return data; } + static ref_data_type get_ref(const data_type & data) { return *data; } + + static value_data_type make_data() { return std::make_shared(); } + static value_data_type make_data(mpt::const_byte_span data) { return std::make_shared(data); } + + static value_data_type make_chunk(shared_data_type data, off_t position, off_t size) + { + return std::static_pointer_cast(std::make_shared(data, position, size)); + } + +}; + +typedef FileReaderTraitsStdStream FileReaderTraitsDefault; + +#else // !MPT_FILEREADER_STD_ISTREAM + +typedef FileReaderTraitsMemory FileReaderTraitsDefault; + +#endif // MPT_FILEREADER_STD_ISTREAM + +namespace detail { + +template +class FileReader +{ + +private: + + typedef Ttraits traits_type; + +public: + + typedef typename traits_type::off_t off_t; + + typedef typename traits_type::data_type data_type; + typedef typename traits_type::ref_data_type ref_data_type; + typedef typename traits_type::shared_data_type shared_data_type; + typedef typename traits_type::value_data_type value_data_type; + +protected: + + shared_data_type SharedDataContainer() const { return traits_type::get_shared(m_data); } + ref_data_type DataContainer() const { return traits_type::get_ref(m_data); } + + static value_data_type DataInitializer() { return traits_type::make_data(); } + static value_data_type DataInitializer(mpt::const_byte_span data) { return traits_type::make_data(data); } + + static value_data_type CreateChunkImpl(shared_data_type data, off_t position, off_t size) { return traits_type::make_chunk(data, position, size); } + +private: + + data_type m_data; + + off_t streamPos; // Cursor location in the file + + const mpt::PathString *fileName; // Filename that corresponds to this FileReader. It is only set if this FileReader represents the whole contents of fileName. May be nullptr. Lifetime is managed outside of FileReader. + +public: + + // Initialize invalid file reader object. + FileReader() : m_data(DataInitializer()), streamPos(0), fileName(nullptr) { } + + // Initialize file reader object with pointer to data and data length. + template FileReader(mpt::span bytedata, const mpt::PathString *filename = nullptr) : m_data(DataInitializer(mpt::byte_cast(bytedata))), streamPos(0), fileName(filename) { } + + // Initialize file reader object based on an existing file reader object window. + explicit FileReader(value_data_type other, const mpt::PathString *filename = nullptr) : m_data(other), streamPos(0), fileName(filename) { } + + // Initialize file reader object based on an existing file reader object. The other object's stream position is copied. + FileReader(const FileReader &) = default; + FileReader& operator=(const FileReader &) = default; + + // Move an existing file reader object + FileReader(FileReader &&) noexcept = default; + FileReader& operator=(FileReader &&) noexcept = default; + +public: + + mpt::PathString GetFileName() const + { + if(!fileName) + { + return mpt::PathString(); + } + return *fileName; + } + + // Returns true if the object points to a valid (non-empty) stream. + operator bool() const + { + return IsValid(); + } + + // Returns true if the object points to a valid (non-empty) stream. + bool IsValid() const + { + return DataContainer().IsValid(); + } + + // Reset cursor to first byte in file. + void Rewind() + { + streamPos = 0; + } + + // Seek to a position in the mapped file. + // Returns false if position is invalid. + bool Seek(off_t position) + { + if(position <= streamPos) + { + streamPos = position; + return true; + } + if(position <= DataContainer().GetLength()) + { + streamPos = position; + return true; + } else + { + return false; + } + } + + // Increases position by skipBytes. + // Returns true if skipBytes could be skipped or false if the file end was reached earlier. + bool Skip(off_t skipBytes) + { + if(CanRead(skipBytes)) + { + streamPos += skipBytes; + return true; + } else + { + streamPos = DataContainer().GetLength(); + return false; + } + } + + // Decreases position by skipBytes. + // Returns true if skipBytes could be skipped or false if the file start was reached earlier. + bool SkipBack(off_t skipBytes) + { + if(streamPos >= skipBytes) + { + streamPos -= skipBytes; + return true; + } else + { + streamPos = 0; + return false; + } + } + + // Returns cursor position in the mapped file. + off_t GetPosition() const + { + return streamPos; + } + + // Return true IFF seeking and GetLength() is fast. + // In particular, it returns false for unseekable stream where GetLength() + // requires pre-caching. + bool HasFastGetLength() const + { + return DataContainer().HasFastGetLength(); + } + + // Returns size of the mapped file in bytes. + FILEREADER_DEPRECATED off_t GetLength() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetLength(); + } + + // Return byte count between cursor position and end of file, i.e. how many bytes can still be read. + FILEREADER_DEPRECATED off_t BytesLeft() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetLength() - streamPos; + } + + bool EndOfFile() const + { + return !CanRead(1); + } + + bool NoBytesLeft() const + { + return !CanRead(1); + } + + // Check if "amount" bytes can be read from the current position in the stream. + bool CanRead(off_t amount) const + { + return DataContainer().CanRead(streamPos, amount); + } + + // Check if file size is at least size, without potentially caching the whole file to query the exact file length. + bool LengthIsAtLeast(off_t size) const + { + return DataContainer().CanRead(0, size); + } + + // Check if file size is exactly size, without potentially caching the whole file if it is larger. + bool LengthIs(off_t size) const + { + return DataContainer().CanRead(0, size) && !DataContainer().CanRead(size, 1); + } + +protected: + + FileReader CreateChunk(off_t position, off_t length) const + { + off_t readableLength = DataContainer().GetReadableLength(position, length); + if(readableLength == 0) + { + return FileReader(); + } + return FileReader(CreateChunkImpl(SharedDataContainer(), position, std::min(length, DataContainer().GetLength() - position))); + } + +public: + + // Create a new FileReader object for parsing a sub chunk at a given position with a given length. + // The file cursor is not modified. + FileReader GetChunkAt(off_t position, off_t length) const + { + return CreateChunk(position, length); + } + + // Create a new FileReader object for parsing a sub chunk at the current position with a given length. + // The file cursor is not advanced. + FileReader GetChunk(off_t length) + { + return CreateChunk(streamPos, length); + } + // Create a new FileReader object for parsing a sub chunk at the current position with a given length. + // The file cursor is advanced by "length" bytes. + FileReader ReadChunk(off_t length) + { + off_t position = streamPos; + Skip(length); + return CreateChunk(position, length); + } + + class PinnedRawDataView + { + private: + std::size_t size_; + const mpt::byte *pinnedData; + std::vector cache; + private: + void Init(const FileReader &file, std::size_t size) + { + size_ = 0; + pinnedData = nullptr; + if(!file.CanRead(size)) + { + size = file.BytesLeft(); + } + size_ = size; + if(file.DataContainer().HasPinnedView()) + { + pinnedData = file.DataContainer().GetRawData() + file.GetPosition(); + } else + { + cache.resize(size_); + if(!cache.empty()) + { + file.GetRaw(&(cache[0]), size); + } + } + } + public: + PinnedRawDataView() + { + return; + } + PinnedRawDataView(const FileReader &file) + { + Init(file, file.BytesLeft()); + } + PinnedRawDataView(const FileReader &file, std::size_t size) + { + Init(file, size); + } + PinnedRawDataView(FileReader &file, bool advance) + { + Init(file, file.BytesLeft()); + if(advance) + { + file.Skip(size_); + } + } + PinnedRawDataView(FileReader &file, std::size_t size, bool advance) + { + Init(file, size); + if(advance) + { + file.Skip(size_); + } + } + public: + mpt::const_byte_span GetSpan() const + { + if(pinnedData) + { + return mpt::as_span(pinnedData, size_); + } else if(!cache.empty()) + { + return mpt::as_span(cache); + } else + { + return mpt::const_byte_span(); + } + } + mpt::const_byte_span span() const { return GetSpan(); } + void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } + const mpt::byte *data() const { return span().data(); } + std::size_t size() const { return size_; } + mpt::const_byte_span::iterator begin() const { return span().begin(); } + mpt::const_byte_span::iterator end() const { return span().end(); } + mpt::const_byte_span::const_iterator cbegin() const { return span().cbegin(); } + mpt::const_byte_span::const_iterator cend() const { return span().cend(); } + }; + + // Returns a pinned view into the remaining raw data from cursor position. + PinnedRawDataView GetPinnedRawDataView() const + { + return PinnedRawDataView(*this); + } + // Returns a pinned view into the remeining raw data from cursor position, clamped at size. + PinnedRawDataView GetPinnedRawDataView(std::size_t size) const + { + return PinnedRawDataView(*this, size); + } + + // Returns a pinned view into the remeining raw data from cursor position. + // File cursor is advaned by the size of the returned pinned view. + PinnedRawDataView ReadPinnedRawDataView() + { + return PinnedRawDataView(*this, true); + } + // Returns a pinned view into the remeining raw data from cursor position, clamped at size. + // File cursor is advaned by the size of the returned pinned view. + PinnedRawDataView ReadPinnedRawDataView(std::size_t size) + { + return PinnedRawDataView(*this, size, true); + } + + // Returns raw stream data at cursor position. + // Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk(). + // Use GetPinnedRawDataView(size) whenever possible. + FILEREADER_DEPRECATED const mpt::byte *GetRawData() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetRawData() + streamPos; + } + template + FILEREADER_DEPRECATED const T *GetRawData() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return mpt::byte_cast(DataContainer().GetRawData() + streamPos); + } + + template + std::size_t GetRaw(T *dst, std::size_t count) const + { + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + } + + template + std::size_t ReadRaw(T *dst, std::size_t count) + { + std::size_t result = static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + streamPos += result; + return result; + } + + std::vector GetRawDataAsByteVector() const + { + PinnedRawDataView view = GetPinnedRawDataView(); + return std::vector(view.span().begin(), view.span().end()); + } + std::vector ReadRawDataAsByteVector() + { + PinnedRawDataView view = ReadPinnedRawDataView(); + return std::vector(view.span().begin(), view.span().end()); + } + std::vector GetRawDataAsByteVector(std::size_t size) const + { + PinnedRawDataView view = GetPinnedRawDataView(size); + return std::vector(view.span().begin(), view.span().end()); + } + std::vector ReadRawDataAsByteVector(std::size_t size) + { + PinnedRawDataView view = ReadPinnedRawDataView(size); + return std::vector(view.span().begin(), view.span().end()); + } + + std::string GetRawDataAsString() const + { + PinnedRawDataView view = GetPinnedRawDataView(); + return std::string(view.span().begin(), view.span().end()); + } + std::string ReadRawDataAsString() + { + PinnedRawDataView view = ReadPinnedRawDataView(); + return std::string(view.span().begin(), view.span().end()); + } + std::string GetRawDataAsString(std::size_t size) const + { + PinnedRawDataView view = GetPinnedRawDataView(size); + return std::string(view.span().begin(), view.span().end()); + } + std::string ReadRawDataAsString(std::size_t size) + { + PinnedRawDataView view = ReadPinnedRawDataView(size); + return std::string(view.span().begin(), view.span().end()); + } + +protected: + + // Read a "T" object from the stream. + // If not enough bytes can be read, false is returned. + // If successful, the file cursor is advanced by the size of "T". + template + bool Read(T &target) + { + if(sizeof(T) != DataContainer().Read(reinterpret_cast(&target), streamPos, sizeof(T))) + { + return false; + } + streamPos += sizeof(T); + return true; + } + +public: + + // Read some kind of integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntLE() + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + T target; + if(Read(target)) + { + return SwapBytesLE(target); + } else + { + return 0; + } + } + + // Read some kind of integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntBE() + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + T target; + if(Read(target)) + { + return SwapBytesBE(target); + } else + { + return 0; + } + } + + // Read a integer in little-endian format which has some of its higher bytes not stored in file. + // If successful, the file cursor is advanced by the given size. + template + T ReadTruncatedIntLE(off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + MPT_ASSERT(sizeof(T) >= size); + if(size == 0) + { + return 0; + } + if(!CanRead(size)) + { + return 0; + } + uint8 buf[sizeof(T)]; + bool negative = false; + for(std::size_t i = 0; i < sizeof(T); ++i) + { + uint8 byte = 0; + if(i < size) + { + Read(byte); + negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); + } else + { + // sign or zero extend + byte = negative ? 0xff : 0x00; + } + buf[i] = byte; + } + T target; + std::memcpy(&target, buf, sizeof(T)); + return SwapBytesLE(target); + } + + // Read a supplied-size little endian integer to a fixed size variable. + // The data is properly sign-extended when fewer bytes are stored. + // If more bytes are stored, higher order bytes are silently ignored. + // If successful, the file cursor is advanced by the given size. + template + T ReadSizedIntLE(off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + if(size == 0) + { + return 0; + } + if(!CanRead(size)) + { + return 0; + } + if(size < sizeof(T)) + { + return ReadTruncatedIntLE(size); + } + T retval = ReadIntLE(); + Skip(size - sizeof(T)); + return retval; + } + + // Read unsigned 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + uint32 ReadUint32LE() + { + return ReadIntLE(); + } + + // Read unsigned 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + uint32 ReadUint32BE() + { + return ReadIntBE(); + } + + // Read signed 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + int32 ReadInt32LE() + { + return ReadIntLE(); + } + + // Read signed 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + int32 ReadInt32BE() + { + return ReadIntBE(); + } + + // Read unsigned 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + uint16 ReadUint16LE() + { + return ReadIntLE(); + } + + // Read unsigned 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + uint16 ReadUint16BE() + { + return ReadIntBE(); + } + + // Read signed 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + int16 ReadInt16LE() + { + return ReadIntLE(); + } + + // Read signed 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + int16 ReadInt16BE() + { + return ReadIntBE(); + } + + // Read unsigned 8-Bit integer. + // If successful, the file cursor is advanced by the size of the integer. + uint8 ReadUint8() + { + uint8 target; + if(Read(target)) + { + return target; + } else + { + return 0; + } + } + + // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. + int8 ReadInt8() + { + int8 target; + if(Read(target)) + { + return target; + } else + { + return 0; + } + } + + // Read 32-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + float ReadFloatLE() + { + IEEE754binary32LE target; + if(Read(target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 32-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + float ReadFloatBE() + { + IEEE754binary32BE target; + if(Read(target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 64-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + double ReadDoubleLE() + { + IEEE754binary64LE target; + if(Read(target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 64-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + double ReadDoubleBE() + { + IEEE754binary64BE target; + if(Read(target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read a struct. + // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. + template + bool ReadStruct(T &target) + { + STATIC_ASSERT(mpt::is_binary_safe::value); + if(Read(target)) + { + return true; + } else + { + MemsetZero(target); + return false; + } + } + + // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). + // The file cursor is advanced by "partialSize" bytes. + template + bool ReadStructPartial(T &target, off_t partialSize = sizeof(T)) + { + STATIC_ASSERT(mpt::is_binary_safe::value); + off_t copyBytes = std::min(partialSize, sizeof(T)); + if(!CanRead(copyBytes)) + { + copyBytes = BytesLeft(); + } + DataContainer().Read(reinterpret_cast(&target), streamPos, copyBytes); + std::memset(reinterpret_cast(&target) + copyBytes, 0, sizeof(target) - copyBytes); + Skip(partialSize); + return true; + } + + // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one byte could be read or 0 bytes were requested. + template + bool ReadString(char (&destBuffer)[destSize], const off_t srcSize) + { + FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::Read(destBuffer, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a std::string dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(std::string &dest, const off_t srcSize) + { + FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::Read(dest, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(mpt::ustring &dest, mpt::Charset charset, const off_t srcSize) + { + FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::Read(dest, charset, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(char (&destBuffer)[destSize], const off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(srcSize)) + return false; + return ReadString(destBuffer, std::min(srcSize, maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(srcSize)) + return false; + return ReadString(dest, std::min(srcSize, maxLength)); + } + + // Read a null-terminated string into a std::string + bool ReadNullString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!CanRead(1)) + return false; + try + { + char buffer[64]; + off_t avail = 0; + while((avail = std::min(DataContainer().Read(reinterpret_cast(buffer), streamPos, sizeof(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find(buffer, buffer + avail, '\0'); + dest.insert(dest.end(), buffer, end); + streamPos += (end - buffer); + if(end < buffer + avail) + { + // Found null char + streamPos++; + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return dest.length() != 0; + } + +private: + static MPT_FORCEINLINE bool IsLineEnding(char c) { return c == '\r' || c == '\n'; } +public: + // Read a string up to the next line terminator into a std::string + bool ReadLine(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!CanRead(1)) + return false; + try + { + char buffer[64], c = '\0'; + off_t avail = 0; + while((avail = std::min(DataContainer().Read(reinterpret_cast(buffer), streamPos, sizeof(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find_if(buffer, buffer + avail, IsLineEnding); + dest.insert(dest.end(), buffer, end); + streamPos += (end - buffer); + if(end < buffer + avail) + { + // Found line ending + streamPos++; + // Handle CRLF line ending + if(*end == '\r') + { + if(Read(c) && c != '\n') + SkipBack(1); + } + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return true; + } + + // Read an array of binary-safe T values. + // If successful, the file cursor is advanced by the size of the array. + // Otherwise, the target is zeroed. + template + bool ReadArray(T (&destArray)[destSize]) + { + STATIC_ASSERT(mpt::is_binary_safe::value); + if(CanRead(sizeof(destArray))) + { + for(auto &element : destArray) + { + Read(element); + } + return true; + } else + { + MemsetZero(destArray); + return false; + } + } + + // Read destSize elements of binary-safe type T into a vector. + // If successful, the file cursor is advanced by the size of the vector. + // Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared. + template + bool ReadVector(std::vector &destVector, size_t destSize) + { + STATIC_ASSERT(mpt::is_binary_safe::value); + destVector.resize(destSize); + if(CanRead(sizeof(T) * destSize)) + { + for(auto &element : destVector) + { + Read(element); + } + return true; + } else + { + return false; + } + } + + // Compare a magic string with the current stream position. + // Returns true if they are identical and advances the file cursor by the the length of the "magic" string. + // Returns false if the string could not be found. The file cursor is not advanced in this case. + template + bool ReadMagic(const char (&magic)[N]) + { + MPT_ASSERT(magic[N - 1] == '\0'); + for(std::size_t i = 0; i < N - 1; ++i) + { + MPT_ASSERT(magic[i] != '\0'); + } + if(CanRead(N - 1)) + { + mpt::byte bytes[N - 1]; + STATIC_ASSERT(sizeof(bytes) == sizeof(magic) - 1); + DataContainer().Read(bytes, streamPos, N - 1); + if(!std::memcmp(bytes, magic, N - 1)) + { + streamPos += (N - 1); + return true; + } + } + return false; + } + + bool ReadMagic(const char *const magic, off_t magicLength) + { + if(CanRead(magicLength)) + { + bool identical = true; + for(std::size_t i = 0; i < magicLength; ++i) + { + mpt::byte c = 0; + DataContainer().Read(&c, streamPos + i, 1); + if(c != mpt::byte_cast(magic[i])) + { + identical = false; + break; + } + } + if(identical) + { + streamPos += magicLength; + return true; + } else + { + return false; + } + } else + { + return false; + } + } + + // Read variable-length unsigned integer (as found in MIDI files). + // If successful, the file cursor is advanced by the size of the integer and true is returned. + // False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer). + // In case of an overflow, the target is also set to the maximum value supported by its data type. + template + bool ReadVarInt(T &target) + { + static_assert(std::numeric_limits::is_integer == true + && std::numeric_limits::is_signed == false, + "Target type is not an unsigned integer"); + + if(NoBytesLeft()) + { + target = 0; + return false; + } + + mpt::byte bytes[16]; // More than enough for any valid VarInt + off_t avail = DataContainer().Read(bytes, streamPos, sizeof(bytes)), readPos = 1; + + size_t writtenBits = 0; + uint8 b = bytes[0]; + target = (b & 0x7F); + + // Count actual bits used in most significant byte (i.e. this one) + for(size_t bit = 0; bit < 7; bit++) + { + if((b & (1u << bit)) != 0) + { + writtenBits = bit + 1; + } + } + + while(readPos < avail && (b & 0x80) != 0) + { + b = bytes[readPos++]; + target <<= 7; + target |= (b & 0x7F); + writtenBits += 7; + if(readPos == avail) + { + streamPos += readPos; + avail = DataContainer().Read(bytes, streamPos, sizeof(bytes)); + readPos = 0; + } + } + streamPos += readPos; + + if(writtenBits > sizeof(target) * 8u) + { + // Overflow + target = Util::MaxValueOfType(target); + return false; + } else if((b & 0x80) != 0) + { + // Reached EOF + return false; + } + return true; + } + +}; + +} // namespace detail + +typedef detail::FileReader FileReader; + +typedef detail::FileReader MemoryFileReader; + + +#if defined(LIBOPENMPT_BUILD) + +// Initialize file reader object with pointer to data and data length. +template static inline FileReader make_FileReader(mpt::span bytedata, const mpt::PathString *filename = nullptr) +{ + return FileReader(mpt::byte_cast(bytedata), filename); +} + +#if defined(MPT_FILEREADER_STD_ISTREAM) + +#if defined(MPT_FILEREADER_CALLBACK_STREAM) + +// Initialize file reader object with a CallbackStream. +static inline FileReader make_FileReader(CallbackStream s, const mpt::PathString *filename = nullptr) +{ + return FileReader( + FileDataContainerCallbackStreamSeekable::IsSeekable(s) ? + std::static_pointer_cast(std::make_shared(s)) + : + std::static_pointer_cast(std::make_shared(s)) + , filename + ); +} +#endif // MPT_FILEREADER_CALLBACK_STREAM + +// Initialize file reader object with a std::istream. +static inline FileReader make_FileReader(std::istream *s, const mpt::PathString *filename = nullptr) +{ + return FileReader( + FileDataContainerStdStreamSeekable::IsSeekable(s) ? + std::static_pointer_cast(std::make_shared(s)) + : + std::static_pointer_cast(std::make_shared(s)) + , filename + ); +} + +#endif // MPT_FILEREADER_STD_ISTREAM + +#endif // LIBOPENMT_BUILD + + +#if defined(MPT_ENABLE_FILEIO) +// templated in order to reduce header inter-dependencies +template +FileReader GetFileReader(TInputFile &file) +{ + #if defined(MPT_FILEREADER_STD_ISTREAM) + typename TInputFile::ContentsRef tmp = file.Get(); + if(!tmp.first) + { + return FileReader(); + } + if(!tmp.first->good()) + { + return FileReader(); + } + return FileReader(tmp.first, tmp.second); + #else + typename TInputFile::ContentsRef tmp = file.Get(); + return FileReader(mpt::as_span(tmp.first.data, tmp.first.size), tmp.second); + #endif +} +#endif // MPT_ENABLE_FILEIO + + +#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS + +class OnDiskFileWrapper +{ + +private: + + mpt::PathString m_Filename; + bool m_IsTempFile; + +public: + + OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension = MPT_PATHSTRING("tmp")); + + ~OnDiskFileWrapper(); + +public: + + bool IsValid() const; + + mpt::PathString GetFilename() const; + +}; // class OnDiskFileWrapper + +#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h new file mode 100644 index 000000000..794d7fdf8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h @@ -0,0 +1,42 @@ +/* + * FileReaderFwd.h + * --------------- + * Purpose: Forward declaration for class FileReader. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "typedefs.h" + +OPENMPT_NAMESPACE_BEGIN + +class FileReaderTraitsMemory; + +#if defined(MPT_FILEREADER_STD_ISTREAM) + +class FileReaderTraitsStdStream; + +typedef FileReaderTraitsStdStream FileReaderTraitsDefault; + +#else // !MPT_FILEREADER_STD_ISTREAM + +typedef FileReaderTraitsMemory FileReaderTraitsDefault; + +#endif // MPT_FILEREADER_STD_ISTREAM + +namespace detail { + +template +class FileReader; + +} // namespace detail + +typedef detail::FileReader FileReader; + +typedef detail::FileReader MemoryFileReader; + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h new file mode 100644 index 000000000..51ec8a0c3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h @@ -0,0 +1,412 @@ +/* + * FlagSet.h + * --------- + * Purpose: A flexible and typesafe flag set class. + * Notes : Originally based on http://stackoverflow.com/questions/4226960/type-safer-bitflags-in-c . + * Rewritten to be standard-conforming. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include + +OPENMPT_NAMESPACE_BEGIN + + +// Be aware of the required size when specializing this. +// We cannot assert the minimum size because some compilers always allocate an 'int', +// even for enums that would fit in smaller integral types. +template +struct enum_traits +{ + typedef typename std::make_unsigned::type store_type; +}; + + +// Type-safe wrapper around an enum, that can represent all enum values and bitwise compositions thereof. +// Conversions to and from plain integers as well as conversions to the base enum are always explicit. +template +class enum_value_type +{ +public: + typedef enum_t enum_type; + typedef enum_value_type value_type; + typedef typename enum_traits::store_type store_type; +private: + store_type bits; +public: + MPT_CONSTEXPR11_FUN enum_value_type() : bits(0) { } + MPT_CONSTEXPR11_FUN enum_value_type(const enum_value_type &x) : bits(x.bits) { } + MPT_CONSTEXPR11_FUN enum_value_type(enum_type x) : bits(static_cast(x)) { } +private: + explicit MPT_CONSTEXPR11_FUN enum_value_type(store_type x) : bits(x) { } // private in order to prevent accidental conversions. use from_bits. + MPT_CONSTEXPR11_FUN operator store_type () const { return bits; } // private in order to prevent accidental conversions. use as_bits. +public: + static MPT_CONSTEXPR11_FUN enum_value_type from_bits(store_type bits) { return value_type(bits); } + MPT_CONSTEXPR11_FUN enum_type as_enum() const { return static_cast(bits); } + MPT_CONSTEXPR11_FUN store_type as_bits() const { return bits; } +public: + MPT_CONSTEXPR11_FUN operator bool () const { return bits != store_type(); } + MPT_CONSTEXPR11_FUN bool operator ! () const { return bits == store_type(); } + + MPT_CONSTEXPR11_FUN const enum_value_type operator ~ () const { return enum_value_type(~bits); } + + friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_value_type b) { return a.bits == b.bits; } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_value_type b) { return a.bits != b.bits; } + + friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_t b) { return a == enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_t b) { return a != enum_value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (enum_t a, enum_value_type b) { return enum_value_type(a) == b; } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_t a, enum_value_type b) { return enum_value_type(a) != b; } + + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits | b.bits); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits & b.bits); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits ^ b.bits); } + + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_t b) { return a | enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_t b) { return a & enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_t b) { return a ^ enum_value_type(b); } + + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_t a, enum_value_type b) { return enum_value_type(a) | b; } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_t a, enum_value_type b) { return enum_value_type(a) & b; } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_t a, enum_value_type b) { return enum_value_type(a) ^ b; } + + MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_value_type b) { *this = *this | b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_value_type b) { *this = *this & b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_value_type b) { *this = *this ^ b; return *this; } + + MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_t b) { *this = *this | b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_t b) { *this = *this & b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_t b) { *this = *this ^ b; return *this; } + +}; + + +// Type-safe enum wrapper that allows type-safe bitwise testing. +template +class Enum +{ +public: + typedef Enum self_type; + typedef enum_t enum_type; + typedef enum_value_type value_type; + typedef typename value_type::store_type store_type; +private: + enum_type value; +public: + explicit MPT_CONSTEXPR11_FUN Enum(enum_type val) : value(val) { } + MPT_CONSTEXPR11_FUN operator enum_type () const { return value; } + MPT_CONSTEXPR14_FUN Enum &operator = (enum_type val) { value = val; return *this; } +public: + MPT_CONSTEXPR11_FUN const value_type operator ~ () const { return ~value_type(value); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return value_type(a) != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return value_type(a) != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return value_type(a) != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return value_type(a) ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return value_type(a) ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return value_type(a) ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ value_type(b); } + +}; + + +template ::store_type > +class FlagSet +{ +public: + typedef FlagSet self_type; + typedef enum_t enum_type; + typedef enum_value_type value_type; + typedef store_t store_type; + +private: + + // support truncated store_type ... : + store_type bits_; + static MPT_CONSTEXPR11_FUN store_type store_from_value(value_type bits) { return static_cast(bits.as_bits()); } + static MPT_CONSTEXPR11_FUN value_type value_from_store(store_type bits) { return value_type::from_bits(static_cast(bits)); } + + MPT_CONSTEXPR14_FUN FlagSet & store(value_type bits) { bits_ = store_from_value(bits); return *this; } + MPT_CONSTEXPR11_FUN value_type load() const { return value_from_store(bits_); } + +public: + + // Default constructor (no flags set) + MPT_CONSTEXPR11_FUN FlagSet() : bits_(store_from_value(value_type())) + { + } + + // Value constructor + MPT_CONSTEXPR11_FUN FlagSet(value_type flags) : bits_(store_from_value(value_type(flags))) + { + } + + MPT_CONSTEXPR11_FUN FlagSet(enum_type flag) : bits_(store_from_value(value_type(flag))) + { + } + + explicit MPT_CONSTEXPR11_FUN FlagSet(store_type flags) : bits_(store_from_value(value_type::from_bits(flags))) + { + } + + MPT_CONSTEXPR11_FUN operator bool () const + { + return load(); + } + // In order to catch undesired conversions to bool in integer contexts, + // add a deprecated conversion operator to store_type. + // C++11 explicit conversion cast operators ('explicit operator bool ();') + // would solve this in a better way and always fail at compile-time instead of this + // solution which just warns in some cases. + // The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion, + // so it is not marked deprecated (for now). + /*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const + { + return load().as_bits(); + } + + MPT_CONSTEXPR11_FUN value_type value() const + { + return load(); + } + + MPT_CONSTEXPR11_FUN operator value_type () const + { + return load(); + } + + // Test if one or more flags are set. Returns true if at least one of the given flags is set. + MPT_CONSTEXPR11_FUN bool operator[] (value_type flags) const + { + return test(flags); + } + + // String representation of flag set + std::string to_string() const + { + std::string str(size_bits(), '0'); + + for(size_t x = 0; x < size_bits(); ++x) + { + str[size_bits() - x - 1] = (load() & (1 << x) ? '1' : '0'); + } + + return str; + } + + // Set one or more flags. + MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags) + { + return store(load() | flags); + } + + // Set or clear one or more flags. + MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags, bool val) + { + return store((val ? (load() | flags) : (load() & ~flags))); + } + + // Clear or flags. + MPT_CONSTEXPR14_FUN FlagSet &reset() + { + return store(value_type()); + } + + // Clear one or more flags. + MPT_CONSTEXPR14_FUN FlagSet &reset(value_type flags) + { + return store(load() & ~flags); + } + + // Toggle all flags. + MPT_CONSTEXPR14_FUN FlagSet &flip() + { + return store(~load()); + } + + // Toggle one or more flags. + MPT_CONSTEXPR14_FUN FlagSet &flip(value_type flags) + { + return store(load() ^ flags); + } + + // Returns the size of the flag set in bytes + MPT_CONSTEXPR11_FUN std::size_t size() const + { + return sizeof(store_type); + } + + // Returns the size of the flag set in bits + MPT_CONSTEXPR11_FUN std::size_t size_bits() const + { + return size() * 8; + } + + // Test if one or more flags are set. Returns true if at least one of the given flags is set. + MPT_CONSTEXPR11_FUN bool test(value_type flags) const + { + return (load() & flags); + } + + // Test if all specified flags are set. + MPT_CONSTEXPR11_FUN bool test_all(value_type flags) const + { + return (load() & flags) == flags; + } + + // Test if any but the specified flags are set. + MPT_CONSTEXPR11_FUN bool test_any_except(value_type flags) const + { + return (load() & ~flags); + } + + // Test if any flag is set. + MPT_CONSTEXPR11_FUN bool any() const + { + return load(); + } + + // Test if no flags are set. + MPT_CONSTEXPR11_FUN bool none() const + { + return !load(); + } + + MPT_CONSTEXPR11_FUN store_type GetRaw() const + { + return bits_; + } + + MPT_CONSTEXPR14_FUN FlagSet & SetRaw(store_type flags) + { + bits_ = flags; + return *this; + } + + MPT_CONSTEXPR14_FUN FlagSet &operator = (value_type flags) + { + return store(flags); + } + + MPT_CONSTEXPR14_FUN FlagSet &operator = (enum_type flag) + { + return store(flag); + } + + MPT_CONSTEXPR14_FUN FlagSet &operator = (FlagSet flags) + { + return store(flags.load()); + } + + MPT_CONSTEXPR14_FUN FlagSet &operator &= (value_type flags) + { + return store(load() & flags); + } + + MPT_CONSTEXPR14_FUN FlagSet &operator |= (value_type flags) + { + return store(load() | flags); + } + + MPT_CONSTEXPR14_FUN FlagSet &operator ^= (value_type flags) + { + return store(load() ^ flags); + } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return a.load() == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return a.load() != b.load(); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return a.load() != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != b.load(); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return a.load() != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != b.load(); } + + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, Enum b) { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, Enum b) { return a.load() != value_type(b); } + + friend MPT_CONSTEXPR11_FUN bool operator == (Enum a, self_type b) { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (Enum a, self_type b) { return value_type(a) != b.load(); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return a.load() | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return a.load() & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return a.load() ^ b.load(); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return a.load() ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ b.load(); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return a.load() ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ b.load(); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, Enum b) { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, Enum b) { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, Enum b) { return a.load() ^ value_type(b); } + + friend MPT_CONSTEXPR11_FUN const value_type operator | (Enum a, self_type b) { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (Enum a, self_type b) { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (Enum a, self_type b) { return value_type(a) ^ b.load(); } + +}; + + +// Declare typesafe logical operators for enum_t +#define MPT_DECLARE_ENUM(enum_t) \ + MPT_CONSTEXPR11_FUN enum_value_type operator | (enum_t a, enum_t b) { return enum_value_type(a) | enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator & (enum_t a, enum_t b) { return enum_value_type(a) & enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator ^ (enum_t a, enum_t b) { return enum_value_type(a) ^ enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator ~ (enum_t a) { return ~enum_value_type(a); } \ +/**/ + +// backwards compatibility +#define DECLARE_FLAGSET MPT_DECLARE_ENUM + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp new file mode 100644 index 000000000..7d2158e7c --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp @@ -0,0 +1,423 @@ +/* + * Logging.cpp + * ----------- + * Purpose: General logging + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "Logging.h" +#include "mptFileIO.h" +#if defined(MODPLUG_TRACKER) +#include +#endif +#include "version.h" + +#include + +#include +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace log +{ + + +#ifndef NO_LOGGING + + + +#if !defined(MPT_LOG_GLOBAL_LEVEL_STATIC) +#if defined(MPT_LOG_GLOBAL_LEVEL) +int GlobalLogLevel = static_cast(MPT_LOG_GLOBAL_LEVEL); +#else +int GlobalLogLevel = static_cast(LogDebug); +#endif +#endif + + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) + +bool FileEnabled = false; +bool DebuggerEnabled = true; +bool ConsoleEnabled = false; + +static char g_FacilitySolo[1024] = {0}; +static char g_FacilityBlocked[1024] = {0}; + +void SetFacilities(const std::string &solo, const std::string &blocked) +{ + std::strcpy(g_FacilitySolo, solo.c_str()); + std::strcpy(g_FacilityBlocked, blocked.c_str()); +} + +bool IsFacilityActive(const char *facility) +{ + if(facility) + { + if(std::strlen(g_FacilitySolo) > 0) + { + if(std::strcmp(facility, g_FacilitySolo) != 0) + { + return false; + } + } + if(std::strlen(g_FacilityBlocked) > 0) + { + if(std::strcmp(facility, g_FacilitySolo) == 0) + { + return false; + } + } + } + return true; +} + +#endif + + +void Logger::SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text) +{ +#ifdef MPT_LOG_IS_DISABLED + MPT_UNREFERENCED_PARAMETER(context); + MPT_UNREFERENCED_PARAMETER(level); + MPT_UNREFERENCED_PARAMETER(facility); + MPT_UNREFERENCED_PARAMETER(text); +#else // !MPT_LOG_IS_DISABLED + MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < level) + { + return; + } + #if defined(MODPLUG_TRACKER) + if(!IsFacilityActive(facility)) + { + return; + } + #else // !MODPLUG_TRACKER + MPT_UNREFERENCED_PARAMETER(facility); + #endif // MODPLUG_TRACKER + // remove eol if already present and add log level prefix + const mpt::ustring message = LogLevelToString(level) + MPT_USTRING(": ") + mpt::String::RTrim(text, MPT_USTRING("\r\n")); + const mpt::ustring file = mpt::ToUnicode(mpt::CharsetASCII, context.file); + const mpt::ustring function = mpt::ToUnicode(mpt::CharsetASCII, context.function); + const mpt::ustring line = mpt::ufmt::dec(context.line); + #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) +#if MPT_OS_WINDOWS + static uint64 s_lastlogtime = 0; + uint64 cur = mpt::Date::ANSI::Now(); + uint64 diff = cur/10000 - s_lastlogtime; + s_lastlogtime = cur/10000; +#else + uint64 cur = 0; + uint64 diff = 0; +#endif + if(mpt::log::FileEnabled) + { + static FILE * s_logfile = nullptr; + if(!s_logfile) + { + s_logfile = mpt_fopen(MPT_PATHSTRING("mptrack.log"), "a"); + } + if(s_logfile) + { + fprintf(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(MPT_USTRING("%1+%2 %3(%4): %5 [%6]\n")) + ( mpt::Date::ANSI::ToString(cur) + , mpt::ufmt::dec<6>(diff) + , file + , line + , message + , function + )).c_str()); + fflush(s_logfile); + } + } + if(mpt::log::DebuggerEnabled) + { + OutputDebugStringW(mpt::ToWide(mpt::format(MPT_USTRING("%1(%2): +%3 %4 [%5]\n")) + ( file + , line + , mpt::ufmt::dec<6>(diff) + , message + , function + )).c_str()); + } + if(mpt::log::ConsoleEnabled) + { + static bool consoleInited = false; + if(!consoleInited) + { + AllocConsole(); + consoleInited = true; + } + std::wstring consoletext = mpt::ToWide(message) + L"\r\n"; + DWORD dummy = 0; + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), consoletext.length(), &dummy, NULL); + } + #elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT) + std::clog + << "NativeSupport: " + << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message) + << " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]" + << std::endl; + #else // !MODPLUG_TRACKER + std::clog + << "libopenmpt: " + << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message) + << " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]" + << std::endl; + #endif // MODPLUG_TRACKER +#endif // MPT_LOG_IS_DISABLED +} + +void LegacyLogger::operator () (const AnyStringLocale &text) +{ + SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", text); +} + +void LegacyLogger::operator () (const char *format, ...) +{ + static const std::size_t LOGBUF_SIZE = 1024; + char message[LOGBUF_SIZE]; + va_list va; + va_start(va, format); + vsnprintf(message, LOGBUF_SIZE, format, va); + va_end(va); + message[LOGBUF_SIZE - 1] = '\0'; + SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message)); +} + +void LegacyLogger::operator () (LogLevel level, const mpt::ustring &text) +{ + SendLogMessage(context, level, "", text); +} + + + +#endif // !NO_LOGGING + + + +#if defined(MODPLUG_TRACKER) + +namespace Trace { + +#if MPT_OS_WINDOWS + +// Debugging functionality will use simple globals. + +bool volatile g_Enabled = false; + +static bool g_Sealed = false; + +struct Entry { + uint32 Index; + uint32 ThreadId; + uint64 Timestamp; + const char * Function; + const char * File; + int Line; +}; + +inline bool operator < (const Entry &a, const Entry &b) +{ +/* + return false + || (a.Timestamp < b.Timestamp) + || (a.ThreadID < b.ThreadID) + || (a.File < b.File) + || (a.Line < b.Line) + || (a.Function < b.Function) + ; +*/ + return false + || (a.Index < b.Index) + ; +} + +static std::vector Entries; + +static std::atomic NextIndex(0); + +static uint32 ThreadIdGUI = 0; +static uint32 ThreadIdAudio = 0; +static uint32 ThreadIdNotify = 0; + +void Enable(std::size_t numEntries) +{ + if(g_Sealed) + { + return; + } + Entries.clear(); + Entries.resize(numEntries); + NextIndex.store(0); + g_Enabled = true; +} + +void Disable() +{ + if(g_Sealed) + { + return; + } + g_Enabled = false; +} + +MPT_NOINLINE void Trace(const mpt::log::Context & context) +{ + // This will get called in realtime contexts and hot paths. + // No blocking allowed here. + const uint32 index = NextIndex.fetch_add(1); +#if 1 + LARGE_INTEGER time; + time.QuadPart = 0; + QueryPerformanceCounter(&time); + const uint64 timestamp = time.QuadPart; +#else + FILETIME time = FILETIME(); + GetSystemTimeAsFileTime(&time); + const uint64 timestamp = (static_cast(time.dwHighDateTime) << 32) | (static_cast(time.dwLowDateTime) << 0); +#endif + const uint32 threadid = static_cast(GetCurrentThreadId()); + mpt::log::Trace::Entry & entry = Entries[index % Entries.size()]; + entry.Index = index; + entry.ThreadId = threadid; + entry.Timestamp = timestamp; + entry.Function = context.function; + entry.File = context.file; + entry.Line = context.line; +} + +void Seal() +{ + if(!g_Enabled) + { + return; + } + g_Enabled = false; + g_Sealed = true; + uint32 count = NextIndex.fetch_add(0); + if(count < Entries.size()) + { + Entries.resize(count); + } +} + +bool Dump(const mpt::PathString &filename) +{ + if(!g_Sealed) + { + return false; + } + + LARGE_INTEGER qpcNow; + qpcNow.QuadPart = 0; + QueryPerformanceCounter(&qpcNow); + uint64 ftNow = mpt::Date::ANSI::Now(); + + // sort according to index in case of overflows + std::stable_sort(Entries.begin(), Entries.end()); + + mpt::ofstream f(filename, std::ios::out); + + f << "Build: OpenMPT " << MptVersion::GetVersionStringExtended() << std::endl; + + bool qpcValid = false; + + LARGE_INTEGER qpcFreq; + qpcFreq.QuadPart = 0; + QueryPerformanceFrequency(&qpcFreq); + if(qpcFreq.QuadPart > 0) + { + qpcValid = true; + } + + f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString(ftNow)) << std::endl; + f << "Captured events: " << Entries.size() << std::endl; + if(qpcValid && (Entries.size() > 0)) + { + double period = static_cast(Entries[Entries.size() - 1].Timestamp - Entries[0].Timestamp) / static_cast(qpcFreq.QuadPart); + double eventsPerSecond = Entries.size() / period; + f << "Period [s]: " << mpt::fmt::fix(period) << std::endl; + f << "Events/second: " << mpt::fmt::fix(eventsPerSecond) << std::endl; + } + + for(std::size_t i = 0; i < Entries.size(); ++i) + { + mpt::log::Trace::Entry & entry = Entries[i]; + if(!entry.Function) entry.Function = ""; + if(!entry.File) entry.File = ""; + std::string time; + if(qpcValid) + { + time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); + } else + { + time = mpt::format("0x%1")(mpt::fmt::hex0<16>(entry.Timestamp)); + } + f << time; + if(entry.ThreadId == ThreadIdGUI) + { + f << " -----GUI "; + } else if(entry.ThreadId == ThreadIdAudio) + { + f << " ---Audio "; + } else if(entry.ThreadId == ThreadIdNotify) + { + f << " --Notify "; + } else + { + f << " " << mpt::fmt::hex0<8>(entry.ThreadId) << " "; + } + f << entry.File << "(" << entry.Line << "): " << entry.Function; + f << std::endl; + } + return true; +} + +void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id) +{ + if(id == 0) + { + return; + } + switch(kind) + { + case ThreadKindGUI: + ThreadIdGUI = id; + break; + case ThreadKindAudio: + ThreadIdAudio = id; + break; + case ThreadKindNotify: + ThreadIdNotify = id; + break; + } +} + +#endif // MPT_OS_WINDOWS + +} // namespace Trace + +#endif // MODPLUG_TRACKER + + +} // namespace log +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.h b/Frameworks/OpenMPT/OpenMPT/common/Logging.h new file mode 100644 index 000000000..9c96f9270 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.h @@ -0,0 +1,264 @@ +/* + * Logging.h + * --------- + * Purpose: General logging + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + + +OPENMPT_NAMESPACE_BEGIN + + +/* + + +Build time logging configuration (in BuildSettings.h): + + * #define NO_LOGGING + Disables all logging completely. + MPT_LOG calls are not even compiled but instead completely removed via the + preprocessor. + + * #define MPT_LOG_GLOBAL_LEVEL_STATIC + #define MPT_LOG_GLOBAL_LEVEL # + Define the former (to anything) and the latter (to one of the log levels + below) in order to statically select the verbosity of logging at build time. + MPT_LOG calls that exceed the specified logging level will get dead-code + eliminated at compile time. + This especially means that, when setting MPT_LOG_GLOBAL_LEVEL to 0, no + MPT_LOG call (with a constant level parameter) remains in the resulting + binary, however, they still do get parsed and properly type checked by the + compiler. + + +Logging: + +If the context is related to a particular CSoundfile instance, use +CSoundfile::AddToLog. + +Logging a simple message: +MPT_LOG(LogWarning, "sounddev", "some message"); +MPT_LOG(LogWarning, "sounddev", MPT_USTRING("some message")); +Facility is some course grained code section identifier (more coarse grained +than the current file name probably), useful to do some selective logging. + +Logging a more complex message: +MPT_LOG(LogWarning, "sounddev", mpt::format(MPT_USTRING("Some message: foo=%1, bar=0x%2"))(foo, mpt::ufmt::hex0<8>(bar))); + +Note that even with full enabled logging and a runtime configurable logging +level, the runtime overhead of a MPT_LOG(level, facility, text) call is just a +single conditional in case the verbosity does not require logging the respective +message. Even the expression "text" is not evaluated. + + +*/ + + +enum LogLevel +{ + LogDebug = 5, + LogInformation = 4, + LogNotification = 3, + LogWarning = 2, + LogError = 1 +}; + + +inline mpt::ustring LogLevelToString(LogLevel level) +{ + switch(level) + { + case LogError: return MPT_USTRING("error"); break; + case LogWarning: return MPT_USTRING("warning"); break; + case LogNotification: return MPT_USTRING("notify"); break; + case LogInformation: return MPT_USTRING("info"); break; + case LogDebug: return MPT_USTRING("debug"); break; + } + return MPT_USTRING("unknown"); +} + + +class ILog +{ +protected: + virtual ~ILog() { } +public: + virtual void AddToLog(LogLevel level, const mpt::ustring &text) const = 0; +}; + + + +namespace mpt +{ +namespace log +{ + + + +#ifndef NO_LOGGING + + +#if defined(MPT_LOG_GLOBAL_LEVEL_STATIC) +#if (MPT_LOG_GLOBAL_LEVEL <= 0) +// Logging framework is enabled (!NO_LOGGING) but all logging has beeen statically disabled. +// All logging code gets compiled and immediately dead-code eliminated. +#define MPT_LOG_IS_DISABLED +#endif +static const int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; +#else +extern int GlobalLogLevel; +#endif + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) +extern bool FileEnabled; +extern bool DebuggerEnabled; +extern bool ConsoleEnabled; +void SetFacilities(const std::string &solo, const std::string &blocked); +bool IsFacilityActive(const char *facility); +#else +static MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; } +#endif + + +#endif // !NO_LOGGING + + +struct Context +{ + const char * const file; + const int line; + const char * const function; + MPT_FORCEINLINE Context(const char *file, int line, const char *function) + : file(file) + , line(line) + , function(function) + { + return; + } + MPT_FORCEINLINE Context(const Context &c) + : file(c.file) + , line(c.line) + , function(c.function) + { + return; + } +}; // class Context + +#define MPT_LOG_CURRENTCONTEXT() mpt::log::Context( __FILE__ , __LINE__ , __FUNCTION__ ) + + +#ifndef NO_LOGGING + + +class Logger +{ +public: + // facility:ASCII + void SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text); +public: + // facility:ASCII, text:ASCII (only string literals) + template MPT_FORCEINLINE void SendLogMessage(const Context &context, LogLevel level, const char *facility, const char (&text)[size]) + { + SendLogMessage(context, level, facility, mpt::ToUnicode(mpt::CharsetASCII, text)); + } +}; + +#define MPT_LOG(level, facility, text) \ + MPT_DO \ + { \ + MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel >= ( level )) \ + { \ + MPT_MAYBE_CONSTANT_IF(mpt::log::IsFacilityActive(( facility ))) \ + { \ + mpt::log::Logger().SendLogMessage( MPT_LOG_CURRENTCONTEXT() , ( level ), ( facility ), ( text )); \ + } \ + } \ + } MPT_WHILE_0 \ +/**/ + + +#define MPT_LEGACY_LOGLEVEL LogDebug + +class LegacyLogger : public Logger +{ +private: + const Context context; +public: + LegacyLogger(const Context &context) : context(context) {} + /* MPT_DEPRECATED */ void MPT_PRINTF_FUNC(2,3) operator () (const char *format, ...); // migrate to type-safe MPT_LOG + /* MPT_DEPRECATED */ void operator () (const AnyStringLocale &text); // migrate to properly namespaced MPT_LOG + /* MPT_DEPRECATED */ void operator () (LogLevel level, const mpt::ustring &text); // migrate to properly namespaced MPT_LOG +}; + +#define Log MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < MPT_LEGACY_LOGLEVEL) { } else MPT_MAYBE_CONSTANT_IF(!mpt::log::IsFacilityActive("")) { } else mpt::log::LegacyLogger(MPT_LOG_CURRENTCONTEXT()) + + +#else // !NO_LOGGING + + +#define MPT_LOG(level, facility, text) MPT_DO { } MPT_WHILE_0 + +struct LegacyLogger +{ + inline void MPT_PRINTF_FUNC(2,3) operator () (const char * /*format*/ , ...) {} + inline void operator () (const AnyStringLocale & /*text*/ ) {} + inline void operator () (LogLevel /*level*/ , const mpt::ustring & /*text*/ ) {} +}; +#define Log MPT_CONSTANT_IF(true) {} else mpt::log::LegacyLogger() // completely compile out arguments to Log() so that they do not even get evaluated + + +#endif // NO_LOGGING + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +namespace Trace { + +// This is not strictly thread safe in all corner cases because of missing barriers. +// We do not care in order to not harm the fast path with additional barriers. +// Enabled tracing incurs a runtime overhead with multiple threads as a global atomic variable +// gets modified. +// This cacheline bouncing does not matter at all +// if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz), +// which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all. +extern bool volatile g_Enabled; +static inline bool IsEnabled() { return g_Enabled; } + +MPT_NOINLINE void Trace(const mpt::log::Context & contexxt); + +enum ThreadKind { + ThreadKindGUI, + ThreadKindAudio, + ThreadKindNotify, +}; + +void Enable(std::size_t numEntries); +void Disable(); + +void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id); + +void Seal(); +bool Dump(const mpt::PathString &filename); + +#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_LOG_CURRENTCONTEXT()); } } MPT_WHILE_0 + +} // namespace Trace + +#else // !MODPLUG_TRACKER + +#define MPT_TRACE() MPT_DO { } MPT_WHILE_0 + +#endif // MODPLUG_TRACKER + + + +} // namespace log +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp new file mode 100644 index 000000000..933097de4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp @@ -0,0 +1,221 @@ +/* + * Profiler.cpp + * ------------ + * Purpose: Performance measuring + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Profiler.h" + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef USE_PROFILER + + +class Statistics +{ +public: + Profile &profile; + Profile::Data data; + double usage; + Statistics(Profile &p) : profile(p) + { + usage = 0.0; + Update(); + } + void Update() + { + data = profile.GetAndResetData(); + uint64 now = profile.GetTime(); + uint64 timewindow = now - data.Start; + if(data.Calls > 0 && timewindow > 0) + { + usage = (double)data.Sum / (double)timewindow; + } else + { + usage = 0.0; + } + } +}; + + +struct ProfileBlock +{ + class Profile * profile; + const char * name; + class Statistics * stats; +}; + +static const std::size_t MAX_PROFILES = 1024; + +static ProfileBlock Profiles[ MAX_PROFILES ]; + +static std::size_t NextProfile = 0; + + +static void RegisterProfile(Profile *newprofile) +{ + if(NextProfile < MAX_PROFILES) + { + Profiles[NextProfile].profile = newprofile; + Profiles[NextProfile].stats = 0; + NextProfile++; + } +} + + +static void UnregisterProfile(Profile *oldprofile) +{ + for(std::size_t i=0; iUpdate(); + } + } +} + + +std::string Profiler::DumpProfiles() +{ + std::string ret; + for(std::size_t i=0; i Profiler::DumpCategories() +{ + std::vector ret; + ret.resize(Profiler::CategoriesCount); + for(std::size_t i=0; iCategory] += Profiles[i].stats->usage; + } + } + return ret; +} + + +uint64 Profile::GetTime() const +{ + LARGE_INTEGER ret; + ret.QuadPart = 0; + QueryPerformanceCounter(&ret); + return ret.QuadPart; +} + + +uint64 Profile::GetFrequency() const +{ + LARGE_INTEGER ret; + ret.QuadPart = 0; + QueryPerformanceFrequency(&ret); + return ret.QuadPart; +} + + +Profile::Profile(Profiler::Category category, const char *name) : Category(category), Name(name) +{ + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + EnterTime = 0; + RegisterProfile(this); +} + + +Profile::~Profile() +{ + UnregisterProfile(this); +} + + +Profile::Data Profile::GetAndResetData() +{ + Profile::Data ret; + datamutex.lock(); + ret = data; + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + datamutex.unlock(); + return ret; +} + + +void Profile::Reset() +{ + datamutex.lock(); + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + datamutex.unlock(); +} + + +void Profile::Enter() +{ + EnterTime = GetTime(); +} + + +void Profile::Leave() +{ + uint64 LeaveTime = GetTime(); + datamutex.lock(); + data.Calls += 1; + data.Sum += LeaveTime - EnterTime; + datamutex.unlock(); +} + + +#else // !USE_PROFILER + +MPT_MSVC_WORKAROUND_LNK4221(Profiler) + +#endif // USE_PROFILER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h new file mode 100644 index 000000000..fdde93b02 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h @@ -0,0 +1,125 @@ +/* + * Profiler.h + * ---------- + * Purpose: Performance measuring + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "../common/mptMutex.h" +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) + +//#define USE_PROFILER + +#endif + +#ifdef USE_PROFILER + +class Profiler +{ +public: + enum Category + { + GUI, + Audio, + Notify, + CategoriesCount + }; + static std::vector GetCategoryNames() + { + std::vector ret; + ret.push_back("GUI"); + ret.push_back("Audio"); + ret.push_back("Notify"); + return ret; + } +public: + static void Update(); + static std::string DumpProfiles(); + static std::vector DumpCategories(); +}; + + +class Profile +{ +private: + mutable mpt::mutex datamutex; +public: + struct Data + { + uint64 Calls; + uint64 Sum; + int64 Overhead; + uint64 Start; + }; +public: + Data data; + uint64 EnterTime; + Profiler::Category Category; + const char * const Name; + uint64 GetTime() const; + uint64 GetFrequency() const; +public: + Profile(Profiler::Category category, const char *name); + ~Profile(); + void Reset(); + void Enter(); + void Leave(); + class Scope + { + private: + Profile &profile; + public: + Scope(Profile &p) : profile(p) { profile.Enter(); } + ~Scope() { profile.Leave(); } + }; +public: + Data GetAndResetData(); +}; + + +#define OPENMPT_PROFILE_SCOPE(cat, name) \ + static Profile OPENMPT_PROFILE_VAR(cat, name);\ + Profile::Scope OPENMPT_PROFILE_SCOPE_VAR(OPENMPT_PROFILE_VAR); \ +/**/ + + +#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __FUNCTION__) + + +#else // !USE_PROFILER + + +class Profiler +{ +public: + enum Category + { + CategoriesCount + }; + static std::vector GetCategoryNames() { return std::vector(); } +public: + static void Update() { } + static std::string DumpProfiles() { return std::string(); } + static std::vector DumpCategories() { return std::vector(); } +}; +#define OPENMPT_PROFILE_SCOPE(cat, name) MPT_DO { } MPT_WHILE_0 +#define OPENMPT_PROFILE_FUNCTION(cat) MPT_DO { } MPT_WHILE_0 + + +#endif // USE_PROFILER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h b/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h new file mode 100644 index 000000000..00dfe71ac --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h @@ -0,0 +1,410 @@ +/* + * StringFixer.h + * ------------- + * Purpose: Various functions for "fixing" char array strings for writing to or + * reading from module files, or for securing char arrays in general. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include +#include +#include + +OPENMPT_NAMESPACE_BEGIN + +namespace mpt { namespace String +{ + + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif // MPT_COMPILER_MSVC + + + // Sets last character to null in given char array. + // Size of the array must be known at compile time. + template + void SetNullTerminator(char (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(char *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + template + void SetNullTerminator(wchar_t (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(wchar_t *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + + // Remove any chars after the first null char + template + void FixNullString(char (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + SetNullTerminator(buffer); + size_t pos = 0; + // Find the first null char. + while(pos < size && buffer[pos] != '\0') + { + pos++; + } + // Remove everything after the null char. + while(pos < size) + { + buffer[pos++] = '\0'; + } + } + + inline void FixNullString(std::string & str) + { + for(std::size_t i = 0; i < str.length(); ++i) + { + if(str[i] == '\0') + { + // if we copied \0 in the middle of the buffer, terminate std::string here + str.resize(i); + break; + } + } + } + + + enum ReadWriteMode + { + // Reading / Writing: Standard null-terminated string handling. + nullTerminated, + // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). + // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). + maybeNullTerminated, + // Reading: String may contain null characters anywhere. They should be treated as spaces. + // Writing: A space-padded string is written. + spacePadded, + // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). + // Writing: A space-padded string with a trailing null is written. + spacePaddedNull + }; + + + namespace detail + { + static inline char NullToSpace(const char &c) + { + return (c != '\0') ? c : ' '; + } + } + + + // Copy a string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(std::string &dest, const Tbyte *srcBuffer, size_t srcSize) + { + + const char *src = mpt::byte_cast(srcBuffer); + + dest.clear(); + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // We assume that the last character of the source buffer is null. + if(srcSize > 0) + { + srcSize -= 1; + } + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + + // Copy null-terminated string, stopping at null. + try + { + dest.assign(src, std::find(src, src + srcSize, '\0')); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + + } else if(mode == spacePadded || mode == spacePaddedNull) + { + + try + { + // Copy string over. + dest.assign(src, src + srcSize); + + // Convert null characters to spaces. + std::transform(dest.begin(), dest.end(), dest.begin(), detail::NullToSpace); + + // Trim trailing spaces. + dest = mpt::String::RTrim(dest, std::string(" ")); + + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + + } + } + + // Copy a charset encoded string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte *srcBuffer, size_t srcSize) + { + std::string tmp; + Read(tmp, srcBuffer, srcSize); + dest = mpt::ToUnicode(charset, tmp); + } + + // Used for reading strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(std::string &dest, const Tbyte (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(srcSize > 0); + Read(dest, srcBuffer, srcSize); + } + + // Used for reading charset encoded strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte(&srcBuffer)[srcSize]) + { + std::string tmp; + Read(tmp, srcBuffer); + dest = mpt::ToUnicode(charset, tmp); + } + + // Copy a string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(char (&destBuffer)[destSize], const Tbyte *srcBuffer, size_t srcSize) + { + STATIC_ASSERT(destSize > 0); + + char *dst = destBuffer; + const char *src = mpt::byte_cast(srcBuffer); + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // We assume that the last character of the source buffer is null. + if(srcSize > 0) + { + srcSize -= 1; + } + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + + // Copy string and leave one character space in the destination buffer for null. + dst = std::copy(src, std::find(src, src + std::min(srcSize, destSize - 1), '\0'), dst); + + } else if(mode == spacePadded || mode == spacePaddedNull) + { + + // Copy string and leave one character space in the destination buffer for null. + // Convert nulls to spaces while copying. + dst = std::replace_copy(src, src + std::min(srcSize, destSize - 1), dst, '\0', ' '); + + // Rewind dst to the first of any trailing spaces. + while(dst - destBuffer > 0) + { + dst--; + char c = *dst; + if(c != ' ') + { + dst++; + break; + } + } + + } + + // Fill rest of string with nulls. + std::fill(dst, destBuffer + destSize, '\0'); + + } + + // Used for reading strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(char (&destBuffer)[destSize], const Tbyte (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(destSize > 0); + STATIC_ASSERT(srcSize > 0); + Read(destBuffer, srcBuffer, srcSize); + } + + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // You should only use this function if src and dest are dynamically sized, + // otherwise use one of the safer overloads below. + template + void Write(char *destBuffer, const size_t destSize, const char *srcBuffer, const size_t srcSize) + { + MPT_ASSERT(destSize > 0); + + const size_t maxSize = std::min(destSize, srcSize); + char *dst = destBuffer; + const char *src = srcBuffer; + + // First, copy over null-terminated string. + size_t pos = maxSize; + while(pos > 0) + { + if((*dst = *src) == '\0') + { + break; + } + pos--; + dst++; + src++; + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + // Fill rest of string with nulls. + std::fill(dst, dst + destSize - maxSize + pos, '\0'); + } else if(mode == spacePadded || mode == spacePaddedNull) + { + // Fill the rest of the destination string with spaces. + std::fill(dst, dst + destSize - maxSize + pos, ' '); + } + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // Make sure that destination is really null-terminated. + SetNullTerminator(destBuffer, destSize); + } + } + + // Copy a string from srcBuffer to a dynamically sized std::vector destBuffer using a given write mode. + // Used for writing strings to files. + // Only use this version of the function if the size of the source buffer is variable and the destination buffer also has variable size. + template + void Write(std::vector &destBuffer, const char *srcBuffer, const size_t srcSize) + { + MPT_ASSERT(destBuffer.size() > 0); + Write(destBuffer.data(), destBuffer.size(), srcBuffer, srcSize); + } + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // Used for writing strings to files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Write(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize) + { + STATIC_ASSERT(destSize > 0); + Write(destBuffer, destSize, srcBuffer, srcSize); + } + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // Used for writing strings to files. + // Preferrably use this version of the function, it is safer. + template + void Write(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(destSize > 0); + STATIC_ASSERT(srcSize > 0); + Write(destBuffer, srcBuffer, srcSize); + } + + template + void Write(char *destBuffer, const size_t destSize, const std::string &src) + { + MPT_ASSERT(destSize > 0); + Write(destBuffer, destSize, src.c_str(), src.length()); + } + + template + void Write(std::vector &destBuffer, const std::string &src) + { + MPT_ASSERT(destBuffer.size() > 0); + Write(destBuffer, src.c_str(), src.length()); + } + + template + void Write(char (&destBuffer)[destSize], const std::string &src) + { + STATIC_ASSERT(destSize > 0); + Write(destBuffer, src.c_str(), src.length()); + } + + + // Copy from a char array to a fixed size char array. + template + void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) + { + const size_t copySize = std::min(destSize - 1u, srcSize); + std::strncpy(destBuffer, srcBuffer, copySize); + destBuffer[copySize] = '\0'; + } + + // Copy at most srcSize characters from srcBuffer to a std::string. + static inline void CopyN(std::string &dest, const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) + { + dest.assign(srcBuffer, srcBuffer + mpt::strnlen(srcBuffer, srcSize)); + } + + + // Copy from one fixed size char array to another one. + template + void Copy(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) + { + CopyN(destBuffer, srcBuffer, srcSize); + } + + // Copy from a std::string to a fixed size char array. + template + void Copy(char (&destBuffer)[destSize], const std::string &src) + { + CopyN(destBuffer, src.c_str(), src.length()); + } + + // Copy from a fixed size char array to a std::string. + template + void Copy(std::string &dest, const char (&srcBuffer)[srcSize]) + { + CopyN(dest, srcBuffer, srcSize); + } + + // Copy from a std::string to a std::string. + static inline void Copy(std::string &dest, const std::string &src) + { + dest.assign(src); + } + + +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + + +} } // namespace mpt::String + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h b/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h new file mode 100644 index 000000000..21717dc59 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h @@ -0,0 +1,71 @@ +/* + * WriteMemoryDump.h + * ----------------- + * Purpose: Code for writing memory dumps to a file. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared +#endif // MPT_COMPILER_MSVC +#include +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + +OPENMPT_NAMESPACE_BEGIN + +typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + +static bool WriteMemoryDump(_EXCEPTION_POINTERS *pExceptionInfo, const WCHAR *filename, bool fullMemDump) +{ + bool result = false; + + HMODULE hDll = ::LoadLibraryW(L"DBGHELP.DLL"); + if (hDll) + { + MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump"); + if (pDump) + { + + HANDLE hFile = ::CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + + if(pExceptionInfo) + { + ExInfo.ThreadId = ::GetCurrentThreadId(); + ExInfo.ExceptionPointers = pExceptionInfo; + ExInfo.ClientPointers = NULL; + } + + pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, + fullMemDump ? + (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo +#if MPT_COMPILER_MSVC + | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation +#endif + ) + : + MiniDumpNormal, + pExceptionInfo ? &ExInfo : NULL, NULL, NULL); + ::CloseHandle(hFile); + + result = true; + } + } + ::FreeLibrary(hDll); + } + return result; +} + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp new file mode 100644 index 000000000..a30b7fbfa --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp @@ -0,0 +1,135 @@ +/* + * misc_util.cpp + * ------------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "misc_util.h" + + +OPENMPT_NAMESPACE_BEGIN + + + +#ifdef MODPLUG_TRACKER + +namespace Util +{ + + +#if MPT_OS_WINDOWS + + +#endif // MPT_OS_WINDOWS + + +} // namespace Util + +#endif // MODPLUG_TRACKER + + +namespace Util +{ + + +static const MPT_UCHAR_TYPE EncodeNibble[16] = { + MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), + MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), + MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'), + MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F') }; + +static inline bool DecodeByte(uint8 &byte, MPT_UCHAR_TYPE c1, MPT_UCHAR_TYPE c2) +{ + byte = 0; + if(MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9')) + { + byte += static_cast((c1 - MPT_UCHAR('0')) << 4); + } else if(MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F')) + { + byte += static_cast((c1 - MPT_UCHAR('A') + 10) << 4); + } else if(MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f')) + { + byte += static_cast((c1 - MPT_UCHAR('a') + 10) << 4); + } else + { + return false; + } + if(MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9')) + { + byte += static_cast(c2 - MPT_UCHAR('0')); + } else if(MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F')) + { + byte += static_cast(c2 - MPT_UCHAR('A') + 10); + } else if(MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f')) + { + byte += static_cast(c2 - MPT_UCHAR('a') + 10); + } else + { + return false; + } + return true; +} + +mpt::ustring BinToHex(mpt::const_byte_span src) +{ + mpt::ustring result; + result.reserve(src.size() * 2); + for(uint8 byte : src) + { + result.push_back(EncodeNibble[(byte & 0xf0) >> 4]); + result.push_back(EncodeNibble[byte & 0x0f]); + } + return result; +} + +std::vector HexToBin(const mpt::ustring &src) +{ + std::vector result; + result.reserve(src.size() / 2); + for(std::size_t i = 0; (i + 1) < src.size(); i += 2) + { + uint8 byte = 0; + if(!DecodeByte(byte, src[i], src[i + 1])) + { + return result; + } + result.push_back(byte); + } + return result; +} + + +} // namespace Util + + +#if defined(MODPLUG_TRACKER) || (defined(LIBOPENMPT_BUILD) && defined(LIBOPENMPT_BUILD_TEST)) + +namespace mpt +{ + +std::string getenv(const std::string &env_var, const std::string &def) +{ +#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT + MPT_UNREFERENCED_PARAMETER(env_var); + return def; +#else + const char *val = std::getenv(env_var.c_str()); + if(!val) + { + return def; + } + return val; +#endif +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST) + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h new file mode 100644 index 000000000..9b357aa31 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h @@ -0,0 +1,1134 @@ +/* + * misc_util.h + * ----------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "mptStringParse.h" +#include "mptCPU.h" +#include "mptOS.h" +#include "mptTime.h" +#include "mptLibrary.h" +#include "mptTypeTraits.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +// cmath fixups +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + + + +namespace mpt { namespace String { + +// Combine a vector of values into a string, separated with the given separator. +// No escaping is performed. +template +mpt::ustring Combine(const std::vector &vals, const mpt::ustring &sep=MPT_USTRING(",")) +{ + mpt::ustring str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::ufmt::val(vals[i]); + } + return str; +} +template +std::string Combine(const std::vector &vals, const std::string &sep=std::string(",")) +{ + std::string str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::fmt::val(vals[i]); + } + return str; +} + +// Split the given string at separator positions into individual values returned as a vector. +// An empty string results in an empty vector. +// Leading or trailing separators result in a default-constructed element being inserted before or after the other elements. +template +std::vector Split(const mpt::ustring &str, const mpt::ustring &sep=MPT_USTRING(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} +template +std::vector Split(const std::string &str, const std::string &sep=std::string(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} + +} } // namespace mpt::String + + +namespace mpt { + +// GCC 4.5 and up provides templated overloads of std::abs that convert +// integer type narrower than int to double. +// As this is apparently valid by the current standard, Library Working Group +// Issue #2735 has been filed (see +// ). +// In any case, avoid this insanity and provide our own mpt::abs implementation +// for signed integer and floating point types. +// Note: We stick to a C++98-style implementation only overloading int and +// greater types in order to keep promotion rules consistent for narrower types, +// which a templated version returning the argument type would not do. OpenMPT +// probably assumes this semantic when calling abs(int8) in various places. +inline int abs(int x) +{ + return std::abs(x); +} +inline long abs(long x) +{ + return std::abs(x); +} +inline long long abs(long long x) +{ + return std::abs(x); +} +inline float abs(float x) +{ + return std::fabs(x); +} +inline double abs(double x) +{ + return std::fabs(x); +} +inline long double abs(long double x) +{ + return std::fabs(x); +} + +// Modulo with more intuitive behaviour for some contexts: +// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range. +// For example, wrapping_modulo(-1, m) == (m - 1). +// Behaviour is undefined if m<=0. +template +MPT_CONSTEXPR11_FUN auto wrapping_modulo(T x, M m) -> decltype(x % m) +{ + return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m)); +} + +template +MPT_CONSTEXPR11_FUN auto wrapping_divide(T x, D d) -> decltype(x / d) +{ + return (x >= 0) ? (x / d) : (((x + 1) / d) - 1); +} + +} // namespace mpt + + +// Memset given object to zero. +template +inline void MemsetZero(T &a) +{ + static_assert(std::is_pointer::value == false, "Won't memset pointers."); +#if MPT_GCC_BEFORE(5,1,0) || MPT_CLANG_BEFORE(3,5,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) + MPT_STATIC_ASSERT(std::is_standard_layout::value); + MPT_STATIC_ASSERT(std::is_trivial::value); // approximation +#else // default + MPT_STATIC_ASSERT(std::is_standard_layout::value); + MPT_STATIC_ASSERT(std::is_trivially_copyable::value); // C++11, but not supported on most compilers we care about +#endif + std::memset(&a, 0, sizeof(T)); +} + + +#ifdef MODPLUG_TRACKER +// Copy given object to other location. +template +void MemCopy(T &destination, const T &source) +{ + static_assert(std::is_pointer::value == false, "Won't copy pointers."); +#if MPT_GCC_BEFORE(5,1,0) || MPT_CLANG_BEFORE(3,5,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) + MPT_STATIC_ASSERT(std::is_trivial::value); // approximation +#else // default + MPT_STATIC_ASSERT(std::is_trivially_copyable::value); // C++11, but not supported on most compilers we care about +#endif + std::memcpy(&destination, &source, sizeof(T)); +} +#endif // MODPLUG_TRACKER + + +namespace mpt { + + +// Simplified version of gsl::span. +// Non-owning read-only or read-write view into a contiguous block of T +// objects, i.e. equivalent to a (beg,end) or (data,size) tuple. +// Can eventually be replaced without further modifications with a full +// gsl::span. +template +class span +{ + +public: + + typedef std::size_t size_type; + + typedef T value_type; + typedef T & reference; + typedef T * pointer; + typedef const T * const_pointer; + typedef const T & const_reference; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef typename std::iterator_traits::difference_type difference_type; + +private: + + T * m_beg; + T * m_end; + +public: + + span() : m_beg(nullptr), m_end(nullptr) { } + + span(pointer beg, pointer end) : m_beg(beg), m_end(end) { } + + span(pointer data, size_type size) : m_beg(data), m_end(data + size) { } + + template span(U (&arr)[N]) : m_beg(arr), m_end(arr + N) { } + + template span(Cont &cont) : m_beg(cont.empty() ? nullptr : &(cont[0])), m_end(cont.empty() ? nullptr : &(cont[0]) + cont.size()) { } + + span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } + + template span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } + + span & operator = (span other) { m_beg = other.begin(); m_end = other.end(); return *this; } + + iterator begin() const { return iterator(m_beg); } + iterator end() const { return iterator(m_end); } + + const_iterator cbegin() const { return const_iterator(begin()); } + const_iterator cend() const { return const_iterator(end()); } + + operator bool () const throw() { return m_beg != nullptr; } + + reference operator[](size_type index) { return at(index); } + const_reference operator[](size_type index) const { return at(index); } + + bool operator==(span const & other) const throw() { return size() == other.size() && (m_beg == other.m_beg || std::equal(begin(), end(), other.begin())); } + bool operator!=(span const & other) const throw() { return !(*this == other); } + + reference at(size_type index) { return m_beg[index]; } + const_reference at(size_type index) const { return m_beg[index]; } + + pointer data() const throw() { return m_beg; } + + bool empty() const throw() { return size() == 0; } + + size_type size() const throw() { return std::distance(m_beg, m_end); } + size_type length() const throw() { return size(); } + +}; // class span + +template inline span as_span(T * beg, T * end) { return span(beg, end); } + +template inline span as_span(T * data, std::size_t size) { return span(data, size); } + +template inline span as_span(T (&arr)[N]) { return span(std::begin(arr), std::end(arr)); } + +template inline span as_span(std::vector & cont) { return span(cont); } + +template inline span as_span(const std::vector & cont) { return span(cont); } + +template inline span as_span(std::basic_string & str) { return span(&(str[0]), str.length()); } + +template inline span as_span(const std::basic_string & str) { return span(&(str[0]), str.length()); } + + +typedef mpt::span byte_span; +typedef mpt::span const_byte_span; + + + +template inline std::vector::type> make_vector(T * beg, T * end) { return std::vector::type>(beg, end); } + +template inline std::vector::type> make_vector(T * data, std::size_t size) { return std::vector::type>(data, data + size); } + +template inline std::vector::type> make_vector(mpt::span data) { return std::vector::type>(data.data(), data.data() + data.size()); } + +template inline std::vector::type> make_vector(T (&arr)[N]) { return std::vector::type>(std::begin(arr), std::end(arr)); } + +template inline std::vector::type> make_vector(const std::basic_string & str) { return std::vector::type>(str.begin(), str.end()); } + + + +template +struct byte_cast_impl +{ + inline Tdst operator () (Tsrc src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + // not checking is_byte_castable here because we are actually + // doing a static_cast and converting the value + STATIC_ASSERT(std::is_integral::value); + STATIC_ASSERT(std::is_integral::value); + return static_cast(src); + } +}; +template +struct byte_cast_impl, mpt::span > +{ + inline mpt::span operator () (mpt::span src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + STATIC_ASSERT(std::is_integral::value); + return mpt::as_span(mpt::byte_cast_impl()(src.begin()), mpt::byte_cast_impl()(src.end())); + } +}; +template +struct byte_cast_impl +{ + inline Tdst* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + STATIC_ASSERT(std::is_integral::value); + return reinterpret_cast(src); + } +}; + +template +struct void_cast_impl; + +template +struct void_cast_impl +{ + inline Tdst* operator () (void* src) const + { + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline Tdst* operator () (const void* src) const + { + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline void* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline const void* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value); + return reinterpret_cast(src); + } +}; + +// casts between different byte (char) types or pointers to these types +template +inline Tdst byte_cast(Tsrc src) +{ + return byte_cast_impl()(src); +} + +// casts between pointers to void and pointers to byte +template +inline Tdst void_cast(Tsrc src) +{ + return void_cast_impl()(src); +} + + +// Saturate the value of src to the domain of Tdst +template +inline Tdst saturate_cast(Tsrc src) +{ + // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting. + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_CONSTANT_IF(std::numeric_limits::is_signed && std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } + return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); + } else MPT_CONSTANT_IF(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } else MPT_CONSTANT_IF(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) > sizeof(Tsrc)) + { + return static_cast(src); + } + MPT_CONSTANT_IF(sizeof(Tdst) == sizeof(Tsrc)) + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } else // Tdst unsigned, Tsrc signed + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(std::max(0, src)); + } + return static_cast(std::max(0, std::min(src, static_cast(std::numeric_limits::max())))); + } +} + +template +inline Tdst saturate_cast(double src) +{ + if(src >= std::numeric_limits::max()) + { + return std::numeric_limits::max(); + } + if(src <= std::numeric_limits::min()) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + +template +inline Tdst saturate_cast(float src) +{ + if(src >= std::numeric_limits::max()) + { + return std::numeric_limits::max(); + } + if(src <= std::numeric_limits::min()) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + + +} // namespace mpt + + +#if defined(MODPLUG_TRACKER) +// Tracker code requires MIN/MAX to work in constexpr contexts. +// We could make MIN/MAX constexpr for supporting compilers, +// but that would just needlessly complicate the support matrix +// for now. +#ifndef MPT_MINMAX_MACROS +#define MPT_MINMAX_MACROS +#endif +#endif + +#if MPT_COMPILER_MSVC +// MSVC disables a bunch of type conversion warnings once a macro is involved. +// Replacing the macro with a template thus spews a TON OF WARNINGS for now. +#ifndef MPT_MINMAX_MACROS +#define MPT_MINMAX_MACROS +#endif +#endif + +#if defined(MPT_MINMAX_MACROS) + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#else + +namespace mpt { namespace Legacy { + +template +MPT_FORCEINLINE auto MAX(const Ta &a, const Tb &b) -> decltype((a>b)?a:b) +{ + return (a > b) ? a : b; +} + +template +MPT_FORCEINLINE auto MIN(const Ta &a, const Tb &b) -> decltype((a +struct ModIfNotZeroImpl +{ + template + inline Tval mod(Tval x) + { + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + return static_cast(x % m); + } +}; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +} // namespace detail +// Returns x % m if m != 0, x otherwise. +// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers +template +inline Tval ModIfNotZero(Tval x) +{ + return detail::ModIfNotZeroImpl().mod(x); +} + +// Returns true iff Tdst can represent the value val. +// Use as if(Util::TypeCanHoldValue(-1)). +template +inline bool TypeCanHoldValue(Tsrc val) +{ + return (static_cast(mpt::saturate_cast(val)) == val); +} + +// Grows x with an exponential factor suitable for increasing buffer sizes. +// Clamps the result at limit. +// And avoids integer overflows while doing its business. +// The growth factor is 1.5, rounding down, execpt for the initial x==1 case. +template +inline T ExponentialGrow(const T &x, const Tlimit &limit) +{ + MPT_ASSERT(x > 0); + MPT_ASSERT(limit > 0); + if(x == 1) + { + return 2; + } + T add = std::min(x >> 1, std::numeric_limits::max() - x); + return std::min(x + add, mpt::saturate_cast(limit)); +} + +template +inline T ExponentialGrow(const T &x) +{ + return Util::ExponentialGrow(x, std::numeric_limits::max()); +} + +} //namespace Util + + +namespace mpt +{ + +// C++17 clamp + +template +MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi, Compare comp) +{ + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; +} + +template +MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi) +{ + return mpt::clamp(v, lo, hi, std::less()); +} + +} // namespace mpt + + +// Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'. +// Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'. +// If 'lowerLimit' > 'upperLimit', 'val' won't be modified. +template +inline void Limit(T& val, const C lowerLimit, const C upperLimit) +{ + if(lowerLimit > upperLimit) return; + if(val < lowerLimit) val = lowerLimit; + else if(val > upperLimit) val = upperLimit; +} + + +// Like Limit, but returns value +template +inline T Clamp(T val, const C lowerLimit, const C upperLimit) +{ + if(val < lowerLimit) return lowerLimit; + else if(val > upperLimit) return upperLimit; + else return val; +} + +// Check if val is in [lo,hi] without causing compiler warnings +// if theses checks are always true due to the domain of T. +// GCC does not warn if the type is templated. +template +inline bool IsInRange(T val, C lo, C hi) +{ + return lo <= val && val <= hi; +} + +// Like Limit, but with upperlimit only. +template +inline void LimitMax(T& val, const C upperLimit) +{ + if(val > upperLimit) + val = upperLimit; +} + + +// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) +template +int sgn(T value) +{ + return (value > T(0)) - (value < T(0)); +} + + + +// mpt::rshift_signed +// mpt::lshift_signed +// Shift a signed integer value in a well-defined manner. +// Does the same thing as MSVC would do. This is verified by the test suite. + +namespace mpt +{ + +template +MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + typedef decltype(x >> y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx >>= y; + urx -= roffset >> y; + return static_cast(urx); +} + +template +MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + typedef decltype(x << y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx <<= y; + urx -= roffset << y; + return static_cast(urx); +} + +#if MPT_COMPILER_SHIFT_SIGNED + +template +MPT_FORCEINLINE auto rshift_signed_undefined(T x, int y) -> decltype(x >> y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + return x >> y; +} + +template +MPT_FORCEINLINE auto lshift_signed_undefined(T x, int y) -> decltype(x << y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + return x << y; +} + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_undefined(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_undefined(x, y); +} + +#else + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_standard(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_standard(x, y); +} + +#endif + +} // namespace mpt + + + +namespace Util +{ + + // Returns maximum value of given integer type. + template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} + + // The following MPT_MAX_* macros are useful as std::numeric_limits is not + // usable in constexpr-like contexts like static_assert in pre-C++11 + // compilers. + + // Returns the maximum value for the signed type or expression at compile time. + #define MPT_MAX_SIGNED_VALUE(integral_expression_or_type) ( ( 1ull << ( sizeof(integral_expression_or_type) * 8 - 1 ) ) - 1 ) + + // Returns the maximum value for the unsigned type or expression at compile time. + // Implemented in terms of MPT_MAX_SIGNED_VALUE in order to avoid overflow in left-shift. + #define MPT_MAX_UNSIGNED_VALUE(integral_expression_or_type) ( ( MPT_MAX_SIGNED_VALUE(integral_expression_or_type) << 1 ) | 1ull ) + + // Return the maximum value of an integral type at compile time. + #define MPT_MAX_VALUE_OF_TYPE(integral_type) ( std::numeric_limits::is_signed ? MPT_MAX_SIGNED_VALUE(integral_type) : MPT_MAX_UNSIGNED_VALUE(integral_type) ) + + /// Returns value rounded to nearest integer. +#if (MPT_OS_EMSCRIPTEN && MPT_OS_EMSCRIPTEN_ANCIENT) + // MSVC before 2013 does not support C99/C++11. + // Certain emscripten versions and/or combinations with nodejs (at least the following combination: emscripten 1.34.8, clang 3.7.0, nodejs 0.10.38) fail assert(std::round(1.5)==2.0). The work-around always works. + inline double Round(const double& val) {if(val >= 0.0) return std::floor(val + 0.5); else return std::ceil(val - 0.5);} + inline float Round(const float& val) {if(val >= 0.0f) return std::floor(val + 0.5f); else return std::ceil(val - 0.5f);} +#elif MPT_OS_ANDROID && defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION) + // NDK 12b gnustl_shared armeabi-v7a only provides round() in ::. + // NDK 12b gnustl_shared arm64-v8a has round() in std::. + // NDK 12b c++_shared armeabi-v7a has round() in std::. + // Just fallback to :: for Android gnustl_shared. + // This work-around can be removed once Android switches to LLVM libc++. + // Currently (ndk-r12b), libc++ has problems with exceptions. + inline double Round(const double& val) { return ::round(val); } + inline float Round(const float& val) { return ::roundf(val); } +#else + inline double Round(const double& val) { return std::round(val); } + inline float Round(const float& val) { return std::round(val); } +#endif + + /// Rounds given double value to nearest integer value of type T. + template inline T Round(const double& val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + const double valRounded = Round(val); + MPT_ASSERT(valRounded >= (std::numeric_limits::min)() && valRounded <= (std::numeric_limits::max)()); + const T intval = static_cast(valRounded); + return intval; + } + + template inline T Round(const float& val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + const float valRounded = Round(val); + MPT_ASSERT(valRounded >= (std::numeric_limits::min)() && valRounded <= (std::numeric_limits::max)()); + const T intval = static_cast(valRounded); + return intval; + } + + template + T Weight(T x) + { + STATIC_ASSERT(std::numeric_limits::is_integer); + T c; + for(c = 0; x; x >>= 1) + { + c += x & 1; + } + return c; + } + +} + +namespace Util { + + // Multiply two 32-bit integers, receive 64-bit result. + // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. + MPT_FORCEINLINE int64 mul32to64(int32 a, int32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emul(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE uint64 mul32to64_unsigned(uint32 a, uint32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emulu(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE int32 muldiv(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( mul32to64( a, b ) / c ); + } + + MPT_FORCEINLINE int32 muldivr(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( ( mul32to64( a, b ) + ( c / 2 ) ) / c ); + } + + // Do not use overloading because catching unsigned version by accident results in slower X86 code. + MPT_FORCEINLINE uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( mul32to64_unsigned( a, b ) / c ); + } + MPT_FORCEINLINE uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c ); + } + + MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c) + { + a *= b; + a += c / 2u; + return (a >= 0) ? mpt::saturate_cast(a / c) : mpt::saturate_cast((a - (c - 1)) / c); + } + + // rounds x up to multiples of target + template + inline T AlignUp(T x, T target) + { + return ((x + (target - 1)) / target) * target; + } + + // rounds x down to multiples of target + template + inline T AlignDown(T x, T target) + { + return (x / target) * target; + } + + // Insert a range of items [insStart, insEnd], and possibly shift item fix to the left. + template + void InsertItem(const T insStart, const T insEnd, T &fix) + { + MPT_ASSERT(insEnd >= insStart); + if(fix >= insStart) + { + fix += (insEnd - insStart + 1); + } + } + + // Insert a range of items [insStart, insEnd], and possibly shift items in range [fixStart, fixEnd] to the right. + template + void InsertRange(const T insStart, const T insEnd, T &fixStart, T &fixEnd) + { + MPT_ASSERT(insEnd >= insStart); + const T insLength = insEnd - insStart + 1; + if(fixStart >= insEnd) + { + fixStart += insLength; + } + if(fixEnd >= insEnd) + { + fixEnd += insLength; + } + } + + // Delete a range of items [delStart, delEnd], and possibly shift item fix to the left. + template + void DeleteItem(const T delStart, const T delEnd, T &fix) + { + MPT_ASSERT(delEnd >= delStart); + if(fix > delEnd) + { + fix -= (delEnd - delStart + 1); + } + } + + // Delete a range of items [delStart, delEnd], and possibly shift items in range [fixStart, fixEnd] to the left. + template + void DeleteRange(const T delStart, const T delEnd, T &fixStart, T &fixEnd) + { + MPT_ASSERT(delEnd >= delStart); + const T delLength = delEnd - delStart + 1; + if(delStart < fixStart && delEnd < fixStart) + { + // cut part is before loop start + fixStart -= delLength; + fixEnd -= delLength; + } else if(delStart < fixStart && delEnd < fixEnd) + { + // cut part is partly before loop start + fixStart = delStart; + fixEnd -= delLength; + } else if(delStart >= fixStart && delEnd < fixEnd) + { + // cut part is in the loop + fixEnd -= delLength; + } else if(delStart >= fixStart && delStart < fixEnd && delEnd > fixEnd) + { + // cut part is partly before loop end + fixEnd = delStart; + } + } + +} // namespace Util + + +namespace mpt +{ + + // Greatest Common Divisor. Always returns non-negative number. + // compatible with C++17 std::gcd + template + inline typename std::common_type::type gcd(A a_, B b_) + { + typename std::common_type::type a = a_; + typename std::common_type::type b = b_; + if(a < 0) + a = -a; + if(b < 0) + b = -b; + for(;;) + { + if(a == 0) + return b; + b %= a; + if(b == 0) + return a; + a %= b; + } + } + + // Least Common Multiple. Always returns non-negative number. + // compatible with C++17 std::lcm + template + inline typename std::common_type::type lcm(A a_, B b_) + { + typename std::common_type::type a = a_; + typename std::common_type::type b = b_; + if(a < 0) + a = -a; + if(b < 0) + b = -b; + if((a | b) == 0) + return 0; + return a / mpt::gcd(a, b) * b; + } + +} // namespace mpt + + +namespace Util +{ + + template + class fixed_size_queue + { + private: + T buffer[n+1]; + std::size_t read_position; + std::size_t write_position; + public: + fixed_size_queue() : read_position(0), write_position(0) + { + return; + } + void clear() + { + read_position = 0; + write_position = 0; + } + std::size_t read_size() const + { + if ( write_position > read_position ) + { + return write_position - read_position; + } else if ( write_position < read_position ) + { + return write_position - read_position + n + 1; + } else + { + return 0; + } + } + std::size_t write_size() const + { + if ( write_position > read_position ) + { + return read_position - write_position + n; + } else if ( write_position < read_position ) + { + return read_position - write_position - 1; + } else + { + return n; + } + } + bool push( const T & v ) + { + if ( !write_size() ) + { + return false; + } + buffer[write_position] = v; + write_position = ( write_position + 1 ) % ( n + 1 ); + return true; + } + bool pop() { + if ( !read_size() ) + { + return false; + } + read_position = ( read_position + 1 ) % ( n + 1 ); + return true; + } + T peek() { + if ( !read_size() ) + { + return T(); + } + return buffer[read_position]; + } + const T * peek_p() + { + if ( !read_size() ) + { + return nullptr; + } + return &(buffer[read_position]); + } + const T * peek_next_p() + { + if ( read_size() < 2 ) + { + return nullptr; + } + return &(buffer[(read_position+1)%(n+1)]); + } + }; + +} // namespace Util + + +namespace Util +{ + +std::vector HexToBin(const mpt::ustring &src); +mpt::ustring BinToHex(mpt::const_byte_span src); + +template inline mpt::ustring BinToHex(mpt::span src) { return Util::BinToHex(mpt::byte_cast(src)); } + +} // namespace Util + + +#if defined(MODPLUG_TRACKER) || (defined(LIBOPENMPT_BUILD) && defined(LIBOPENMPT_BUILD_TEST)) + +namespace mpt +{ + +// Wrapper around std::getenv. +// Instead of returning null pointer if the environment variable is not set, +// this wrapper returns the provided default value. +std::string getenv(const std::string &env_var, const std::string &def = std::string()); + +} // namespace mpt + +#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST) + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h new file mode 100644 index 000000000..34bd45eaa --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h @@ -0,0 +1,175 @@ +/* + * mptBufferIO.h + * ------------- + * Purpose: A wrapper around std::stringstream, fixing MSVC tell/seek problems with empty streams. + * Notes : You should only ever use these wrappers instead of plain std::stringstream classes. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include +#include +#include +#include +#include + +OPENMPT_NAMESPACE_BEGIN + + +// MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and +// std::stringstream) fail seekpos() and seekoff() when the stringbuf is +// currently empty. +// seekpos() and seekoff() can get called via tell*() or seek*() iostream +// members. seekoff() (and thereby tell*()), but not seekpos(), has been fixed +// from VS2010 onwards to handle this specific case and changed to not fail +// when the stringbuf is empty. +// Work-around strategy: +// As re-implementing or duplicating the whole std::stringbuf semantics would be +// rather convoluted, we make use of the knowledge of specific inner workings of +// the MSVC implementation here and just fix-up where it causes problems. This +// keeps the additional code at a minimum size. + + +namespace mpt +{ + +#if MPT_COMPILER_MSVC + +class stringbuf + : public std::stringbuf +{ +public: + stringbuf(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::stringbuf(mode) + { + return; + } + stringbuf(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::stringbuf(str, mode) + { + return; + } +public: + virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) + { + pos_type result = std::stringbuf::seekoff(off, way, which); + if(result == pos_type(-1)) + { + if((which & std::ios_base::in) || (which & std::ios_base::out)) + { + if(off == 0) + { + result = 0; + } + } + } + return result; + } + virtual pos_type seekpos(pos_type ptr, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + pos_type result = std::stringbuf::seekpos(ptr, mode); + if(result == pos_type(-1)) + { + if((mode & std::ios_base::in) || (mode & std::ios_base::out)) + { + if(static_cast(ptr) == 0) + { + result = 0; + } + } + } + return result; + } +}; + +class istringstream + : public std::basic_istream +{ +private: + mpt::stringbuf buf; +public: + istringstream(std::ios_base::openmode mode = std::ios_base::in) + : std::basic_istream(&buf) + , buf(mode | std::ios_base::in) + { + } + istringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_istream(&buf) + , buf(str, mode | std::ios_base::in) + { + } + ~istringstream() + { + } +public: + mpt::stringbuf *rdbuf() const { return const_cast(&buf); } + std::string str() const { return buf.str(); } + void str(const std::string &str) { buf.str(str); } +}; + +class ostringstream + : public std::basic_ostream +{ +private: + mpt::stringbuf buf; +public: + ostringstream(std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ostream(&buf) + , buf(mode | std::ios_base::out) + { + } + ostringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ostream(&buf) + , buf(str, mode | std::ios_base::out) + { + } + ~ostringstream() + { + } +public: + mpt::stringbuf *rdbuf() const { return const_cast(&buf); } + std::string str() const { return buf.str(); } + void str(const std::string &str) { buf.str(str); } +}; + +class stringstream + : public std::basic_iostream +{ +private: + mpt::stringbuf buf; +public: + stringstream(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_iostream(&buf) + , buf(mode | std::ios_base::in | std::ios_base::out) + { + } + stringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_iostream(&buf) + , buf(str, mode | std::ios_base::in | std::ios_base::out) + { + } + ~stringstream() + { + } +public: + mpt::stringbuf *rdbuf() const { return const_cast(&buf); } + std::string str() const { return buf.str(); } + void str(const std::string &str) { buf.str(str); } +}; + +#else + +typedef std::stringbuf stringbuf; +typedef std::istringstream istringstream; +typedef std::ostringstream ostringstream; +typedef std::stringstream stringstream; + +#endif + +} // namespace mpt + + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp new file mode 100644 index 000000000..e5b7067ce --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp @@ -0,0 +1,376 @@ +/* + * mptCPU.cpp + * ---------- + * Purpose: CPU feature detection. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptCPU.h" + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(ENABLE_ASM) + + +uint32 RealProcSupport = 0; +uint32 ProcSupport = 0; +char ProcVendorID[16+1] = ""; +uint16 ProcFamily = 0; +uint8 ProcModel = 0; +uint8 ProcStepping = 0; + + +#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) + + +#include + + +typedef char cpuid_result_string[12]; + + +struct cpuid_result { + uint32 a; + uint32 b; + uint32 c; + uint32 d; + std::string as_string() const + { + cpuid_result_string result; + result[0+0] = (b >> 0) & 0xff; + result[0+1] = (b >> 8) & 0xff; + result[0+2] = (b >>16) & 0xff; + result[0+3] = (b >>24) & 0xff; + result[4+0] = (d >> 0) & 0xff; + result[4+1] = (d >> 8) & 0xff; + result[4+2] = (d >>16) & 0xff; + result[4+3] = (d >>24) & 0xff; + result[8+0] = (c >> 0) & 0xff; + result[8+1] = (c >> 8) & 0xff; + result[8+2] = (c >>16) & 0xff; + result[8+3] = (c >>24) & 0xff; + return std::string(result, result + 12); + } +}; + + +static cpuid_result cpuid(uint32 function) +{ + cpuid_result result; + int CPUInfo[4]; + __cpuid(CPUInfo, function); + result.a = CPUInfo[0]; + result.b = CPUInfo[1]; + result.c = CPUInfo[2]; + result.d = CPUInfo[3]; + return result; +} + + +#if 0 + +static cpuid_result cpuidex(uint32 function_a, uint32 function_c) +{ + cpuid_result result; + int CPUInfo[4]; + __cpuidex(CPUInfo, function_a, function_c); + result.a = CPUInfo[0]; + result.b = CPUInfo[1]; + result.c = CPUInfo[2]; + result.d = CPUInfo[3]; + return result; +} + +#endif + + +static MPT_NOINLINE bool has_cpuid() +{ + const size_t eflags_cpuid = 1 << 21; + size_t eflags_old = __readeflags(); + size_t eflags_flipped = eflags_old ^ eflags_cpuid; + __writeeflags(eflags_flipped); + size_t eflags_testchanged = __readeflags(); + __writeeflags(eflags_old); + return ((eflags_testchanged ^ eflags_old) & eflags_cpuid) != 0; +} + + +void InitProcSupport() +{ + + RealProcSupport = 0; + ProcSupport = 0; + MemsetZero(ProcVendorID); + ProcFamily = 0; + ProcModel = 0; + ProcStepping = 0; + + if(has_cpuid()) + { + + ProcSupport |= PROCSUPPORT_CPUID; + + cpuid_result VendorString = cpuid(0x00000000u); + std::strcpy(ProcVendorID, VendorString.as_string().c_str()); + + // Cyrix 6x86 and 6x86MX do not specify the value returned in eax. + // They both support 0x00000001u however. + if((VendorString.as_string() == "CyrixInstead") || (VendorString.a >= 0x00000001u)) + { + cpuid_result StandardFeatureFlags = cpuid(0x00000001u); + uint32 Stepping = (StandardFeatureFlags.a >> 0) & 0x0f; + uint32 BaseModel = (StandardFeatureFlags.a >> 4) & 0x0f; + uint32 BaseFamily = (StandardFeatureFlags.a >> 8) & 0x0f; + uint32 ExtModel = (StandardFeatureFlags.a >> 16) & 0x0f; + uint32 ExtFamily = (StandardFeatureFlags.a >> 20) & 0xff; + if(VendorString.as_string() == "GenuineIntel") + { + if(BaseFamily == 0xf) + { + ProcFamily = static_cast(ExtFamily + BaseFamily); + } else + { + ProcFamily = static_cast(BaseFamily); + } + if(BaseFamily == 0x6 || BaseFamily == 0xf) + { + ProcModel = static_cast((ExtModel << 4) | (BaseModel << 0)); + } else + { + ProcModel = static_cast(BaseModel); + } + } else if(VendorString.as_string() == "AuthenticAMD") + { + if(BaseFamily == 0xf) + { + ProcFamily = static_cast(ExtFamily + BaseFamily); + ProcModel = static_cast((ExtModel << 4) | (BaseModel << 0)); + } else + { + ProcFamily = static_cast(BaseFamily); + ProcModel = static_cast(BaseModel); + } + } else + { + ProcFamily = static_cast(BaseFamily); + ProcModel = static_cast(BaseModel); + } + ProcStepping = static_cast(Stepping); + if(StandardFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC; + if(StandardFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; + if(StandardFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU; + if(StandardFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; + if(StandardFeatureFlags.d & (1<<25)) ProcSupport |= PROCSUPPORT_SSE; + if(StandardFeatureFlags.d & (1<<26)) ProcSupport |= PROCSUPPORT_SSE2; + if(StandardFeatureFlags.c & (1<< 0)) ProcSupport |= PROCSUPPORT_SSE3; + if(StandardFeatureFlags.c & (1<< 9)) ProcSupport |= PROCSUPPORT_SSSE3; + if(StandardFeatureFlags.c & (1<<19)) ProcSupport |= PROCSUPPORT_SSE4_1; + if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2; + } + + // 3DNow! manual recommends to just execute 0x80000000u. + // It is totally unknown how earlier CPUs from other vendors + // would behave. + // Thus we only execute 0x80000000u on other vendors CPUs for the earliest + // that we found it documented for and that actually supports 3DNow!. + // We only need 0x80000000u in order to detect 3DNow!. + // Thus, this is enough for us. + if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!")) + { // AMD + + if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 8))) + { // >= K6-2 (K6 = Family 5, K6-2 = Model 8) + // Not sure if earlier AMD CPUs support 0x80000000u. + // AMD 5k86 and AMD K5 manuals do not mention it. + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC; + if(ExtendedFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; + if(ExtendedFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU; + if(ExtendedFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; + if(ExtendedFeatureFlags.d & (1<<22)) ProcSupport |= PROCSUPPORT_AMD_MMXEXT; + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + if(ExtendedFeatureFlags.d & (1<<30)) ProcSupport |= PROCSUPPORT_AMD_3DNOWEXT; + } + } + + } else if(VendorString.as_string() == "CentaurHauls") + { // Centaur (IDT WinChip or VIA C3) + + if(ProcFamily == 5) + { // IDT + + if(ProcModel >= 8) + { // >= WinChip 2 + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + } + } + + } else if(ProcFamily >= 6) + { // VIA + + if((ProcFamily >= 7) || ((ProcFamily == 6) && (ProcModel >= 7))) + { // >= C3 Samuel 2 + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + } + } + + } + + } else if(VendorString.as_string() == "CyrixInstead") + { // Cyrix + + // 6x86 : 5.2.x + // 6x86L : 5.2.x + // MediaGX : 4.4.x + // 6x86MX : 6.0.x + // MII : 6.0.x + // MediaGXm: 5.4.x + // well, doh ... + + if((ProcFamily == 5) && (ProcModel >= 4)) + { // Cyrix MediaGXm + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + } + } + + } else if(VendorString.as_string() == "Geode by NSC") + { // National Semiconductor + + if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 5))) + { // >= Geode GX2 + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + } + } + + } + + } else + { + + ProcSupport |= PROCSUPPORT_FPU; // We assume FPU because we require it. + + } + + // We do not have to check if SSE got enabled by the OS because we only do + // support Windows >= 98 SE which will always enable SSE if available. + + RealProcSupport = ProcSupport; + +} + + +#else // !( MPT_COMPILER_MSVC && ENABLE_X86 ) + + +void InitProcSupport() +{ + ProcSupport = 0; +} + + +#endif // MPT_COMPILER_MSVC && ENABLE_X86 + +#endif // ENABLE_ASM + + +#ifdef MODPLUG_TRACKER + + +uint32 GetMinimumProcSupportFlags() +{ + uint32 flags = 0; + #ifdef ENABLE_ASM + #if MPT_COMPILER_MSVC + #if defined(_M_X64) + flags |= PROCSUPPORT_AMD64; + #elif defined(_M_IX86) + #if defined(_M_IX86_FP) + #if (_M_IX86_FP >= 2) + flags |= PROCSUPPORT_x86_SSE2; + #elif (_M_IX86_FP == 1) + flags |= PROCSUPPORT_x86_SSE; + #endif + #else + flags |= PROCSUPPORT_i586; + #endif + #endif + #endif + #endif // ENABLE_ASM + return flags; +} + + + +int GetMinimumSSEVersion() +{ + int minimumSSEVersion = 0; + #if MPT_COMPILER_MSVC + #if defined(_M_X64) + minimumSSEVersion = 2; + #elif defined(_M_IX86) + #if defined(_M_IX86_FP) + #if (_M_IX86_FP >= 2) + minimumSSEVersion = 2; + #elif (_M_IX86_FP == 1) + minimumSSEVersion = 1; + #endif + #endif + #endif + #endif + return minimumSSEVersion; +} + + +int GetMinimumAVXVersion() +{ + int minimumAVXVersion = 0; + #if MPT_COMPILER_MSVC + #if defined(_M_IX86_FP) + #if defined(__AVX2__) + minimumAVXVersion = 2; + #elif defined(__AVX__) + minimumAVXVersion = 1; + #endif + #endif + #endif + return minimumAVXVersion; +} + + +#endif + + +#if !defined(MODPLUG_TRACKER) && !defined(ENABLE_ASM) + +MPT_MSVC_WORKAROUND_LNK4221(mptCPU) + +#endif + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h new file mode 100644 index 000000000..241e6b46e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h @@ -0,0 +1,72 @@ +/* + * mptCPU.h + * -------- + * Purpose: CPU feature detection. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef ENABLE_ASM + +#define PROCSUPPORT_CPUID 0x00001 // Processor supports CPUID instruction (i586) +#define PROCSUPPORT_TSC 0x00002 // Processor supports RDTSC instruction (i586) +#define PROCSUPPORT_CMOV 0x00004 // Processor supports conditional move instructions (i686) +#define PROCSUPPORT_FPU 0x00008 // Processor supports x87 instructions +#define PROCSUPPORT_MMX 0x00010 // Processor supports MMX instructions +#define PROCSUPPORT_AMD_MMXEXT 0x00020 // Processor supports AMD MMX extensions +#define PROCSUPPORT_AMD_3DNOW 0x00040 // Processor supports AMD 3DNow! instructions +#define PROCSUPPORT_AMD_3DNOWEXT 0x00080 // Processor supports AMD 3DNow!2 instructions +#define PROCSUPPORT_SSE 0x00100 // Processor supports SSE instructions +#define PROCSUPPORT_SSE2 0x00200 // Processor supports SSE2 instructions +#define PROCSUPPORT_SSE3 0x00400 // Processor supports SSE3 instructions +#define PROCSUPPORT_SSSE3 0x00800 // Processor supports SSSE3 instructions +#define PROCSUPPORT_SSE4_1 0x01000 // Processor supports SSE4.1 instructions +#define PROCSUPPORT_SSE4_2 0x02000 // Processor supports SSE4.2 instructions + +static const uint32 PROCSUPPORT_i486 = 0u | PROCSUPPORT_FPU ; +static const uint32 PROCSUPPORT_i586 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_FPU ; +static const uint32 PROCSUPPORT_i686 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU ; +static const uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE ; +static const uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE | PROCSUPPORT_SSE2; +static const uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2; + +extern uint32 RealProcSupport; +extern uint32 ProcSupport; +extern char ProcVendorID[16+1]; +extern uint16 ProcFamily; +extern uint8 ProcModel; +extern uint8 ProcStepping; + +void InitProcSupport(); + +// enabled processor features for inline asm and intrinsics +static inline uint32 GetProcSupport() +{ + return ProcSupport; +} + +// available processor features +static inline uint32 GetRealProcSupport() +{ + return RealProcSupport; +} + +#endif // ENABLE_ASM + + +#ifdef MODPLUG_TRACKER +uint32 GetMinimumProcSupportFlags(); +int GetMinimumSSEVersion(); +int GetMinimumAVXVersion(); +#endif + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h new file mode 100644 index 000000000..c3e930995 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h @@ -0,0 +1,245 @@ +/* + * mptCRC.h + * -------- + * Purpose: generic CRC implementation + * Notes : (currently none) + * Authors: Joern Heusipp + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +OPENMPT_NAMESPACE_BEGIN + +namespace mpt +{ + +namespace checksum +{ + +template +class crc +{ + +public: + + typedef crc self_type; + typedef T value_type; + typedef uint8 byte_type; + + static const std::size_t size_bytes = sizeof(value_type); + static const std::size_t size_bits = sizeof(value_type) * 8; + static const value_type top_bit = static_cast(1) << ((sizeof(value_type) * 8) - 1); + +private: + + template + static inline Tint reverse(Tint value) + { + const std::size_t bits = sizeof(Tint) * 8; + Tint result = 0; + for(std::size_t i = 0; i < bits; ++i) + { + result <<= 1; + result |= static_cast(value & 0x1); + value >>= 1; + } + return result; + } + + static inline value_type calculate_table_entry(byte_type pos) + { + value_type value = 0; + value = (static_cast(reverseData ? reverse(pos) : pos) << (size_bits - 8)); + for(std::size_t bit = 0; bit < 8; ++bit) + { + if(value & top_bit) + { + value = (value << 1) ^ polynomial; + } else + { + value = (value << 1); + } + } + value = (reverseData ? reverse(value) : value); + return value; + } + +private: + + static value_type table[256]; + + static inline void fill_table() + { + for(std::size_t i = 0; i < 256; ++i) + { + table[i] = calculate_table_entry(static_cast(i)); + } + } + + struct table_filler + { + inline table_filler() + { + self_type::fill_table(); + } + }; + + static inline void init() + { + static table_filler table_filler; + } + +private: + + inline value_type read_table(byte_type pos) const + { + return table[pos]; + } + +private: + + value_type value; + +public: + + crc() + : value(initial) + { + init(); + } + + inline void processByte(byte_type byte) + { + MPT_CONSTANT_IF(reverseData) + { + value = (value >> 8) ^ read_table(static_cast((value & 0xff) ^ byte)); + } else + { + value = (value << 8) ^ read_table(static_cast(((value >> (size_bits - 8)) & 0xff) ^ byte)); + } + } + + inline value_type result() const + { + return (value ^ resultXOR); + } + +public: + + inline operator value_type () const + { + return result(); + } + + inline crc & process(char c) + { + processByte(static_cast(c)); + return *this; + } + + inline crc & process(signed char c) + { + processByte(static_cast(c)); + return *this; + } + + inline crc & process(unsigned char c) + { + processByte(static_cast(c)); + return *this; + } + + template + crc & process(InputIt beg, InputIt end) + { + for(InputIt it = beg; it != end; ++it) + { + static_assert(sizeof(*it) == 1, "1 byte type required"); + process(*it); + } + return *this; + } + + template + inline crc & process(const Container &data) + { + operator () (data.begin(), data.end()); + return *this; + } + + inline crc & operator () (char c) + { + processByte(static_cast(c)); + return *this; + } + + inline crc & operator () (signed char c) + { + processByte(static_cast(c)); + return *this; + } + + inline crc & operator () (unsigned char c) + { + processByte(static_cast(c)); + return *this; + } + + template + crc & operator () (InputIt beg, InputIt end) + { + for(InputIt it = beg; it != end; ++it) + { + static_assert(sizeof(*it) == 1, "1 byte type required"); + operator () (*it); + } + return *this; + } + + template + inline crc & operator () (const Container &data) + { + operator () (data.begin(), data.end()); + return *this; + } + + template + crc(InputIt beg, InputIt end) + : value(initial) + { + init(); + for(InputIt it = beg; it != end; ++it) + { + static_assert(sizeof(*it) == 1, "1 byte type required"); + process(*it); + } + } + + template + inline crc(const Container &data) + : value(initial) + { + init(); + process(data.begin(), data.end()); + } + +}; + +template +typename crc::value_type crc::table[256]; + +typedef crc crc16; +typedef crc crc32; +typedef crc crc32_ogg; +typedef crc crc32c; +typedef crc crc64_jones; + +} // namespace checksum + +using mpt::checksum::crc32; +using mpt::checksum::crc32_ogg; + +} // namespace mpt + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp new file mode 100644 index 000000000..ecaa58e83 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp @@ -0,0 +1,383 @@ +/* + * mptFileIO.cpp + * ------------- + * Purpose: File I/O wrappers + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptFileIO.h" + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +#include +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_FILEIO) + + + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +bool SetFilesystemCompression(HANDLE hFile) +{ + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + USHORT format = COMPRESSION_FORMAT_DEFAULT; + DWORD dummy = 0; + BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL); + return result ? true : false; +} +bool SetFilesystemCompression(int fd) +{ + if(fd < 0) + { + return false; + } + uintptr_t fhandle = _get_osfhandle(fd); + HANDLE hFile = (HANDLE)fhandle; + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + return SetFilesystemCompression(hFile); +} +#if defined(MPT_ENABLE_FILEIO_STDIO) +bool SetFilesystemCompression(FILE *file) +{ + if(!file) + { + return false; + } + int fd = _fileno(file); + if(fd == -1) + { + return false; + } + return SetFilesystemCompression(fd); +} +#endif // MPT_ENABLE_FILEIO_STDIO +bool SetFilesystemCompression(const mpt::PathString &filename) +{ + DWORD attributes = GetFileAttributesW(filename.AsNativePrefixed().c_str()); + if(attributes == INVALID_FILE_ATTRIBUTES) + { + return false; + } + if(attributes & FILE_ATTRIBUTE_COMPRESSED) + { + return true; + } + HANDLE hFile = CreateFileW(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + bool result = SetFilesystemCompression(hFile); + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + return result; +} +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +namespace mpt { + +LazyFileRef & LazyFileRef::operator = (const std::vector &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::vector &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::string &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef::operator std::vector () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, buf.data(), buf.size()); + return buf; +} + +LazyFileRef::operator std::vector () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, buf.data(), buf.size()); + return buf; +} + +LazyFileRef::operator std::string () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::string(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, buf.data(), buf.size()); + return std::string(buf.begin(), buf.end()); +} + +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER + +#if MPT_OS_WINDOWS + +CMappedFile::~CMappedFile() +{ + Close(); +} + + +bool CMappedFile::Open(const mpt::PathString &filename) +{ + m_hFile = CreateFileW( + filename.AsNativePrefixed().c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(m_hFile == INVALID_HANDLE_VALUE) + { + m_hFile = nullptr; + return false; + } + m_FileName = filename; + return true; +} + + +void CMappedFile::Close() +{ + m_FileName = mpt::PathString(); + // Unlock file + if(m_hFMap) + { + if(m_pData) + { + UnmapViewOfFile(m_pData); + m_pData = nullptr; + } + CloseHandle(m_hFMap); + m_hFMap = nullptr; + } else if(m_pData) + { + free(m_pData); + m_pData = nullptr; + } + + // Close file handle + if(m_hFile) + { + CloseHandle(m_hFile); + m_hFile = nullptr; + } +} + + +size_t CMappedFile::GetLength() +{ + LARGE_INTEGER size; + if(GetFileSizeEx(m_hFile, &size) == FALSE) + { + return 0; + } + return mpt::saturate_cast(size.QuadPart); +} + + +const mpt::byte *CMappedFile::Lock() +{ + size_t length = GetLength(); + if(!length) return nullptr; + + void *lpStream; + + HANDLE hmf = CreateFileMapping( + m_hFile, + NULL, + PAGE_READONLY, + 0, 0, + NULL); + + // Try memory-mapping first + if(hmf) + { + lpStream = MapViewOfFile( + hmf, + FILE_MAP_READ, + 0, 0, + length); + if(lpStream) + { + m_hFMap = hmf; + m_pData = lpStream; + return mpt::void_cast(lpStream); + } + CloseHandle(hmf); + hmf = nullptr; + } + + // Fallback if memory-mapping fails for some weird reason + if((lpStream = malloc(length)) == nullptr) return nullptr; + memset(lpStream, 0, length); + size_t bytesToRead = length; + size_t bytesRead = 0; + while(bytesToRead > 0) + { + DWORD chunkToRead = mpt::saturate_cast(length); + DWORD chunkRead = 0; + if(ReadFile(m_hFile, mpt::void_cast(lpStream) + bytesRead, chunkToRead, &chunkRead, NULL) == FALSE) + { + // error + free(lpStream); + return nullptr; + } + bytesRead += chunkRead; + bytesToRead -= chunkRead; + } + m_pData = lpStream; + return mpt::void_cast(lpStream); +} + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + +InputFile::InputFile() +{ + return; +} + +InputFile::InputFile(const mpt::PathString &filename) + : m_Filename(filename) +{ +#if defined(MPT_FILEREADER_STD_ISTREAM) + m_File.open(m_Filename, std::ios::binary | std::ios::in); +#else + m_File.Open(m_Filename); +#endif +} + +InputFile::~InputFile() +{ + return; +} + + +bool InputFile::Open(const mpt::PathString &filename) +{ + m_Filename = filename; +#if defined(MPT_FILEREADER_STD_ISTREAM) + m_File.open(m_Filename, std::ios::binary | std::ios::in); + return m_File.good(); +#else + return m_File.Open(m_Filename); +#endif +} + + +bool InputFile::IsValid() const +{ +#if defined(MPT_FILEREADER_STD_ISTREAM) + return m_File.good(); +#else + return m_File.IsOpen(); +#endif +} + +#if defined(MPT_FILEREADER_STD_ISTREAM) + +InputFile::ContentsRef InputFile::Get() +{ + InputFile::ContentsRef result; + result.first = &m_File; + result.second = m_File.good() ? &m_Filename : nullptr; + return result; +} + +#else + +InputFile::ContentsRef InputFile::Get() +{ + InputFile::ContentsRef result; + result.first.data = nullptr; + result.first.size = 0; + result.second = nullptr; + if(!m_File.IsOpen()) + { + return result; + } + result.first.data = m_File.Lock(); + result.first.size = m_File.GetLength(); + result.second = &m_Filename; + return result; +} + +#endif + +#else // !MPT_ENABLE_FILEIO + +MPT_MSVC_WORKAROUND_LNK4221(mptFileIO) + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h new file mode 100644 index 000000000..164ac57bb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h @@ -0,0 +1,589 @@ +/* + * mptFileIO.h + * ----------- + * Purpose: A wrapper around std::fstream, fixing VS2008 charset conversion braindamage, and enforcing usage of mpt::PathString. + * Notes : You should only ever use these wrappers instead of plain std::fstream classes. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#if defined(MPT_ENABLE_FILEIO) + +#include "../common/mptString.h" +#include "../common/mptPathString.h" +#include "../common/mptIO.h" + +#include +#include +#include +#include +#include + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_FILEIO) + + +#if defined(MPT_ENABLE_FILEIO_STDIO) + +static inline FILE * mpt_fopen(const mpt::PathString &filename, const char *mode) +{ + #if MPT_OS_WINDOWS + return _wfopen(filename.AsNativePrefixed().c_str(), mode ? mpt::ToWide(mpt::CharsetASCII, mode).c_str() : L""); + #else // !MPT_OS_WINDOWS + return fopen(filename.AsNative().c_str(), mode); + #endif // MPT_OS_WINDOWS +} + +#endif // MPT_ENABLE_FILEIO_STDIO + + +// Sets the NTFS compression attribute on the file or directory. +// Requires read and write permissions for already opened files. +// Returns true if the attribute has been set. +// In almost all cases, the return value should be ignored because most filesystems other than NTFS do not support compression. +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +bool SetFilesystemCompression(HANDLE hFile); +bool SetFilesystemCompression(int fd); +#if defined(MPT_ENABLE_FILEIO_STDIO) +bool SetFilesystemCompression(FILE *file); +#endif // MPT_ENABLE_FILEIO_STDIO +bool SetFilesystemCompression(const mpt::PathString &filename); +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +namespace mpt +{ + +#if MPT_COMPILER_GCC + +#if MPT_OS_WINDOWS +// GCC C++ library has no wchar_t overloads +#define MPT_FSTREAM_DO_CONVERSIONS +#define MPT_FSTREAM_DO_CONVERSIONS_ANSI +#endif + +#endif + +#ifdef MPT_FSTREAM_DO_CONVERSIONS +#define MPT_FSTREAM_OPEN(filename, mode) detail::fstream_open(*this, (filename), (mode)) +#else +#define MPT_FSTREAM_OPEN(filename, mode) Tbase::open((filename), (mode)) +#endif + +namespace detail +{ + +template +inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode) +{ +#if defined(MPT_FSTREAM_DO_CONVERSIONS_ANSI) + base.open(mpt::ToCharset(mpt::CharsetLocale, filename.AsNative()).c_str(), mode); +#else + base.open(filename.AsNativePrefixed().c_str(), mode); +#endif +} + +#ifdef MPT_FSTREAM_DO_CONVERSIONS + +template +inline void fstream_open(Tbase & base, const std::wstring & filename, std::ios_base::openmode mode) +{ + detail::fstream_open(base, mpt::PathString::FromWide(filename), mode); +} + +template +inline void fstream_open(Tbase & base, const wchar_t * filename, std::ios_base::openmode mode) +{ + detail::fstream_open(base, mpt::PathString::FromWide(filename ? std::wstring(filename) : std::wstring()), mode); +} + +template +inline void fstream_open(Tbase & base, const std::string & filename, std::ios_base::openmode mode) +{ + detail::fstream_open(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename)), mode); +} + +template +inline void fstream_open(Tbase & base, const char * filename, std::ios_base::openmode mode) +{ + detail::fstream_open(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename ? std::string(filename) : std::string())), mode); +} + +#endif + +} // namespace detail + +class fstream + : public std::fstream +{ +private: + typedef std::fstream Tbase; +public: + fstream() {} + fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#if MPT_OS_WINDOWS + MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#endif +}; + +class ifstream + : public std::ifstream +{ +private: + typedef std::ifstream Tbase; +public: + ifstream() {} + ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) + { + detail::fstream_open(*this, filename, mode); + } + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) + { + detail::fstream_open(*this, filename, mode); + } + MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#if MPT_OS_WINDOWS + MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#endif +}; + +class ofstream + : public std::ofstream +{ +private: + typedef std::ofstream Tbase; +public: + ofstream() {} + ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#if MPT_OS_WINDOWS + MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename, mode); + } + MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) + { + MPT_FSTREAM_OPEN(filename.c_str(), mode); + } +#endif +}; + +#undef MPT_FSTREAM_OPEN + + + +// LazyFileRef is a simple reference to an on-disk file by the means of a +// filename which allows easy assignment of the whole file contents to and from +// byte buffers. +class LazyFileRef { +private: + const mpt::PathString m_Filename; +public: + LazyFileRef(const mpt::PathString &filename) + : m_Filename(filename) + { + return; + } +public: + LazyFileRef & operator = (const std::vector &data); + LazyFileRef & operator = (const std::vector &data); + LazyFileRef & operator = (const std::string &data); + operator std::vector () const; + operator std::vector () const; + operator std::string () const; +}; + + + +#if defined(MPT_ENABLE_FILEIO_STDIO) + +// class FILE_ostream, FILE_output_streambuf and FILE_output_buffered_streambuf +// provide a portable way of wrapping a std::ostream around an FILE* opened for output. +// They offer similar functionality to the badly documented +// MSVC std::fstream(FILE*) constructor or GCC libstdc++ __gnu_cxx::stdio_sync_filebuf class, +// and, for other compilers, provide a race-free alternative to +// closing the FILE* and opening it again as a std::ofstream. +// +// Only output functionality is implemented because we have no need for an input wrapper. +// +// During the whole lifetime of the iostream wrappers, the FILE* object is assumend to be +// either +// - NULL +// or +// - valid +// - opened for writing in non-append mode +// - opened in binary mode +// - seekable +// Some of these preconditions cannot be verified, +// and even the others do not get verified. +// Behaviour in case of any unmet preconditions is undefined. +// +// The buffered streambuf and the ostream use a buffer of 64KiB by default. +// +// For FILE_output_streambuf, coherency with the underlying FILE* is always guaranteed. +// For FILE_ostream and FILE_output_buffered_streambuf, coherence is only +// guaranteed when flush() or pubsync() get called. +// The constructors and destructors take care to not violate coherency. +// When mixing FILE* and iostream I/O during the lifetime of the iostream objects, +// the user is responsible for providing coherency via the appropriate +// flush and sync functions. +// Behaviour in case of incoherent access is undefined. + + +class FILE_output_streambuf : public std::streambuf +{ +public: + typedef std::streambuf::char_type char_type; + typedef std::streambuf::traits_type traits_type; + typedef traits_type::int_type int_type; + typedef traits_type::pos_type pos_type; + typedef traits_type::off_type off_type; +protected: + FILE *f; +public: + FILE_output_streambuf(FILE *f) + : f(f) + { + return; + } + ~FILE_output_streambuf() + { + return; + } +protected: + virtual int_type overflow(int_type ch) + { + if(!mpt::IO::IsValid(f)) + { + return traits_type::eof(); + } + if(traits_type::eq_int_type(ch, traits_type::eof())) + { + return traits_type::eof(); + } + char_type c = traits_type::to_char_type(ch); + if(!mpt::IO::WriteRaw(f, &c, 1)) + { + return traits_type::eof(); + } + return ch; + } + virtual int sync() + { + if(!mpt::IO::IsValid(f)) + { + return -1; + } + if(!mpt::IO::Flush(f)) + { + return -1; + } + return 0; + } + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which) + { + if(!mpt::IO::IsValid(f)) + { + return pos_type(off_type(-1)); + } + return seekoff(pos, std::ios_base::beg, which); + } + virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) + { + if(!mpt::IO::IsValid(f)) + { + return pos_type(off_type(-1)); + } + if(which & std::ios_base::in) + { + return pos_type(off_type(-1)); + } + if(!(which & std::ios_base::out)) + { + return pos_type(off_type(-1)); + } + mpt::IO::Offset oldpos = mpt::IO::TellWrite(f); + if(dir == std::ios_base::beg) + { + if(!mpt::IO::SeekAbsolute(f, off)) + { + mpt::IO::SeekAbsolute(f, oldpos); + return pos_type(off_type(-1)); + } + } else if(dir == std::ios_base::cur) + { + if(!mpt::IO::SeekRelative(f, off)) + { + mpt::IO::SeekAbsolute(f, oldpos); + return pos_type(off_type(-1)); + } + } else if(dir == std::ios_base::end) + { + if(!(mpt::IO::SeekEnd(f) && mpt::IO::SeekRelative(f, off))) + { + mpt::IO::SeekAbsolute(f, oldpos); + return pos_type(off_type(-1)); + } + } else + { + return pos_type(off_type(-1)); + } + mpt::IO::Offset newpos = mpt::IO::TellWrite(f); + if(!mpt::IO::OffsetFits(newpos)) + { + mpt::IO::SeekAbsolute(f, oldpos); + return pos_type(off_type(-1)); + } + return pos_type(static_cast(newpos)); + } +}; // class FILE_output_streambuf + + +class FILE_output_buffered_streambuf : public FILE_output_streambuf +{ +public: + typedef std::streambuf::char_type char_type; + typedef std::streambuf::traits_type traits_type; + typedef traits_type::int_type int_type; + typedef traits_type::pos_type pos_type; + typedef traits_type::off_type off_type; +private: + typedef FILE_output_streambuf Tparent; + std::vector buf; +public: + FILE_output_buffered_streambuf(FILE *f, std::size_t bufSize = 64*1024) + : FILE_output_streambuf(f) + , buf((bufSize > 0) ? bufSize : 1) + { + setp(buf.data(), buf.data() + buf.size()); + } + ~FILE_output_buffered_streambuf() + { + if(!mpt::IO::IsValid(f)) + { + return; + } + WriteOut(); + } +private: + bool IsDirty() const + { + return ((pptr() - pbase()) > 0); + } + bool WriteOut() + { + std::ptrdiff_t n = pptr() - pbase(); + std::ptrdiff_t left = n; + while(left > 0) + { + int backchunk = mpt::saturate_cast(-left); + pbump(backchunk); + left += backchunk; + } + return mpt::IO::WriteRaw(f, pbase(), n); + } +protected: + virtual int_type overflow(int_type ch) + { + if(!mpt::IO::IsValid(f)) + { + return traits_type::eof(); + } + if(traits_type::eq_int_type(ch, traits_type::eof())) + { + return traits_type::eof(); + } + if(!WriteOut()) + { + return traits_type::eof(); + } + char_type c = traits_type::to_char_type(ch); + *pptr() = c; + pbump(1); + return ch; + } + virtual int sync() + { + if(!mpt::IO::IsValid(f)) + { + return -1; + } + if(!WriteOut()) + { + return -1; + } + return Tparent::sync(); + } + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which) + { + if(!mpt::IO::IsValid(f)) + { + return pos_type(off_type(-1)); + } + if(!WriteOut()) + { + return pos_type(off_type(-1)); + } + return Tparent::seekpos(pos, which); + } + virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) + { + if(!mpt::IO::IsValid(f)) + { + return pos_type(off_type(-1)); + } + if(!WriteOut()) + { + return pos_type(off_type(-1)); + } + return Tparent::seekoff(off, dir, which); + } +}; // class FILE_output_buffered_streambuf + + +class FILE_ostream : public std::ostream { +private: + FILE *f; + FILE_output_buffered_streambuf buf; +public: + FILE_ostream(FILE *f, std::size_t bufSize = 64*1024) + : std::ostream(&buf) + , f(f) + , buf(f, bufSize) + { + if(mpt::IO::IsValid(f)) mpt::IO::Flush(f); + } + ~FILE_ostream() + { + flush(); + buf.pubsync(); + if(mpt::IO::IsValid(f)) mpt::IO::Flush(f); + } +}; // class FILE_ostream + +#endif // MPT_ENABLE_FILEIO_STDIO + + +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +class CMappedFile +{ +protected: + HANDLE m_hFile; + HANDLE m_hFMap; + void *m_pData; + mpt::PathString m_FileName; + +public: + CMappedFile() : m_hFile(nullptr), m_hFMap(nullptr), m_pData(nullptr) { } + ~CMappedFile(); + +public: + bool Open(const mpt::PathString &filename); + bool IsOpen() const { return m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE; } + const mpt::PathString * GetpFilename() const { return &m_FileName; } + void Close(); + size_t GetLength(); + const mpt::byte *Lock(); +}; +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +class InputFile +{ +private: + mpt::PathString m_Filename; + #ifdef MPT_FILEREADER_STD_ISTREAM + mpt::ifstream m_File; + #else + CMappedFile m_File; + #endif +public: + InputFile(); + InputFile(const mpt::PathString &filename); + ~InputFile(); + bool Open(const mpt::PathString &filename); + bool IsValid() const; +#if defined(MPT_FILEREADER_STD_ISTREAM) + typedef std::pair ContentsRef; +#else + struct Data + { + const mpt::byte *data; + std::size_t size; + }; + typedef std::pair ContentsRef; +#endif + InputFile::ContentsRef Get(); +}; + + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp new file mode 100644 index 000000000..ba42afaf6 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp @@ -0,0 +1,827 @@ +/* + * mptIO.cpp + * --------- + * Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams. + * Notes : This is work-in-progress. + * Some useful functions for reading and writing are still missing. + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "mptIO.h" + +#include +#include +#include +#include +#if MPT_COMPILER_MSVC +#include +#endif // MPT_COMPILER_MSVC + +#if defined(MPT_ENABLE_FILEIO_STDIO) +#include +#include +#endif // MPT_ENABLE_FILEIO_STDIO + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt { + +namespace IO { + + +#if MPT_COMPILER_MSVC + +// MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and +// std::stringstream) fail seekoff() when the stringbuf is currently empty. +// seekoff() can get called via tell*() or seek*() iostream members. tell*() has +// been special cased from VS2010 onwards to handle this specific case and +// changed to not fail when the stringbuf is empty. +// In addition to using out own wrapper around std::stringstream and +// std::stringbuf, we also work-around the plain native type's problem in case +// we get handed such an object from third party code. This mitigation of course +// requires using our consolidated and normalized IO functions. +// We use the following work-around strategy: +// * If the stream is already in failed state, we do not do any work-around +// and bail out early. +// * If the underlying streambuf is not a std::stringbuf, the work-around is +// not necessary and we skip it. +// * If querying the current position does not fail and returns a +// position > 0, the underlying stringbuf is not empty and we also bail out. +// * Otherwise, we actually query the string contained in the stringbuf to be +// empty. This operation is slow as it has to copy the string into a +// temporary. +// Note, however, that this is only ever necessary if the current position +// is 0. If it always has been 0, the stringbuf will be empty anyway and the +// copy does not cost anything measurable. If it got seeked to position 0, +// we have to pay the price. However, this should be relatively uncommmon in +// pratice. +// * The actual work-around consists of performing or emulating the requested +// operation and resetting the failed state afterwards. + +static bool StreamIsStringStreamAndValidAndEmpty(std::ostream & f) +{ + if(f.fail() || !f.rdbuf()) + { // failed + return false; + } + if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) + { // no stringbuf + return false; + } + std::streampos pos = f.tellp(); + f.clear(f.rdstate() & ~std::ios::failbit); + if(pos != std::streampos(-1) && pos > 0) + { // if the position is not 0, the streambuf is not empty + return false; + } + return dynamic_cast(f.rdbuf())->str().empty(); // slow +} + +static bool StreamIsStringStreamAndValidAndEmpty(std::istream & f) +{ + if(f.fail() || !f.rdbuf()) + { // failed + return false; + } + if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) + { // no stringbuf + return false; + } + std::streampos pos = f.tellg(); + f.clear(f.rdstate() & ~std::ios::failbit); + if(pos != std::streampos(-1) && pos > 0) + { // if the position is not 0, the streambuf is not empty + return false; + } + return dynamic_cast(f.rdbuf())->str().empty(); // slow +} + +static bool StreamIsStringStreamAndValidAndEmpty(std::iostream & f) +{ + if(f.fail() || !f.rdbuf()) + { // failed + return false; + } + if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) + { // no stringbuf + return false; + } + std::streampos ipos = f.tellg(); + f.clear(f.rdstate() & ~std::ios::failbit); + std::streampos opos = f.tellp(); + f.clear(f.rdstate() & ~std::ios::failbit); + if((ipos != std::streampos(-1) && ipos > 0) || (opos != std::streampos(-1) && opos > 0)) + { // if the position is not 0, the streambuf is not empty + return false; + } + return dynamic_cast(f.rdbuf())->str().empty(); // slow +} + +#endif // MPT_COMPILER_MSVC + +//STATIC_ASSERT(sizeof(std::streamoff) == 8); // Assert 64bit file support. +bool IsValid(std::ostream & f) { return !f.fail(); } +bool IsValid(std::istream & f) { return !f.fail(); } +bool IsValid(std::iostream & f) { return !f.fail(); } +IO::Offset TellRead(std::istream & f) +{ + return f.tellg(); +} +IO::Offset TellWrite(std::ostream & f) +{ + return f.tellp(); +} +bool SeekBegin(std::ostream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { // VS std::stringbuf fail seek when the internal buffer is empty. Work-around it in case the stream is not already in failed state. + f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekp(0); return !f.fail(); +} +bool SeekBegin(std::istream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekg(0); return !f.fail(); +} +bool SeekBegin(std::iostream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekg(0); f.seekp(0); return !f.fail(); +} +bool SeekEnd(std::ostream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekp(0, std::ios::end); return !f.fail(); +} +bool SeekEnd(std::istream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekg(0, std::ios::end); return !f.fail(); +} +bool SeekEnd(std::iostream & f) +{ + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + #endif + f.seekg(0, std::ios::end); f.seekp(0, std::ios::end); return !f.fail(); +} +bool SeekAbsolute(std::ostream & f, IO::Offset pos) +{ + if(!OffsetFits(pos)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(pos == 0) + { + f.seekp(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekp(static_cast(pos), std::ios::beg); return !f.fail(); +} +bool SeekAbsolute(std::istream & f, IO::Offset pos) +{ + if(!OffsetFits(pos)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(pos == 0) + { + f.seekg(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekg(static_cast(pos), std::ios::beg); return !f.fail(); +} +bool SeekAbsolute(std::iostream & f, IO::Offset pos) +{ + if(!OffsetFits(pos)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(pos == 0) + { + f.seekg(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekg(static_cast(pos), std::ios::beg); f.seekp(static_cast(pos), std::ios::beg); return !f.fail(); +} +bool SeekRelative(std::ostream & f, IO::Offset off) +{ + if(!OffsetFits(off)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(off == 0) + { + f.seekp(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekp(static_cast(off), std::ios::cur); return !f.fail(); +} +bool SeekRelative(std::istream & f, IO::Offset off) +{ + if(!OffsetFits(off)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(off == 0) + { + f.seekg(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekg(static_cast(off), std::ios::cur); return !f.fail(); +} +bool SeekRelative(std::iostream & f, IO::Offset off) +{ + if(!OffsetFits(off)) { return false; } + #if MPT_COMPILER_MSVC + if(StreamIsStringStreamAndValidAndEmpty(f)) + { + if(off == 0) + { + f.seekg(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; + } + } + #endif + f.seekg(static_cast(off), std::ios::cur); f.seekp(static_cast(off), std::ios::cur); return !f.fail(); +} +IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size) { return f.read(mpt::byte_cast(data), size) ? f.gcount() : std::streamsize(0); } +bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size) { f.write(mpt::byte_cast(data), size); return !f.fail(); } +bool IsEof(std::istream & f) { return f.eof(); } +bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } + + + +#if defined(MPT_ENABLE_FILEIO_STDIO) + +bool IsValid(FILE* & f) { return f != NULL; } + +#if MPT_COMPILER_MSVC + +IO::Offset TellRead(FILE* & f) { return _ftelli64(f); } +IO::Offset TellWrite(FILE* & f) { return _ftelli64(f); } +bool SeekBegin(FILE* & f) { return _fseeki64(f, 0, SEEK_SET) == 0; } +bool SeekEnd(FILE* & f) { return _fseeki64(f, 0, SEEK_END) == 0; } +bool SeekAbsolute(FILE* & f, IO::Offset pos) { return _fseeki64(f, pos, SEEK_SET) == 0; } +bool SeekRelative(FILE* & f, IO::Offset off) { return _fseeki64(f, off, SEEK_CUR) == 0; } + +#elif defined(_POSIX_SOURCE) && (_POSIX_SOURCE > 0) + +//STATIC_ASSERT(sizeof(off_t) == 8); +IO::Offset TellRead(FILE* & f) { return ftello(f); } +IO::Offset TellWrite(FILE* & f) { return ftello(f); } +bool SeekBegin(FILE* & f) { return fseeko(f, 0, SEEK_SET) == 0; } +bool SeekEnd(FILE* & f) { return fseeko(f, 0, SEEK_END) == 0; } +bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits(pos) && (fseek(f, mpt::saturate_cast(pos), SEEK_SET) == 0); } +bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits(off) && (fseek(f, mpt::saturate_cast(off), SEEK_CUR) == 0); } + +#else + +//STATIC_ASSERT(sizeof(long) == 8); // Fails on 32bit non-POSIX systems for now. +IO::Offset TellRead(FILE* & f) { return ftell(f); } +IO::Offset TellWrite(FILE* & f) { return ftell(f); } +bool SeekBegin(FILE* & f) { return fseek(f, 0, SEEK_SET) == 0; } +bool SeekEnd(FILE* & f) { return fseek(f, 0, SEEK_END) == 0; } +bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits(pos) && (fseek(f, mpt::saturate_cast(pos), SEEK_SET) == 0); } +bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits(off) && (fseek(f, mpt::saturate_cast(off), SEEK_CUR) == 0); } + +#endif + +IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size) { return fread(mpt::void_cast(data), 1, size, f); } +bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size) { return fwrite(mpt::void_cast(data), 1, size, f) == size; } +bool IsEof(FILE * & f) { return feof(f) != 0; } +bool Flush(FILE* & f) { return fflush(f) == 0; } + +#endif // MPT_ENABLE_FILEIO_STDIO + + + +} // namespace IO + +} // namespace mpt + + + +#if defined(MPT_FILEREADER_STD_ISTREAM) + + + +FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength) + : streamLength(streamLength) + , cached(false) +{ + return; +} + +FileDataContainerSeekable::~FileDataContainerSeekable() +{ + return; +} +void FileDataContainerSeekable::CacheStream() const +{ + if(cached) + { + return; + } + cache.resize(streamLength); + InternalRead(cache.data(), 0, streamLength); + cached = true; +} + +bool FileDataContainerSeekable::IsValid() const +{ + return true; +} + +bool FileDataContainerSeekable::HasFastGetLength() const +{ + return true; +} + +bool FileDataContainerSeekable::HasPinnedView() const +{ + return cached; +} + +const mpt::byte *FileDataContainerSeekable::GetRawData() const +{ + CacheStream(); + return cache.data(); +} + +IFileDataContainer::off_t FileDataContainerSeekable::GetLength() const +{ + return streamLength; +} + +IFileDataContainer::off_t FileDataContainerSeekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +{ + if(cached) + { + IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cache.size()) - pos, count); + std::copy(cache.begin() + pos, cache.begin() + pos + cache_avail, dst); + return cache_avail; + } else + { + return InternalRead(dst, pos, count); + } +} + + + +bool FileDataContainerStdStreamSeekable::IsSeekable(std::istream *stream) +{ + stream->clear(); + std::streampos oldpos = stream->tellg(); + if(stream->fail() || oldpos == std::streampos(-1)) + { + stream->clear(); + return false; + } + stream->seekg(0, std::ios::beg); + if(stream->fail()) + { + stream->clear(); + stream->seekg(oldpos); + stream->clear(); + return false; + } + stream->seekg(0, std::ios::end); + if(stream->fail()) + { + stream->clear(); + stream->seekg(oldpos); + stream->clear(); + return false; + } + std::streampos length = stream->tellg(); + if(stream->fail() || length == std::streampos(-1)) + { + stream->clear(); + stream->seekg(oldpos); + stream->clear(); + return false; + } + stream->seekg(oldpos); + stream->clear(); + return true; +} + +IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::istream *stream) +{ + stream->clear(); + std::streampos oldpos = stream->tellg(); + stream->seekg(0, std::ios::end); + std::streampos length = stream->tellg(); + stream->seekg(oldpos); + return mpt::saturate_cast(static_cast(length)); +} + +FileDataContainerStdStreamSeekable::FileDataContainerStdStreamSeekable(std::istream *s) + : FileDataContainerSeekable(GetLength(s)) + , stream(s) +{ + return; +} + +FileDataContainerStdStreamSeekable::~FileDataContainerStdStreamSeekable() +{ + return; +} + +IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +{ + stream->clear(); // tellg needs eof and fail bits unset + std::streampos currentpos = stream->tellg(); + if(currentpos == std::streampos(-1) || static_cast(pos) != currentpos) + { // inefficient istream implementations might invalidate their buffer when seeking, even when seeking to the current position + stream->seekg(pos); + } + stream->read(mpt::byte_cast(dst), count); + return static_cast(stream->gcount()); +} + + +FileDataContainerUnseekable::FileDataContainerUnseekable() + : cachesize(0), streamFullyCached(false) +{ + return; +} + +FileDataContainerUnseekable::~FileDataContainerUnseekable() +{ + return; +} + +void FileDataContainerUnseekable::EnsureCacheBuffer(std::size_t requiredbuffersize) const +{ + if(cache.size() >= cachesize + requiredbuffersize) + { + return; + } + if(cache.size() == 0) + { + cache.resize(Util::AlignUp(cachesize + requiredbuffersize, BUFFER_SIZE)); + } else if(Util::ExponentialGrow(cache.size()) < cachesize + requiredbuffersize) + { + cache.resize(Util::AlignUp(cachesize + requiredbuffersize, BUFFER_SIZE)); + } else + { + cache.resize(Util::ExponentialGrow(cache.size())); + } +} + +void FileDataContainerUnseekable::CacheStream() const +{ + if(streamFullyCached) + { + return; + } + while(!InternalEof()) + { + EnsureCacheBuffer(BUFFER_SIZE); + std::size_t readcount = InternalRead(&cache[cachesize], BUFFER_SIZE); + cachesize += readcount; + } + streamFullyCached = true; +} + +void FileDataContainerUnseekable::CacheStreamUpTo(off_t pos, off_t length) const +{ + if(streamFullyCached) + { + return; + } + if(length > std::numeric_limits::max() - pos) + { + length = std::numeric_limits::max() - pos; + } + std::size_t target = mpt::saturate_cast(pos + length); + if(target <= cachesize) + { + return; + } + std::size_t alignedpos = Util::AlignUp(target, QUANTUM_SIZE); + std::size_t needcount = alignedpos - cachesize; + EnsureCacheBuffer(needcount); + std::size_t readcount = InternalRead(&cache[cachesize], alignedpos - cachesize); + cachesize += readcount; + if(!InternalEof()) + { + // can read further + return; + } + streamFullyCached = true; +} + +void FileDataContainerUnseekable::ReadCached(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +{ + std::copy(cache.begin() + pos, cache.begin() + pos + count, dst); +} + +bool FileDataContainerUnseekable::IsValid() const +{ + return true; +} + +bool FileDataContainerUnseekable::HasFastGetLength() const +{ + return false; +} + +bool FileDataContainerUnseekable::HasPinnedView() const +{ + return true; // we have the cache which is required for seeking anyway +} + +const mpt::byte *FileDataContainerUnseekable::GetRawData() const +{ + CacheStream(); + return cache.data(); +} + +IFileDataContainer::off_t FileDataContainerUnseekable::GetLength() const +{ + CacheStream(); + return cachesize; +} + +IFileDataContainer::off_t FileDataContainerUnseekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +{ + CacheStreamUpTo(pos, count); + if(pos >= IFileDataContainer::off_t(cachesize)) + { + return 0; + } + IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cachesize) - pos, count); + ReadCached(dst, pos, cache_avail); + return cache_avail; +} + +bool FileDataContainerUnseekable::CanRead(IFileDataContainer::off_t pos, IFileDataContainer::off_t length) const +{ + CacheStreamUpTo(pos, length); + if((pos == IFileDataContainer::off_t(cachesize)) && (length == 0)) + { + return true; + } + if(pos >= IFileDataContainer::off_t(cachesize)) + { + return false; + } + return length <= IFileDataContainer::off_t(cachesize) - pos; +} + +IFileDataContainer::off_t FileDataContainerUnseekable::GetReadableLength(IFileDataContainer::off_t pos, IFileDataContainer::off_t length) const +{ + CacheStreamUpTo(pos, length); + if(pos >= cachesize) + { + return 0; + } + return std::min(cachesize - pos, length); +} + + + +FileDataContainerStdStream::FileDataContainerStdStream(std::istream *s) + : stream(s) +{ + return; +} + +FileDataContainerStdStream::~FileDataContainerStdStream() +{ + return; +} + +bool FileDataContainerStdStream::InternalEof() const +{ + if(*stream) + { + return false; + } else + { + return true; + } +} + +IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(mpt::byte *dst, off_t count) const +{ + stream->read(mpt::byte_cast(dst), count); + return static_cast(stream->gcount()); +} + + + +#if defined(MPT_FILEREADER_CALLBACK_STREAM) + + +bool FileDataContainerCallbackStreamSeekable::IsSeekable(CallbackStream stream) +{ + if(!stream.stream) + { + return false; + } + if(!stream.seek) + { + return false; + } + if(!stream.tell) + { + return false; + } + int64 oldpos = stream.tell(stream.stream); + if(oldpos < 0) + { + return false; + } + if(stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return false; + } + if(stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return false; + } + int64 length = stream.tell(stream.stream); + if(length < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return false; + } + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return true; +} + +IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::GetLength(CallbackStream stream) +{ + if(!stream.stream) + { + return 0; + } + if(!stream.seek) + { + return false; + } + if(!stream.tell) + { + return false; + } + int64 oldpos = stream.tell(stream.stream); + if(oldpos < 0) + { + return 0; + } + if(stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return 0; + } + if(stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return 0; + } + int64 length = stream.tell(stream.stream); + if(length < 0) + { + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return 0; + } + stream.seek(stream.stream, oldpos, CallbackStream::SeekSet); + return mpt::saturate_cast(length); +} + +FileDataContainerCallbackStreamSeekable::FileDataContainerCallbackStreamSeekable(CallbackStream s) + : FileDataContainerSeekable(GetLength(s)) + , stream(s) +{ + return; +} + +FileDataContainerCallbackStreamSeekable::~FileDataContainerCallbackStreamSeekable() +{ + return; +} + +IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +{ + if(!stream.read) + { + return 0; + } + if(stream.seek(stream.stream, pos, CallbackStream::SeekSet) < 0) + { + return 0; + } + int64 totalread = 0; + while(count > 0) + { + int64 readcount = stream.read(stream.stream, dst, count); + if(readcount <= 0) + { + break; + } + dst += static_cast(readcount); + count -= static_cast(readcount); + totalread += readcount; + } + return static_cast(totalread); +} + + + +FileDataContainerCallbackStream::FileDataContainerCallbackStream(CallbackStream s) + : FileDataContainerUnseekable() + , stream(s) + , eof_reached(false) +{ + return; +} + +FileDataContainerCallbackStream::~FileDataContainerCallbackStream() +{ + return; +} + +bool FileDataContainerCallbackStream::InternalEof() const +{ + return eof_reached; +} + +IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byte *dst, off_t count) const +{ + if(eof_reached) + { + return 0; + } + if(!stream.read) + { + eof_reached = true; + return 0; + } + int64 totalread = 0; + while(count > 0) + { + int64 readcount = stream.read(stream.stream, dst, count); + if(readcount <= 0) + { + eof_reached = true; + break; + } + dst += static_cast(readcount); + count -= static_cast(readcount); + totalread += readcount; + } + return static_cast(totalread); +} + + +#endif // MPT_FILEREADER_CALLBACK_STREAM + + +#endif + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h new file mode 100644 index 000000000..4899124b5 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h @@ -0,0 +1,926 @@ +/* + * mptIO.h + * ------- + * Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams. + * Notes : This is work-in-progress. + * Some useful functions for reading and writing are still missing. + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "../common/typedefs.h" +#include "../common/mptTypeTraits.h" +#include "../common/Endianness.h" +#include +#include +#include +#include + +#if defined(MPT_ENABLE_FILEIO_STDIO) +#include +#include +#endif // MPT_ENABLE_FILEIO_STDIO + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt { + +namespace IO { + +typedef int64 Offset; + +static const std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage +static const std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap +static const std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O +static const std::size_t BUFFERSIZE_LARGE = 1024 * 1024; + + + +// Returns true iff 'off' fits into 'Toff'. +template < typename Toff > +inline bool OffsetFits(IO::Offset off) +{ + return (static_cast(mpt::saturate_cast(off)) == off); +} + + + +bool IsValid(std::ostream & f); +bool IsValid(std::istream & f); +bool IsValid(std::iostream & f); +IO::Offset TellRead(std::istream & f); +IO::Offset TellWrite(std::ostream & f); +bool SeekBegin(std::ostream & f); +bool SeekBegin(std::istream & f); +bool SeekBegin(std::iostream & f); +bool SeekEnd(std::ostream & f); +bool SeekEnd(std::istream & f); +bool SeekEnd(std::iostream & f); +bool SeekAbsolute(std::ostream & f, IO::Offset pos); +bool SeekAbsolute(std::istream & f, IO::Offset pos); +bool SeekAbsolute(std::iostream & f, IO::Offset pos); +bool SeekRelative(std::ostream & f, IO::Offset off); +bool SeekRelative(std::istream & f, IO::Offset off); +bool SeekRelative(std::iostream & f, IO::Offset off); +IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size); +bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size); +bool IsEof(std::istream & f); +bool Flush(std::ostream & f); + + + +#if defined(MPT_ENABLE_FILEIO_STDIO) + +bool IsValid(FILE* & f); +IO::Offset TellRead(FILE* & f); +IO::Offset TellWrite(FILE* & f); +bool SeekBegin(FILE* & f); +bool SeekEnd(FILE* & f); +bool SeekAbsolute(FILE* & f, IO::Offset pos); +bool SeekRelative(FILE* & f, IO::Offset off); +IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size); +bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size); +bool IsEof(FILE * & f); +bool Flush(FILE* & f); + +#endif // MPT_ENABLE_FILEIO_STDIO + + + +template bool IsValid(std::pair, IO::Offset> & f) +{ + return (f.second >= 0); +} +template IO::Offset TellRead(std::pair, IO::Offset> & f) +{ + return f.second; +} +template IO::Offset TellWrite(std::pair, IO::Offset> & f) +{ + return f.second; +} +template bool SeekBegin(std::pair, IO::Offset> & f) +{ + f.second = 0; + return true; +} +template bool SeekEnd(std::pair, IO::Offset> & f) +{ + f.second = f.first.size(); + return true; +} +template bool SeekAbsolute(std::pair, IO::Offset> & f, IO::Offset pos) +{ + f.second = pos; + return true; +} +template bool SeekRelative(std::pair, IO::Offset> & f, IO::Offset off) +{ + if(f.second < 0) + { + return false; + } + f.second += off; + return true; +} +template IO::Offset ReadRawImpl(std::pair, IO::Offset> & f, mpt::byte * data, std::size_t size) +{ + if(f.second < 0) + { + return 0; + } + if(f.second >= static_cast(f.first.size())) + { + return 0; + } + std::size_t num = mpt::saturate_cast(std::min(f.first.size() - f.second, size)); + std::copy(mpt::byte_cast(f.first.data() + f.second), mpt::byte_cast(f.first.data() + f.second + num), data); + f.second += num; + return num; +} +template bool WriteRawImpl(std::pair, IO::Offset> & f, const mpt::byte * data, std::size_t size) +{ + if(f.second < 0) + { + return false; + } + if(f.second >= static_cast(f.first.size())) + { + return false; + } + std::size_t num = mpt::saturate_cast(std::min(f.first.size() - f.second, size)); + if(num != size) + { + return false; + } + std::copy(data, data + num, mpt::byte_cast(f.first.data() + f.second)); + f.second += num; + return true; +} +template bool IsEof(std::pair, IO::Offset> & f) +{ + return (f.second >= static_cast(f.first.size())); +} +template bool Flush(std::pair, IO::Offset> & f) +{ + MPT_UNREFERENCED_PARAMTER(f); + return true; +} + + + +template +inline IO::Offset ReadRaw(Tfile & f, Tbyte * data, std::size_t size) +{ + return IO::ReadRawImpl(f, mpt::byte_cast(data), size); +} + +template +inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) +{ + return IO::WriteRawImpl(f, mpt::byte_cast(data), size); +} + +template +inline bool Read(Tfile & f, Tbinary & v) +{ + return IO::ReadRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary)) == sizeof(Tbinary); +} + +template +inline bool Write(Tfile & f, const Tbinary & v) +{ + return IO::WriteRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary)); +} + +template +inline bool WritePartial(Tfile & f, const T & v, size_t size = sizeof(T)) +{ + MPT_ASSERT(size <= sizeof(T)); + return IO::WriteRaw(f, mpt::as_raw_memory(v), size); +} + +template +inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) +{ + bool result = false; + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + mpt::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + const IO::Offset readResult = IO::ReadRaw(f, bytes, std::min(size, sizeof(T))); + if(readResult < 0) + { + result = false; + } else + { + result = (static_cast(readResult) == std::min(size, sizeof(T))); + } + #ifdef MPT_PLATFORM_BIG_ENDIAN + std::reverse(bytes, bytes + sizeof(T)); + #endif + std::memcpy(&v, bytes, sizeof(T)); + return result; +} + +template +inline bool ReadIntLE(Tfile & f, T & v) +{ + bool result = false; + STATIC_ASSERT(std::numeric_limits::is_integer); + mpt::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); + if(readResult < 0) + { + result = false; + } else + { + result = (static_cast(readResult) == sizeof(T)); + } + T val = 0; + std::memcpy(&val, bytes, sizeof(T)); + v = SwapBytesLE(val); + return result; +} + +template +inline bool ReadIntBE(Tfile & f, T & v) +{ + bool result = false; + STATIC_ASSERT(std::numeric_limits::is_integer); + mpt::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); + if(readResult < 0) + { + result = false; + } else + { + result = (static_cast(readResult) == sizeof(T)); + } + T val = 0; + std::memcpy(&val, bytes, sizeof(T)); + v = SwapBytesBE(val); + return result; +} + +template +inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) +{ + bool result = true; + mpt::byte byte = 0; + std::size_t additionalBytes = 0; + v = 0; + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + additionalBytes = (byte & 0x01); + v = byte >> 1; + for(std::size_t i = 0; i < additionalBytes; ++i) + { + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + v |= (static_cast(byte) << (((i+1)*8) - 1)); + } + return result; +} + +template +inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) +{ + bool result = true; + mpt::byte byte = 0; + std::size_t additionalBytes = 0; + v = 0; + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + additionalBytes = (byte & 0x03); + v = byte >> 2; + for(std::size_t i = 0; i < additionalBytes; ++i) + { + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + v |= (static_cast(byte) << (((i+1)*8) - 2)); + } + return result; +} + +template +inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) +{ + bool result = true; + mpt::byte byte = 0; + std::size_t additionalBytes = 0; + v = 0; + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + additionalBytes = (1 << (byte & 0x03)) - 1; + v = byte >> 2; + for(std::size_t i = 0; i < additionalBytes; ++i) + { + byte = 0; + if(!IO::ReadIntLE(f, byte)) result = false; + v |= (static_cast(byte) << (((i+1)*8) - 2)); + } + return result; +} + +template +inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits::max()) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + str.clear(); + Tsize size = 0; + if(!mpt::IO::ReadIntLE(f, size)) + { + return false; + } + if(size > maxSize) + { + return false; + } + for(Tsize i = 0; i != size; ++i) + { + char c = '\0'; + if(!mpt::IO::ReadIntLE(f, c)) + { + return false; + } + str.push_back(c); + } + return true; +} + + +template +inline bool WriteIntLE(Tfile & f, const T v) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + const T val = SwapBytesLE(v); + mpt::byte bytes[sizeof(T)]; + std::memcpy(bytes, &val, sizeof(T)); + return IO::WriteRaw(f, bytes, sizeof(T)); +} + +template +inline bool WriteIntBE(Tfile & f, const T v) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + const T val = SwapBytesBE(v); + mpt::byte bytes[sizeof(T)]; + std::memcpy(bytes, &val, sizeof(T)); + return IO::WriteRaw(f, bytes, sizeof(T)); +} + +template +inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t minSize = 0, std::size_t maxSize = 0) +{ + MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2); + MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2); + MPT_ASSERT(maxSize == 0 || maxSize >= minSize); + if(v < 0x80 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 1) | 0x00); + } else if(v < 0x8000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 1) | 0x01); + } else + { + MPT_ASSERT_NOTREACHED(); + return false; + } +} + +template +inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t minSize = 0, std::size_t maxSize = 0) +{ + MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4); + MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4); + MPT_ASSERT(maxSize == 0 || maxSize >= minSize); + if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x00); + } else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x01); + } else if(v < 0x400000 && minSize <= 3 && (3 <= maxSize || maxSize == 0)) + { + uint32 value = static_cast(v << 2) | 0x02; + mpt::byte bytes[3]; + bytes[0] = static_cast(value >> 0); + bytes[1] = static_cast(value >> 8); + bytes[2] = static_cast(value >> 16); + return IO::WriteRaw(f, bytes, 3); + } else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x03); + } else + { + MPT_ASSERT_NOTREACHED(); + return false; + } +} + +template +inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t minSize = 0, std::size_t maxSize = 0) +{ + MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8); + MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8); + MPT_ASSERT(maxSize == 0 || maxSize >= minSize); + if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x00); + } else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x01); + } else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x02); + } else if(v < 0x4000000000000000ull && minSize <= 8 && (8 <= maxSize || maxSize == 0)) + { + return IO::WriteIntLE(f, static_cast(v << 2) | 0x03); + } else + { + MPT_ASSERT_NOTREACHED(); + return false; + } +} + +// Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter. +template +bool WriteVarInt(Tfile & f, const T v, size_t *bytesWritten = nullptr) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + mpt::byte out[(sizeof(T) * 8 + 6) / 7]; + size_t numBytes = 0; + for(uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) + { + if(v >= (static_cast(1) << (n * 7u))) + { + out[numBytes++] = static_cast(((v >> (n * 7u)) & 0x7F) | 0x80); + } + } + out[numBytes++] = static_cast(v & 0x7F); + MPT_ASSERT(numBytes <= mpt::size(out)); + if(bytesWritten != nullptr) *bytesWritten = numBytes; + return mpt::IO::WriteRaw(f, out, numBytes); +} + +template +inline bool WriteSizedStringLE(Tfile & f, const std::string & str) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + if(str.size() > std::numeric_limits::max()) + { + return false; + } + Tsize size = static_cast(str.size()); + if(!mpt::IO::WriteIntLE(f, size)) + { + return false; + } + if(!mpt::IO::WriteRaw(f, str.data(), str.size())) + { + return false; + } + return true; +} + +template +inline bool WriteText(Tfile &f, const std::string &s) +{ + return mpt::IO::WriteRaw(f, s.data(), s.size()); +} + +template +inline bool WriteTextCRLF(Tfile &f) +{ + return mpt::IO::WriteText(f, "\r\n"); +} + +template +inline bool WriteTextLF(Tfile &f) +{ + return mpt::IO::WriteText(f, "\n"); +} + +template +inline bool WriteTextCRLF(Tfile &f, const std::string &s) +{ + return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f); +} + +template +inline bool WriteTextLF(Tfile &f, const std::string &s) +{ + return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f); +} + +} // namespace IO + + +} // namespace mpt + + + +#if defined(MPT_FILEREADER_STD_ISTREAM) + +class IFileDataContainer { +public: + typedef std::size_t off_t; +protected: + IFileDataContainer() { } +public: + virtual ~IFileDataContainer() { } +public: + virtual bool IsValid() const = 0; + virtual bool HasFastGetLength() const = 0; + virtual bool HasPinnedView() const = 0; + virtual const mpt::byte *GetRawData() const = 0; + virtual off_t GetLength() const = 0; + virtual off_t Read(mpt::byte *dst, off_t pos, off_t count) const = 0; + + virtual bool CanRead(off_t pos, off_t length) const + { + off_t dataLength = GetLength(); + if((pos == dataLength) && (length == 0)) + { + return true; + } + if(pos >= dataLength) + { + return false; + } + return length <= dataLength - pos; + } + + virtual off_t GetReadableLength(off_t pos, off_t length) const + { + off_t dataLength = GetLength(); + if(pos >= dataLength) + { + return 0; + } + return std::min(length, dataLength - pos); + } +}; + + +class FileDataContainerDummy : public IFileDataContainer { +public: + FileDataContainerDummy() { } + virtual ~FileDataContainerDummy() { } +public: + bool IsValid() const + { + return false; + } + + bool HasFastGetLength() const + { + return true; + } + + bool HasPinnedView() const + { + return true; + } + + const mpt::byte *GetRawData() const + { + return nullptr; + } + + off_t GetLength() const + { + return 0; + } + off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const + { + return 0; + } +}; + + +class FileDataContainerWindow : public IFileDataContainer +{ +private: + std::shared_ptr data; + const off_t dataOffset; + const off_t dataLength; +public: + FileDataContainerWindow(std::shared_ptr src, off_t off, off_t len) : data(src), dataOffset(off), dataLength(len) { } + virtual ~FileDataContainerWindow() { } + + bool IsValid() const + { + return data->IsValid(); + } + bool HasFastGetLength() const + { + return data->HasFastGetLength(); + } + bool HasPinnedView() const + { + return data->HasPinnedView(); + } + const mpt::byte *GetRawData() const { + return data->GetRawData() + dataOffset; + } + off_t GetLength() const { + return dataLength; + } + off_t Read(mpt::byte *dst, off_t pos, off_t count) const + { + if(pos >= dataLength) + { + return 0; + } + return data->Read(dst, dataOffset + pos, std::min(count, dataLength - pos)); + } + bool CanRead(off_t pos, off_t length) const { + if((pos == dataLength) && (length == 0)) + { + return true; + } + if(pos >= dataLength) + { + return false; + } + return (length <= dataLength - pos); + } + off_t GetReadableLength(off_t pos, off_t length) const + { + if(pos >= dataLength) + { + return 0; + } + return std::min(length, dataLength - pos); + } +}; + + +class FileDataContainerSeekable : public IFileDataContainer { + +private: + + off_t streamLength; + + mutable bool cached; + mutable std::vector cache; + +protected: + + FileDataContainerSeekable(off_t length); + virtual ~FileDataContainerSeekable(); + +private: + + void CacheStream() const; + +public: + + bool IsValid() const; + bool HasFastGetLength() const; + bool HasPinnedView() const; + const mpt::byte *GetRawData() const; + off_t GetLength() const; + off_t Read(mpt::byte *dst, off_t pos, off_t count) const; + +private: + + virtual off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const = 0; + +}; + + +class FileDataContainerStdStreamSeekable : public FileDataContainerSeekable { + +private: + + std::istream *stream; + +public: + + FileDataContainerStdStreamSeekable(std::istream *s); + virtual ~FileDataContainerStdStreamSeekable(); + + static bool IsSeekable(std::istream *stream); + static off_t GetLength(std::istream *stream); + +private: + + off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const; + +}; + + +class FileDataContainerUnseekable : public IFileDataContainer { + +private: + + mutable std::vector cache; + mutable std::size_t cachesize; + mutable bool streamFullyCached; + +protected: + + FileDataContainerUnseekable(); + virtual ~FileDataContainerUnseekable(); + +private: + + static const std::size_t QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL; + static const std::size_t BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL; + + void EnsureCacheBuffer(std::size_t requiredbuffersize) const; + void CacheStream() const; + void CacheStreamUpTo(off_t pos, off_t length) const; + +private: + + void ReadCached(mpt::byte *dst, off_t pos, off_t count) const; + +public: + + bool IsValid() const; + bool HasFastGetLength() const; + bool HasPinnedView() const; + const mpt::byte *GetRawData() const; + off_t GetLength() const; + off_t Read(mpt::byte *dst, off_t pos, off_t count) const; + bool CanRead(off_t pos, off_t length) const; + off_t GetReadableLength(off_t pos, off_t length) const; + +private: + + virtual bool InternalEof() const = 0; + virtual off_t InternalRead(mpt::byte *dst, off_t count) const = 0; + +}; + + +class FileDataContainerStdStream : public FileDataContainerUnseekable { + +private: + + std::istream *stream; + +public: + + FileDataContainerStdStream(std::istream *s); + virtual ~FileDataContainerStdStream(); + +private: + + bool InternalEof() const; + off_t InternalRead(mpt::byte *dst, off_t count) const; + +}; + + +#if defined(MPT_FILEREADER_CALLBACK_STREAM) + + +struct CallbackStream +{ + static const int SeekSet = 0; + static const int SeekCur = 1; + static const int SeekEnd = 2; + void *stream; + std::size_t (*read)( void * stream, void * dst, std::size_t bytes ); + int (*seek)( void * stream, int64 offset, int whence ); + int64 (*tell)( void * stream ); +}; + + +class FileDataContainerCallbackStreamSeekable : public FileDataContainerSeekable +{ +private: + CallbackStream stream; +public: + static bool IsSeekable(CallbackStream stream); + static off_t GetLength(CallbackStream stream); + FileDataContainerCallbackStreamSeekable(CallbackStream s); + virtual ~FileDataContainerCallbackStreamSeekable(); +private: + off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const; +}; + + +class FileDataContainerCallbackStream : public FileDataContainerUnseekable +{ +private: + CallbackStream stream; + mutable bool eof_reached; +public: + FileDataContainerCallbackStream(CallbackStream s); + virtual ~FileDataContainerCallbackStream(); +private: + bool InternalEof() const; + off_t InternalRead(mpt::byte *dst, off_t count) const; +}; + + +#endif // MPT_FILEREADER_CALLBACK_STREAM + + +#endif + + +class FileDataContainerMemory +#if defined(MPT_FILEREADER_STD_ISTREAM) + : public IFileDataContainer +#endif +{ + +#if !defined(MPT_FILEREADER_STD_ISTREAM) +public: + typedef std::size_t off_t; +#endif + +private: + + const mpt::byte *streamData; // Pointer to memory-mapped file + off_t streamLength; // Size of memory-mapped file in bytes + +public: + FileDataContainerMemory() : streamData(nullptr), streamLength(0) { } + FileDataContainerMemory(mpt::const_byte_span data) : streamData(data.data()), streamLength(data.size()) { } +#if defined(MPT_FILEREADER_STD_ISTREAM) + virtual +#endif + ~FileDataContainerMemory() { } + +public: + + bool IsValid() const + { + return streamData != nullptr; + } + + bool HasFastGetLength() const + { + return true; + } + + bool HasPinnedView() const + { + return true; + } + + const mpt::byte *GetRawData() const + { + return streamData; + } + + off_t GetLength() const + { + return streamLength; + } + + off_t Read(mpt::byte *dst, off_t pos, off_t count) const + { + if(pos >= streamLength) + { + return 0; + } + off_t avail = std::min(streamLength - pos, count); + std::copy(streamData + pos, streamData + pos + avail, dst); + return avail; + } + + bool CanRead(off_t pos, off_t length) const + { + if((pos == streamLength) && (length == 0)) + { + return true; + } + if(pos >= streamLength) + { + return false; + } + return (length <= streamLength - pos); + } + + off_t GetReadableLength(off_t pos, off_t length) const + { + if(pos >= streamLength) + { + return 0; + } + return std::min(length, streamLength - pos); + } + +}; + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp new file mode 100644 index 000000000..2bc01fe69 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp @@ -0,0 +1,550 @@ +/* + * mptLibrary.cpp + * -------------- + * Purpose: Shared library handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptLibrary.h" + +#if defined(MPT_ENABLE_DYNBIND) +#if MPT_OS_WINDOWS +#include +#elif MPT_OS_ANDROID +#include +#elif defined(MPT_WITH_LTDL) +#include +#elif defined(MPT_WITH_DL) +#include +#endif +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_DYNBIND) + + +namespace mpt +{ + + +#if MPT_OS_WINDOWS + + +// KB2533623 / Win8 +#ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS +#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 +#endif +#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR +#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 +#endif +#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 +#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#endif +#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR +#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 +#endif + + +class LibraryHandle +{ + +private: + + HMODULE hModule; + +public: + + LibraryHandle(const mpt::LibraryPath &path) + : hModule(NULL) + { + +#if MPT_OS_WINDOWS_WINRT + +#if (_WIN32_WINNT < 0x0602) + (void)path; + hModule = NULL; // unsupported +#else + switch(path.GetSearchPath()) + { + case mpt::LibrarySearchPathDefault: + hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0); + break; + case mpt::LibrarySearchPathApplication: + hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0); + break; + case mpt::LibrarySearchPathSystem: + hModule = NULL; // Only application packaged libraries can be loaded dynamically in WinRT + break; + case mpt::LibrarySearchPathFullPath: + hModule = NULL; // Absolute path is not supported in WinRT + break; + case mpt::LibrarySearchPathInvalid: + MPT_ASSERT_NOTREACHED(); + break; + } +#endif + +#else // !MPT_OS_WINDOWS_WINRT + + mpt::Windows::Version WindowsVersion = mpt::Windows::Version::Current(); + + // Check for KB2533623: + bool hasKB2533623 = false; + if(WindowsVersion.IsAtLeast(mpt::Windows::Version::Win8)) + { + hasKB2533623 = true; + } else if(WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista)) + { + HMODULE hKernel32DLL = LoadLibraryW(L"kernel32.dll"); + if(hKernel32DLL) + { + if(::GetProcAddress(hKernel32DLL, "SetDefaultDllDirectories") != nullptr) + { + hasKB2533623 = true; + } + FreeLibrary(hKernel32DLL); + hKernel32DLL = NULL; + } + } + + if(hasKB2533623) + { + switch(path.GetSearchPath()) + { + case mpt::LibrarySearchPathDefault: + hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + break; + case mpt::LibrarySearchPathSystem: + hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + break; +#if defined(MODPLUG_TRACKER) + // Using restricted search paths applies to potential DLL dependencies + // recursively. + // This fails loading for e.g. Codec or Plugin DLLs in application + // directory if they depend on the MSVC C or C++ runtime (which is + // located in the system directory). + // Just rely on the default search path here. + case mpt::LibrarySearchPathApplication: + { + const mpt::PathString dllPath = mpt::GetAppPath(); + if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) + { + hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + } + } + break; + case mpt::LibrarySearchPathFullPath: + hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + break; +#else + // For libopenmpt, do the safe thing. + case mpt::LibrarySearchPathApplication: + hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + break; + case mpt::LibrarySearchPathFullPath: + hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + break; +#endif + case mpt::LibrarySearchPathInvalid: + MPT_ASSERT_NOTREACHED(); + break; + } + } else + { + switch(path.GetSearchPath()) + { + case mpt::LibrarySearchPathDefault: + hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + break; + case mpt::LibrarySearchPathApplication: + { + const mpt::PathString dllPath = mpt::GetAppPath(); + if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) + { + hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + } + } + break; + case mpt::LibrarySearchPathSystem: + { + const mpt::PathString dllPath = mpt::GetSystemPath(); + if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) + { + hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + } + } + break; + case mpt::LibrarySearchPathFullPath: + hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + break; + case mpt::LibrarySearchPathInvalid: + MPT_ASSERT_NOTREACHED(); + break; + } + } + +#endif // MPT_OS_WINDOWS_WINRT + + } + + ~LibraryHandle() + { + if(IsValid()) + { + FreeLibrary(hModule); + } + hModule = NULL; + } + +public: + + bool IsValid() const + { + return (hModule != NULL); + } + + FuncPtr GetProcAddress(const std::string &symbol) const + { + if(!IsValid()) + { + return nullptr; + } + return reinterpret_cast(::GetProcAddress(hModule, symbol.c_str())); + } + +}; + + +#elif MPT_OS_ANDROID + + +// Fake implementation. +// Load shared objects from the JAVA side of things. +class LibraryHandle +{ + +public: + + LibraryHandle(const mpt::LibraryPath &path) + { + return; + } + + ~LibraryHandle() + { + return; + } + +public: + + bool IsValid() const + { + return true; + } + + FuncPtr GetProcAddress(const std::string &symbol) const + { + if(!IsValid()) + { + return nullptr; + } + return reinterpret_cast(dlsym(0, symbol.c_str())); + } + +}; + + + +#elif defined(MPT_WITH_LTDL) + + +class LibraryHandle +{ + +private: + + bool inited; + lt_dlhandle handle; + +public: + + LibraryHandle(const mpt::LibraryPath &path) + : inited(false) + , handle(0) + { + if(lt_dlinit() != 0) + { + return; + } + inited = true; + handle = lt_dlopenext(path.GetFileName().AsNative().c_str()); + } + + ~LibraryHandle() + { + if(IsValid()) + { + lt_dlclose(handle); + } + handle = 0; + if(inited) + { + lt_dlexit(); + inited = false; + } + } + +public: + + bool IsValid() const + { + return handle != 0; + } + + FuncPtr GetProcAddress(const std::string &symbol) const + { + if(!IsValid()) + { + return nullptr; + } + return reinterpret_cast(lt_dlsym(handle, symbol.c_str())); + } + +}; + + +#elif defined(MPT_WITH_DL) + + +class LibraryHandle +{ + +private: + + void* handle; + +public: + + LibraryHandle(const mpt::LibraryPath &path) + : handle(NULL) + { + handle = dlopen(path.GetFileName().AsNative().c_str(), RTLD_NOW); + } + + ~LibraryHandle() + { + if(IsValid()) + { + dlclose(handle); + } + handle = NULL; + } + +public: + + bool IsValid() const + { + return handle != NULL; + } + + FuncPtr GetProcAddress(const std::string &symbol) const + { + if(!IsValid()) + { + return NULL; + } + return reinterpret_cast(dlsym(handle, symbol.c_str())); + } + +}; + + +#else // MPT_OS + + +// dummy implementation + +class LibraryHandle +{ +public: + + LibraryHandle(const mpt::LibraryPath &path) + { + MPT_UNREFERENCED_PARAMETER(path); + return; + } + + ~LibraryHandle() + { + return; + } + +public: + + bool IsValid() const + { + return false; + } + + FuncPtr GetProcAddress(const std::string &symbol) const + { + MPT_UNREFERENCED_PARAMETER(symbol); + if(!IsValid()) + { + return nullptr; + } + return nullptr; + } + +}; + + +#endif // MPT_OS + + +LibraryPath::LibraryPath(mpt::LibrarySearchPath searchPath, class mpt::PathString const &fileName) + : searchPath(searchPath) + , fileName(fileName) +{ + return; +} + + +mpt::LibrarySearchPath LibraryPath::GetSearchPath() const +{ + return searchPath; +} + + +mpt::PathString LibraryPath::GetFileName() const +{ + return fileName; +} + + +mpt::PathString LibraryPath::GetDefaultPrefix() +{ + #if MPT_OS_WINDOWS + return MPT_PATHSTRING(""); + #elif MPT_OS_ANDROID + return MPT_PATHSTRING("lib"); + #elif defined(MPT_WITH_LTDL) + return MPT_PATHSTRING("lib"); + #elif defined(MPT_WITH_DL) + return MPT_PATHSTRING("lib"); + #else + return MPT_PATHSTRING("lib"); + #endif +} + + +mpt::PathString LibraryPath::GetDefaultSuffix() +{ + #if MPT_OS_WINDOWS + return MPT_PATHSTRING(".dll"); + #elif MPT_OS_ANDROID + return MPT_PATHSTRING(".so"); + #elif defined(MPT_WITH_LTDL) + return MPT_PATHSTRING(""); // handled by libltdl + #elif defined(MPT_WITH_DL) + return MPT_PATHSTRING(".so"); + #else + return mpt::PathString(); + #endif +} + + +LibraryPath LibraryPath::App(const mpt::PathString &basename) +{ + return LibraryPath(mpt::LibrarySearchPathApplication, GetDefaultPrefix() + basename + GetDefaultSuffix()); +} + + +LibraryPath LibraryPath::AppFullName(const mpt::PathString &fullname) +{ + return LibraryPath(mpt::LibrarySearchPathApplication, fullname + GetDefaultSuffix()); +} + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) + +LibraryPath LibraryPath::AppDataFullName(const mpt::PathString &fullname, const mpt::PathString &appdata) +{ + if(appdata.empty()) + { + return LibraryPath(mpt::LibrarySearchPathInvalid, MPT_PATHSTRING("")); + } + return LibraryPath(mpt::LibrarySearchPathFullPath, appdata.WithTrailingSlash() + fullname + GetDefaultSuffix()); +} + +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + +LibraryPath LibraryPath::System(const mpt::PathString &basename) +{ + return LibraryPath(mpt::LibrarySearchPathSystem, GetDefaultPrefix() + basename + GetDefaultSuffix()); +} + + +LibraryPath LibraryPath::FullPath(const mpt::PathString &path) +{ + return LibraryPath(mpt::LibrarySearchPathFullPath, path); +} + + +Library::Library() +{ + return; +} + + +Library::Library(const mpt::LibraryPath &path) +{ + if(path.GetSearchPath() == mpt::LibrarySearchPathInvalid) + { + return; + } + if(path.GetFileName().empty()) + { + return; + } + m_Handle = std::make_shared(path); +} + + +void Library::Unload() +{ + *this = mpt::Library(); +} + + +bool Library::IsValid() const +{ + return m_Handle && m_Handle->IsValid(); +} + + +FuncPtr Library::GetProcAddress(const std::string &symbol) const +{ + if(!IsValid()) + { + return nullptr; + } + return m_Handle->GetProcAddress(symbol); +} + + +} // namespace mpt + + +#endif // MPT_ENABLE_DYNBIND + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h new file mode 100644 index 000000000..36a7148c4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h @@ -0,0 +1,118 @@ +/* + * mptLibrary.h + * ------------ + * Purpose: Shared library handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_DYNBIND) + +namespace mpt +{ + + +typedef void* (*FuncPtr)(); // pointer to function returning void* + +class LibraryHandle; + +enum LibrarySearchPath +{ + LibrarySearchPathInvalid, + LibrarySearchPathDefault, + LibrarySearchPathApplication, + LibrarySearchPathSystem, + LibrarySearchPathFullPath, +}; + +class LibraryPath +{ + +private: + + mpt::LibrarySearchPath searchPath; + mpt::PathString fileName; + +private: + + LibraryPath(mpt::LibrarySearchPath searchPath, const mpt::PathString &fileName); + +public: + + mpt::LibrarySearchPath GetSearchPath() const; + + mpt::PathString GetFileName() const; + +public: + + // "lib" on Unix-like systems, "" on Windows + static mpt::PathString GetDefaultPrefix(); + + // ".so" or ".dylib" or ".dll" + static mpt::PathString GetDefaultSuffix(); + + // Returns the library path in the application directory, with os-specific prefix and suffix added to basename. + // e.g.: basename = "unmo3" --> "libunmo3.so" / "apppath/unmo3.dll" + static LibraryPath App(const mpt::PathString &basename); + + // Returns the library path in the application directory, with os-specific suffix added to fullname. + // e.g.: fullname = "libunmo3" --> "libunmo3.so" / "apppath/libunmo3.dll" + static LibraryPath AppFullName(const mpt::PathString &fullname); + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) + + // Returns the library path in the application data directory, with os-specific suffix added to fullname. + // e.g.: fullname = "libunmo3" --> "libunmo3.so" / "appdata/libunmo3.dll" (appdata == C:\Users\SOMEUSER\AppData\OpenMPT\Components\) + static LibraryPath AppDataFullName(const mpt::PathString &fullname, const mpt::PathString &appdata); + +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + // Returns a system library name with os-specific prefix and suffix added to basename, but without any full path in order to be searched in the default search path. + // e.g.: basename = "unmo3" --> "libunmo3.so" / "unmo3.dll" + static LibraryPath System(const mpt::PathString &basename); + + // Returns a system library name with os-specific suffix added to path. + // e.g.: path = "somepath/foo" --> "somepath/foo.so" / "somepath/foo.dll" + static LibraryPath FullPath(const mpt::PathString &path); + +}; + +class Library +{ +protected: + std::shared_ptr m_Handle; +public: + Library(); + Library(const mpt::LibraryPath &path); +public: + void Unload(); + bool IsValid() const; + FuncPtr GetProcAddress(const std::string &symbol) const; + template + bool Bind(Tfunc * & f, const std::string &symbol) const + { + #if !(MPT_OS_WINDOWS && MPT_COMPILER_GCC) + // MinGW64 std::is_function is always false for non __cdecl functions. + // See https://connect.microsoft.com/VisualStudio/feedback/details/774720/stl-is-function-bug . + STATIC_ASSERT(std::is_function::value); + #endif + const FuncPtr addr = GetProcAddress(symbol); + f = reinterpret_cast(addr); + return (addr != nullptr); + } +}; + +} // namespace mpt + +#endif // MPT_ENABLE_DYNBIND + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h new file mode 100644 index 000000000..26d0971a1 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h @@ -0,0 +1,233 @@ +/* + * mptMutex.h + * ---------- + * Purpose: Partially implement c++ mutexes as far as openmpt needs them. Can eventually go away when we only support c++11 compilers some time. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include // some C++ header in order to have the C++ standard library version information available + +#if !MPT_PLATFORM_MULTITHREADED +#define MPT_MUTEX_STD 0 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_OS_EMSCRIPTEN +#define MPT_MUTEX_STD 0 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_COMPILER_GENERIC && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_COMPILER_MSVC && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__) && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION) && !defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_MUTEX_STD 1 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#elif MPT_OS_WINDOWS +#define MPT_MUTEX_STD 0 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 1 +#else +#define MPT_MUTEX_STD 0 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 0 +#endif + +#if !MPT_MUTEX_STD && !MPT_MUTEX_PTHREAD && !MPT_MUTEX_WIN32 +#define MPT_MUTEX_NONE 1 +#else +#define MPT_MUTEX_NONE 0 +#endif + +#if defined(MODPLUG_TRACKER) && MPT_MUTEX_NONE +#error "OpenMPT requires mutexes." +#endif + +#if MPT_MUTEX_STD +#include +#elif MPT_MUTEX_WIN32 +#include +#elif MPT_MUTEX_PTHREAD +#include +#endif // MPT_MUTEX + +OPENMPT_NAMESPACE_BEGIN + +namespace mpt { + +#if MPT_MUTEX_STD + +typedef std::mutex mutex; +typedef std::recursive_mutex recursive_mutex; + +#elif MPT_MUTEX_WIN32 + +// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site +class mutex { +private: + CRITICAL_SECTION impl; +public: + mutex() { InitializeCriticalSection(&impl); } + ~mutex() { DeleteCriticalSection(&impl); } + void lock() { EnterCriticalSection(&impl); } + bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; } + void unlock() { LeaveCriticalSection(&impl); } +}; + +// compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site +class recursive_mutex { +private: + CRITICAL_SECTION impl; +public: + recursive_mutex() { InitializeCriticalSection(&impl); } + ~recursive_mutex() { DeleteCriticalSection(&impl); } + void lock() { EnterCriticalSection(&impl); } + bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; } + void unlock() { LeaveCriticalSection(&impl); } +}; + +#elif MPT_MUTEX_PTHREAD + +class mutex { +private: + pthread_mutex_t hLock; +public: + mutex() + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&hLock, &attr); + pthread_mutexattr_destroy(&attr); + } + ~mutex() { pthread_mutex_destroy(&hLock); } + void lock() { pthread_mutex_lock(&hLock); } + bool try_lock() { return (pthread_mutex_trylock(&hLock) == 0); } + void unlock() { pthread_mutex_unlock(&hLock); } +}; + +class recursive_mutex { +private: + pthread_mutex_t hLock; +public: + recursive_mutex() + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&hLock, &attr); + pthread_mutexattr_destroy(&attr); + } + ~recursive_mutex() { pthread_mutex_destroy(&hLock); } + void lock() { pthread_mutex_lock(&hLock); } + bool try_lock() { return (pthread_mutex_trylock(&hLock) == 0); } + void unlock() { pthread_mutex_unlock(&hLock); } +}; + +#else // MPT_MUTEX_NONE + +class mutex { +public: + mutex() { } + ~mutex() { } + void lock() { } + bool try_lock() { return true; } + void unlock() { } +}; + +class recursive_mutex { +public: + recursive_mutex() { } + ~recursive_mutex() { } + void lock() { } + bool try_lock() { return true; } + void unlock() { } +}; + +#endif // MPT_MUTEX + +#if MPT_MUTEX_STD + +#define MPT_LOCK_GUARD std::lock_guard + +#else // !MPT_MUTEX_STD + +// compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site +template< typename mutex_type > +class lock_guard { +private: + mutex_type & mutex; +public: + lock_guard( mutex_type & m ) : mutex(m) { mutex.lock(); } + ~lock_guard() { mutex.unlock(); } +}; + +#define MPT_LOCK_GUARD mpt::lock_guard + +#endif // MPT_MUTEX_STD + +#ifdef MODPLUG_TRACKER + +class recursive_mutex_with_lock_count { +private: + mpt::recursive_mutex mutex; + long lockCount; +public: + recursive_mutex_with_lock_count() + : lockCount(0) + { + return; + } + ~recursive_mutex_with_lock_count() + { + return; + } + void lock() + { + mutex.lock(); + lockCount++; + } + void unlock() + { + lockCount--; + mutex.unlock(); + } +public: + bool IsLockedByCurrentThread() // DEBUGGING only + { + bool islocked = false; + if(mutex.try_lock()) + { + islocked = (lockCount > 0); + mutex.unlock(); + } + return islocked; + } +}; + +#endif // MODPLUG_TRACKER + +} // namespace mpt + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp new file mode 100644 index 000000000..89c15f6d4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp @@ -0,0 +1,564 @@ +/* + * mptOS.cpp + * --------- + * Purpose: Operating system version information. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptOS.h" + +#if MPT_OS_WINDOWS +#include +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace Windows +{ + + +#if MPT_OS_WINDOWS + + +#if !MPT_OS_WINDOWS_WINRT + +static uint32 VersionDecimalTo_WIN32_WINNT(uint32 major, uint32 minor) +{ + // GetVersionEx returns decimal. + // _WIN32_WINNT macro uses BCD for the minor byte (see Windows 98 / ME). + // We use what _WIN32_WINNT does. + uint32 result = 0; + minor = mpt::clamp(minor, 0, 99); + result |= major; + result <<= 8; + result |= minor/10*0x10 + minor%10; + return result; +} + +#endif // !MPT_OS_WINDOWS_WINRT + + +static void GatherWindowsVersion(uint32 & SystemVersion) +{ + // Initialize to used SDK version + SystemVersion = + #if NTDDI_VERSION >= 0x0A000000 // NTDDI_WIN10 + mpt::Windows::Version::Win10 + #elif NTDDI_VERSION >= 0x06030000 // NTDDI_WINBLUE + mpt::Windows::Version::Win81 + #elif NTDDI_VERSION >= 0x06020000 // NTDDI_WIN8 + mpt::Windows::Version::Win8 + #elif NTDDI_VERSION >= 0x06010000 // NTDDI_WIN7 + mpt::Windows::Version::Win7 + #elif NTDDI_VERSION >= 0x06000000 // NTDDI_VISTA + mpt::Windows::Version::WinVista + #elif NTDDI_VERSION >= 0x05020000 // NTDDI_WS03 + mpt::Windows::Version::WinXP64 + #elif NTDDI_VERSION >= NTDDI_WINXP + mpt::Windows::Version::WinXP + #elif NTDDI_VERSION >= NTDDI_WIN2K + mpt::Windows::Version::Win2000 + #else + mpt::Windows::Version::WinNT4 + #endif + ; +#if !MPT_OS_WINDOWS_WINRT + OSVERSIONINFOEXW versioninfoex; + MemsetZero(versioninfoex); + versioninfoex.dwOSVersionInfoSize = sizeof(versioninfoex); +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4996) // 'GetVersionExW': was declared deprecated +#endif // MPT_COMPILER_MSVC +#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif // MPT_COMPILER_CLANG + GetVersionExW((LPOSVERSIONINFOW)&versioninfoex); +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC +#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG + SystemVersion = VersionDecimalTo_WIN32_WINNT(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion); +#endif // !MPT_OS_WINDOWS_WINRT +} + + +#ifdef MODPLUG_TRACKER + +namespace { +struct WindowsVersionCache +{ + uint32 SystemVersion; + WindowsVersionCache() + : SystemVersion(mpt::Windows::Version::WinNT4) + { + GatherWindowsVersion(SystemVersion); + } +}; +} + +static void GatherWindowsVersionFromCache(uint32 & SystemVersion) +{ + static WindowsVersionCache gs_WindowsVersionCache; + SystemVersion = gs_WindowsVersionCache.SystemVersion; +} + +#endif // MODPLUG_TRACKER + + +#endif // MPT_OS_WINDOWS + + +Version::Version() + : SystemIsWindows(false) + , SystemVersion(mpt::Windows::Version::WinNT4) +{ + return; +} + + +mpt::Windows::Version Version::Current() +{ + mpt::Windows::Version result; + #if MPT_OS_WINDOWS + result.SystemIsWindows = true; + #ifdef MODPLUG_TRACKER + GatherWindowsVersionFromCache(result.SystemVersion); + #else // !MODPLUG_TRACKER + GatherWindowsVersion(result.SystemVersion); + #endif // MODPLUG_TRACKER + #endif // MPT_OS_WINDOWS + return result; +} + + +bool Version::IsWindows() const +{ + return SystemIsWindows; +} + + +bool Version::IsBefore(mpt::Windows::Version::Number version) const +{ + if(!SystemIsWindows) + { + return false; + } + return (SystemVersion < static_cast(version)); +} + + +bool Version::IsAtLeast(mpt::Windows::Version::Number version) const +{ + if(!SystemIsWindows) + { + return false; + } + return (SystemVersion >= static_cast(version)); +} + + +static MPT_CONSTEXPR11_VAR struct { Version::Number version; const MPT_UCHAR_TYPE * name; } versionMap[] = +{ + { mpt::Windows::Version::WinNewer, MPT_ULITERAL("Windows 10 (or newer)") }, + { mpt::Windows::Version::Win10, MPT_ULITERAL("Windows 10") }, + { mpt::Windows::Version::Win81, MPT_ULITERAL("Windows 8.1") }, + { mpt::Windows::Version::Win8, MPT_ULITERAL("Windows 8") }, + { mpt::Windows::Version::Win7, MPT_ULITERAL("Windows 7") }, + { mpt::Windows::Version::WinVista, MPT_ULITERAL("Windows Vista") }, + { mpt::Windows::Version::WinXP64, MPT_ULITERAL("Windows XP x64 / Windows Server 2003") }, + { mpt::Windows::Version::WinXP, MPT_ULITERAL("Windows XP") }, + { mpt::Windows::Version::Win2000, MPT_ULITERAL("Windows 2000") }, + { mpt::Windows::Version::WinME, MPT_ULITERAL("Windows ME") }, + { mpt::Windows::Version::Win98, MPT_ULITERAL("Windows 98") }, + { mpt::Windows::Version::WinNT4, MPT_ULITERAL("Windows NT4") } +}; + + +mpt::ustring Version::VersionToString(uint16 version) +{ + mpt::ustring result; + for(const auto &v : versionMap) + { + if(version > v.version) + { + result = MPT_USTRING("> ") + v.name; + break; + } else if(version == v.version) + { + result = v.name; + break; + } + } + if(result.empty()) + { + result = mpt::format(MPT_USTRING("0x%1"))(mpt::ufmt::dec0<4>(version)); + } + return result; +} + + + +mpt::ustring Version::VersionToString(Number version) +{ + return VersionToString(static_cast(version)); +} + + +mpt::ustring Version::GetName() const +{ + mpt::ustring name = MPT_USTRING("Generic Windows NT"); + for(const auto &v : versionMap) + { + if(mpt::Windows::Version::IsAtLeast(v.version)) + { + name = v.name; + break; + } + } + mpt::ustring result = name; + #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + if(mpt::Windows::IsWine()) + { + mpt::Wine::VersionContext v; + if(v.Version().IsValid()) + { + result = mpt::format(MPT_USTRING("Wine %1 (%2)"))( + v.Version().AsString() + , name + ); + } else + { + result = mpt::format(MPT_USTRING("Wine (unknown version: '%1') (%2)"))( + mpt::ToUnicode(mpt::CharsetUTF8, v.RawVersion()) + , name + ); + } + } + #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + return result; +} + + +#ifdef MODPLUG_TRACKER +mpt::ustring Version::GetNameShort() const +{ + mpt::ustring name; + if(mpt::Windows::IsWine()) + { + mpt::Wine::VersionContext v; + if(v.Version().IsValid()) + { + name = mpt::format(MPT_USTRING("wine-%1"))(v.Version().AsString()); + } else if(v.RawVersion().length() > 0) + { + name = MPT_USTRING("wine-") + Util::BinToHex(mpt::as_span(v.RawVersion())); + } else + { + name = MPT_USTRING("wine-"); + } + name += MPT_USTRING("-") + Util::BinToHex(mpt::as_span(v.RawHostSysName())); + } else + { + name = mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::dec(SystemVersion >> 8), mpt::ufmt::HEX0<2>(SystemVersion & 0xFF)); + } + return name; +} +#endif // MODPLUG_TRACKER + + +mpt::Windows::Version::Number Version::GetMinimumKernelLevel() +{ + uint16 minimumKernelVersion = 0; + #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC + #if !defined(MPT_BUILD_TARGET_XP) + minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinVista); + #else + minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinXP); + #endif + #endif + return static_cast(minimumKernelVersion); +} + + +mpt::Windows::Version::Number Version::GetMinimumAPILevel() +{ + uint16 minimumApiVersion = 0; + #if MPT_OS_WINDOWS && defined(_WIN32_WINNT) + minimumApiVersion = std::max(minimumApiVersion, _WIN32_WINNT); + #endif + return static_cast(minimumApiVersion); +} + + +#if defined(MODPLUG_TRACKER) + + +#if MPT_OS_WINDOWS + +static bool GatherSystemIsWine() +{ + bool SystemIsWine = false; + HMODULE hNTDLL = LoadLibraryW(L"ntdll.dll"); + if(hNTDLL) + { + SystemIsWine = (GetProcAddress(hNTDLL, "wine_get_version") != NULL); + FreeLibrary(hNTDLL); + hNTDLL = NULL; + } + return SystemIsWine; +} + +#endif // MPT_OS_WINDOWS + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +namespace { +struct SystemIsWineCache +{ + bool SystemIsWine; + SystemIsWineCache() + : SystemIsWine(GatherSystemIsWine()) + { + return; + } + SystemIsWineCache(bool isWine) + : SystemIsWine(isWine) + { + return; + } +}; +} + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + +static bool SystemIsWine(bool allowDetection = true) +{ + #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + static SystemIsWineCache gs_SystemIsWineCache = allowDetection ? SystemIsWineCache() : SystemIsWineCache(false); + if(!allowDetection) + { // catch too late calls of PreventWineDetection + MPT_ASSERT(!gs_SystemIsWineCache.SystemIsWine); + } + return gs_SystemIsWineCache.SystemIsWine; + #elif MPT_OS_WINDOWS + MPT_UNREFERENCED_PARAMETER(allowDetection); + return GatherSystemIsWine(); + #else + MPT_UNREFERENCED_PARAMETER(allowDetection); + return false; + #endif +} + +void PreventWineDetection() +{ + SystemIsWine(false); +} + +bool IsOriginal() +{ + return mpt::Windows::Version::Current().IsWindows() && !SystemIsWine(); +} + +bool IsWine() +{ + return mpt::Windows::Version::Current().IsWindows() && SystemIsWine(); +} + + +#endif // MODPLUG_TRACKER + + +} // namespace Windows +} // namespace mpt + + + +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ +namespace Wine +{ + + +Version::Version() + : valid(false) + , vmajor(0) + , vminor(0) + , vupdate(0) +{ + return; +} + + +Version::Version(const mpt::ustring &rawVersion) + : valid(false) + , vmajor(0) + , vminor(0) + , vupdate(0) +{ + if(rawVersion.empty()) + { + return; + } + std::vector version = mpt::String::Split(rawVersion, MPT_USTRING(".")); + if(version.size() < 2) + { + return; + } + mpt::ustring parsedVersion = mpt::String::Combine(version, MPT_USTRING(".")); + std::size_t len = std::min(parsedVersion.length(), rawVersion.length()); + if(len == 0) + { + return; + } + if(parsedVersion.substr(0, len) != rawVersion.substr(0, len)) + { + return; + } + valid = true; + vmajor = version[0]; + vminor = version[1]; + vupdate = (version.size() >= 3) ? version[2] : 0; +} + + +Version::Version(uint8 vmajor, uint8 vminor, uint8 vupdate) + : valid((vmajor > 0) || (vminor > 0) || (vupdate > 0)) + , vmajor(vmajor) + , vminor(vminor) + , vupdate(vupdate) +{ + return; +} + + +mpt::Wine::Version Version::FromInteger(uint32 version) +{ + mpt::Wine::Version result; + result.valid = (version <= 0xffffff); + result.vmajor = static_cast(version >> 16); + result.vminor = static_cast(version >> 8); + result.vupdate = static_cast(version >> 0); + return result; +} + + +bool Version::IsValid() const +{ + return valid; +} + + +mpt::ustring Version::AsString() const +{ + return mpt::ufmt::dec(vmajor) + MPT_USTRING(".") + mpt::ufmt::dec(vminor) + MPT_USTRING(".") + mpt::ufmt::dec(vupdate); +} + + +uint32 Version::AsInteger() const +{ + uint32 version = 0; + version |= static_cast(vmajor) << 16; + version |= static_cast(vminor) << 8; + version |= static_cast(vupdate) << 0; + return version; +} + + +bool Version::IsBefore(mpt::Wine::Version other) const +{ + if(!IsValid()) + { + return false; + } + return (AsInteger() < other.AsInteger()); +} + + +bool Version::IsAtLeast(mpt::Wine::Version other) const +{ + if(!IsValid()) + { + return false; + } + return (AsInteger() >= other.AsInteger()); +} + + +mpt::Wine::Version GetMinimumWineVersion() +{ + mpt::Wine::Version minimumWineVersion = mpt::Wine::Version(0,0,0); + #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC + #if !defined(MPT_BUILD_TARGET_XP) + minimumWineVersion = mpt::Wine::Version(1,8,0); + #else + minimumWineVersion = mpt::Wine::Version(1,6,0); + #endif + #endif + return minimumWineVersion; +} + + +VersionContext::VersionContext() + : m_IsWine(false) + , m_HostIsLinux(false) + , m_HostIsBSD(false) +{ + #if MPT_OS_WINDOWS + m_IsWine = mpt::Windows::IsWine(); + if(!m_IsWine) + { + return; + } + m_NTDLL = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("ntdll.dll"))); + if(m_NTDLL.IsValid()) + { + const char * (__cdecl * wine_get_version)(void) = nullptr; + const char * (__cdecl * wine_get_build_id)(void) = nullptr; + void (__cdecl * wine_get_host_version)(const char * *, const char * *) = nullptr; + m_NTDLL.Bind(wine_get_version, "wine_get_version"); + m_NTDLL.Bind(wine_get_build_id, "wine_get_build_id"); + m_NTDLL.Bind(wine_get_host_version, "wine_get_host_version"); + const char * wine_version = nullptr; + const char * wine_build_id = nullptr; + const char * wine_host_sysname = nullptr; + const char * wine_host_release = nullptr; + wine_version = wine_get_version ? wine_get_version() : ""; + wine_build_id = wine_get_build_id ? wine_get_build_id() : ""; + if(wine_get_host_version) + { + wine_get_host_version(&wine_host_sysname, &wine_host_release); + } + m_RawVersion = wine_version ? wine_version : ""; + m_RawBuildID = wine_build_id ? wine_build_id : ""; + m_RawHostSysName = wine_host_sysname ? wine_host_sysname : ""; + m_RawHostRelease = wine_host_release ? wine_host_release : ""; + } + m_Version = mpt::Wine::Version(mpt::ToUnicode(mpt::CharsetUTF8, m_RawVersion)); + m_HostIsLinux = (m_RawHostSysName == "Linux"); + m_HostIsBSD = (m_RawHostSysName == "FreeBSD" || m_RawHostSysName == "DragonFly" || m_RawHostSysName == "NetBSD" || m_RawHostSysName == "OpenBSD"); + #endif // MPT_OS_WINDOWS +} + + +} // namespace Wine +} // namespace mpt + +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h new file mode 100644 index 000000000..fe213d10e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h @@ -0,0 +1,163 @@ +/* + * mptOS.h + * ------- + * Purpose: Operating system version information. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "mptLibrary.h" + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace Windows +{ + +class Version +{ + +public: + + enum Number + { + + WinNT4 = 0x0400, + Win98 = 0x0410, + WinME = 0x0490, + Win2000 = 0x0500, + WinXP = 0x0501, + WinXP64 = 0x0502, + WinVista = 0x0600, + Win7 = 0x0601, + Win8 = 0x0602, + Win81 = 0x0603, + Win10 = 0x0a00, + + WinNewer = Win10 + 1 + + }; + + static mpt::ustring VersionToString(uint16 version); + static mpt::ustring VersionToString(Number version); + +private: + + bool SystemIsWindows; + + uint32 SystemVersion; + +private: + + Version(); + +public: + + static mpt::Windows::Version Current(); + +public: + + bool IsWindows() const; + + bool IsBefore(mpt::Windows::Version::Number version) const; + bool IsAtLeast(mpt::Windows::Version::Number version) const; + + mpt::ustring GetName() const; +#ifdef MODPLUG_TRACKER + mpt::ustring GetNameShort() const; +#endif // MODPLUG_TRACKER + +public: + + static mpt::Windows::Version::Number GetMinimumKernelLevel(); + static mpt::Windows::Version::Number GetMinimumAPILevel(); + +}; // class Version + +#if defined(MODPLUG_TRACKER) + +void PreventWineDetection(); + +bool IsOriginal(); +bool IsWine(); + +#endif // MODPLUG_TRACKER + +} // namespace Windows +} // namespace mpt + + +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ + +namespace Wine +{ + +class Version +{ +private: + bool valid; + uint8 vmajor; + uint8 vminor; + uint8 vupdate; +public: + Version(); + Version(uint8 vmajor, uint8 vminor, uint8 vupdate); + explicit Version(const mpt::ustring &version); +public: + bool IsValid() const; + mpt::ustring AsString() const; +private: + static mpt::Wine::Version FromInteger(uint32 version); + uint32 AsInteger() const; +public: + bool IsBefore(mpt::Wine::Version other) const; + bool IsAtLeast(mpt::Wine::Version other) const; +}; + +mpt::Wine::Version GetMinimumWineVersion(); + +class VersionContext +{ +protected: + bool m_IsWine; + mpt::Library m_NTDLL; + std::string m_RawVersion; + std::string m_RawBuildID; + std::string m_RawHostSysName; + std::string m_RawHostRelease; + mpt::Wine::Version m_Version; + bool m_HostIsLinux; + bool m_HostIsBSD; +public: + VersionContext(); +public: + bool IsWine() const { return m_IsWine; } + mpt::Library NTDLL() const { return m_NTDLL; } + std::string RawVersion() const { return m_RawVersion; } + std::string RawBuildID() const { return m_RawBuildID; } + std::string RawHostSysName() const { return m_RawHostSysName; } + std::string RawHostRelease() const { return m_RawHostRelease; } + mpt::Wine::Version Version() const { return m_Version; } + bool HostIsLinux() const { return m_HostIsLinux; } + bool HostIsBSD() const { return m_HostIsBSD; } +}; + +} // namespace Wine + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp new file mode 100644 index 000000000..76d1b1a0f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp @@ -0,0 +1,889 @@ +/* + * mptPathString.cpp + * ----------------- + * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptPathString.h" + +#include "misc_util.h" + +#include "mptUUID.h" + +#if MPT_OS_WINDOWS +#include +#if defined(MODPLUG_TRACKER) +#include +#endif +#endif + +#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT +#if defined(__MINGW32__) || defined(__MINGW64__) +// MinGW-w64 headers do not declare this for WinRT, which is wrong. +extern "C" { +WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart); +} +#endif +#endif + +OPENMPT_NAMESPACE_BEGIN + +#if MPT_OS_WINDOWS +#define MPT_PATHSTRING_LITERAL(x) ( L ## x ) +#else +#define MPT_PATHSTRING_LITERAL(x) ( x ) +#endif + +#if MPT_OS_WINDOWS + +namespace mpt +{ + + +RawPathString PathString::AsNativePrefixed() const +{ + if(path.length() <= MAX_PATH || path.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + { + // Path is short enough or already in prefixed form + return path; + } + const RawPathString absPath = mpt::GetAbsolutePath(path).AsNative(); + if(absPath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + { + // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar + return MPT_PATHSTRING_LITERAL("\\\\?\\UNC") + absPath.substr(1); + } else + { + // Regular file: C:\foo.bar -> \\?\C:\foo.bar + return MPT_PATHSTRING_LITERAL("\\\\?\\") + absPath; + } +} + + +#if !MPT_OS_WINDOWS_WINRT + +int PathString::CompareNoCase(const PathString & a, const PathString & b) +{ + return lstrcmpiW(a.path.c_str(), b.path.c_str()); +} + +#endif // !MPT_OS_WINDOWS_WINRT + + +// Convert a path to its simplified form, i.e. remove ".\" and "..\" entries +// Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH +// and unlimited versions are only available on Windows 8 and later. +// Furthermore, we also convert forward-slashes to backslashes and always remove trailing slashes. +PathString PathString::Simplify() const +{ + if(path.empty()) + return PathString(); + + std::vector components; + RawPathString root; + RawPathString::size_type startPos = 0; + if(path.size() >= 2 && path[1] == MPT_PATHSTRING_LITERAL(':')) + { + // Drive letter + root = path.substr(0, 2) + MPT_PATHSTRING_LITERAL('\\'); + startPos = 2; + } else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + { + // Network share + root = MPT_PATHSTRING_LITERAL("\\\\"); + startPos = 2; + } else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\") || path.substr(0, 2) == MPT_PATHSTRING_LITERAL("./")) + { + // Special case for relative paths + root = MPT_PATHSTRING_LITERAL(".\\"); + startPos = 2; + } else if(path.size() >= 1 && (path[0] == MPT_PATHSTRING_LITERAL('\\') || path[0] == MPT_PATHSTRING_LITERAL('/'))) + { + // Special case for relative paths + root = MPT_PATHSTRING_LITERAL("\\"); + startPos = 1; + } + + while(startPos < path.size()) + { + auto pos = path.find_first_of(MPT_PATHSTRING_LITERAL("\\/"), startPos); + if(pos == RawPathString::npos) + pos = path.size(); + mpt::RawPathString dir = path.substr(startPos, pos - startPos); + if(dir == MPT_PATHSTRING_LITERAL("..")) + { + // Go back one directory + if(!components.empty()) + { + components.pop_back(); + } + } else if(dir == MPT_PATHSTRING_LITERAL(".")) + { + // nop + } else if(!dir.empty()) + { + components.push_back(std::move(dir)); + } + startPos = pos + 1; + } + + RawPathString result = root; + result.reserve(path.size()); + for(const auto &component : components) + { + result += component + MPT_PATHSTRING_LITERAL("\\"); + } + if(!components.empty()) + result.pop_back(); + return result; +} + +} // namespace mpt + +#endif // MPT_OS_WINDOWS + + +namespace mpt +{ + + +#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)) + +void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const +{ + // We cannot use CRT splitpath here, because: + // * limited to _MAX_PATH or similar + // * no support for UNC paths + // * no support for \\?\ prefixed paths + + if(drive) *drive = mpt::PathString(); + if(dir) *dir = mpt::PathString(); + if(fname) *fname = mpt::PathString(); + if(ext) *ext = mpt::PathString(); + + mpt::RawPathString p = path; + + // remove \\?\\ prefix + if(p.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\")) + { + p = MPT_PATHSTRING_LITERAL("\\\\") + p.substr(8); + } else if(p.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + { + p = p.substr(4); + } + + if (p.length() >= 2 && ( + p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\") + || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\/") + || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("/\\") + || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("//") + )) + { // UNC + mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(MPT_PATHSTRING_LITERAL("\\/")); + if(first_slash != mpt::RawPathString::npos) + { + mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(MPT_PATHSTRING_LITERAL("\\/")); + if(second_slash != mpt::RawPathString::npos) + { + if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash)); + p = p.substr(2 + first_slash + 1 + second_slash); + } else + { + if(drive) *drive = mpt::PathString::FromNative(p); + p = mpt::RawPathString(); + } + } else + { + if(drive) *drive = mpt::PathString::FromNative(p); + p = mpt::RawPathString(); + } + } else + { // local + if(p.length() >= 2 && (p[1] == MPT_PATHSTRING_LITERAL(':'))) + { + if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2)); + p = p.substr(2); + } else + { + if(drive) *drive = mpt::PathString(); + } + } + mpt::RawPathString::size_type last_slash = p.find_last_of(MPT_PATHSTRING_LITERAL("\\/")); + if(last_slash != mpt::RawPathString::npos) + { + if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1)); + p = p.substr(last_slash + 1); + } else + { + if(dir) *dir = mpt::PathString(); + } + mpt::RawPathString::size_type last_dot = p.find_last_of(MPT_PATHSTRING_LITERAL(".")); + if(last_dot == mpt::RawPathString::npos) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); + } else + { + if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot)); + if(ext) *ext = mpt::PathString::FromNative(p.substr(last_dot)); + } + +} + +PathString PathString::GetDrive() const +{ + PathString drive; + SplitPath(&drive, nullptr, nullptr, nullptr); + return drive; +} +PathString PathString::GetDir() const +{ + PathString dir; + SplitPath(nullptr, &dir, nullptr, nullptr); + return dir; +} +PathString PathString::GetPath() const +{ + PathString drive, dir; + SplitPath(&drive, &dir, nullptr, nullptr); + return drive + dir; +} +PathString PathString::GetFileName() const +{ + PathString fname; + SplitPath(nullptr, nullptr, &fname, nullptr); + return fname; +} +PathString PathString::GetFileExt() const +{ + PathString ext; + SplitPath(nullptr, nullptr, nullptr, &ext); + return ext; +} +PathString PathString::GetFullFileName() const +{ + PathString name, ext; + SplitPath(nullptr, nullptr, &name, &ext); + return name + ext; +} + + +bool PathString::IsDirectory() const +{ + // Using PathIsDirectoryW here instead would increase libopenmpt dependencies by shlwapi.dll. + // GetFileAttributesW also does the job just fine. + #if MPT_OS_WINDOWS_WINRT + WIN32_FILE_ATTRIBUTE_DATA data; + MemsetZero(data); + if(::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) + { + return false; + } + DWORD dwAttrib = data.dwFileAttributes; + #else // !MPT_OS_WINDOWS_WINRT + DWORD dwAttrib = ::GetFileAttributesW(path.c_str()); + #endif // MPT_OS_WINDOWS_WINRT + return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +bool PathString::IsFile() const +{ + #if MPT_OS_WINDOWS_WINRT + WIN32_FILE_ATTRIBUTE_DATA data; + MemsetZero(data); + if (::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) + { + return false; + } + DWORD dwAttrib = data.dwFileAttributes; + #else // !MPT_OS_WINDOWS_WINRT + DWORD dwAttrib = ::GetFileAttributesW(path.c_str()); + #endif // MPT_OS_WINDOWS_WINRT + return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE) + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +bool PathString::FileOrDirectoryExists() const +{ + return ::PathFileExistsW(path.c_str()) != FALSE; +} + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +PathString PathString::ReplaceExt(const mpt::PathString &newExt) const +{ + return GetDrive() + GetDir() + GetFileName() + newExt; +} + + +PathString PathString::SanitizeComponent() const +{ + PathString result = *this; + SanitizeFilename(result); + return result; +} + + +// Convert an absolute path to a path that's relative to "&relativeTo". +PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const +{ + mpt::PathString result = path; + if(path.empty()) + { + return result; + } + if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length())) + { + // Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath") + result = MPT_PATHSTRING(".\\"); // ".\" + result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length())); + } else if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2)) + { + // Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath") + result = mpt::PathString::FromNative(AsNative().substr(2)); + } + return result; +} + + +// Convert a path that is relative to "&relativeTo" to an absolute path. +PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const +{ + mpt::PathString result = path; + if(path.empty()) + { + return result; + } + if(path.length() >= 2 && path.at(0) == MPT_PATHSTRING_LITERAL('\\') && path.at(1) != MPT_PATHSTRING_LITERAL('\\')) + { + // Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\" + result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2)); + result += path; + } else if(path.length() >= 2 && path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\")) + { + // Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\") + result = relativeTo; // "C:\OpenMPT\" + result += mpt::PathString::FromNative(AsNative().substr(2)); + } + return result; +} + + +#if defined(_MFC_VER) + +mpt::PathString PathString::TunnelOutofCString(const CString &path) +{ + #ifdef UNICODE + return mpt::PathString::FromWide(path.GetString()); + #else + // Since MFC code can call into our code from a lot of places, we cannot assume + // that filenames we get from MFC are always encoded in our hacked UTF8-in-CString encoding. + // Instead, we use a rough heuristic: if the string is parseable as UTF8, we assume it is. + // This fails for CP_ACP strings, that are also valid UTF8. That's the trade-off here. + if(mpt::IsUTF8(path.GetString())) + { + // utf8 + return mpt::PathString::FromUTF8(path.GetString()); + } else + { + // ANSI + return mpt::PathString::FromWide(mpt::ToWide(path)); + } + #endif +} + + +CString PathString::TunnelIntoCString(const mpt::PathString &path) +{ + #ifdef UNICODE + return path.ToWide().c_str(); + #else + return path.ToUTF8().c_str(); + #endif +} + +#endif // MFC + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + +} // namespace mpt + + + +namespace mpt +{ + + +bool IsPathSeparator(mpt::RawPathString::value_type c) { +#if MPT_OS_WINDOWS + return (c == MPT_PATHSTRING_LITERAL('\\')) || (c == MPT_PATHSTRING_LITERAL('/')); +#else + return c == MPT_PATHSTRING_LITERAL('/'); +#endif +} + +bool PathIsAbsolute(const mpt::PathString &path) { + mpt::RawPathString rawpath = path.AsNative(); +#if MPT_OS_WINDOWS + if(rawpath.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\")) + { + return true; + } + if(rawpath.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + { + return true; + } + if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + { + return true; // UNC + } + if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("//")) + { + return true; // UNC + } + return (rawpath.length()) >= 3 && (rawpath[1] == ':') && IsPathSeparator(rawpath[2]); +#else + return (rawpath.length() >= 1) && IsPathSeparator(rawpath[0]); +#endif +} + + +#if MPT_OS_WINDOWS + +mpt::PathString GetAbsolutePath(const mpt::PathString &path) +{ + DWORD size = GetFullPathNameW(path.AsNative().c_str(), 0, nullptr, nullptr); + if(size == 0) + { + return path; + } + std::vector fullPathName(size, L'\0'); + if(GetFullPathNameW(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0) + { + return path; + } + return mpt::PathString::FromNative(fullPathName.data()); +} + +#ifdef MODPLUG_TRACKER + +bool DeleteWholeDirectoryTree(mpt::PathString path) +{ + if(path.AsNative().empty()) + { + return false; + } + if(PathIsRelativeW(path.AsNative().c_str()) == TRUE) + { + return false; + } + if(!path.FileOrDirectoryExists()) + { + return true; + } + if(!path.IsDirectory()) + { + return false; + } + path.EnsureTrailingSlash(); + HANDLE hFind = NULL; + WIN32_FIND_DATAW wfd; + MemsetZero(wfd); + hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd); + if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) + { + do + { + mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); + if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING("..")) + { + filename = path + filename; + if(filename.IsDirectory()) + { + if(!DeleteWholeDirectoryTree(filename)) + { + return false; + } + } else if(filename.IsFile()) + { + if(DeleteFileW(filename.AsNative().c_str()) == 0) + { + return false; + } + } + } + } while(FindNextFileW(hFind, &wfd)); + FindClose(hFind); + } + if(RemoveDirectoryW(path.AsNative().c_str()) == 0) + { + return false; + } + return true; +} + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS + + + +#if MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) + +mpt::PathString GetAppPath() +{ + std::vector exeFileName(MAX_PATH); + while(GetModuleFileNameW(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) + { + if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + return mpt::PathString(); + } + exeFileName.resize(exeFileName.size() * 2); + } + return mpt::GetAbsolutePath(mpt::PathString::FromNative(exeFileName.data()).GetPath()); +} + +#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE + + +#if defined(MPT_ENABLE_DYNBIND) + +#if !MPT_OS_WINDOWS_WINRT + +mpt::PathString GetSystemPath() +{ + DWORD size = GetSystemDirectoryW(nullptr, 0); + std::vector path(size + 1); + if(!GetSystemDirectoryW(path.data(), size + 1)) + { + return mpt::PathString(); + } + return mpt::PathString::FromNative(path.data()) + MPT_PATHSTRING("\\"); +} + +#endif // !MPT_OS_WINDOWS_WINRT + +#endif // MPT_ENABLE_DYNBIND + +#endif // MPT_OS_WINDOWS + + + +#if defined(MPT_ENABLE_TEMPFILE) +#if MPT_OS_WINDOWS + +mpt::PathString GetTempDirectory() +{ + DWORD size = GetTempPathW(0, nullptr); + if(size) + { + std::vector tempPath(size + 1); + if(GetTempPathW(size + 1, tempPath.data())) + { + return mpt::PathString::FromNative(tempPath.data()); + } + } + // use app directory as fallback + return mpt::GetAppPath(); +} + +mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) +{ + mpt::PathString filename = mpt::GetTempDirectory(); + filename += (!fileNamePrefix.empty() ? fileNamePrefix + MPT_PATHSTRING("_") : mpt::PathString()); + filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly().ToUString()); + filename += (!fileNameExtension.empty() ? MPT_PATHSTRING(".") + fileNameExtension : mpt::PathString()); + return filename; +} + +TempFileGuard::TempFileGuard(const mpt::PathString &filename) + : filename(filename) +{ + return; +} + +mpt::PathString TempFileGuard::GetFilename() const +{ + return filename; +} + +TempFileGuard::~TempFileGuard() +{ + if(!filename.empty()) + { + DeleteFileW(filename.AsNative().c_str()); + } +} + +#ifdef MODPLUG_TRACKER + +TempDirGuard::TempDirGuard(const mpt::PathString &dirname_) + : dirname(dirname_.WithTrailingSlash()) +{ + if(dirname.empty()) + { + return; + } + if(::CreateDirectoryW(dirname.AsNative().c_str(), NULL) == 0) + { // fail + dirname = mpt::PathString(); + } +} + +mpt::PathString TempDirGuard::GetDirname() const +{ + return dirname; +} + +TempDirGuard::~TempDirGuard() +{ + if(!dirname.empty()) + { + DeleteWholeDirectoryTree(dirname); + } +} + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS +#endif // MPT_ENABLE_TEMPFILE + +} // namespace mpt + + + +#if defined(MODPLUG_TRACKER) + +static inline char SanitizeFilenameChar(char c) +{ + if( c == '\\' || + c == '\"' || + c == '/' || + c == ':' || + c == '?' || + c == '<' || + c == '>' || + c == '|' || + c == '*') + { + c = '_'; + } + return c; +} + +static inline wchar_t SanitizeFilenameChar(wchar_t c) +{ + if( c == L'\\' || + c == L'\"' || + c == L'/' || + c == L':' || + c == L'?' || + c == L'<' || + c == L'>' || + c == L'|' || + c == L'*') + { + c = L'_'; + } + return c; +} + +void SanitizeFilename(mpt::PathString &filename) +{ + mpt::RawPathString tmp = filename.AsNative(); + for(auto &c : tmp) + { + c = SanitizeFilenameChar(c); + } + filename = mpt::PathString::FromNative(tmp); +} + +void SanitizeFilename(char *beg, char *end) +{ + for(char *it = beg; it != end; ++it) + { + *it = SanitizeFilenameChar(*it); + } +} + +void SanitizeFilename(wchar_t *beg, wchar_t *end) +{ + for(wchar_t *it = beg; it != end; ++it) + { + *it = SanitizeFilenameChar(*it); + } +} + +void SanitizeFilename(std::string &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} + +void SanitizeFilename(std::wstring &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} + +#if MPT_USTRING_MODE_UTF8 +void SanitizeFilename(mpt::u8string &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} +#endif // MPT_USTRING_MODE_UTF8 + +#if defined(_MFC_VER) +void SanitizeFilename(CString &str) +{ + for(int i = 0; i < str.GetLength(); i++) + { + str.SetAt(i, SanitizeFilenameChar(str.GetAt(i))); + } +} +#endif // MFC + +#endif // MODPLUG_TRACKER + + +#if defined(MODPLUG_TRACKER) + + +mpt::PathString FileType::AsFilterString(FlagSet format) const +{ + mpt::PathString filter; + if(GetShortName().empty() || GetExtensions().empty()) + { + return filter; + } + if(!GetDescription().empty()) + { + filter += mpt::PathString::FromUnicode(GetDescription()); + } else + { + filter += mpt::PathString::FromUnicode(GetShortName()); + } + const auto extensions = GetExtensions(); + if(format[FileTypeFormatShowExtensions]) + { + filter += MPT_PATHSTRING(" ("); + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += MPT_PATHSTRING(","); + } + filter += MPT_PATHSTRING("*."); + filter += ext; + } + filter += MPT_PATHSTRING(")"); + } + filter += MPT_PATHSTRING("|"); + { + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += MPT_PATHSTRING(";"); + } + filter += MPT_PATHSTRING("*."); + filter += ext; + } + } + filter += MPT_PATHSTRING("|"); + return filter; +} + + +mpt::PathString FileType::AsFilterOnlyString() const +{ + mpt::PathString filter; + const auto extensions = GetExtensions(); + { + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += MPT_PATHSTRING(";"); + } + filter += MPT_PATHSTRING("*."); + filter += ext; + } + } + return filter; +} + + +mpt::PathString ToFilterString(const FileType &fileType, FlagSet format) +{ + return fileType.AsFilterString(format); +} + + +mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format) +{ + mpt::PathString filter; + for(const auto &type : fileTypes) + { + filter += type.AsFilterString(format); + } + return filter; +} + + +mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty) +{ + mpt::PathString filter = fileType.AsFilterOnlyString(); + return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? MPT_PATHSTRING(";") : MPT_PATHSTRING("")) + filter; +} + + +mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty) +{ + mpt::PathString filter; + for(const auto &type : fileTypes) + { + filter += type.AsFilterOnlyString(); + } + return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? MPT_PATHSTRING(";") : MPT_PATHSTRING("")) + filter; +} + + +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h new file mode 100644 index 000000000..98461e707 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h @@ -0,0 +1,503 @@ +/* + * mptPathString.h + * --------------- + * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include + +#include "FlagSet.h" + +OPENMPT_NAMESPACE_BEGIN + + + +#define MPT_DEPRECATED_PATH +//#define MPT_DEPRECATED_PATH MPT_DEPRECATED + + + +namespace mpt +{ + +#if MPT_OS_WINDOWS +typedef std::wstring RawPathString; +#else // !MPT_OS_WINDOWS +typedef std::string RawPathString; +#endif // if MPT_OS_WINDOWS + + + +class PathString +{ + +private: + + RawPathString path; + +private: + + PathString(const RawPathString & path) + : path(path) + { + return; + } + +public: + + PathString() + { + return; + } + PathString(const PathString & other) + : path(other.path) + { + return; + } + PathString & assign(const PathString & other) + { + path = other.path; + return *this; + } + PathString & operator = (const PathString & other) + { + return assign(other); + } + PathString & append(const PathString & other) + { + path.append(other.path); + return *this; + } + PathString & operator += (const PathString & other) + { + return append(other); + } + + friend PathString operator + (const PathString & a, const PathString & b) + { + return PathString(a).append(b); + } + + friend bool operator < (const PathString & a, const PathString & b) + { + return a.AsNative() < b.AsNative(); + } + friend bool operator == (const PathString & a, const PathString & b) + { + return a.AsNative() == b.AsNative(); + } + friend bool operator != (const PathString & a, const PathString & b) + { + return a.AsNative() != b.AsNative(); + } + + bool empty() const { return path.empty(); } + + std::size_t Length() const { return path.size(); } + + + +public: + +#if MPT_OS_WINDOWS +#if !MPT_OS_WINDOWS_WINRT + static int CompareNoCase(const PathString & a, const PathString & b); +#endif // !MPT_OS_WINDOWS_WINRT +#endif + +#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)) + + void SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const; + // \\?\ prefixes will be removed and \\?\\UNC prefixes converted to canonical \\ form. + PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share + PathString GetDir() const; // Directory, e.g. "\OpenMPT\" + PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\" + PathString GetFileName() const; // File name without extension, e.g. "mptrack" + PathString GetFileExt() const; // Extension including dot, e.g. ".exe" + PathString GetFullFileName() const; // File name + extension, e.g. "mptrack.exe" + + // Verify if this path represents a valid directory on the file system. + bool IsDirectory() const; + // Verify if this path exists and is a file on the file system. + bool IsFile() const; + +#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE) + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + bool FileOrDirectoryExists() const; + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + // Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt" + PathString ReplaceExt(const mpt::PathString &newExt) const; + + // Removes special characters from a filename component and replaces them with a safe replacement character ("_" on windows). + // Returns the result. + // Note that this also removes path component separators, so this should only be used on single-component PathString objects. + PathString SanitizeComponent() const; + + bool HasTrailingSlash() const + { + if(empty()) + return false; + RawPathString::value_type c = path[path.length() - 1]; +#if MPT_OS_WINDOWS + return (c == L'\\' || c == L'/'); +#else + return (c == '/'); +#endif + } + mpt::PathString &EnsureTrailingSlash() + { + if(!path.empty() && !HasTrailingSlash()) + { +#if MPT_OS_WINDOWS + path += L'\\'; +#else + path += '/'; +#endif + } + return *this; + } + + mpt::PathString WithoutTrailingSlash() const + { + mpt::PathString result = *this; + while(result.HasTrailingSlash()) + { + if(result.Length() == 1) + { + return result; + } + result = result.AsNative().substr(0, result.AsNative().length() - 1); + } + return result; + } + + mpt::PathString WithTrailingSlash() const + { + mpt::PathString result = *this; + result.EnsureTrailingSlash(); + return result; + } + + // Relative / absolute paths conversion + mpt::PathString AbsolutePathToRelative(const mpt::PathString &relativeTo) const; + mpt::PathString RelativePathToAbsolute(const mpt::PathString &relativeTo) const; + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + +public: + +#if MPT_OS_WINDOWS + +#if !(MPT_WSTRING_CONVERT) +#error "mpt::PathString on Windows depends on MPT_WSTRING_CONVERT)" +#endif + // conversions +#if defined(MPT_ENABLE_CHARSET_LOCALE) + MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::CharsetLocale, path); } +#endif + std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, path); } + std::wstring ToWide() const { return path; } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) + MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); } +#endif + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetUTF8, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(path); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWide(path)); } + RawPathString AsNative() const { return path; } + // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters. + RawPathString AsNativePrefixed() const; + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#if defined(_MFC_VER) + // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE + MPT_DEPRECATED_PATH CString ToCString() const { return mpt::ToCString(path); } + MPT_DEPRECATED_PATH static PathString FromCString(const CString &path) { return PathString(mpt::ToWide(path)); } + // Non-warning-generating versions of the above. Use with extra care. + CString ToCStringSilent() const { return mpt::ToCString(path); } + static PathString FromCStringSilent(const CString &path) { return PathString(mpt::ToWide(path)); } + // really special purpose, if !UNICODE, encode unicode in CString as UTF8: + static mpt::PathString TunnelOutofCString(const CString &path); + static CString TunnelIntoCString(const mpt::PathString &path); + // CStringW +#ifdef UNICODE + MPT_DEPRECATED_PATH CString ToCStringW() const { return mpt::ToCString(path); } + MPT_DEPRECATED_PATH static PathString FromCStringW(const CString &path) { return PathString(mpt::ToWide(path)); } +#else + CStringW ToCStringW() const { return mpt::ToCStringW(path); } + static PathString FromCStringW(const CStringW &path) { return PathString(mpt::ToWide(path)); } +#endif +#endif + + // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries + mpt::PathString Simplify() const; + +#else // !MPT_OS_WINDOWS + + // conversions +#if defined(MPT_ENABLE_CHARSET_LOCALE) + std::string ToLocale() const { return path; } + std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, path); } +#if MPT_WSTRING_CONVERT + std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetLocale, path); } +#endif + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetLocale, path); } + static PathString FromLocale(const std::string &path) { return PathString(path); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(path); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, mpt::CharsetUTF8, path)); } +#if MPT_WSTRING_CONVERT + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } +#endif + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } + RawPathString AsNative() const { return path; } + RawPathString AsNativePrefixed() const { return path; } + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#else // !MPT_ENABLE_CHARSET_LOCALE + std::string ToUTF8() const { return path; } +#if MPT_WSTRING_CONVERT + std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetUTF8, path); } +#endif + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetUTF8, path); } + static PathString FromUTF8(const std::string &path) { return path; } +#if MPT_WSTRING_CONVERT + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } +#endif + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } + RawPathString AsNative() const { return path; } + RawPathString AsNativePrefixed() const { return path; } + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#endif // MPT_ENABLE_CHARSET_LOCALE + + // Convert a path to its simplified form (currently only implemented on Windows) + MPT_DEPRECATED mpt::PathString Simplify() const { return path; } + +#endif // MPT_OS_WINDOWS + +}; + + + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } +#endif +static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } +#if MPT_WSTRING_FORMAT +static inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); } +#endif + +} // namespace mpt + +#if MPT_OS_WINDOWS + +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x ) + +#else // !MPT_OS_WINDOWS + +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) + +#endif // MPT_OS_WINDOWS + +namespace mpt +{ + +bool IsPathSeparator(mpt::RawPathString::value_type c); + + + +bool PathIsAbsolute(const mpt::PathString &path); + +#if MPT_OS_WINDOWS + +// Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW) +mpt::PathString GetAbsolutePath(const mpt::PathString &path); + +#ifdef MODPLUG_TRACKER + +// Deletes a complete directory tree. Handle with EXTREME care. +// Returns false if any file could not be removed and aborts as soon as it +// encounters any error. path must be absolute. +bool DeleteWholeDirectoryTree(mpt::PathString path); + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS + +#if MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) + +// Returns the application path or an empty string (if unknown), e.g. "C:\mptrack\" +mpt::PathString GetAppPath(); + +#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE + +#if defined(MPT_ENABLE_DYNBIND) + +#if !MPT_OS_WINDOWS_WINRT +// Returns the system directory path, e.g. "C:\Windows\System32\" +mpt::PathString GetSystemPath(); +#endif // !MPT_OS_WINDOWS_WINRT + +#endif // MPT_ENABLE_DYNBIND + +#endif // MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_TEMPFILE) +#if MPT_OS_WINDOWS + +// Returns temporary directory (with trailing backslash added) (e.g. "C:\TEMP\") +mpt::PathString GetTempDirectory(); + +// Returns a new unique absolute path. +mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = MPT_PATHSTRING("tmp")); + + + +// Scoped temporary file guard. Deletes the file when going out of scope. +// The file itself is not created automatically. +class TempFileGuard +{ +private: + const mpt::PathString filename; +public: + TempFileGuard(const mpt::PathString &filename = CreateTempFileName()); + mpt::PathString GetFilename() const; + ~TempFileGuard(); +}; + +#ifdef MODPLUG_TRACKER + +// Scoped temporary directory guard. Deletes the directory when going out of scope. +// The directory itself is created automatically. +class TempDirGuard +{ +private: + mpt::PathString dirname; +public: + TempDirGuard(const mpt::PathString &dirname_ = CreateTempFileName()); + mpt::PathString GetDirname() const; + ~TempDirGuard(); +}; + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS +#endif // MPT_ENABLE_TEMPFILE + +} // namespace mpt + + + +#if defined(MODPLUG_TRACKER) + +// Sanitize a filename (remove special chars) +void SanitizeFilename(mpt::PathString &filename); + +void SanitizeFilename(char *beg, char *end); +void SanitizeFilename(wchar_t *beg, wchar_t *end); + +void SanitizeFilename(std::string &str); +void SanitizeFilename(std::wstring &str); +#if MPT_USTRING_MODE_UTF8 +void SanitizeFilename(mpt::u8string &str); +#endif // MPT_USTRING_MODE_UTF8 + +template +void SanitizeFilename(char (&buffer)[size]) +{ + STATIC_ASSERT(size > 0); + SanitizeFilename(buffer, buffer + size); +} + +template +void SanitizeFilename(wchar_t (&buffer)[size]) +{ + STATIC_ASSERT(size > 0); + SanitizeFilename(buffer, buffer + size); +} + +#if defined(_MFC_VER) +void SanitizeFilename(CString &str); +#endif + +#endif // MODPLUG_TRACKER + + +#if defined(MODPLUG_TRACKER) + +enum FileTypeFormat +{ + FileTypeFormatNone = 0 , // do not show extensions after description, i.e. "Foo Files" + FileTypeFormatShowExtensions = 1<<0, // show extensions after descripten, i.e. "Foo Files (*.foo,*.bar)" +}; +MPT_DECLARE_ENUM(FileTypeFormat) + +class FileType +{ +private: + mpt::ustring m_ShortName; // "flac", "mod" (lowercase) + mpt::ustring m_Description; // "FastTracker 2 Module" + std::vector m_MimeTypes; // "audio/ogg" (in ASCII) + std::vector m_Extensions; // "mod", "xm" (lowercase) + std::vector m_Prefixes; // "mod" for "mod.*" +public: + FileType() { } + FileType(const std::vector &group) + { + for(const auto &type : group) + { + m_MimeTypes.insert(m_MimeTypes.end(), type.m_MimeTypes.begin(), type.m_MimeTypes.end()); + m_Extensions.insert(m_Extensions.end(), type.m_Extensions.begin(), type.m_Extensions.end()); + m_Prefixes.insert(m_Prefixes.end(), type.m_Prefixes.begin(), type.m_Prefixes.end()); + } + } + static FileType Any() + { + return FileType().ShortName(MPT_USTRING("*")).Description(MPT_USTRING("All Files")).AddExtension(MPT_PATHSTRING("*")); + } +public: + FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; } + FileType& Description(const mpt::ustring &description) { m_Description = description; return *this; } + FileType& MimeTypes(const std::vector &mimeTypes) { m_MimeTypes = mimeTypes; return *this; } + FileType& Extensions(const std::vector &extensions) { m_Extensions = extensions; return *this; } + FileType& Prefixes(const std::vector &prefixes) { m_Prefixes = prefixes; return *this; } + FileType& AddMimeType(const std::string &mimeType) { m_MimeTypes.push_back(mimeType); return *this; } + FileType& AddExtension(const mpt::PathString &extension) { m_Extensions.push_back(extension); return *this; } + FileType& AddPrefix(const mpt::PathString &prefix) { m_Prefixes.push_back(prefix); return *this; } +public: + mpt::ustring GetShortName() const { return m_ShortName; } + mpt::ustring GetDescription() const { return m_Description; } + std::vector GetMimeTypes() const { return m_MimeTypes; } + std::vector GetExtensions() const { return m_Extensions; } + std::vector GetPrefixes() const { return m_Prefixes; } +public: + mpt::PathString AsFilterString(FlagSet format = FileTypeFormatNone) const; + mpt::PathString AsFilterOnlyString() const; +}; // class FileType + + +// "Ogg Vorbis|*.ogg;*.oga|" // FileTypeFormatNone +// "Ogg Vorbis (*.ogg,*.oga)|*.ogg;*.oga|" // FileTypeFormatShowExtensions +mpt::PathString ToFilterString(const FileType &fileType, FlagSet format = FileTypeFormatNone); +mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format = FileTypeFormatNone); + +// "*.ogg;*.oga" / ";*.ogg;*.oga" +mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty = false); +mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty = false); + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp new file mode 100644 index 000000000..5689a4ead --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp @@ -0,0 +1,318 @@ +/* + * mptRandom.cpp + * ------------- + * Purpose: PRNG + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "mptRandom.h" + +#include "Endianness.h" +#include "mptCRC.h" + +#include + +#include +#include +#include + +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ + + +template +static T log2(T x) +{ + return std::log(x) / std::log(static_cast(2)); +} + + +static MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +{ + return detail::lower_bound_entropy_bits(x); +} + + +template +static inline bool is_mask(T x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + unsigned_T ux = static_cast(x); + unsigned_T mask = 0; + for(std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) + { + mask = (mask << 1) | 1u; + if(ux == mask) + { + return true; + } + } + return false; +} + + +namespace { +template struct default_hash { }; +template <> struct default_hash { typedef mpt::checksum::crc16 type; }; +template <> struct default_hash { typedef mpt::checksum::crc16 type; }; +template <> struct default_hash { typedef mpt::checksum::crc32c type; }; +template <> struct default_hash { typedef mpt::checksum::crc64_jones type; }; +} + +template +static T generate_timeseed() +{ + // Note: CRC is actually not that good a choice here, but it is simple and we + // already have an implementaion available. Better choices for mixing entropy + // would be a hash function with proper avalanche characteristics or a block + // or stream cipher with any pre-choosen random key and IV. The only aspect we + // really need here is whitening of the bits. + typename mpt::default_hash::type hash; + +#ifdef MPT_BUILD_FUZZER + + return static_cast(mpt::FUZZER_RNG_SEED); + +#else // !MPT_BUILD_FUZZER + + { + #if MPT_OS_WINDOWS + FILETIME t; + MemsetZero(t); + GetSystemTimeAsFileTime(&t); + #else // !MPT_OS_WINDOWS + std::time_t t = std::time(nullptr); + #endif // MPT_OS_WINDOWS + mpt::byte bytes[sizeof(t)]; + std::memcpy(bytes, &t, sizeof(t)); + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + { + std::reverse(std::begin(bytes), std::end(bytes)); + } + hash(std::begin(bytes), std::end(bytes)); + } + + { + std::clock_t c = std::clock(); + mpt::byte bytes[sizeof(c)]; + std::memcpy(bytes, &c, sizeof(c)); + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + { + std::reverse(std::begin(bytes), std::end(bytes)); + } + hash(std::begin(bytes), std::end(bytes)); + } + + return static_cast(hash.result()); + +#endif // MPT_BUILD_FUZZER + +} + + +#ifdef MODPLUG_TRACKER + +namespace rng +{ + +void crand::reseed(uint32 seed) +{ + std::srand(seed); +} + +crand::result_type crand::operator()() +{ + return std::rand(); +} + +} // namespace rng + +#endif // MODPLUG_TRACKER + +sane_random_device::sane_random_device() + : rd_reliable(rd.entropy() > 0.0) +{ + if(!rd_reliable) + { + init_fallback(); + } +} + +sane_random_device::sane_random_device(const std::string & token_) + : token(token_) + , rd(token) + , rd_reliable(rd.entropy() > 0.0) +{ + if(!rd_reliable) + { + init_fallback(); + } +} + +void sane_random_device::init_fallback() +{ + if(!rd_fallback) + { + if(token.length() > 0) + { + uint64 seed_val = mpt::generate_timeseed(); + std::vector seeds; + seeds.push_back(static_cast(seed_val >> 32)); + seeds.push_back(static_cast(seed_val >> 0)); + for(std::size_t i = 0; i < token.length(); ++i) + { + seeds.push_back(static_cast(static_cast(token[i]))); + } + std::seed_seq seed(seeds.begin(), seeds.end()); + rd_fallback = mpt::make_unique(seed); + } else + { + uint64 seed_val = mpt::generate_timeseed(); + unsigned int seeds[2]; + seeds[0] = static_cast(seed_val >> 32); + seeds[1] = static_cast(seed_val >> 0); + std::seed_seq seed(seeds + 0, seeds + 2); + rd_fallback = mpt::make_unique(seed); + } + } +} + +sane_random_device::result_type sane_random_device::operator()() +{ + MPT_LOCK_GUARD l(m); + result_type result = 0; + try + { + if(rd.min() != 0 || !mpt::is_mask(rd.max())) + { // insane std::random_device + // This implementation is not exactly uniformly distributed but good enough + // for OpenMPT. + double rd_min = static_cast(rd.min()); + double rd_max = static_cast(rd.max()); + double rd_range = rd_max - rd_min; + double rd_size = rd_range + 1.0; + double rd_entropy = mpt::log2(rd_size); + int iterations = static_cast(std::ceil(result_bits() / rd_entropy)); + double tmp = 0.0; + for(int i = 0; i < iterations; ++i) + { + tmp = (tmp * rd_size) + (static_cast(rd()) - rd_min); + } + double result_01 = std::floor(tmp / std::pow(rd_size, iterations)); + result = static_cast(std::floor(result_01 * (static_cast(max() - min()) + 1.0))) + min(); + } else + { // sane std::random_device + result = 0; + std::size_t rd_bits = mpt::lower_bound_entropy_bits(rd.max()); + for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) + { + if(rd_bits < (sizeof(result_type) * 8)) + { + result = (result << rd_bits) | static_cast(rd()); + } else + { + result = result | static_cast(rd()); + } + } + } + } catch(const std::exception &) + { + rd_reliable = false; + init_fallback(); + } + if(!rd_reliable) + { // std::random_device is unreliable + // XOR the generated random number with more entropy from the time-seeded + // PRNG. + // Note: This is safe even if the std::random_device itself is implemented + // as a std::mt19937 PRNG because we are very likely using a different + // seed. + result ^= mpt::random(*rd_fallback); + } + return result; +} + +prng_random_device_seeder::prng_random_device_seeder() +{ + return; +} + +uint8 prng_random_device_seeder::generate_seed8() +{ + return mpt::generate_timeseed(); +} + +uint16 prng_random_device_seeder::generate_seed16() +{ + return mpt::generate_timeseed(); +} + +uint32 prng_random_device_seeder::generate_seed32() +{ + return mpt::generate_timeseed(); +} + +uint64 prng_random_device_seeder::generate_seed64() +{ + return mpt::generate_timeseed(); +} + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) + +static mpt::random_device *g_rd = nullptr; +static mpt::thread_safe_prng *g_best_prng = nullptr; + +void set_global_random_device(mpt::random_device *rd) +{ + g_rd = rd; +} + +void set_global_prng(mpt::thread_safe_prng *prng) +{ + g_best_prng = prng; +} + +mpt::random_device & global_random_device() +{ + return *g_rd; +} + +mpt::thread_safe_prng & global_prng() +{ + return *g_best_prng; +} + +#else + +mpt::random_device & global_random_device() +{ + static mpt::random_device g_rd; + return g_rd; +} + +mpt::thread_safe_prng & global_prng() +{ + static mpt::thread_safe_prng g_best_prng(global_random_device()); + return g_best_prng; +} + +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h new file mode 100644 index 000000000..ec75928d1 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h @@ -0,0 +1,643 @@ +/* + * mptRandom.h + * ----------- + * Purpose: PRNG + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "mptMutex.h" + +#include +#include + +#ifdef MODPLUG_TRACKER +#include +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_BEGIN + + +// NOTE: +// We implement our own PRNG and distribution functions as the implementations +// of std::uniform_int_distribution is either wrong (not uniform in MSVC2010) or +// not guaranteed to be livelock-free for bad PRNGs (in GCC, Clang, boost). +// We resort to a simpler implementation with only power-of-2 result ranges for +// both the underlying PRNG and our interface function. This saves us from +// complicated code having to deal with partial bits of entropy. +// Our interface still somewhat follows the mindset of C++11 (with the +// addition of a simple wrapper function mpt::random which saves the caller from +// instantiating distribution objects for the common uniform distribution case. +// We are still using std::random_device for initial seeding when avalable and +// after working around its set of problems. + + +namespace mpt +{ + + +#ifdef MPT_BUILD_FUZZER +static const uint32 FUZZER_RNG_SEED = 3141592653u; // pi +#endif // MPT_BUILD_FUZZER + + +namespace detail +{ + +MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +{ + // easy to compile-time evaluate even for stupid compilers + return + x >= 0xffffffffu ? 32 : + x >= 0x7fffffffu ? 31 : + x >= 0x3fffffffu ? 30 : + x >= 0x1fffffffu ? 29 : + x >= 0x0fffffffu ? 28 : + x >= 0x07ffffffu ? 27 : + x >= 0x03ffffffu ? 26 : + x >= 0x01ffffffu ? 25 : + x >= 0x00ffffffu ? 24 : + x >= 0x007fffffu ? 23 : + x >= 0x003fffffu ? 22 : + x >= 0x001fffffu ? 21 : + x >= 0x000fffffu ? 20 : + x >= 0x0007ffffu ? 19 : + x >= 0x0003ffffu ? 18 : + x >= 0x0001ffffu ? 17 : + x >= 0x0000ffffu ? 16 : + x >= 0x00007fffu ? 15 : + x >= 0x00003fffu ? 14 : + x >= 0x00001fffu ? 13 : + x >= 0x00000fffu ? 12 : + x >= 0x000007ffu ? 11 : + x >= 0x000003ffu ? 10 : + x >= 0x000001ffu ? 9 : + x >= 0x000000ffu ? 8 : + x >= 0x0000007fu ? 7 : + x >= 0x0000003fu ? 6 : + x >= 0x0000001fu ? 5 : + x >= 0x0000000fu ? 4 : + x >= 0x00000007u ? 3 : + x >= 0x00000003u ? 2 : + x >= 0x00000001u ? 1 : + 0; +} + +} + + +template struct engine_traits +{ + typedef typename Trng::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() + { + return Trng::result_bits(); + } + template + static inline Trng make(Trd & rd) + { + return Trng(rd); + } +}; + + +template +inline T random(Trng & rng) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) + { + MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + { + MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + return static_cast(result); +} + +template +inline T random(Trng & rng) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + { + MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + { + MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + MPT_CONSTANT_IF(required_entropy_bits >= (sizeof(T) * 8)) + { + return static_cast(result); + } else + { + return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); + } +} + +template +inline T random(Trng & rng, std::size_t required_entropy_bits) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + { + MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + { + MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + if(required_entropy_bits >= (sizeof(T) * 8)) + { + return static_cast(result); + } else + { + return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); + } +} + +template struct float_traits { }; +template <> struct float_traits { + typedef uint32 mantissa_uint_type; + static const int mantissa_bits = 24; +}; +template <> struct float_traits { + typedef uint64 mantissa_uint_type; + static const int mantissa_bits = 53; +}; +template <> struct float_traits { + typedef uint64 mantissa_uint_type; + static const int mantissa_bits = 63; +}; + +template +struct uniform_real_distribution +{ +private: + T a; + T b; +public: + inline uniform_real_distribution(T a, T b) + : a(a) + , b(b) + { + return; + } + template + inline T operator()(Trng & rng) const + { + typedef typename float_traits::mantissa_uint_type uint_type; + const int bits = float_traits::mantissa_bits; + return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << bits))) + a; + } +}; + + +template +inline T random(Trng & rng, T min, T max) +{ + STATIC_ASSERT(!std::numeric_limits::is_integer); + typedef mpt::uniform_real_distribution dis_type; + dis_type dis(min, max); + return static_cast(dis(rng)); +} + + +namespace rng +{ + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4724) // potential mod by 0 +#endif // MPT_COMPILER_MSVC + +template +class lcg +{ +public: + typedef Tstate state_type; + typedef Tvalue result_type; +private: + state_type state; +public: + template + explicit inline lcg(Trng & rd) + : state(mpt::random(rd)) + { + operator()(); // we return results from the current state and update state after returning. results in better pipelining. + } + explicit inline lcg(state_type seed) + : state(seed) + { + operator()(); // we return results from the current state and update state after returning. results in better pipelining. + } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return static_cast(0); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + STATIC_ASSERT(((result_mask >> result_shift) << result_shift) == result_mask); + return static_cast(result_mask >> result_shift); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + STATIC_ASSERT(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); + return result_bits_; + } + inline result_type operator()() + { + // we return results from the current state and update state after returning. results in better pipelining. + state_type s = state; + result_type result = static_cast((s & result_mask) >> result_shift); + s = Util::ModIfNotZero((a * s) + c); + state = s; + return result; + } +}; + +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + +typedef lcg lcg_msvc; +typedef lcg lcg_c99; +typedef lcg lcg_musl; + +} // namespace rng + + +#ifdef MODPLUG_TRACKER + +namespace rng +{ + +class crand +{ +public: + typedef void state_type; + typedef int result_type; +private: + static void reseed(uint32 seed); +public: + template + static void reseed(Trd & rd) + { + reseed(mpt::random(rd)); + } +public: + crand() { } + explicit crand(const std::string &) { } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return 0; + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return RAND_MAX; + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return detail::lower_bound_entropy_bits(RAND_MAX); + } + result_type operator()(); +}; + +} // namespace rng + +#endif // MODPLUG_TRACKER + + +// C++11 std::random_device may be implemented as a deterministic PRNG. +// There is no way to seed this PRNG and it is allowed to be seeded with the +// same value on each program invocation. This makes std::random_device +// completely useless even as a non-cryptographic entropy pool. +// We fallback to time-seeded std::mt19937 if std::random_device::entropy() is +// 0 or less. +class sane_random_device +{ +private: + mpt::mutex m; + std::string token; + std::random_device rd; + bool rd_reliable; + std::unique_ptr rd_fallback; +public: + typedef unsigned int result_type; +private: + void init_fallback(); +public: + sane_random_device(); + sane_random_device(const std::string & token); + static MPT_CONSTEXPR11_FUN result_type min() + { + return std::numeric_limits::min(); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return sizeof(result_type) * 8; + } + result_type operator()(); +}; + + +template +class seed_seq_values +{ +private: + unsigned int seeds[N]; +public: + template + explicit seed_seq_values(Trd & rd) + { + for(std::size_t i = 0; i < N; ++i) + { + seeds[i] = rd(); + } + } + const unsigned int * begin() const + { + return seeds + 0; + } + const unsigned int * end() const + { + return seeds + N; + } +}; + + +// C++11 random does not provide any sane way to determine the amount of entropy +// required to seed a particular engine. VERY STUPID. +// List the ones we are likely to use. + +template <> struct engine_traits { + static const std::size_t seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size; + typedef std::mt19937 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + static const std::size_t seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size; + typedef std::mt19937_64 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + static const std::size_t seed_bits = std::ranlux24_base::word_size; + typedef std::ranlux24_base rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + static const std::size_t seed_bits = std::ranlux48_base::word_size; + typedef std::ranlux48_base rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + static const std::size_t seed_bits = std::ranlux24_base::word_size; + typedef std::ranlux24 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux24_base::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + static const std::size_t seed_bits = std::ranlux48_base::word_size; + typedef std::ranlux48 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux48_base::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + + +class prng_random_device_seeder +{ +private: + uint8 generate_seed8(); + uint16 generate_seed16(); + uint32 generate_seed32(); + uint64 generate_seed64(); +protected: + template inline T generate_seed(); +protected: + prng_random_device_seeder(); +}; + +template <> inline uint8 prng_random_device_seeder::generate_seed() { return generate_seed8(); } +template <> inline uint16 prng_random_device_seeder::generate_seed() { return generate_seed16(); } +template <> inline uint32 prng_random_device_seeder::generate_seed() { return generate_seed32(); } +template <> inline uint64 prng_random_device_seeder::generate_seed() { return generate_seed64(); } + +template +class prng_random_device + : public prng_random_device_seeder +{ +public: + typedef unsigned int result_type; +private: + mpt::mutex m; + Trng rng; +public: + prng_random_device() + : rng(generate_seed()) + { + return; + } + prng_random_device(const std::string &) + : rng(generate_seed()) + { + return; + } + static MPT_CONSTEXPR11_FUN result_type min() + { + return std::numeric_limits::min(); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return sizeof(unsigned int) * 8; + } + result_type operator()() + { + MPT_LOCK_GUARD l(m); + return mpt::random(rng); + } +}; + + +#ifdef MPT_BUILD_FUZZER + +// 1. Use deterministic seeding +typedef mpt::prng_random_device random_device; + +// 2. Use fast PRNGs in order to not waste time fuzzing more complex PRNG +// implementations. +typedef mpt::rng::lcg_msvc fast_prng; +typedef mpt::rng::lcg_c99 main_prng; +typedef mpt::rng::lcg_musl best_prng; + +#else // !MPT_BUILD_FUZZER + +// mpt::random_device always generates 32 bits of entropy +typedef mpt::sane_random_device random_device; + +// We cannot use std::minstd_rand here because it has not a power-of-2 sized +// output domain which we rely upon. +typedef mpt::rng::lcg_msvc fast_prng; // about 3 ALU operations, ~32bit of state, suited for inner loops +typedef std::mt19937 main_prng; +#if MPT_MSVC_AT_LEAST(2017,5) && defined(_MSC_FULL_VER) +#if (_MSC_FULL_VER < 191225831) +// work-around compiler crash +// c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\random(978): fatal error C1001: An internal error has occurred in the compiler. +// (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 258) +// reported at: https://developercommunity.visualstudio.com/content/problem/162089/random-engines-crashing-vs-155-optimizer.html?childToView=164098#comment-164098 +typedef std::mt19937_64 best_prng; +#else +typedef std::ranlux48 best_prng; +#endif +#else +typedef std::ranlux48 best_prng; +#endif + +#endif // MPT_BUILD_FUZZER + + +typedef mpt::main_prng default_prng; +typedef mpt::main_prng prng; + + +template +inline Trng make_prng(Trd & rd) +{ + return mpt::engine_traits::make(rd); +} + + +template +class thread_safe_prng + : private Trng +{ +private: + mpt::mutex m; +public: + typedef typename Trng::result_type result_type; +public: + template + explicit thread_safe_prng(Trd & rd) + : Trng(mpt::make_prng(rd)) + { + return; + } + thread_safe_prng(Trng rng) + : Trng(rng) + { + return; + } +public: + static MPT_CONSTEXPR11_FUN typename engine_traits::result_type min() + { + return Trng::min(); + } + static MPT_CONSTEXPR11_FUN typename engine_traits::result_type max() + { + return Trng::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return engine_traits::result_bits(); + } +public: + typename engine_traits::result_type operator()() + { + MPT_LOCK_GUARD l(m); + return Trng::operator()(); + } +}; + + +mpt::random_device & global_random_device(); +mpt::thread_safe_prng & global_prng(); + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) +void set_global_random_device(mpt::random_device *rd); +void set_global_prng(mpt::thread_safe_prng *rng); +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp new file mode 100644 index 000000000..dbf68221a --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp @@ -0,0 +1,1691 @@ +/* + * mptString.cpp + * ------------- + * Purpose: Small string-related utilities, number and message formatting. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptString.h" + +#include "Endianness.h" + +#if defined(MPT_CHARSET_CODECVTUTF8) +#include +#endif +#if defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) +#include +#endif +#include +#include +#include +#include + +#if defined(MODPLUG_TRACKER) +#include +#endif // MODPLUG_TRACKER + +#if defined(MODPLUG_TRACKER) +#include +#endif // MODPLUG_TRACKER + +#if MPT_OS_WINDOWS +#include +#endif + +#if defined(MPT_CHARSET_ICONV) +#include +#include +#endif + + +OPENMPT_NAMESPACE_BEGIN + + + +/* + + + +Quick guide to the OpenMPT string type jungle +============================================= + + + +This quick guide is only meant as a hint. There may be valid reasons to not +honor the recommendations found here. Staying consistent with surrounding and/or +related code sections may also be important. + + + +List of string types +-------------------- + + * std::string (OpenMPT, libopenmpt) + C++ string of unspecifed 8bit encoding. Try to always document the + encoding if not clear from context. Do not use unless there is an obvious + reason to do so. + + * std::wstring (OpenMPT) + UTF16 (on windows) or UTF32 (otherwise). Do not use unless there is an + obvious reason to do so. + + * char* (OpenMPT, libopenmpt) + C string of unspecified encoding. Use only for static literals or in + performance critical inner loops where full control and avoidance of memory + allocations is required. + + * wchar_t* (OpenMPT) + C wide string. Use only if Unicode is required for static literals or in + performance critical inner loops where full control and avoidance of memory + allocation is required. + + * CString (OpenMPT) + MFC string type, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if + UNICODE). Specify literals with _T(""). Use in MFC GUI code. + + * CStringA (OpenMPT) + MFC ANSI string type. The encoding is always CP_ACP. Do not use unless there + is an obvious reason to do so. + + * CStringW (OpenMPT) + MFC Unicode string type. Use in MFC GUI code when explicit Unicode support + is required. + + * mpt::PathString (OpenMPT, libopenmpt) + String type representing paths and filenames. Always use for these in order + to avoid potentially lossy conversions. Use MPT_PATHSTRING("") macro for + literals. + + * mpt::ustring (OpenMPT, libopenmpt) + The default unicode string type. Can be encoded in UTF8 or UTF16 or UTF32, + depending on MPT_USTRING_MODE_* and sizeof(wchar_t). Literals can written as + MPT_USTRING(""). Use as your default string type if no other string type is + a measurably better fit. + + * MPT_UTF8 (OpenMPT, libopenmpt) + Macro that generates a mpt::ustring from string literals containing + non-ascii characters. In order to keep the source code in ascii encoding, + always express non-ascii characters using explicit \x23 escaping. Note that + depending on the underlying type of mpt::ustring, MPT_UTF8 *requires* a + runtime conversion. Only use for string literals containing non-ascii + characters (use MPT_USTRING otherwise). + + * MPT_ULITERAL / MPT_UCHAR / MPT_UCHAR_TYPE (OpenMPT, libopenmpt) + Macros which generate string literals, char literals and the char literal + type respectively. These are especially useful in constexpr contexts or + global data where MPT_USTRING is either unusable or requires a global + contructor to run. Do NOT use as a performance optimization in place of + MPT_USTRING however, because MPT_USTRING can be converted to C++11/14 user + defined literals eventually, while MPT_ULITERAL cannot because of constexpr + requirements. + + * mpt::RawPathString (OpenMPT, libopenmpt) + Internal representation of mpt::PathString. Only use for parsing path + fragments. + + * mpt::u8string (OpenMPT, libopenmpt) + Internal representation of mpt::ustring. Do not use directly. Ever. + + * std::basic_string (OpenMPT) + Same as std::string. Do not use std::basic_string in the templated form. + + * std::basic_string (OpenMPT) + Same as std::wstring. Do not use std::basic_string in the templated form. + +The following string types are available in order to avoid the need to overload +functions on a huge variety of string types. Use only ever as function argument +types. +Note that the locale charset is not available on all libopenmpt builds (in which +case the option is ignored or a sensible fallback is used; these types are +always available). +All these types publicly inherit from mpt::ustring and do not contain any +additional state. This means that they work the same way as mpt::ustring does +and do support type-slicing for both, read and write accesses. +These types only add conversion constructors for all string types that have a +defined encoding and for all 8bit string types using the specified encoding +heuristic. + + * AnyUnicodeString (OpenMPT, libopenmpt) + Is constructible from any Unicode string. + + * AnyString (OpenMPT, libopenmpt) + Tries to do the smartest auto-magic we can do. + + * AnyLocaleString (OpenMPT, libopenmpt) + char-based strings are assumed to be in locale encoding. + + * AnyStringUTF8orLocale (OpenMPT, libopenmpt) + char-based strings are tried in UTF8 first, if this fails, locale is used. + + * AnyStringUTF8 (OpenMPT, libopenmpt) + char-based strings are assumed to be in UTF8. + + + +Encoding of 8bit strings +------------------------ + +8bit strings have an unspecified encoding. When the string is contained within a +CSoundFile object, the encoding is most likely CSoundFile::GetCharsetInternal(), +otherwise, try to gather the most probable encoding from surrounding or related +code sections. + + + +Decision tree to help deciding which string type to use +------------------------------------------------------- + +if in libopenmpt + if in libopenmpt c++ interface + T = std::string, the encoding is utf8 + elif in libopenmpt c interface + T = char*, the encoding is utf8 + elif performance critical inner loop + T = char*, document the encoding if not clear from context + elif string literal containing non-ascii characters + T = MPT_UTF8 + elif path or file + if parsing path fragments + T = mpt::RawPathString + template your function on the concrete underlying string type + (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS + else + T = mpt::PathString + fi + else + T = mpt::ustring + fi +else + if performance critical inner loop + if needs unicode support + T = MPT_UCHAR_TYPE* / MPT_ULITERAL + else + T = char*, document the encoding if not clear from context + fi + elif string literal containing non-ascii characters + T = MPT_UTF8 + elif path or file + if parsing path fragments + T = mpt::RawPathString + template your function on the concrete underlying string type + (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS + else + T = mpt::PathString + fi + elif mfc/gui code + if directly interface with wide winapi + T = CStringW + elif needs unicode support + T = CStringW + else + T = CString + fi + else + if directly interfacing with wide winapi + T = std::wstring + else + if constexpr context or global data + T = MPT_UCHAR_TYPE* / MPT_ULITERAL + else + T = mpt::ustring + fi + fi + fi +fi + +This boils down to: Prefer mpt::PathString and mpt::ustring, and only use any +other string type if there is an obvious reason to do so. + + + +Character set conversions +------------------------- + +Character set conversions in OpenMPT are always fuzzy. + +Behaviour in case of an invalid source encoding and behaviour in case of an +unrepresentable destination encoding can be any of the following: + * The character is replaced by some replacement character ('?' or L'\ufffd' in + most cases). + * The character is replaced by a similar character (either semantically + similiar or visually similar). + * The character is transcribed with some ASCII text. + * The character is discarded. + * Conversion stops at this very character. + +Additionally. conversion may stop or continue on \0 characters in the middle of +the string. + +Behaviour can vary from one conversion tuple to any other. + +If you need to ensure lossless conversion, do a roundtrip conversion and check +for equality. + + + +Unicode handling +---------------- + +OpenMPT is generally not aware of and does not handle different Unicode +normalization forms. +You should be aware of the following possibilities: + * Conversion between UTF8, UTF16, UTF32 may or may not change between NFC and + NFD. + * Conversion from any non-Unicode 8bit encoding can result in both, NFC or NFD + forms. + * Conversion to any non-Unicode 8bit encoding may or may not involve + conversion to NFC, NFD, NFKC or NFKD during the conversion. This in + particular means that conversion of decomposed german umlauts to ISO8859-1 + may fail. + * Changing the normalization form of path strings may render the file + inaccessible. + +Unicode BOM may or may not be preserved and/or discarded during conversion. + +Invalid Unicode code points may be treated as invalid or as valid characters +when converting between different Unicode encodings. + + + +Interfacing with WinAPI +----------------------- + +When in MFC code, use CString or CStringW as appropriate. +When in non MFC code, either use std::wstring when directly interfacing with the +Unicode API, or use the TCHAR helper functions: ToTcharBuf, FromTcharBuf, +ToTcharStr, FromTcharStr. + + + +*/ + + + +namespace mpt { namespace String { + + + +/* +default 1:1 mapping +static const uint32 CharsetTableISO8859_1[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; +*/ + +#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) + +static const uint32 CharsetTableISO8859_15[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x20ac,0x00a5,0x0160,0x00a7,0x0161,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x017d,0x00b5,0x00b6,0x00b7,0x017e,0x00b9,0x00ba,0x00bb,0x0152,0x0153,0x0178,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; + +static const uint32 CharsetTableWindows1252[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x20ac,0x0081,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,0x02c6,0x2030,0x0160,0x2039,0x0152,0x008d,0x017d,0x008f, + 0x0090,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,0x02dc,0x2122,0x0161,0x203a,0x0153,0x009d,0x017e,0x0178, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; + +static const uint32 CharsetTableCP437[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +#endif // MPT_CHARSET_CODECVTUTF8 || MPT_CHARSET_INTERNAL || MPT_CHARSET_WIN32 + + +#define C(x) (static_cast((x))) + +// AMS1 actually only supports ASCII plus the modified control characters and no high chars at all. +// Just default to CP437 for those to keep things simple. +static const uint32 CharsetTableCP437AMS[256] = { + C(' '),0x0001,0x0002,0x0003,0x00e4,0x0005,0x00e5,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x00c4,0x00c5, // differs from CP437 + 0x0010,0x0011,0x0012,0x0013,0x00f6,0x0015,0x0016,0x0017,0x0018,0x00d6,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, // differs from CP437 + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +// AMS2: Looking at Velvet Studio's bitmap font (TPIC32.PCX), these appear to be the only supported non-ASCII chars. +static const uint32 CharsetTableCP437AMS2[256] = { + C(' '),0x00a9,0x221a,0x00b7,C('0'),C('1'),C('2'),C('3'),C('4'),C('5'),C('6'),C('7'),C('8'),C('9'),C('A'),C('B'), // differs from CP437 + C('C'),C('D'),C('E'),C('F'),C(' '),0x00a7,C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '), // differs from CP437 + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +#undef C + +#if MPT_COMPILER_MSVC +#pragma warning(disable:4428) // universal-character-name encountered in source +#endif + +static std::wstring From8bit(const std::string &str, const uint32 (&table)[256], wchar_t replacement = L'\uFFFD') +{ + std::wstring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint32 c = static_cast(static_cast(str[i])); + if(c < mpt::size(table)) + { + res.push_back(static_cast(static_cast(table[c]))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static std::string To8bit(const std::wstring &str, const uint32 (&table)[256], char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint32 c = str[i]; + bool found = false; + // Try non-control characters first. + // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), + // characters in the common range are preferred this way. + for(std::size_t x = 0x20; x < mpt::size(table); ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + if(!found) + { + // try control characters + for(std::size_t x = 0x00; x < mpt::size(table) && x < 0x20; ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + } + if(!found) + { + res.push_back(replacement); + } + } + return res; +} + +#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) + +static std::wstring FromAscii(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + std::wstring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint8 c = str[i]; + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static std::string ToAscii(const std::wstring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint32 c = str[i]; + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static std::wstring FromISO_8859_1(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + MPT_UNREFERENCED_PARAMETER(replacement); + std::wstring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint8 c = str[i]; + res.push_back(static_cast(static_cast(c))); + } + return res; +} + +static std::string ToISO_8859_1(const std::wstring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint32 c = str[i]; + if(c <= 0xff) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +#if defined(MPT_ENABLE_CHARSET_LOCALE) + +// Note: +// +// std::codecvt::out in LLVM libc++ does not advance in and out pointers when +// running into a non-convertible cahracter. This can happen when no locale is +// set on FreeBSD or MacOSX. This behaviour violates the C++ standard. +// +// We apply the following (albeit costly, even on other platforms) work-around: +// If the conversion errors out and does not advance the pointers at all, we +// retry the conversion with a space character prepended to the string. If it +// still does error our, we retry the whole conversion character by character. +// This is costly even on other platforms in one single case: The first +// character is an invalid Unicode code point or otherwise not convertible. Any +// following non-convertible characters are not a problem. + +static std::wstring LocaleDecode(const std::string &str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::wstring(); + } + std::vector out; + typedef std::codecvt codecvt_type; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const char * in_begin = str.data(); + const char * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + wchar_t * out_begin = &(out[0]); + wchar_t * out_end = &(out[0]) + out.size(); + const char * in_next = nullptr; + wchar_t * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleDecode(std::string(" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleDecode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::wstring(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::wstring(&(out[0]), out_next); +} + +static std::string LocaleEncode(const std::wstring &str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::string(); + } + std::vector out; + typedef std::codecvt codecvt_type; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const wchar_t * in_begin = str.data(); + const wchar_t * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + char * out_begin = &(out[0]); + char * out_end = &(out[0]) + out.size(); + const wchar_t * in_next = nullptr; + char * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleEncode(std::wstring(L" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleEncode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::string(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::string(&(out[0]), out_next); +} + +static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + try + { + std::locale locale(""); // user locale + return String::LocaleDecode(str, locale, replacement); + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return String::LocaleDecode(str, locale, replacement); + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return String::LocaleDecode(str, locale, replacement); + } catch(...) + { + // nothing + } + MPT_ASSERT_NOTREACHED(); + return String::FromAscii(str, replacement); // fallback +} + +static std::string ToLocale(const std::wstring &str, char replacement = '?') +{ + try + { + std::locale locale(""); // user locale + return String::LocaleEncode(str, locale, replacement); + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return String::LocaleEncode(str, locale, replacement); + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return String::LocaleEncode(str, locale, replacement); + } catch(...) + { + // nothing + } + MPT_ASSERT_NOTREACHED(); + return String::ToAscii(str, replacement); // fallback +} + +#endif + +#endif // MPT_CHARSET_CODECVTUTF8 || MPT_CHARSET_INTERNAL || MPT_CHARSET_WIN32 + +#if defined(MPT_CHARSET_CODECVTUTF8) + +static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + MPT_UNREFERENCED_PARAMETER(replacement); + std::wstring_convert > conv; + return conv.from_bytes(str); +} + +static std::string ToUTF8(const std::wstring &str, char replacement = '?') +{ + MPT_UNREFERENCED_PARAMETER(replacement); + std::wstring_convert > conv; + return conv.to_bytes(str); +} + +#endif // MPT_CHARSET_CODECVTUTF8 + +#if defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) + +static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + const std::string &in = str; + + std::wstring out; + + // state: + std::size_t charsleft = 0; + uint32 ucs4 = 0; + + for ( uint8 c : in ) { + + if ( charsleft == 0 ) { + + if ( ( c & 0x80 ) == 0x00 ) { + out.push_back( (wchar_t)c ); + } else if ( ( c & 0xE0 ) == 0xC0 ) { + ucs4 = c & 0x1F; + charsleft = 1; + } else if ( ( c & 0xF0 ) == 0xE0 ) { + ucs4 = c & 0x0F; + charsleft = 2; + } else if ( ( c & 0xF8 ) == 0xF0 ) { + ucs4 = c & 0x07; + charsleft = 3; + } else { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + } else { + + if ( ( c & 0xC0 ) != 0x80 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + ucs4 <<= 6; + ucs4 |= c & 0x3F; + charsleft--; + + if ( charsleft == 0 ) { + MPT_CONSTANT_IF ( sizeof( wchar_t ) == 2 ) { + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + if ( ucs4 <= 0xffff ) { + out.push_back( (uint16)ucs4 ); + } else { + uint32 surrogate = ucs4 - 0x10000; + uint16 hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); + uint16 lo_sur = static_cast( ( 0x37 << 10 ) | ( (surrogate>> 0) & ((1<<10)-1) ) ); + out.push_back( hi_sur ); + out.push_back( lo_sur ); + } + } else { + out.push_back( static_cast( ucs4 ) ); + } + ucs4 = 0; + } + + } + + } + + if ( charsleft != 0 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + return out; + +} + +static std::string ToUTF8(const std::wstring &str, char replacement = '?') +{ + const std::wstring &in = str; + + std::string out; + + for ( std::size_t i=0; i( wc ); + if ( i + 1 < in.length() ) { + // check for surrogate pair + uint16 hi_sur = in[i+0]; + uint16 lo_sur = in[i+1]; + if ( hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37 ) { + // surrogate pair + ++i; + hi_sur &= (1<<10)-1; + lo_sur &= (1<<10)-1; + ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); + } else { + // no surrogate pair + ucs4 = static_cast( c ); + } + } else { + // no surrogate possible + ucs4 = static_cast( c ); + } + } else { + ucs4 = static_cast( wc ); + } + + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + continue; + } + + uint8 utf8[6]; + std::size_t numchars = 0; + for ( numchars = 0; numchars < 6; numchars++ ) { + utf8[numchars] = ucs4 & 0x3F; + ucs4 >>= 6; + if ( ucs4 == 0 ) { + break; + } + } + numchars++; + + if ( numchars == 1 ) { + out.push_back( utf8[0] ); + continue; + } + + if ( numchars == 2 && utf8[numchars-1] == 0x01 ) { + // generate shortest form + out.push_back( utf8[0] | 0x40 ); + continue; + } + + std::size_t charsleft = numchars; + while ( charsleft > 0 ) { + if ( charsleft == numchars ) { + out.push_back( utf8[ charsleft - 1 ] | ( ((1< +Tdststring EncodeImplFallback(Charset charset, const std::wstring &src); +#endif // !MPT_CHARSET_ICONV + +// templated on 8bit strings because of type-safe variants +template +Tdststring EncodeImpl(Charset charset, const std::wstring &src) +{ + STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); + if(charset == CharsetCP437AMS || charset == CharsetCP437AMS2) + { + std::string out; + if(charset == CharsetCP437AMS ) out = String::To8bit(src, CharsetTableCP437AMS ); + if(charset == CharsetCP437AMS2) out = String::To8bit(src, CharsetTableCP437AMS2); + return Tdststring(out.begin(), out.end()); + } +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == CharsetLocale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif +#endif + #if defined(MPT_CHARSET_WIN32) + if(!HasCharset(charset)) + { + return EncodeImplFallback(charset, src); + } + const UINT codepage = CharsetToCodepage(charset); + int required_size = WideCharToMultiByte(codepage, 0, src.c_str(), -1, nullptr, 0, nullptr, nullptr); + if(required_size <= 0) + { + return Tdststring(); + } + std::vector encoded_string(required_size); + WideCharToMultiByte(codepage, 0, src.c_str(), -1, encoded_string.data(), required_size, nullptr, nullptr); + return reinterpret_cast(encoded_string.data()); + #elif defined(MPT_CHARSET_ICONV) + iconv_t conv = iconv_t(); + conv = iconv_open(CharsetToStringTranslit(charset), Charset_wchar_t()); + if(!conv) + { + conv = iconv_open(CharsetToString(charset), Charset_wchar_t()); + if(!conv) + { + throw std::runtime_error("iconv conversion not working"); + } + } + std::vector wide_string(src.c_str(), src.c_str() + src.length() + 1); + std::vector encoded_string(wide_string.size() * 8); // large enough + char * inbuf = reinterpret_cast(wide_string.data()); + size_t inbytesleft = wide_string.size() * sizeof(wchar_t); + char * outbuf = encoded_string.data(); + size_t outbytesleft = encoded_string.size(); + while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) + { + if(errno == EILSEQ || errno == EILSEQ) + { + inbuf += sizeof(wchar_t); + inbytesleft -= sizeof(wchar_t); + outbuf[0] = '?'; + outbuf++; + outbytesleft--; + iconv(conv, NULL, NULL, NULL, NULL); // reset state + } else + { + iconv_close(conv); + conv = iconv_t(); + return Tdststring(); + } + } + iconv_close(conv); + conv = iconv_t(); + return reinterpret_cast(encoded_string.data()); + #else + return EncodeImplFallback(charset, src); + #endif +} + + +#if !defined(MPT_CHARSET_ICONV) +template +Tdststring EncodeImplFallback(Charset charset, const std::wstring &src) +{ + std::string out; + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case CharsetLocale: out = String::ToLocale(src); break; +#endif + case CharsetUTF8: out = String::ToUTF8(src); break; + case CharsetASCII: out = String::ToAscii(src); break; + case CharsetISO8859_1: out = String::ToISO_8859_1(src); break; + case CharsetISO8859_15: out = String::To8bit(src, CharsetTableISO8859_15); break; + case CharsetCP437: out = String::To8bit(src, CharsetTableCP437); break; + case CharsetCP437AMS: out = String::To8bit(src, CharsetTableCP437AMS); break; + case CharsetCP437AMS2: out = String::To8bit(src, CharsetTableCP437AMS2); break; + case CharsetWindows1252: out = String::To8bit(src, CharsetTableWindows1252); break; + } + return Tdststring(out.begin(), out.end()); +} +#endif // !MPT_CHARSET_ICONV + + +#if !defined(MPT_CHARSET_ICONV) +template +std::wstring DecodeImplFallback(Charset charset, const Tsrcstring &src); +#endif // !MPT_CHARSET_ICONV + +// templated on 8bit strings because of type-safe variants +template +std::wstring DecodeImpl(Charset charset, const Tsrcstring &src) +{ + STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + if(charset == CharsetCP437AMS || charset == CharsetCP437AMS2) + { + std::string in(src.begin(), src.end()); + std::wstring out; + if(charset == CharsetCP437AMS ) out = String::From8bit(in, CharsetTableCP437AMS ); + if(charset == CharsetCP437AMS2) out = String::From8bit(in, CharsetTableCP437AMS2); + return out; + } +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == CharsetLocale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif +#endif + #if defined(MPT_CHARSET_WIN32) + if(!HasCharset(charset)) + { + return DecodeImplFallback(charset, src); + } + const UINT codepage = CharsetToCodepage(charset); + int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, nullptr, 0); + if(required_size <= 0) + { + return std::wstring(); + } + std::vector decoded_string(required_size); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); + return decoded_string.data(); + #elif defined(MPT_CHARSET_ICONV) + iconv_t conv = iconv_t(); + conv = iconv_open(Charset_wchar_t(), CharsetToString(charset)); + if(!conv) + { + throw std::runtime_error("iconv conversion not working"); + } + std::vector encoded_string(reinterpret_cast(src.c_str()), reinterpret_cast(src.c_str()) + src.length() + 1); + std::vector wide_string(encoded_string.size() * 8); // large enough + char * inbuf = encoded_string.data(); + size_t inbytesleft = encoded_string.size(); + char * outbuf = reinterpret_cast(wide_string.data()); + size_t outbytesleft = wide_string.size() * sizeof(wchar_t); + while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) + { + if(errno == EILSEQ || errno == EILSEQ) + { + inbuf++; + inbytesleft--; + for(std::size_t i = 0; i < sizeof(wchar_t); ++i) + { + outbuf[i] = 0; + } + #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + outbuf[1] = uint8(0xff); outbuf[0] = uint8(0xfd); + #elif defined(MPT_PLATFORM_BIG_ENDIAN) + outbuf[sizeof(wchar_t)-1 - 1] = uint8(0xff); outbuf[sizeof(wchar_t)-1 - 0] = uint8(0xfd); + #else + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + { + outbuf[1] = uint8(0xff); outbuf[0] = uint8(0xfd); + } + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) + { + outbuf[sizeof(wchar_t)-1 - 1] = uint8(0xff); outbuf[sizeof(wchar_t)-1 - 0] = uint8(0xfd); + } + #endif + outbuf += sizeof(wchar_t); + outbytesleft -= sizeof(wchar_t); + iconv(conv, NULL, NULL, NULL, NULL); // reset state + } else + { + iconv_close(conv); + conv = iconv_t(); + return std::wstring(); + } + } + iconv_close(conv); + conv = iconv_t(); + return wide_string.data(); + #else + return DecodeImplFallback(charset, src); + #endif +} + +#if !defined(MPT_CHARSET_ICONV) +template +std::wstring DecodeImplFallback(Charset charset, const Tsrcstring &src) +{ + std::string in(src.begin(), src.end()); + std::wstring out; + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case CharsetLocale: out = String::FromLocale(in); break; +#endif + case CharsetUTF8: out = String::FromUTF8(in); break; + case CharsetASCII: out = String::FromAscii(in); break; + case CharsetISO8859_1: out = String::FromISO_8859_1(in); break; + case CharsetISO8859_15: out = String::From8bit(in, CharsetTableISO8859_15); break; + case CharsetCP437: out = String::From8bit(in, CharsetTableCP437); break; + case CharsetCP437AMS: out = String::From8bit(in, CharsetTableCP437AMS); break; + case CharsetCP437AMS2: out = String::From8bit(in, CharsetTableCP437AMS2); break; + case CharsetWindows1252: out = String::From8bit(in, CharsetTableWindows1252); break; + } + return out; +} +#endif // !MPT_CHARSET_ICONV + + +// templated on 8bit strings because of type-safe variants +template +Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) +{ + STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); + STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + if(to == from) + { + const typename Tsrcstring::value_type * src_beg = src.data(); + const typename Tsrcstring::value_type * src_end = src_beg + src.size(); + return Tdststring(reinterpret_cast(src_beg), reinterpret_cast(src_end)); + } + #if defined(MPT_CHARSET_ICONV) + if(to == CharsetCP437AMS || to == CharsetCP437AMS2 || from == CharsetCP437AMS || from == CharsetCP437AMS2) + { + return EncodeImpl(to, DecodeImpl(from, src)); + } + iconv_t conv = iconv_t(); + conv = iconv_open(CharsetToStringTranslit(to), CharsetToString(from)); + if(!conv) + { + conv = iconv_open(CharsetToString(to), CharsetToString(from)); + if(!conv) + { + throw std::runtime_error("iconv conversion not working"); + } + } + std::vector src_string(reinterpret_cast(src.c_str()), reinterpret_cast(src.c_str()) + src.length() + 1); + std::vector dst_string(src_string.size() * 8); // large enough + char * inbuf = src_string.data(); + size_t inbytesleft = src_string.size(); + char * outbuf = dst_string.data(); + size_t outbytesleft = dst_string.size(); + while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) + { + if(errno == EILSEQ || errno == EILSEQ) + { + inbuf++; + inbytesleft--; + outbuf[0] = '?'; + outbuf++; + outbytesleft--; + iconv(conv, NULL, NULL, NULL, NULL); // reset state + } else + { + iconv_close(conv); + conv = iconv_t(); + return Tdststring(); + } + } + iconv_close(conv); + conv = iconv_t(); + return reinterpret_cast(dst_string.data()); + #else + return EncodeImpl(to, DecodeImpl(from, src)); + #endif +} + + +} // namespace String + + +bool IsUTF8(const std::string &str) +{ + return (str == String::EncodeImpl(mpt::CharsetUTF8, String::DecodeImpl(mpt::CharsetUTF8, str))); +} + + +#if MPT_WSTRING_CONVERT +std::wstring ToWide(Charset from, const std::string &str) +{ + return String::DecodeImpl(from, str); +} +#endif + +#if MPT_WSTRING_CONVERT +std::string ToCharset(Charset to, const std::wstring &str) +{ + return String::EncodeImpl(to, str); +} +#endif +std::string ToCharset(Charset to, Charset from, const std::string &str) +{ + return String::ConvertImpl(to, from, str); +} + + +#if defined(_MFC_VER) + +CString ToCString(const std::wstring &str) +{ + #ifdef UNICODE + return str.c_str(); + #else + return ToCharset(CharsetLocale, str).c_str(); + #endif +} +CString ToCString(Charset from, const std::string &str) +{ + #ifdef UNICODE + return ToWide(from, str).c_str(); + #else + return ToCharset(CharsetLocale, from, str).c_str(); + #endif +} +std::wstring ToWide(const CString &str) +{ + #ifdef UNICODE + return str.GetString(); + #else + return ToWide(CharsetLocale, str.GetString()); + #endif +} +std::string ToCharset(Charset to, const CString &str) +{ + #ifdef UNICODE + return ToCharset(to, str.GetString()); + #else + return ToCharset(to, CharsetLocale, str.GetString()); + #endif +} + +#ifdef UNICODE +// inline +#else // !UNICODE +CStringW ToCStringW(const CString &str) +{ + return ToWide(str).c_str(); +} +CStringW ToCStringW(const std::wstring &str) +{ + return str.c_str(); +} +CStringW ToCStringW(Charset from, const std::string &str) +{ + return ToWide(from, str).c_str(); +} +CStringW ToCStringW(const CStringW &str) +{ + return str; +} +std::wstring ToWide(const CStringW &str) +{ + return str.GetString(); +} +std::string ToCharset(Charset to, const CStringW &str) +{ + return ToCharset(to, str.GetString()); +} +CString ToCString(const CStringW &str) +{ + return ToCharset(CharsetLocale, str).c_str(); +} +#endif // UNICODE + +#endif // MFC + + +#if MPT_USTRING_MODE_WIDE +// inline +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +mpt::ustring ToUnicode(const std::wstring &str) +{ + return String::EncodeImpl(mpt::CharsetUTF8, str); +} +#endif +mpt::ustring ToUnicode(Charset from, const std::string &str) +{ + return String::ConvertImpl(mpt::CharsetUTF8, from, str); +} +#if defined(_MFC_VER) +mpt::ustring ToUnicode(const CString &str) +{ + #ifdef UNICODE + return String::EncodeImpl(mpt::CharsetUTF8, str.GetString()); + #else // !UNICODE + return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str.GetString()); + #endif // UNICODE +} +#ifndef UNICODE +mpt::ustring ToUnicode(const CStringW &str) +{ + return String::EncodeImpl(mpt::CharsetUTF8, str.GetString()); +} +#endif // !UNICODE +#endif // MFC +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_WIDE +// nothing, std::wstring overloads will catch all stuff +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +std::wstring ToWide(const mpt::ustring &str) +{ + return String::DecodeImpl(mpt::CharsetUTF8, str); +} +#endif +std::string ToCharset(Charset to, const mpt::ustring &str) +{ + return String::ConvertImpl(to, mpt::CharsetUTF8, str); +} +#if defined(_MFC_VER) +CString ToCString(const mpt::ustring &str) +{ + #ifdef UNICODE + return String::DecodeImpl(mpt::CharsetUTF8, str).c_str(); + #else // !UNICODE + return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str).c_str(); + #endif // UNICODE +} +#endif // MFC +#endif // MPT_USTRING_MODE_WIDE + + + + + +char ToLowerCaseAscii(char c) +{ + if('A' <= c && c <= 'Z') + { + c += 'a' - 'A'; + } + return c; +} + +char ToUpperCaseAscii(char c) +{ + if('a' <= c && c <= 'z') + { + c -= 'a' - 'A'; + } + return c; +} + +std::string ToLowerCaseAscii(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToLowerCaseAscii)); + return s; +} + +std::string ToUpperCaseAscii(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToUpperCaseAscii)); + return s; +} + +int CompareNoCaseAscii(const char *a, const char *b, std::size_t n) +{ + while(n--) + { + unsigned char ac = static_cast(mpt::ToLowerCaseAscii(*a)); + unsigned char bc = static_cast(mpt::ToLowerCaseAscii(*b)); + if(ac != bc) + { + return ac < bc ? -1 : 1; + } else if(!ac && !bc) + { + return 0; + } + ++a; + ++b; + } + return 0; +} + +int CompareNoCaseAscii(const std::string &a, const std::string &b) +{ + for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i) + { + unsigned char ac = static_cast(mpt::ToLowerCaseAscii(a[i])); + unsigned char bc = static_cast(mpt::ToLowerCaseAscii(b[i])); + if(ac != bc) + { + return ac < bc ? -1 : 1; + } else if(!ac && !bc) + { + return 0; + } + } + if(a.length() == b.length()) + { + return 0; + } + return a.length() < b.length() ? -1 : 1; +} + +#if defined(MODPLUG_TRACKER) + +mpt::ustring ToLowerCase(const mpt::ustring &s) +{ + #if defined(_MFC_VER) + #if defined(UNICODE) + CString tmp = mpt::ToCString(s); + tmp.MakeLower(); + return mpt::ToUnicode(tmp); + #else // !UNICODE + CStringW tmp = mpt::ToCStringW(s); + tmp.MakeLower(); + return mpt::ToUnicode(tmp); + #endif // UNICODE + #else // !_MFC_VER + std::wstring ws = mpt::ToWide(s); + std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); + return mpt::ToUnicode(ws); + #endif // _MFC_VER +} + +mpt::ustring ToUpperCase(const mpt::ustring &s) +{ + #if defined(_MFC_VER) + #if defined(UNICODE) + CString tmp = mpt::ToCString(s); + tmp.MakeUpper(); + return mpt::ToUnicode(tmp); + #else // !UNICODE + CStringW tmp = mpt::ToCStringW(s); + tmp.MakeUpper(); + return mpt::ToUnicode(tmp); + #endif // UNICODE + #else // !_MFC_VER + std::wstring ws = mpt::ToWide(s); + std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); + return mpt::ToUnicode(ws); + #endif // _MFC_VER +} + +#endif // MODPLUG_TRACKER + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.h b/Frameworks/OpenMPT/OpenMPT/common/mptString.h new file mode 100644 index 000000000..157a0e49b --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.h @@ -0,0 +1,668 @@ +/* + * mptString.h + * ---------- + * Purpose: Small string-related utilities, number and message formatting. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "mptTypeTraits.h" + +#include +#include +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ + + +namespace String +{ + + +template struct Traits { + static const char * GetDefaultWhitespace() { return " \n\r\t"; } +}; + +template <> struct Traits { + static const char * GetDefaultWhitespace() { return " \n\r\t"; } +}; + +template <> struct Traits { + static const wchar_t * GetDefaultWhitespace() { return L" \n\r\t"; } +}; + + +// Remove whitespace at start of string +template +inline Tstring LTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + typename Tstring::size_type pos = str.find_first_not_of(whitespace); + if(pos != Tstring::npos) + { + str.erase(str.begin(), str.begin() + pos); + } else if(pos == Tstring::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1) + { + return Tstring(); + } + return str; +} + + +// Remove whitespace at end of string +template +inline Tstring RTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + typename Tstring::size_type pos = str.find_last_not_of(whitespace); + if(pos != Tstring::npos) + { + str.erase(str.begin() + pos + 1, str.end()); + } else if(pos == Tstring::npos && str.length() > 0 && str.find_first_of(whitespace) == 0) + { + return Tstring(); + } + return str; +} + + +// Remove whitespace at start and end of string +template +inline Tstring Trim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + return RTrim(LTrim(str, whitespace), whitespace); +} + + +template +inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &newStr_) +{ + std::size_t pos = 0; + const Tstring oldStr = oldStr_; + const Tstring newStr = newStr_; + while((pos = str.find(oldStr, pos)) != Tstring::npos) + { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return str; +} + + +} // namespace String + + +static inline std::size_t strnlen(const char *str, std::size_t n) +{ +#if MPT_COMPILER_MSVC + return ::strnlen(str, n); +#else + if(n >= std::numeric_limits::max()) + { + return std::strlen(str); + } + for(std::size_t i = 0; i < n; ++i) + { + if(str[i] == '\0') + { + return i; + } + } + return n; +#endif +} + + +enum Charset { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + CharsetLocale, // CP_ACP on windows, current C locale otherwise +#endif + + CharsetUTF8, + + CharsetASCII, // strictly 7-bit ASCII + + CharsetISO8859_1, + CharsetISO8859_15, + + CharsetCP437, + CharsetCP437AMS, + CharsetCP437AMS2, + + CharsetWindows1252, + +}; + + +// Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +const Charset CharsetLocaleOrUTF8 = CharsetLocale; +#else +const Charset CharsetLocaleOrUTF8 = CharsetUTF8; +#endif + + + +// Checks if the std::string represents an UTF8 string. +// This is currently implemented as converting to std::wstring and back assuming UTF8 both ways, +// and comparing the result to the original string. +// Caveats: +// - can give false negatives because of possible unicode normalization during conversion +// - can give false positives if the 8bit encoding contains high-ascii only in valid utf8 groups +// - slow because of double conversion +bool IsUTF8(const std::string &str); + + +#define MPT_CHAR_TYPE char +#define MPT_CHAR(x) x +#define MPT_LITERAL(x) x +#define MPT_STRING(x) std::string( x ) + +#define MPT_WCHAR_TYPE wchar_t +#define MPT_WCHAR(x) L ## x +#define MPT_WLITERAL(x) L ## x +#define MPT_WSTRING(x) std::wstring( L ## x ) + + +#if MPT_ENABLE_U8STRING + +template +struct charset_char_traits : std::char_traits { + static mpt::Charset charset() { return charset_tag; } +}; +#define MPT_ENCODED_STRING_TYPE(charset) std::basic_string< char, mpt::charset_char_traits< charset > > + +typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; + +#define MPT_U8CHAR_TYPE char +#define MPT_U8CHAR(x) x +#define MPT_U8LITERAL(x) x +#define MPT_U8STRING(x) mpt::u8string( x ) + +// mpt::u8string is a moderately type-safe string that is meant to contain +// UTF-8 encoded char bytes. +// +// mpt::u8string is not implicitely convertible to/from std::string, but +// it is convertible to/from C strings the same way as std::string is. +// +// The implementation of mpt::u8string is a compromise of compatibilty +// with implementation-defined STL details, efficiency, source code size, +// executable bloat, type-safety and simplicity. +// +// mpt::u8string is not meant to be used directly though. +// mpt::u8string is meant as an alternative implementaion to std::wstring +// for implementing the unicode string type mpt::ustring. + +#endif // MPT_ENABLE_U8STRING + + +#if MPT_WSTRING_CONVERT +// Convert to a wide character string. +// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t). +// If str does not contain any invalid characters, this conversion is lossless. +// Invalid source bytes will be replaced by some replacement character or string. +static inline std::wstring ToWide(const std::wstring &str) { return str; } +static inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } +std::wstring ToWide(Charset from, const std::string &str); +static inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); } +#endif + +// Convert to a string encoded in the 'to'-specified character set. +// If str does not contain any invalid characters, +// this conversion will be lossless iff, and only iff, +// 'to' is UTF8. +// Invalid source bytes or characters that are not representable in the +// destination charset will be replaced by some replacement character or string. +#if MPT_WSTRING_CONVERT +std::string ToCharset(Charset to, const std::wstring &str); +static inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); } +#endif +std::string ToCharset(Charset to, Charset from, const std::string &str); +static inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); } + + +#if defined(_MFC_VER) +#if !(MPT_WSTRING_CONVERT) +#error "MFC depends on MPT_WSTRING_CONVERT)" +#endif + +// Convert to a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting to TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. +static inline CString ToCString(const CString &str) { return str; } +CString ToCString(const std::wstring &str); +static inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); } +CString ToCString(Charset from, const std::string &str); +static inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } + +// Convert from a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting from TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. +std::wstring ToWide(const CString &str); +std::string ToCharset(Charset to, const CString &str); + +#ifdef UNICODE +MPT_DEPRECATED static inline CString ToCStringW(const CString &str) { return ToCString(str); } +MPT_DEPRECATED static inline CString ToCStringW(const std::wstring &str) { return ToCString(str); } +MPT_DEPRECATED static inline CString ToCStringW(Charset from, const std::string &str) { return ToCString(from, str); } +MPT_DEPRECATED static inline CString ToCStringW(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } +#else // !UNICODE +CStringW ToCStringW(const CString &str); +CStringW ToCStringW(const std::wstring &str); +CStringW ToCStringW(Charset from, const std::string &str); +static inline CStringW ToCStringW(Charset from, const char * str) { return ToCStringW(from, str ? std::string(str) : std::string()); } +CStringW ToCStringW(const CStringW &str); +std::wstring ToWide(const CStringW &str); +std::string ToCharset(Charset to, const CStringW &str); +CString ToCString(const CStringW &str); +#endif // UNICODE + +#endif // MFC + + +// mpt::ustring +// +// mpt::ustring is a string type that can hold unicode strings. +// It is implemented as a std::basic_string either based on wchar_t (i.e. the +// same as std::wstring) or a custom-defined char_traits class that is derived +// from std::char_traits. +// The selection of the underlying implementation is done at compile-time. +// MPT_UCHAR, MPT_ULITERAL and MPT_USTRING are macros that ease construction +// of ustring char literals, ustring char array literals and ustring objects +// from ustring char literals that work consistently in both modes. +// Note that these are not supported for non-ASCII characters appearing in +// the macro argument. +// Also note that, as both UTF8 and UTF16 (it is less of an issue for UTF32) +// are variable-length encodings and mpt::ustring is implemented as a +// std::basic_string, all member functions that require individual character +// access will not work consistently or even at all in a meaningful way. +// This in particular affects operator[], at(), find() and substr(). +// The code makes no effort in preventing these or generating warnings when +// these are used on mpt::ustring objects. However, compiling in the +// respectively other mpt::ustring mode will catch most of these anyway. + +#if MPT_USTRING_MODE_WIDE +#if MPT_USTRING_MODE_UTF8 +#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." +#endif + +typedef std::wstring ustring; +#define MPT_UCHAR_TYPE wchar_t +#define MPT_UCHAR(x) L ## x +#define MPT_ULITERAL(x) L ## x +#define MPT_USTRING(x) std::wstring( L ## x ) + +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_UTF8 +#if MPT_USTRING_MODE_WIDE +#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." +#endif + +typedef mpt::u8string ustring; +#define MPT_UCHAR_TYPE char +#define MPT_UCHAR(x) x +#define MPT_ULITERAL(x) x +#define MPT_USTRING(x) mpt::ustring( x ) + +#endif // MPT_USTRING_MODE_UTF8 + +#if MPT_USTRING_MODE_WIDE +#if !(MPT_WSTRING_CONVERT) +#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" +#endif +static inline mpt::ustring ToUnicode(const std::wstring &str) { return str; } +static inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } +static inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); } +static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(_MFC_VER) +static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } +#ifndef UNICODE +static inline mpt::ustring ToUnicode(const CStringW &str) { return ToWide(str); } +#endif // !UNICODE +#endif // MFC +#else // !MPT_USTRING_MODE_WIDE +static inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; } +#if MPT_WSTRING_CONVERT +mpt::ustring ToUnicode(const std::wstring &str); +static inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); } +#endif +mpt::ustring ToUnicode(Charset from, const std::string &str); +static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(_MFC_VER) +mpt::ustring ToUnicode(const CString &str); +#ifndef UNICODE +mpt::ustring ToUnicode(const CStringW &str); +#endif // !UNICODE +#endif // MFC +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_WIDE +#if !(MPT_WSTRING_CONVERT) +#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" +#endif +// nothing, std::wstring overloads will catch all stuff +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +std::wstring ToWide(const mpt::ustring &str); +#endif +std::string ToCharset(Charset to, const mpt::ustring &str); +#if defined(_MFC_VER) +CString ToCString(const mpt::ustring &str); +#ifdef UNICODE +MPT_DEPRECATED static inline CString ToCStringW(const mpt::ustring &str) { return ToCString(str); } +#else // !UNICODE +static inline CStringW ToCStringW(const mpt::ustring &str) { return ToCStringW(ToWide(str)); } +#endif // UNICODE +#endif // MFC +#endif // MPT_USTRING_MODE_WIDE + +// The MPT_UTF8 allows specifying UTF8 char arrays. +// The resulting type is mpt::ustring and the construction might require runtime translation, +// i.e. it is NOT generally available at compile time. +// Use explicit UTF8 encoding, +// i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC". +#define MPT_UTF8(x) mpt::ToUnicode(mpt::CharsetUTF8, x ) + + + + + +#ifdef MODPLUG_TRACKER + +#if MPT_OS_WINDOWS + +namespace String { namespace detail +{ + + template + inline mpt::ustring StringFromBuffer(const char (&buf)[size]) + { + STATIC_ASSERT(size > 0); + std::size_t len = std::find(buf, buf + size, '\0') - buf; // terminate at \0 + return mpt::ToUnicode(charset, std::string(buf, buf + len)); + } + + template + inline mpt::ustring StringFromBuffer(const wchar_t (&buf)[size]) + { + STATIC_ASSERT(size > 0); + std::size_t len = std::find(buf, buf + size, L'\0') - buf; // terminate at \0 + return mpt::ToUnicode(std::wstring(buf, buf + len)); + } + + template + inline bool StringToBuffer(char (&buf)[size], const mpt::ustring &str) + { + STATIC_ASSERT(size > 0); + MemsetZero(buf); + std::string encoded = mpt::ToCharset(charset, str); + std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf); + buf[size - 1] = '\0'; + return (encoded.length() <= size - 1); + } + + template + inline bool StringToBuffer(wchar_t (&buf)[size], const mpt::ustring &str) + { + STATIC_ASSERT(size > 0); + MemsetZero(buf); + std::wstring encoded = mpt::ToWide(str); + std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf); + buf[size - 1] = L'\0'; + return (encoded.length() <= size - 1); + } + +} } // namespace String::detail + +// mpt::FromTcharBuf +// A lot of expecially older WinAPI functions return strings by filling in +// fixed-width TCHAR arrays inside some struct. +// mpt::FromTcharBuf converts these string to mpt::ustring regardless of whether +// in ANSI or UNICODE build and properly handles potentially missing NULL +// termination. + +template +inline mpt::ustring FromTcharBuf(const Tchar (&buf)[size]) +{ + return mpt::String::detail::StringFromBuffer(buf); +} + +// mpt::FromTcharStr +// Converts TCHAR strings to mpt::ustring in both ANSI and UNICODE builds. +// Useful when going through CString is not appropriate. + +template mpt::ustring FromTcharStr(const Tchar *str); +template <> inline mpt::ustring FromTcharStr(const char *str) +{ + if(!str) + { + return mpt::ustring(); + } + return mpt::ToUnicode(mpt::CharsetLocale, std::string(str)); +} +template <> inline mpt::ustring FromTcharStr(const wchar_t *str) +{ + if(!str) + { + return mpt::ustring(); + } + return mpt::ToUnicode(std::wstring(str)); +} + +// mpt::ToTcharBuf +// The inverse of mpt::FromTcharBuf. +// Always NULL-terminates the buffer. +// Return false if the string has been truncated to fit. + +template +inline bool ToTcharBuf(Tchar (&buf)[size], const mpt::ustring &str) +{ + return mpt::String::detail::StringToBuffer(buf, str); +} + +// mpt::ToTcharStr +// Converts mpt::ustring to std::basic_stringy, +// which is usable in both ANSI and UNICODE builds. +// Useful when going through CString is not appropriate. + +template std::basic_string ToTcharStrImpl(const mpt::ustring &str); +template <> inline std::string ToTcharStrImpl(const mpt::ustring &str) +{ + return mpt::ToCharset(mpt::CharsetLocale, str); +} +template <> inline std::wstring ToTcharStrImpl(const mpt::ustring &str) +{ + return mpt::ToWide(str); +} + +inline std::basic_string ToTcharStr(const mpt::ustring &str) +{ + return ToTcharStrImpl(str); +} + +#if defined(_MFC_VER) + +template +inline CString CStringFromBuffer(const TCHAR (&buf)[size]) +{ + MPT_STATIC_ASSERT(size > 0); + std::size_t len = std::find(buf, buf + size, _T('\0')) - buf; // terminate at \0 + return CString(buf, len); +} + +template +inline void CopyCStringToBuffer(TCHAR (&buf)[size], const CString &str) +{ + MPT_STATIC_ASSERT(size > 0); + MemsetZero(buf); + std::copy(str.GetString(), str.GetString() + std::min(static_cast(str.GetLength()), size - 1), buf); + buf[size - 1] = _T('\0'); +} + +#endif // _MFC_VER + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + + + +char ToLowerCaseAscii(char c); +char ToUpperCaseAscii(char c); +std::string ToLowerCaseAscii(std::string s); +std::string ToUpperCaseAscii(std::string s); + +int CompareNoCaseAscii(const char *a, const char *b, std::size_t n); +int CompareNoCaseAscii(const std::string &a, const std::string &b); + +#if defined(MODPLUG_TRACKER) + +mpt::ustring ToLowerCase(const mpt::ustring &s); +mpt::ustring ToUpperCase(const mpt::ustring &s); + +#endif // MODPLUG_TRACKER + + + + + +} // namespace mpt + + + + + +// The AnyString types are meant to be used as function argument types only, +// and only during the transition phase to all-unicode strings in the whole codebase. +// Using an AnyString type as function argument avoids the need to overload a function for all the +// different string types that we currently have. +// Warning: These types will silently do charset conversions. Only use them when this can be tolerated. + +// BasicAnyString is convertable to mpt::ustring and constructable from any string at all. +template +class BasicAnyString : public mpt::ustring +{ + +private: + + static mpt::ustring From8bit(const std::string &str) + { + if(charset == mpt::CharsetUTF8) + { + return mpt::ToUnicode(mpt::CharsetUTF8, str); + } + // auto utf8 detection + if(tryUTF8 && mpt::IsUTF8(str)) + { + return mpt::ToUnicode(mpt::CharsetUTF8, str); + } else + { + return mpt::ToUnicode(charset, str); + } + } + +public: + + // 8 bit + BasicAnyString(const char *str) : mpt::ustring(str ? mpt::ToUnicode(charset, str) : mpt::ustring()) { } + BasicAnyString(const std::string str) : mpt::ustring(mpt::ToUnicode(charset, str)) { } + + // unicode + BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { } + BasicAnyString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT + BasicAnyString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#if MPT_WSTRING_CONVERT + BasicAnyString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } +#endif + + // mfc +#if defined(_MFC_VER) + BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#ifndef UNICODE + BasicAnyString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#endif + + // fallback for custom string types + template BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } + template BasicAnyString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } + +}; + +// AnyUnicodeString is convertable to mpt::ustring and constructable from any unicode string, +class AnyUnicodeString : public mpt::ustring +{ + +public: + + // unicode + AnyUnicodeString(const mpt::ustring &str) : mpt::ustring(str) { } + AnyUnicodeString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT + AnyUnicodeString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#if MPT_WSTRING_CONVERT + AnyUnicodeString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } +#endif + + // mfc +#if defined(_MFC_VER) + AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#ifndef UNICODE + AnyUnicodeString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#endif + + // fallback for custom string types + template AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } + template AnyUnicodeString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } + +}; + +// AnyString +// Try to do the smartest auto-magic we can do. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +typedef BasicAnyString AnyString; +#elif MPT_OS_WINDOWS +typedef BasicAnyString AnyString; +#else +typedef BasicAnyString AnyString; +#endif + +// AnyStringLocale +// char-based strings are assumed to be in locale encoding. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +typedef BasicAnyString AnyStringLocale; +#else +typedef BasicAnyString AnyStringLocale; +#endif + +// AnyStringUTF8orLocale +// char-based strings are tried in UTF8 first, if this fails, locale is used. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +typedef BasicAnyString AnyStringUTF8orLocale; +#else +typedef BasicAnyString AnyStringUTF8orLocale; +#endif + +// AnyStringUTF8 +// char-based strings are assumed to be in UTF8. +typedef BasicAnyString AnyStringUTF8; + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp new file mode 100644 index 000000000..cbf21e0d5 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp @@ -0,0 +1,498 @@ +/* + * mptStringFormat.cpp + * ------------------- + * Purpose: Convert other types to strings. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptStringFormat.h" + +#include +#include +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + + +template inline void SaneInsert(Tstream & s, const T & x) { s << x; } +// do the right thing for signed/unsigned char and bool +template void SaneInsert(Tstream & s, const bool & x) { s << static_cast(x); } +template void SaneInsert(Tstream & s, const signed char & x) { s << static_cast(x); } +template void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast(x); } + +template +inline std::string ToStringHelper(const T & x) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +inline std::wstring ToWStringHelper(const T & x) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} +#endif + +#if MPT_WSTRING_CONVERT +std::string ToString(const std::wstring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } +std::string ToString(const wchar_t * const & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } +std::string ToString(const wchar_t & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, std::wstring(1, x)); } +#endif +#if MPT_USTRING_MODE_UTF8 +std::string ToString(const mpt::ustring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } +#endif +#if defined(_MFC_VER) +std::string ToString(const CString & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } +#endif +std::string ToString(const bool & x) { return ToStringHelper(x); } +std::string ToString(const signed char & x) { return ToStringHelper(x); } +std::string ToString(const unsigned char & x) { return ToStringHelper(x); } +std::string ToString(const signed short & x) { return ToStringHelper(x); } +std::string ToString(const unsigned short & x) { return ToStringHelper(x); } +std::string ToString(const signed int & x) { return ToStringHelper(x); } +std::string ToString(const unsigned int & x) { return ToStringHelper(x); } +std::string ToString(const signed long & x) { return ToStringHelper(x); } +std::string ToString(const unsigned long & x) { return ToStringHelper(x); } +std::string ToString(const signed long long & x) { return ToStringHelper(x); } +std::string ToString(const unsigned long long & x) { return ToStringHelper(x); } +std::string ToString(const float & x) { return ToStringHelper(x); } +std::string ToString(const double & x) { return ToStringHelper(x); } +std::string ToString(const long double & x) { return ToStringHelper(x); } + +mpt::ustring ToUString(const std::string & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); } +mpt::ustring ToUString(const char * const & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); } +mpt::ustring ToUString(const char & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, std::string(1, x)); } +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const std::wstring & x) { return mpt::ToUnicode(x); } +#endif +mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); } +mpt::ustring ToUString(const wchar_t & x) { return mpt::ToUnicode(std::wstring(1, x)); } +#endif +#if defined(_MFC_VER) +mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } +#endif +#if MPT_USTRING_MODE_WIDE +mpt::ustring ToUString(const bool & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const signed char & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const signed short & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const signed int & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const signed long & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const signed long long & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const float & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const double & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const long double & x) { return ToWStringHelper(x); } +#endif +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +#endif + +#if MPT_WSTRING_FORMAT +std::wstring ToWString(const std::string & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); } +std::wstring ToWString(const char * const & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); } +std::wstring ToWString(const char & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, std::string(1, x)); } +#if MPT_USTRING_MODE_UTF8 +std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); } +#endif +#if defined(_MFC_VER) +std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } +#endif +std::wstring ToWString(const bool & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed char & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned char & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed short & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned short & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed int & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned int & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const signed long long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const unsigned long long & x) { return ToWStringHelper(x); } +std::wstring ToWString(const float & x) { return ToWStringHelper(x); } +std::wstring ToWString(const double & x) { return ToWStringHelper(x); } +std::wstring ToWString(const long double & x) { return ToWStringHelper(x); } +#endif + + +template +inline void ApplyFormat(Tostream & o, const FormatSpec & format) +{ + FormatFlags f = format.GetFlags(); + std::size_t width = format.GetWidth(); + int precision = format.GetPrecision(); + if(precision != -1 && width != 0 && !(f & fmt_base::NotaFix) && !(f & fmt_base::NotaSci)) + { + // fixup: + // precision behaves differently from .# + // avoid default format when precision and width are set + f &= ~fmt_base::NotaNrm; + f |= fmt_base::NotaFix; + } + if(f & fmt_base::BaseDec) { o << std::dec; } + else if(f & fmt_base::BaseHex) { o << std::hex; } + if(f & fmt_base::NotaNrm ) { /*nothing*/ } + else if(f & fmt_base::NotaFix ) { o << std::setiosflags(std::ios::fixed); } + else if(f & fmt_base::NotaSci ) { o << std::setiosflags(std::ios::scientific); } + if(f & fmt_base::CaseLow) { o << std::nouppercase; } + else if(f & fmt_base::CaseUpp) { o << std::uppercase; } + if(f & fmt_base::FillOff) { /* nothing */ } + else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } + else if(f & fmt_base::FillSpc) { o << std::setw(width) << std::setfill(typename Tostream::char_type(' ')); } + if(precision != -1) { o << std::setprecision(precision); } +} + + +template +inline std::string FormatValHelper(const T & x, const FormatSpec & f) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +inline std::wstring FormatValWHelper(const T & x, const FormatSpec & f) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f); + SaneInsert(o, x); + return o.str(); +} +#endif + +// Parses a useful subset of standard sprintf syntax for specifying floating point formatting. +template +static inline FormatSpec ParseFormatStringFloat(const Tchar * str) +{ + MPT_ASSERT(str); + FormatFlags f = FormatFlags(); + std::size_t width = 0; + int precision = -1; + if(!str) + { + return FormatSpec(); + } + const Tchar * p = str; + while(*p && *p != Tchar('%')) + { + ++p; + } + ++p; + while(*p && (*p == Tchar(' ') || *p == Tchar('0'))) + { + if(*p == Tchar(' ')) f |= mpt::fmt_base::FillSpc; + if(*p == Tchar('0')) f |= mpt::fmt_base::FillNul; + ++p; + } + if(!(f & mpt::fmt_base::FillSpc) && !(f & mpt::fmt_base::FillNul)) + { + f |= mpt::fmt_base::FillOff; + } + while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) + { + if(f & mpt::fmt_base::FillOff) + { + f &= ~mpt::fmt_base::FillOff; + f |= mpt::fmt_base::FillSpc; + } + width *= 10; + width += *p - Tchar('0'); + ++p; + } + if(*p && *p == Tchar('.')) + { + ++p; + precision = 0; + while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) + { + precision *= 10; + precision += *p - Tchar('0'); + ++p; + } + } + if(*p && (*p == Tchar('g') || *p == Tchar('G') || *p == Tchar('f') || *p == Tchar('F') || *p == Tchar('e') || *p == Tchar('E'))) + { + if(*p == Tchar('g')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseLow; + if(*p == Tchar('G')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseUpp; + if(*p == Tchar('f')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseLow; + if(*p == Tchar('F')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseUpp; + if(*p == Tchar('e')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseLow; + if(*p == Tchar('E')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseUpp; + ++p; + } + return FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision); +} + +FormatSpec & FormatSpec::ParsePrintf(const char * format) +{ + *this = ParseFormatStringFloat(format); + return *this; +} +FormatSpec & FormatSpec::ParsePrintf(const wchar_t * format) +{ + *this = ParseFormatStringFloat(format); + return *this; +} +FormatSpec & FormatSpec::ParsePrintf(const std::string & format) +{ + *this = ParseFormatStringFloat(format.c_str()); + return *this; +} +FormatSpec & FormatSpec::ParsePrintf(const std::wstring & format) +{ + *this = ParseFormatStringFloat(format.c_str()); + return *this; +} + + +std::string FormatVal(const char & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const wchar_t & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelper(x, f); } +std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelper(x, f); } + +#if MPT_WSTRING_FORMAT +std::wstring FormatValW(const char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +#endif + + +namespace String +{ + + +namespace detail +{ + +template +Tstring PrintImplTemplate(const Tstring & format + , const Tstring & x1 + , const Tstring & x2 + , const Tstring & x3 + , const Tstring & x4 + , const Tstring & x5 + , const Tstring & x6 + , const Tstring & x7 + , const Tstring & x8 + ) +{ + Tstring result; + const std::size_t len = format.length(); + result.reserve(len); + for(std::size_t pos = 0; pos != len; ++pos) + { + typename Tstring::value_type c = format[pos]; + if(pos + 1 != len && c == '%') + { + pos++; + c = format[pos]; + if('1' <= c && c <= '9') + { + const std::size_t n = c - '0'; + switch(n) + { + case 1: result.append(x1); break; + case 2: result.append(x2); break; + case 3: result.append(x3); break; + case 4: result.append(x4); break; + case 5: result.append(x5); break; + case 6: result.append(x6); break; + case 7: result.append(x7); break; + case 8: result.append(x8); break; + } + continue; + } else if(c != '%') + { + result.append(1, '%'); + } + } + result.append(1, c); + } + return result; +} + +#if defined(_MFC_VER) +template<> +CString PrintImplTemplate(const CString & format + , const CString & x1 + , const CString & x2 + , const CString & x3 + , const CString & x4 + , const CString & x5 + , const CString & x6 + , const CString & x7 + , const CString & x8 + ) +{ + CString result; + const int len = format.GetLength(); + result.Preallocate(len); + for(int pos = 0; pos != len; ++pos) + { + CString::XCHAR c = format[pos]; + if(pos + 1 != len && c == _T('%')) + { + pos++; + c = format[pos]; + if(_T('1') <= c && c <= _T('9')) + { + const std::size_t n = c - _T('0'); + switch(n) + { + case 1: result += x1; break; + case 2: result += x2; break; + case 3: result += x3; break; + case 4: result += x4; break; + case 5: result += x5; break; + case 6: result += x6; break; + case 7: result += x7; break; + case 8: result += x8; break; + } + continue; + } else if(c != _T('%')) + { + result.AppendChar(_T('%')); + } + } + result.AppendChar(c); + } + return result; +} +#endif + +std::string PrintImpl(const std::string & format + , const std::string & x1 + , const std::string & x2 + , const std::string & x3 + , const std::string & x4 + , const std::string & x5 + , const std::string & x6 + , const std::string & x7 + , const std::string & x8 + ) +{ + return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); +} + +#if MPT_WSTRING_FORMAT +std::wstring PrintImpl(const std::wstring & format + , const std::wstring & x1 + , const std::wstring & x2 + , const std::wstring & x3 + , const std::wstring & x4 + , const std::wstring & x5 + , const std::wstring & x6 + , const std::wstring & x7 + , const std::wstring & x8 + ) +{ + return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); +} +#endif + +#if MPT_USTRING_MODE_UTF8 +mpt::ustring PrintImpl(const mpt::ustring & format + , const mpt::ustring & x1 + , const mpt::ustring & x2 + , const mpt::ustring & x3 + , const mpt::ustring & x4 + , const mpt::ustring & x5 + , const mpt::ustring & x6 + , const mpt::ustring & x7 + , const mpt::ustring & x8 + ) +{ + return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); +} +#endif + +#if defined(_MFC_VER) +CString PrintImpl(const CString & format + , const CString & x1 + , const CString & x2 + , const CString & x3 + , const CString & x4 + , const CString & x5 + , const CString & x6 + , const CString & x7 + , const CString & x8 + ) +{ + return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); +} +#endif + +} // namespace detail + + +} // namespace String + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h new file mode 100644 index 000000000..3db356def --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h @@ -0,0 +1,782 @@ +/* + * mptStringFormat.h + * ----------------- + * Purpose: Convert other types to strings. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "mptString.h" + + +OPENMPT_NAMESPACE_BEGIN + + + +// The following section demands a rationale. +// 1. mpt::fmt::val(), mpt::wfmt::val() and mpt::ufmt::val() mimic the semantics of c++11 std::to_string() and std::to_wstring(). +// There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn +// depends on the current C locale. This renders these functions unusable in a library context because the current +// C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics +// out of these functions. It is thus better to just avoid them. +// ToString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(), +// which results in "C" ASCII locale behavior. +// 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality +// is provided here. +// For convenience, mpt::fmt::f(const char *, float) allows formatting a single floating point value with a +// standard printf-like format string. This itself relies on iostream with classic() locale internally and is thus current locale +// agnostic. +// When formatting integers, it is recommended to use mpt::fmt::dec or mpt::fmt::hex. Appending a template argument '' sets the width, +// the same way as '%nd' would do. Appending a '0' to the function name causes zero-filling as print-like '%0nd' would do. Spelling 'HEX' +// in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatVal(int, format) can be +// used. +// 3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting. +// The only specifier allowed is '%' followed by a single digit n. It references to n-th parameter after the format string (1-based). +// This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality +// with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter +// ordering. +// There are macro verions (MPT_FORMAT and variants) which properly use wide string literals for the format parameter. +// 4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which +// basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. +// std::string std::wstring mpt::ustring CString +// mpt::fmt::val mpt::wfmt::val mpt::ufmt::val mpt::tfmt::val +// mpt::FormatVal mpt::FormatValW mpt::FormatValTFunctor() mpt::FormatValTFunctor() +// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt +// mpt::format mpt::format mpt::format mpt::format +// 5. All functionality here delegates real work outside of the header file so that and do not need to be included when +// using this functionality. +// Advantages: +// - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site. +// - Faster compile times because and (2 very complex headers) are not included everywhere. +// Disadvantages: +// - Slightly more c++ code is required for delegating work. +// - As the header does not use iostreams, custom types need to overload mpt::String, mpt::ToWstring and mpt::UString instead of +// iostream operator << to allow for custom type formatting. +// - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is +// written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate +// almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where +// move-semantics will kick in if RVO/NRVO fails). + +namespace mpt +{ + +// ToString() converts various built-in types to a well-defined, locale-independent string representation. +// This is also used as a type-tunnel pattern for mpt::format. +// Custom types that need to be converted to strings are encouraged to overload ToString() and ToWString(). + +// fallback to member function ToString() +template auto ToString(const T & x) -> decltype(x.ToString()) { return x.ToString(); } + +static inline std::string ToString(const std::string & x) { return x; } +static inline std::string ToString(const char * const & x) { return x; } +MPT_DEPRECATED static inline std::string ToString(const char & x) { return std::string(1, x); } // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if MPT_WSTRING_FORMAT +MPT_DEPRECATED std::string ToString(const std::wstring & x); // Unknown encoding. +MPT_DEPRECATED std::string ToString(const wchar_t * const & x); // Unknown encoding. +MPT_DEPRECATED std::string ToString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif +#if MPT_USTRING_MODE_UTF8 +MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding. +#endif +#if defined(_MFC_VER) +MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding. +#endif +#if defined(_MFC_VER) +MPT_DEPRECATED std::string ToString(const CString & x); +#endif +std::string ToString(const bool & x); +std::string ToString(const signed char & x); +std::string ToString(const unsigned char & x); +std::string ToString(const signed short & x); +std::string ToString(const unsigned short & x); +std::string ToString(const signed int & x); +std::string ToString(const unsigned int & x); +std::string ToString(const signed long & x); +std::string ToString(const unsigned long & x); +std::string ToString(const signed long long & x); +std::string ToString(const unsigned long long & x); +std::string ToString(const float & x); +std::string ToString(const double & x); +std::string ToString(const long double & x); + +// fallback to member function ToUString() +template auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); } + +static inline mpt::ustring ToUString(const mpt::ustring & x) { return x; } +MPT_DEPRECATED mpt::ustring ToUString(const std::string & x); // Unknown encoding. +MPT_DEPRECATED mpt::ustring ToUString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +MPT_DEPRECATED mpt::ustring ToUString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const std::wstring & x); +#endif +mpt::ustring ToUString(const wchar_t * const & x); +MPT_DEPRECATED mpt::ustring ToUString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif +#if defined(_MFC_VER) +mpt::ustring ToUString(const CString & x); +#endif +mpt::ustring ToUString(const bool & x); +mpt::ustring ToUString(const signed char & x); +mpt::ustring ToUString(const unsigned char & x); +mpt::ustring ToUString(const signed short & x); +mpt::ustring ToUString(const unsigned short & x); +mpt::ustring ToUString(const signed int & x); +mpt::ustring ToUString(const unsigned int & x); +mpt::ustring ToUString(const signed long & x); +mpt::ustring ToUString(const unsigned long & x); +mpt::ustring ToUString(const signed long long & x); +mpt::ustring ToUString(const unsigned long long & x); +mpt::ustring ToUString(const float & x); +mpt::ustring ToUString(const double & x); +mpt::ustring ToUString(const long double & x); + +#if MPT_WSTRING_FORMAT +MPT_DEPRECATED std::wstring ToWString(const std::string & x); // Unknown encoding. +MPT_DEPRECATED std::wstring ToWString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +MPT_DEPRECATED std::wstring ToWString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +static inline std::wstring ToWString(const std::wstring & x) { return x; } +static inline std::wstring ToWString(const wchar_t * const & x) { return x; } +MPT_DEPRECATED static inline std::wstring ToWString(const wchar_t & x) { return std::wstring(1, x); } // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#if MPT_USTRING_MODE_UTF8 +std::wstring ToWString(const mpt::ustring & x); +#endif +#if defined(_MFC_VER) +std::wstring ToWString(const CString & x); +#endif +std::wstring ToWString(const bool & x); +std::wstring ToWString(const signed char & x); +std::wstring ToWString(const unsigned char & x); +std::wstring ToWString(const signed short & x); +std::wstring ToWString(const unsigned short & x); +std::wstring ToWString(const signed int & x); +std::wstring ToWString(const unsigned int & x); +std::wstring ToWString(const signed long & x); +std::wstring ToWString(const unsigned long & x); +std::wstring ToWString(const signed long long & x); +std::wstring ToWString(const unsigned long long & x); +std::wstring ToWString(const float & x); +std::wstring ToWString(const double & x); +std::wstring ToWString(const long double & x); +#endif + +#if defined(_MFC_VER) +#ifdef UNICODE +#if MPT_WSTRING_FORMAT +template static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToWString(x)); } +#else +template static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToUString(x)); } +#endif +#else +namespace detail { +template struct CstringToStdStringImpl { CString operator () (const T & v) { return mpt::ToCString(mpt::CharsetLocale, ToString(v)); } }; +template <> struct CstringToStdStringImpl { CString operator () (const CString & v) { return v; } }; +} +template static inline CString ToCStringHelper(const T & x) { return mpt::detail::CstringToStdStringImpl()(x); } +#endif +#endif + +template struct ToStringTFunctor {}; +template <> struct ToStringTFunctor { template inline std::string operator() (const T & x) { return ToString(x); } }; +template <> struct ToStringTFunctor { template inline mpt::ustring operator() (const T & x) { return ToUString(x); } }; +#if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8 +template <> struct ToStringTFunctor { template inline std::wstring operator() (const T & x) { return ToWString(x); } }; +#endif +#if defined(_MFC_VER) +template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper(x); } }; +#endif + +template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } + + +struct fmt_base +{ + +enum FormatFlagsEnum +{ + BaseDec = 0x0001, // base 10 (integers only) + BaseHex = 0x0002, // base 16 (integers only) + CaseLow = 0x0010, // lower case hex digits + CaseUpp = 0x0020, // upper case hex digits + FillOff = 0x0100, // do not fill up width + FillSpc = 0x0200, // fill up width with spaces + FillNul = 0x0400, // fill up width with zeros + NotaNrm = 0x1000, // float: normal/default notation + NotaFix = 0x2000, // float: fixed point notation + NotaSci = 0x4000, // float: scientific notation +}; + +}; // struct fmt_base + +typedef unsigned int FormatFlags; + +STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum)); + +class FormatSpec; + +MPT_DEPRECATED std::string FormatVal(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::string FormatVal(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::string FormatVal(const bool & x, const FormatSpec & f); +std::string FormatVal(const signed char & x, const FormatSpec & f); +std::string FormatVal(const unsigned char & x, const FormatSpec & f); +std::string FormatVal(const signed short & x, const FormatSpec & f); +std::string FormatVal(const unsigned short & x, const FormatSpec & f); +std::string FormatVal(const signed int & x, const FormatSpec & f); +std::string FormatVal(const unsigned int & x, const FormatSpec & f); +std::string FormatVal(const signed long & x, const FormatSpec & f); +std::string FormatVal(const unsigned long & x, const FormatSpec & f); +std::string FormatVal(const signed long long & x, const FormatSpec & f); +std::string FormatVal(const unsigned long long & x, const FormatSpec & f); +std::string FormatVal(const float & x, const FormatSpec & f); +std::string FormatVal(const double & x, const FormatSpec & f); +std::string FormatVal(const long double & x, const FormatSpec & f); + +#if MPT_WSTRING_FORMAT +MPT_DEPRECATED std::wstring FormatValW(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +MPT_DEPRECATED std::wstring FormatValW(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::wstring FormatValW(const bool & x, const FormatSpec & f); +std::wstring FormatValW(const signed char & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned char & x, const FormatSpec & f); +std::wstring FormatValW(const signed short & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned short & x, const FormatSpec & f); +std::wstring FormatValW(const signed int & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned int & x, const FormatSpec & f); +std::wstring FormatValW(const signed long & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned long & x, const FormatSpec & f); +std::wstring FormatValW(const signed long long & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f); +std::wstring FormatValW(const float & x, const FormatSpec & f); +std::wstring FormatValW(const double & x, const FormatSpec & f); +std::wstring FormatValW(const long double & x, const FormatSpec & f); +#endif + +template struct FormatValTFunctor {}; +template <> struct FormatValTFunctor { template inline std::string operator() (const T & x, const FormatSpec & f) { return FormatVal(x, f); } }; +#if MPT_WSTRING_FORMAT +template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; +#endif +#if MPT_USTRING_MODE_UTF8 +template <> struct FormatValTFunctor { template inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatVal(x, f)); } }; +#endif +#if defined(_MFC_VER) +#ifdef UNICODE +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } }; +#else +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::CharsetLocale, FormatVal(x, f)); } }; +#endif +#endif + + +class FormatSpec +{ +private: + FormatFlags flags; + std::size_t width; + int precision; +public: + FormatSpec() : flags(0), width(0), precision(-1) {} + FormatFlags GetFlags() const { return flags; } + std::size_t GetWidth() const { return width; } + int GetPrecision() const { return precision; } + FormatSpec & SetFlags(FormatFlags f) { flags = f; return *this; } + FormatSpec & SetWidth(std::size_t w) { width = w; return *this; } + FormatSpec & SetPrecision(int p) { precision = p; return *this; } +public: + // short-hand construction + explicit FormatSpec(FormatFlags f, std::size_t w = 0, int p = -1) : flags(f), width(w), precision(p) {} + explicit FormatSpec(const char * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit FormatSpec(const wchar_t * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit FormatSpec(const std::string & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + explicit FormatSpec(const std::wstring & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } +public: + // only for floating point formats + FormatSpec & ParsePrintf(const char * format); + FormatSpec & ParsePrintf(const wchar_t * format); + FormatSpec & ParsePrintf(const std::string & format); + FormatSpec & ParsePrintf(const std::wstring & format); +public: + FormatSpec & BaseDec() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; } + FormatSpec & BaseHex() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; } + FormatSpec & CaseLow() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; } + FormatSpec & CaseUpp() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; } + FormatSpec & FillOff() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; } + FormatSpec & FillSpc() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillSpc; return *this; } + FormatSpec & FillNul() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; } + FormatSpec & NotaNrm() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; } + FormatSpec & NotaFix() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; } + FormatSpec & NotaSci() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; } + FormatSpec & Width(std::size_t w) { width = w; return *this; } + FormatSpec & Prec(int p) { precision = p; return *this; } +public: + FormatSpec & Dec() { return BaseDec(); } + FormatSpec & Hex() { return BaseHex(); } + FormatSpec & Low() { return CaseLow(); } + FormatSpec & Upp() { return CaseUpp(); } + FormatSpec & Off() { return FillOff(); } + FormatSpec & Spc() { return FillSpc(); } + FormatSpec & Nul() { return FillNul(); } + FormatSpec & Nrm() { return NotaNrm(); } + FormatSpec & Fix() { return NotaFix(); } + FormatSpec & Sci() { return NotaSci(); } +public: + FormatSpec & Decimal() { return BaseDec(); } + FormatSpec & Hexadecimal() { return BaseHex(); } + FormatSpec & Lower() { return CaseLow(); } + FormatSpec & Upper() { return CaseUpp(); } + FormatSpec & FillNone() { return FillOff(); } + FormatSpec & FillSpace() { return FillSpc(); } + FormatSpec & FillZero() { return FillNul(); } + FormatSpec & FloatNormal() { return NotaNrm(); } + FormatSpec & FloatFixed() { return NotaFix(); } + FormatSpec & FloatScientific() { return NotaSci(); } + FormatSpec & Precision(int p) { return Prec(p); } + template + inline Tstring ToStringT(const T & x) const + { + return FormatValTFunctor()(x, *this); + } + template + inline std::string ToString(const T & x) const + { + return FormatVal(x, *this); + } +#if MPT_WSTRING_FORMAT + template + inline std::wstring ToWString(const T & x) const + { + return FormatValW(x, *this); + } +#endif +}; + + +template +struct fmtT : fmt_base +{ + +template +static inline Tstring val(const T& x) +{ + return ToStringTFunctor()(x); +} + +template +static inline Tstring dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); +} +template +static inline Tstring dec(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillSpc().Width(width)); +} +template +static inline Tstring dec0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width)); +} + +template +static inline Tstring hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff()); +} +template +static inline Tstring HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); +} +template +static inline Tstring hex(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillSpc().Width(width)); +} +template +static inline Tstring HEX(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillSpc().Width(width)); +} +template +static inline Tstring hex0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width)); +} +template +static inline Tstring HEX0(const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width)); +} + +template +static inline Tstring flt(const T& x, std::size_t width = 0, int precision = -1) +{ + STATIC_ASSERT(std::is_floating_point::value); + if(width == 0) + { + return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); + } else + { + return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillSpc().Width(width).Precision(precision)); + } +} +template +static inline Tstring fix(const T& x, std::size_t width = 0, int precision = -1) +{ + STATIC_ASSERT(std::is_floating_point::value); + if(width == 0) + { + return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); + } else + { + return FormatValTFunctor()(x, FormatSpec().NotaFix().FillSpc().Width(width).Precision(precision)); + } +} +template +static inline Tstring sci(const T& x, std::size_t width = 0, int precision = -1) +{ + STATIC_ASSERT(std::is_floating_point::value); + if(width == 0) + { + return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); + } else + { + return FormatValTFunctor()(x, FormatSpec().NotaSci().FillSpc().Width(width).Precision(precision)); + } +} + +template +static inline Tstring f(const Tformat & format, const T& x) +{ + STATIC_ASSERT(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().ParsePrintf(format)); +} + +}; // struct fmtT + +typedef fmtT fmt; +#if MPT_WSTRING_FORMAT +typedef fmtT wfmt; +#endif +#if MPT_USTRING_MODE_WIDE +typedef fmtT ufmt; +#else +typedef fmtT ufmt; +#endif +#if defined(_MFC_VER) +typedef fmtT tfmt; +#endif + +} // namespace mpt + +namespace mpt { + +namespace String { + +namespace detail +{ + +template struct to_string_type { }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +#if MPT_USTRING_MODE_UTF8 +template <> struct to_string_type { typedef mpt::ustring type; }; +#endif +#if defined(_MFC_VER) +template <> struct to_string_type { typedef CString type; }; +#endif +template struct to_string_type { typedef typename to_string_type::type type; }; + +std::string PrintImpl(const std::string & format + , const std::string & x1 = std::string() + , const std::string & x2 = std::string() + , const std::string & x3 = std::string() + , const std::string & x4 = std::string() + , const std::string & x5 = std::string() + , const std::string & x6 = std::string() + , const std::string & x7 = std::string() + , const std::string & x8 = std::string() + ); + +#if MPT_WSTRING_FORMAT +std::wstring PrintImpl(const std::wstring & format + , const std::wstring & x1 = std::wstring() + , const std::wstring & x2 = std::wstring() + , const std::wstring & x3 = std::wstring() + , const std::wstring & x4 = std::wstring() + , const std::wstring & x5 = std::wstring() + , const std::wstring & x6 = std::wstring() + , const std::wstring & x7 = std::wstring() + , const std::wstring & x8 = std::wstring() + ); +#endif + +#if MPT_USTRING_MODE_UTF8 +mpt::ustring PrintImpl(const mpt::ustring & format + , const mpt::ustring & x1 = mpt::ustring() + , const mpt::ustring & x2 = mpt::ustring() + , const mpt::ustring & x3 = mpt::ustring() + , const mpt::ustring & x4 = mpt::ustring() + , const mpt::ustring & x5 = mpt::ustring() + , const mpt::ustring & x6 = mpt::ustring() + , const mpt::ustring & x7 = mpt::ustring() + , const mpt::ustring & x8 = mpt::ustring() + ); +#endif + +#if defined(_MFC_VER) +CString PrintImpl(const CString & format + , const CString & x1 = CString() + , const CString & x2 = CString() + , const CString & x3 = CString() + , const CString & x4 = CString() + , const CString & x5 = CString() + , const CString & x6 = CString() + , const CString & x7 = CString() + , const CString & x8 = CString() + ); +#endif + +} // namespace detail + +} // namespace String + +template +struct message_formatter +{ +typedef typename mpt::String::detail::to_string_type::type Tstring; +Tstring format; +message_formatter(const Tstring & format) : format(format) {} + +Tstring operator() ( + ) const +{ + return mpt::String::detail::PrintImpl(format + ); +} + +template< + typename T1 +> +Tstring operator() ( + const T1& x1 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + ); +} + +template< + typename T1 + , typename T2 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 + , typename T4 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + , ToStringTFunctor()(x4) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 + , typename T4 + , typename T5 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + , ToStringTFunctor()(x4) + , ToStringTFunctor()(x5) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 + , typename T4 + , typename T5 + , typename T6 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + , ToStringTFunctor()(x4) + , ToStringTFunctor()(x5) + , ToStringTFunctor()(x6) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 + , typename T4 + , typename T5 + , typename T6 + , typename T7 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 + , const T7& x7 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + , ToStringTFunctor()(x4) + , ToStringTFunctor()(x5) + , ToStringTFunctor()(x6) + , ToStringTFunctor()(x7) + ); +} + +template< + typename T1 + , typename T2 + , typename T3 + , typename T4 + , typename T5 + , typename T6 + , typename T7 + , typename T8 +> +Tstring operator() ( + const T1& x1 + , const T2& x2 + , const T3& x3 + , const T4& x4 + , const T5& x5 + , const T6& x6 + , const T7& x7 + , const T8& x8 + ) const { + return mpt::String::detail::PrintImpl(format + , ToStringTFunctor()(x1) + , ToStringTFunctor()(x2) + , ToStringTFunctor()(x3) + , ToStringTFunctor()(x4) + , ToStringTFunctor()(x5) + , ToStringTFunctor()(x6) + , ToStringTFunctor()(x7) + , ToStringTFunctor()(x8) + ); +} + +}; // struct message_formatter + +template +message_formatter::type> format(const Tformat &format) +{ + typedef typename mpt::String::detail::to_string_type::type Tstring; + return message_formatter(Tstring(format)); +} + +#if MPT_WSTRING_FORMAT +static inline message_formatter wformat(const std::wstring &format) +{ + return message_formatter(format); +} +#endif + +static inline message_formatter uformat(const mpt::ustring &format) +{ + return message_formatter(format); +} + +#if defined(_MFC_VER) +static inline message_formatter tformat(const CString &format) +{ + return message_formatter(format); +} +#endif + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.cpp new file mode 100644 index 000000000..89c948b9d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.cpp @@ -0,0 +1,120 @@ +/* + * mptStringParse.cpp + * ------------------ + * Purpose: Convert strings to other types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptStringParse.h" + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +template +inline T ConvertStrToHelper(const std::string &str) +{ + std::istringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::string &str) { return ConvertStrToHelper(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } + +#if MPT_WSTRING_FORMAT +template +inline T ConvertStrToHelper(const std::wstring &str) +{ + std::wistringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::wstring &str) { return ConvertStrToHelper(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } +#endif + +bool ConvertStrToBool(const std::string &str) { return ConvertStrToHelper(str); } +signed char ConvertStrToSignedChar(const std::string &str) { return ConvertStrToHelper(str); } +unsigned char ConvertStrToUnsignedChar(const std::string &str) { return ConvertStrToHelper(str); } +signed short ConvertStrToSignedShort(const std::string &str) { return ConvertStrToHelper(str); } +unsigned short ConvertStrToUnsignedShort(const std::string &str) { return ConvertStrToHelper(str); } +signed int ConvertStrToSignedInt(const std::string &str) { return ConvertStrToHelper(str); } +unsigned int ConvertStrToUnsignedInt(const std::string &str) { return ConvertStrToHelper(str); } +signed long ConvertStrToSignedLong(const std::string &str) { return ConvertStrToHelper(str); } +unsigned long ConvertStrToUnsignedLong(const std::string &str) { return ConvertStrToHelper(str); } +signed long long ConvertStrToSignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } +float ConvertStrToFloat(const std::string &str) { return ConvertStrToHelper(str); } +double ConvertStrToDouble(const std::string &str) { return ConvertStrToHelper(str); } +long double ConvertStrToLongDouble(const std::string &str) { return ConvertStrToHelper(str); } + +#if MPT_WSTRING_FORMAT +bool ConvertStrToBool(const std::wstring &str) { return ConvertStrToHelper(str); } +signed char ConvertStrToSignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned char ConvertStrToUnsignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } +signed short ConvertStrToSignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned short ConvertStrToUnsignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } +signed int ConvertStrToSignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned int ConvertStrToUnsignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } +signed long ConvertStrToSignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned long ConvertStrToUnsignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } +signed long long ConvertStrToSignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } +float ConvertStrToFloat(const std::wstring &str) { return ConvertStrToHelper(str); } +double ConvertStrToDouble(const std::wstring &str) { return ConvertStrToHelper(str); } +long double ConvertStrToLongDouble(const std::wstring &str) { return ConvertStrToHelper(str); } +#endif + + +namespace mpt +{ +namespace String +{ +namespace Parse +{ + +template +T HexToHelper(const std::string &str) +{ + std::istringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> std::hex >> x)) + { + return T(); + } + return x; +} +template<> unsigned char HexToHelper(const std::string &str) { return static_cast(HexToHelper(str)); } + +unsigned char HexToUnsignedChar(const std::string &str) { return HexToHelper(str); } +unsigned short HexToUnsignedShort(const std::string &str) { return HexToHelper(str); } +unsigned int HexToUnsignedInt(const std::string &str) { return HexToHelper(str); } +unsigned long HexToUnsignedLong(const std::string &str) { return HexToHelper(str); } +unsigned long long HexToUnsignedLongLong(const std::string &str) { return HexToHelper(str); } + +} // namespace Parse +} // namespace String +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h new file mode 100644 index 000000000..62307a6e0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h @@ -0,0 +1,196 @@ +/* + * mptStringParse.h + * ---------------- + * Purpose: Convert strings to other types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +OPENMPT_NAMESPACE_BEGIN + + +bool ConvertStrToBool(const std::string &str); +signed char ConvertStrToSignedChar(const std::string &str); +unsigned char ConvertStrToUnsignedChar(const std::string &str); +signed short ConvertStrToSignedShort(const std::string &str); +unsigned short ConvertStrToUnsignedShort(const std::string &str); +signed int ConvertStrToSignedInt(const std::string &str); +unsigned int ConvertStrToUnsignedInt(const std::string &str); +signed long ConvertStrToSignedLong(const std::string &str); +unsigned long ConvertStrToUnsignedLong(const std::string &str); +signed long long ConvertStrToSignedLongLong(const std::string &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str); +float ConvertStrToFloat(const std::string &str); +double ConvertStrToDouble(const std::string &str); +long double ConvertStrToLongDouble(const std::string &str); +template inline T ConvertStrTo(const std::string &str); // not defined, generates compiler error for non-specialized types +template<> inline std::string ConvertStrTo(const std::string &str) { return str; } +template<> inline bool ConvertStrTo(const std::string &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::string &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::string &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::string &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::string &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::string &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::string &str) { return ConvertStrToLongDouble(str); } + +#if MPT_WSTRING_FORMAT +bool ConvertStrToBool(const std::wstring &str); +signed char ConvertStrToSignedChar(const std::wstring &str); +unsigned char ConvertStrToUnsignedChar(const std::wstring &str); +signed short ConvertStrToSignedShort(const std::wstring &str); +unsigned short ConvertStrToUnsignedShort(const std::wstring &str); +signed int ConvertStrToSignedInt(const std::wstring &str); +unsigned int ConvertStrToUnsignedInt(const std::wstring &str); +signed long ConvertStrToSignedLong(const std::wstring &str); +unsigned long ConvertStrToUnsignedLong(const std::wstring &str); +signed long long ConvertStrToSignedLongLong(const std::wstring &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str); +float ConvertStrToFloat(const std::wstring &str); +double ConvertStrToDouble(const std::wstring &str); +long double ConvertStrToLongDouble(const std::wstring &str); +template inline T ConvertStrTo(const std::wstring &str); // not defined, generates compiler error for non-specialized types +template<> inline std::wstring ConvertStrTo(const std::wstring &str) { return str; } +template<> inline bool ConvertStrTo(const std::wstring &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::wstring &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } +#endif + +#if defined(_MFC_VER) +template +inline T ConvertStrTo(const CString &str) +{ + #if defined(UNICODE) && MPT_WSTRING_FORMAT + return ConvertStrTo(mpt::ToWide(str)); + #elif defined(UNICODE) + return ConvertStrTo(mpt::ToCharset(mpt::CharsetUTF8, str)); + #else // !UNICODE + return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); + #endif // UNICODE +} +#endif // _MFC_VER + +template +inline T ConvertStrTo(const char *str) +{ + if(!str) + { + return T(); + } + return ConvertStrTo(std::string(str)); +} + +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +template<> inline mpt::ustring ConvertStrTo(const std::wstring &str) { return mpt::ToUnicode(str); } +#endif +template +inline T ConvertStrTo(const wchar_t *str) +{ + if(!str) + { + return T(); + } + return ConvertStrTo(std::wstring(str)); +} +#endif + +#if MPT_USTRING_MODE_UTF8 +template +inline T ConvertStrTo(const mpt::ustring &str) +{ + return ConvertStrTo(mpt::ToCharset(mpt::CharsetUTF8, str)); +} +template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; } +#if MPT_WSTRING_CONVERT +template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mpt::ToWide(str); } +#endif +#endif + + +namespace mpt +{ +namespace String +{ +namespace Parse +{ + +unsigned char HexToUnsignedChar(const std::string &str); +unsigned short HexToUnsignedShort(const std::string &str); +unsigned int HexToUnsignedInt(const std::string &str); +unsigned long HexToUnsignedLong(const std::string &str); +unsigned long long HexToUnsignedLongLong(const std::string &str); + +template inline T Hex(const std::string &str); // not defined, generates compiler error for non-specialized types +template<> inline unsigned char Hex(const std::string &str) { return HexToUnsignedChar(str); } +template<> inline unsigned short Hex(const std::string &str) { return HexToUnsignedShort(str); } +template<> inline unsigned int Hex(const std::string &str) { return HexToUnsignedInt(str); } +template<> inline unsigned long Hex(const std::string &str) { return HexToUnsignedLong(str); } +template<> inline unsigned long long Hex(const std::string &str) { return HexToUnsignedLongLong(str); } + +template +inline T Hex(const char *str) +{ + if(!str) + { + return T(); + } + return Hex(std::string(str)); +} + +#if MPT_WSTRING_FORMAT + +template +inline T Hex(const std::wstring &str) +{ + return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); +} + +template +inline T Hex(const wchar_t *str) +{ + if(!str) + { + return T(); + } + return Hex(std::wstring(str)); +} + +#endif + +#if MPT_USTRING_MODE_UTF8 +template +inline T Hex(const mpt::ustring &str) +{ + return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); +} +#endif + +} // namespace Parse +} // namespace String +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h new file mode 100644 index 000000000..499fc17e3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h @@ -0,0 +1,460 @@ +/* + * mptThread.h + * ----------- + * Purpose: Helper class for running threads, with a more or less platform-independent interface. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#if defined(MPT_ENABLE_THREAD) + +#include // some C++ header in order to have the C++ standard library version information available + +#if defined(MPT_QUIRK_NO_CPP_THREAD) +#define MPT_STD_THREAD 0 +#elif MPT_COMPILER_GENERIC +#define MPT_STD_THREAD 1 +#elif MPT_COMPILER_MSVC +#define MPT_STD_THREAD 1 +#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS +#define MPT_STD_THREAD 1 +#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__) +#define MPT_STD_THREAD 1 +#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG +#define MPT_STD_THREAD 1 +#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION) +#define MPT_STD_THREAD 1 +#else +#define MPT_STD_THREAD 0 +#endif + +#if MPT_STD_THREAD +#include +#else // !MPT_STD_THREAD +#if MPT_OS_WINDOWS +#include +#else // !MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MPT_STD_THREAD + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#endif // MPT_ENABLE_THREAD + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_THREAD) + +namespace mpt +{ + + + +#if MPT_STD_THREAD + + + +typedef std::thread::native_handle_type native_handle_type; +typedef std::thread thread; + + + +#else // !MPT_STD_THREAD + + + +#if MPT_OS_WINDOWS + + + +typedef HANDLE native_handle_type; + +// std::thread +// NOTE: This implementation is not movable and prevents copying. +// Therefore, it is not as versatile as a full C++11 std::thread implementation. +// It is only a strict subset. +class thread +{ + +private: + + thread(const thread &) = delete; + thread & operator = (const thread &) = delete; + +private: + + class functor_helper_base { + protected: + functor_helper_base() {} + public: + virtual ~functor_helper_base() {} + public: + virtual void operator () () = 0; + }; + + template + class functor_helper : public functor_helper_base { + private: + Tfunc func; + public: + functor_helper(Tfunc func_) : func(func_) { return; } + virtual ~functor_helper() { return; } + virtual void operator () () { func(); } + }; + + enum FunctionMode + { + FunctionModeNone = 0, + FunctionModeParamNone = 1, + FunctionModeParamPointer = 2, + FunctionModeFunctor = 3, + }; + + native_handle_type threadHandle; + + // Thread startup accesses members of mpt::thread. + // If the mpt::thread instanced gets detached and destroyed directly after initialization, + // there is a race between thread startup and detach/destroy. + // startupDoneEvent protects against this race. + HANDLE startupDoneEvent; + + FunctionMode functionMode; + union { + struct { + void (*function)(void); + } ModeParamNone; + struct { + void (*function)(void*); + void * userdata; + } ModeParamPointer; + struct { + functor_helper_base * pfunctor; + } ModeFunctor; + } modeState; + +private: + + uintptr_t ThreadFuntion() + { + switch(functionMode) + { + case FunctionModeNone: + SetEvent(startupDoneEvent); + return 0; + break; + case FunctionModeParamNone: + { + void (*f)(void) = modeState.ModeParamNone.function; + SetEvent(startupDoneEvent); + f(); + } + return 0; + break; + case FunctionModeParamPointer: + { + void (*f)(void*) = modeState.ModeParamPointer.function; + void * d = modeState.ModeParamPointer.userdata; + SetEvent(startupDoneEvent); + f(d); + } + return 0; + break; + case FunctionModeFunctor: + { + functor_helper_base * pf = modeState.ModeFunctor.pfunctor; + SetEvent(startupDoneEvent); + (*pf)(); + delete pf; + } + return 0; + break; + default: + SetEvent(startupDoneEvent); + return 0; + break; + } + SetEvent(startupDoneEvent); + return 0; + } + + static DWORD WINAPI ThreadFunctionWrapper(LPVOID param) + { + reinterpret_cast(param)->ThreadFuntion(); + return 0; + } + +public: + + mpt::native_handle_type native_handle() + { + return threadHandle; + } + + bool joinable() const + { + return (threadHandle != nullptr); + } + + void join() + { + if(!joinable()) + { + throw std::invalid_argument("thread::joinable() == false"); + } + WaitForSingleObject(threadHandle, INFINITE); + CloseHandle(threadHandle); + threadHandle = nullptr; + } + + void detach() + { + if(!joinable()) + { + throw std::invalid_argument("thread::joinable() == false"); + } + CloseHandle(threadHandle); + threadHandle = nullptr; + } + + void swap(thread & other) noexcept + { + using std::swap; + swap(threadHandle, other.threadHandle); + swap(startupDoneEvent, other.startupDoneEvent); + swap(functionMode, other.functionMode); + } + + friend void swap(thread & a, thread & b) noexcept + { + a.swap(b); + } + + thread(thread && other) noexcept + : threadHandle(nullptr) + , startupDoneEvent(nullptr) + , functionMode(FunctionModeNone) + { + swap(other); + } + + thread & operator=(thread && other) noexcept + { + if(joinable()) + { + std::terminate(); + } + swap(other); + return *this; + } + + thread() + : threadHandle(nullptr) + , startupDoneEvent(nullptr) + , functionMode(FunctionModeNone) + { + std::memset(&modeState, 0, sizeof(modeState)); + } + + thread(void (*function)(void)) + : threadHandle(nullptr) + , startupDoneEvent(nullptr) + , functionMode(FunctionModeParamNone) + { + std::memset(&modeState, 0, sizeof(modeState)); + modeState.ModeParamNone.function = function; + startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } + DWORD dummy = 0; // For Win9x + threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); + if(threadHandle) + { + WaitForSingleObject(startupDoneEvent, INFINITE); + } + CloseHandle(startupDoneEvent); + startupDoneEvent = nullptr; + if(!threadHandle) { throw std::runtime_error("unable to start thread"); } + } + + thread(void (*function)(void*), void * userdata) + : threadHandle(nullptr) + , startupDoneEvent(nullptr) + , functionMode(FunctionModeParamPointer) + { + std::memset(&modeState, 0, sizeof(modeState)); + modeState.ModeParamPointer.function = function; + modeState.ModeParamPointer.userdata = userdata; + startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } + DWORD dummy = 0; // For Win9x + threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); + if(threadHandle) + { + WaitForSingleObject(startupDoneEvent, INFINITE); + } + CloseHandle(startupDoneEvent); + startupDoneEvent = nullptr; + if(!threadHandle) { throw std::runtime_error("unable to start thread"); } + } + + template + thread(Tfunctor functor) + : threadHandle(nullptr) + , startupDoneEvent(nullptr) + , functionMode(FunctionModeFunctor) + { + std::memset(&modeState, 0, sizeof(modeState)); + modeState.ModeFunctor.pfunctor = new functor_helper(functor); + startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } + DWORD dummy = 0; // For Win9x + threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); + if(threadHandle) + { + WaitForSingleObject(startupDoneEvent, INFINITE); + } + CloseHandle(startupDoneEvent); + startupDoneEvent = nullptr; + if(!threadHandle) { throw std::runtime_error("unable to start thread"); } + } + + ~thread() + { + MPT_ASSERT(!joinable()); + } + +public: + + static unsigned int hardware_concurrency() + { + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return std::max(sysInfo.dwNumberOfProcessors, 1); + } + +}; + + + +#endif // MPT_OS_WINDOWS + + + +#endif // MPT_STD_THREAD + + + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +enum ThreadPriority +{ + ThreadPriorityLowest = THREAD_PRIORITY_LOWEST, + ThreadPriorityLower = THREAD_PRIORITY_BELOW_NORMAL, + ThreadPriorityNormal = THREAD_PRIORITY_NORMAL, + ThreadPriorityHigh = THREAD_PRIORITY_ABOVE_NORMAL, + ThreadPriorityHighest = THREAD_PRIORITY_HIGHEST +}; + +inline void SetThreadPriority(mpt::thread &t, mpt::ThreadPriority priority) +{ + ::SetThreadPriority(t.native_handle(), priority); +} + +inline void SetCurrentThreadPriority(mpt::ThreadPriority priority) +{ + ::SetThreadPriority(GetCurrentThread(), priority); +} + +#else // !MPT_OS_WINDOWS + +enum ThreadPriority +{ + ThreadPriorityLowest = -2, + ThreadPriorityLower = -1, + ThreadPriorityNormal = 0, + ThreadPriorityHigh = 1, + ThreadPriorityHighest = 2 +}; + +inline void SetThreadPriority(mpt::thread & /*t*/ , mpt::ThreadPriority /*priority*/ ) +{ + // nothing +} + +inline void SetCurrentThreadPriority(mpt::ThreadPriority /*priority*/ ) +{ + // nothing +} + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +// Default WinAPI thread +class UnmanagedThread +{ +protected: + HANDLE threadHandle; + +public: + + operator HANDLE& () { return threadHandle; } + operator bool () const { return threadHandle != nullptr; } + + UnmanagedThread() : threadHandle(nullptr) { } + UnmanagedThread(LPTHREAD_START_ROUTINE function, void *userData = nullptr) + { + DWORD dummy = 0; // For Win9x + threadHandle = CreateThread(NULL, 0, function, userData, 0, &dummy); + } +}; + +// Thread that operates on a member function +template +class UnmanagedThreadMember : public mpt::UnmanagedThread +{ +protected: + static DWORD WINAPI wrapperFunc(LPVOID param) + { + (static_cast(param)->*Fun)(); + return 0; + } + +public: + + UnmanagedThreadMember(T *instance) : mpt::UnmanagedThread(wrapperFunc, instance) { } +}; + +inline void SetThreadPriority(mpt::UnmanagedThread &t, mpt::ThreadPriority priority) +{ + ::SetThreadPriority(t, priority); +} + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + +} // namespace mpt + +#endif // MPT_ENABLE_THREAD + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp new file mode 100644 index 000000000..dec625f89 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp @@ -0,0 +1,307 @@ +/* + * mptTime.cpp + * ----------- + * Purpose: Various time utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptTime.h" + +#include + +#if MPT_OS_WINDOWS +#include +#if defined(MODPLUG_TRACKER) +#include +#endif +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace Date +{ + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +namespace ANSI +{ + +uint64 Now() +{ + FILETIME filetime; + GetSystemTimeAsFileTime(&filetime); + return ((uint64)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime); +} + +mpt::ustring ToString(uint64 time100ns) +{ + static const std::size_t bufsize = 256; + + mpt::ustring result; + + FILETIME filetime; + SYSTEMTIME systime; + filetime.dwHighDateTime = (DWORD)(((uint64)time100ns) >> 32); + filetime.dwLowDateTime = (DWORD)((uint64)time100ns); + FileTimeToSystemTime(&filetime, &systime); + + WCHAR buf[bufsize]; + + GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &systime, L"yyyy-MM-dd", buf, bufsize); + result.append(mpt::ToUnicode(buf)); + + result.append(MPT_USTRING(" ")); + + GetTimeFormatW(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, L"HH:mm:ss", buf, bufsize); + result.append(mpt::ToUnicode(buf)); + + result.append(MPT_USTRING(".")); + + result.append(mpt::ufmt::dec0<3>((unsigned)systime.wMilliseconds)); + + return result; + +} + +} // namespace ANSI + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + +Unix::Unix() + : Value(0) +{ + return; +} + +Unix::Unix(int64 unixtime) + : Value(unixtime) +{ + return; +} + +Unix::operator int64 () const +{ + return Value; +} + +static int32 ToDaynum(int32 year, int32 month, int32 day) +{ + month = (month + 9) % 12; + year = year - (month / 10); + int32 daynum = year*365 + year/4 - year/100 + year/400 + (month*306 + 5)/10 + (day - 1); + return daynum; +} + +static void FromDaynum(int32 d, int32 & year, int32 & month, int32 & day) +{ + int64 g = d; + int64 y,ddd,mi,mm,dd; + + y = (10000*g + 14780)/3652425; + ddd = g - (365*y + y/4 - y/100 + y/400); + if(ddd < 0) + { + y = y - 1; + ddd = g - (365*y + y/4 - y/100 + y/400); + } + mi = (100*ddd + 52)/3060; + mm = (mi + 2)%12 + 1; + y = y + (mi + 2)/12; + dd = ddd - (mi*306 + 5)/10 + 1; + + year = static_cast(y); + month = static_cast(mm); + day = static_cast(dd); +} + +mpt::Date::Unix Unix::FromUTC(tm timeUtc) +{ + int32 daynum = ToDaynum(timeUtc.tm_year+1900, timeUtc.tm_mon+1, timeUtc.tm_mday); + int64 seconds = static_cast(daynum - ToDaynum(1970,1,1))*24*60*60 + timeUtc.tm_hour*60*60 + timeUtc.tm_min*60 + timeUtc.tm_sec; + return mpt::Date::Unix(seconds); +} + +tm Unix::AsUTC() const +{ + int64 tmp = Value; + int64 seconds = tmp % 60; tmp /= 60; + int64 minutes = tmp % 60; tmp /= 60; + int64 hours = tmp % 24; tmp /= 24; + int32 year = 0, month = 0, day = 0; + FromDaynum(static_cast(tmp) + ToDaynum(1970,1,1), year, month, day); + tm result; + MemsetZero(result); + result.tm_year = year - 1900; + result.tm_mon = month - 1; + result.tm_mday = day; + result.tm_hour = static_cast(hours); + result.tm_min = static_cast(minutes); + result.tm_sec = static_cast(seconds); + return result; +} + +mpt::ustring ToShortenedISO8601(tm date) +{ + // We assume date in UTC here. + // There are too many differences in supported format specifiers in strftime() + // and strftime does not support reduced precision ISO8601 at all. + // Just do the formatting ourselves. + mpt::ustring result; + mpt::ustring tz = MPT_USTRING("Z"); + if(date.tm_year == 0) + { + return result; + } + result += mpt::ufmt::dec0<4>(date.tm_year + 1900); + if(date.tm_mon < 0 || date.tm_mon > 11) + { + return result; + } + result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); + if(date.tm_mday < 1 || date.tm_mday > 31) + { + return result; + } + result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mday); + if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0) + { + return result; + } + if(date.tm_hour < 0 || date.tm_hour > 23) + { + return result; + } + if(date.tm_min < 0 || date.tm_min > 59) + { + return result; + } + result += MPT_USTRING("T"); + if(date.tm_isdst > 0) + { + tz = MPT_USTRING("+01:00"); + } + result += mpt::ufmt::dec0<2>(date.tm_hour) + MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_min); + if(date.tm_sec < 0 || date.tm_sec > 61) + { + return result + tz; + } + result += MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_sec); + result += tz; + return result; +} + +} // namespace Date +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER + +namespace Util +{ + +#if MPT_OS_WINDOWS + +void MultimediaClock::Init() +{ + m_CurrentPeriod = 0; +} + +void MultimediaClock::SetPeriod(uint32 ms) +{ + TIMECAPS caps; + MemsetZero(caps); + if(timeGetDevCaps(&caps, sizeof(caps)) != MMSYSERR_NOERROR) + { + return; + } + if((caps.wPeriodMax == 0) || (caps.wPeriodMin > caps.wPeriodMax)) + { + return; + } + ms = mpt::clamp(ms, caps.wPeriodMin, caps.wPeriodMax); + if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) + { + return; + } + m_CurrentPeriod = ms; +} + +void MultimediaClock::Cleanup() +{ + if(m_CurrentPeriod > 0) + { + if(timeEndPeriod(m_CurrentPeriod) != MMSYSERR_NOERROR) + { + // should not happen + MPT_ASSERT_NOTREACHED(); + } + m_CurrentPeriod = 0; + } +} + +MultimediaClock::MultimediaClock() +{ + Init(); +} + +MultimediaClock::MultimediaClock(uint32 ms) +{ + Init(); + SetResolution(ms); +} + +MultimediaClock::~MultimediaClock() +{ + Cleanup(); +} + +uint32 MultimediaClock::SetResolution(uint32 ms) +{ + if(m_CurrentPeriod == ms) + { + return m_CurrentPeriod; + } + Cleanup(); + if(ms != 0) + { + SetPeriod(ms); + } + return GetResolution(); +} + +uint32 MultimediaClock::GetResolution() const +{ + return m_CurrentPeriod; +} + +uint32 MultimediaClock::Now() const +{ + return timeGetTime(); +} + +uint64 MultimediaClock::NowNanoseconds() const +{ + return (uint64)timeGetTime() * (uint64)1000000; +} + +#endif // MPT_OS_WINDOWS + +} // namespace Util + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.h b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h new file mode 100644 index 000000000..9d876f1e2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h @@ -0,0 +1,114 @@ +/* + * mptTime.h + * --------- + * Purpose: Various time utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ +namespace Date +{ + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +namespace ANSI +{ +// uint64 counts 100ns since 1601-01-01T00:00Z + +uint64 Now(); + +mpt::ustring ToString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718 + +} // namespacee ANSI + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + +class Unix +{ +// int64 counts 1s since 1970-01-01T00:00Z +private: + int64 Value; +public: + Unix(); + explicit Unix(int64 unixtime); + operator int64 () const; +public: + static mpt::Date::Unix FromUTC(tm timeUtc); + tm AsUTC() const; +}; + +mpt::ustring ToShortenedISO8601(tm date); // i.e. 2015-01-15T18:32:01Z + +} // namespace Date +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER + +namespace Util +{ + +#if MPT_OS_WINDOWS + +// RAII wrapper around timeBeginPeriod/timeEndPeriod/timeGetTime (on Windows). +// This clock is monotonic, even across changing its resolution. +// This is needed to synchronize time in Steinberg APIs (ASIO and VST). +class MultimediaClock +{ +private: + uint32 m_CurrentPeriod; +private: + void Init(); + void SetPeriod(uint32 ms); + void Cleanup(); +public: + MultimediaClock(); + MultimediaClock(uint32 ms); + ~MultimediaClock(); +public: + // Sets the desired resolution in milliseconds, returns the obtained resolution in milliseconds. + // A parameter of 0 causes the resolution to be reset to system defaults. + // A return value of 0 means the resolution is unknown, but timestamps will still be valid. + uint32 SetResolution(uint32 ms); + // Returns obtained resolution in milliseconds. + // A return value of 0 means the resolution is unknown, but timestamps will still be valid. + uint32 GetResolution() const; + // Returns current instantaneous timestamp in milliseconds. + // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. + // The resolution is the value returned from GetResolution(). + uint32 Now() const; + // Returns current instantaneous timestamp in nanoseconds. + // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. + // The resolution is the value returned from GetResolution() in milliseconds. + uint64 NowNanoseconds() const; +}; + +#endif // MPT_OS_WINDOWS + +} // namespace Util + +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h b/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h new file mode 100644 index 000000000..762f8e4b0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h @@ -0,0 +1,142 @@ +/* + * mptTypeTraits.h + * --------------- + * Purpose: C++11 similar type_traits header plus some OpenMPT specific traits. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + +template struct int_of_size { }; +template <> struct int_of_size<1> { typedef int8 type; }; +template <> struct int_of_size<2> { typedef int16 type; }; +template <> struct int_of_size<3> { typedef int32 type; }; +template <> struct int_of_size<4> { typedef int32 type; }; +template <> struct int_of_size<5> { typedef int64 type; }; +template <> struct int_of_size<6> { typedef int64 type; }; +template <> struct int_of_size<7> { typedef int64 type; }; +template <> struct int_of_size<8> { typedef int64 type; }; + +template struct uint_of_size { }; +template <> struct uint_of_size<1> { typedef uint8 type; }; +template <> struct uint_of_size<2> { typedef uint16 type; }; +template <> struct uint_of_size<3> { typedef uint32 type; }; +template <> struct uint_of_size<4> { typedef uint32 type; }; +template <> struct uint_of_size<5> { typedef uint64 type; }; +template <> struct uint_of_size<6> { typedef uint64 type; }; +template <> struct uint_of_size<7> { typedef uint64 type; }; +template <> struct uint_of_size<8> { typedef uint64 type; }; + + +// Tell which types are safe for mpt::byte_cast. +// signed char is actually not allowed to alias into an object representation, +// which means that, if the actual type is not itself signed char but char or +// unsigned char instead, dereferencing the signed char pointer is undefined +// behaviour. +template struct is_byte_castable : public std::false_type { }; +template <> struct is_byte_castable : public std::true_type { }; +template <> struct is_byte_castable : public std::true_type { }; +template <> struct is_byte_castable : public std::true_type { }; +template <> struct is_byte_castable : public std::true_type { }; + + +// Tell which types are safe to binary write into files. +// By default, no types are safe. +// When a safe type gets defined, +// also specialize this template so that IO functions will work. +template struct is_binary_safe : public std::false_type { }; + +// Specialization for byte types. +template <> struct is_binary_safe : public std::true_type { }; +template <> struct is_binary_safe : public std::true_type { }; +template <> struct is_binary_safe : public std::true_type { }; + +// Generic Specialization for arrays. +template struct is_binary_safe : public is_binary_safe { }; +template struct is_binary_safe : public is_binary_safe { }; + +template +struct GetRawBytesFunctor +{ + inline const mpt::byte * operator () (const T & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return reinterpret_cast(&v); + } + inline mpt::byte * operator () (T & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return reinterpret_cast(&v); + } +}; + +template +struct GetRawBytesFunctor +{ + inline const mpt::byte * operator () (const T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return reinterpret_cast(v); + } + inline mpt::byte * operator () (T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return reinterpret_cast(v); + } +}; + +template +struct GetRawBytesFunctor +{ + inline const mpt::byte * operator () (const T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return reinterpret_cast(v); + } +}; + +// In order to be able to partially specialize it, +// as_raw_memory is implemented via a class template. +// Do not overload or specialize as_raw_memory directly. +// Using a wrapper (by default just around a cast to const mpt::byte *), +// allows for implementing raw memory access +// via on-demand generating a cached serialized representation. +template inline const mpt::byte * as_raw_memory(const T & v) +{ + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::GetRawBytesFunctor()(v); +} +template inline mpt::byte * as_raw_memory(T & v) +{ + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::GetRawBytesFunctor()(v); +} + +} // namespace mpt + +#define MPT_BINARY_STRUCT(type, size) \ + MPT_STATIC_ASSERT(sizeof( type ) == (size) ); \ + MPT_STATIC_ASSERT(alignof( type ) == 1); \ + MPT_STATIC_ASSERT(std::is_standard_layout< type >::value); \ + namespace mpt { \ + template <> struct is_binary_safe< type > : public std::true_type { }; \ + } \ +/**/ + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp new file mode 100644 index 000000000..fa4c9e3b7 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp @@ -0,0 +1,756 @@ +/* + * mptUUID.cpp + * ----------- + * Purpose: UUID utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptUUID.h" + +#include "mptRandom.h" +#include "mptStringFormat.h" +#include "Endianness.h" + +#include + +#if MPT_OS_WINDOWS +#include +#include +#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) || MPT_OS_WINDOWS_WINRT +#include +#endif // MODPLUG_TRACKER || !NO_DMO || MPT_OS_WINDOWS_WINRT +#endif // MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_BEGIN + + +#if MPT_OS_WINDOWS + + +namespace Util +{ + + +#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) + + +std::wstring CLSIDToString(CLSID clsid) +{ + std::wstring str; + LPOLESTR tmp = nullptr; + switch(::StringFromCLSID(clsid, &tmp)) + { + case S_OK: + break; + case E_OUTOFMEMORY: + if(tmp) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + } + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + break; + default: + if(tmp) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + } + throw std::logic_error("StringFromCLSID() failed."); + break; + } + if(!tmp) + { + throw std::logic_error("StringFromCLSID() failed."); + } + try + { + str = tmp; + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + MPT_UNUSED_VARIABLE(e); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); + } + ::CoTaskMemFree(tmp); + tmp = nullptr; + return str; +} + + +CLSID StringToCLSID(const std::wstring &str) +{ + CLSID clsid = CLSID(); + std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); + switch(::CLSIDFromString(tmp.data(), &clsid)) + { + case NOERROR: + // nothing + break; + case E_INVALIDARG: + clsid = CLSID(); + break; + case CO_E_CLASSSTRING: + clsid = CLSID(); + break; + case REGDB_E_CLASSNOTREG: + clsid = CLSID(); + break; + case REGDB_E_READREGDB: + clsid = CLSID(); + throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); + break; + default: + clsid = CLSID(); + throw std::logic_error("CLSIDFromString() failed."); + break; + } + return clsid; +} + + +bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid) +{ + bool result = false; + clsid = CLSID(); + std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); + switch(::CLSIDFromString(tmp.data(), &clsid)) + { + case NOERROR: + result = true; + break; + case E_INVALIDARG: + result = false; + break; + case CO_E_CLASSSTRING: + result = false; + break; + case REGDB_E_CLASSNOTREG: + result = false; + break; + case REGDB_E_READREGDB: + throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); + break; + default: + throw std::logic_error("CLSIDFromString() failed."); + break; + } + return result; +} + + +bool IsCLSID(const std::wstring &str) +{ + bool result = false; + CLSID clsid = CLSID(); + std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); + switch(::CLSIDFromString(tmp.data(), &clsid)) + { + case NOERROR: + result = true; + break; + case E_INVALIDARG: + result = false; + break; + case CO_E_CLASSSTRING: + result = false; + break; + case REGDB_E_CLASSNOTREG: + result = false; + break; + case REGDB_E_READREGDB: + result = false; + throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB."); + break; + default: + result = false; + throw std::logic_error("CLSIDFromString() failed."); + break; + } + return result; +} + + +std::wstring IIDToString(IID iid) +{ + std::wstring str; + LPOLESTR tmp = nullptr; + switch(::StringFromIID(iid, &tmp)) + { + case S_OK: + break; + case E_OUTOFMEMORY: + if(tmp) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + } + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + break; + default: + if(tmp) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + } + throw std::logic_error("StringFromIID() failed."); + break; + } + if(!tmp) + { + throw std::logic_error("StringFromIID() failed."); + } + try + { + str = tmp; + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + ::CoTaskMemFree(tmp); + tmp = nullptr; + MPT_UNUSED_VARIABLE(e); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); + } + return str; +} + + +IID StringToIID(const std::wstring &str) +{ + IID iid = IID(); + std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); + switch(::IIDFromString(tmp.data(), &iid)) + { + case S_OK: + // nothing + break; + case E_OUTOFMEMORY: + iid = IID(); + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + break; + case E_INVALIDARG: + iid = IID(); + break; + default: + iid = IID(); + throw std::logic_error("IIDFromString() failed."); + break; + } + return iid; +} + + +std::wstring GUIDToString(GUID guid) +{ + std::vector tmp(256); + if(::StringFromGUID2(guid, tmp.data(), static_cast(tmp.size())) <= 0) + { + throw std::logic_error("StringFromGUID2() failed."); + } + return tmp.data(); +} + + +GUID StringToGUID(const std::wstring &str) +{ + return StringToIID(str); +} + + +GUID CreateGUID() +{ + GUID guid = GUID(); + switch(::CoCreateGuid(&guid)) + { + case S_OK: + // nothing + break; + default: + guid = GUID(); + throw std::runtime_error("CoCreateGuid() failed."); + } + return guid; +} + + +#if !MPT_OS_WINDOWS_WINRT + +UUID StringToUUID(const mpt::ustring &str) +{ + UUID uuid = UUID(); + std::wstring wstr = mpt::ToWide(str); + std::vector tmp(wstr.c_str(), wstr.c_str() + wstr.length() + 1); + switch(::UuidFromStringW((RPC_WSTR)(&(tmp[0])), &uuid)) + { + case RPC_S_OK: + // nothing + break; + case RPC_S_INVALID_STRING_UUID: + uuid = UUID(); + break; + default: + throw std::logic_error("UuidFromStringW() failed."); + break; + } + return uuid; +} + + +mpt::ustring UUIDToString(UUID uuid) +{ + std::wstring wstr; + RPC_WSTR tmp = nullptr; + switch(::UuidToStringW(&uuid, &tmp)) + { + case RPC_S_OK: + // nothing + break; + case RPC_S_OUT_OF_MEMORY: + if(tmp) + { + ::RpcStringFreeW(&tmp); + tmp = nullptr; + } + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + break; + default: + throw std::logic_error("UuidToStringW() failed."); + break; + } + try + { + std::size_t len = 0; + for(len = 0; tmp[len] != 0; ++len) + { + // nothing + } + wstr = std::wstring(tmp, tmp + len); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + ::RpcStringFreeW(&tmp); + tmp = nullptr; + MPT_UNUSED_VARIABLE(e); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); + } + return mpt::ToUnicode(wstr); +} + +#endif // !MPT_OS_WINDOWS_WINRT + + +bool IsValid(UUID uuid) +{ + return false + || uuid.Data1 != 0 + || uuid.Data2 != 0 + || uuid.Data3 != 0 + || uuid.Data4[0] != 0 + || uuid.Data4[1] != 0 + || uuid.Data4[2] != 0 + || uuid.Data4[3] != 0 + || uuid.Data4[4] != 0 + || uuid.Data4[5] != 0 + || uuid.Data4[6] != 0 + || uuid.Data4[7] != 0 + ; +} + + +#endif // MODPLUG_TRACKER || !NO_DMO + + +} // namespace Util + + +#endif // MPT_OS_WINDOWS + + +namespace mpt +{ + +#if MPT_OS_WINDOWS + +mpt::UUID UUIDFromWin32(::UUID uuid) +{ + return mpt::UUID + ( uuid.Data1 + , uuid.Data2 + , uuid.Data3 + , (static_cast(0) + | (static_cast(uuid.Data4[0]) << 56) + | (static_cast(uuid.Data4[1]) << 48) + | (static_cast(uuid.Data4[2]) << 40) + | (static_cast(uuid.Data4[3]) << 32) + | (static_cast(uuid.Data4[4]) << 24) + | (static_cast(uuid.Data4[5]) << 16) + | (static_cast(uuid.Data4[6]) << 8) + | (static_cast(uuid.Data4[7]) << 0) + ) + ); +} + +::UUID UUIDToWin32(mpt::UUID uuid) +{ + ::UUID result = ::UUID(); + result.Data1 = uuid.GetData1(); + result.Data2 = uuid.GetData2(); + result.Data3 = uuid.GetData3(); + result.Data4[0] = static_cast(uuid.GetData4() >> 56); + result.Data4[1] = static_cast(uuid.GetData4() >> 48); + result.Data4[2] = static_cast(uuid.GetData4() >> 40); + result.Data4[3] = static_cast(uuid.GetData4() >> 32); + result.Data4[4] = static_cast(uuid.GetData4() >> 24); + result.Data4[5] = static_cast(uuid.GetData4() >> 16); + result.Data4[6] = static_cast(uuid.GetData4() >> 8); + result.Data4[7] = static_cast(uuid.GetData4() >> 0); + return result; +} + +#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) + +UUID::UUID(::UUID uuid) +{ + *this = UUIDFromWin32(uuid); +} + +UUID::operator ::UUID () const +{ + return UUIDToWin32(*this); +} + +mpt::UUID UUID::FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5) +{ + MPT_ASSERT((group5 & 0xffff000000000000ull) == 0ull); + return mpt::UUID + ( group1 + , group2 + , group3 + , (static_cast(group4) << 48) | group5 + ); +} + +#endif // MODPLUG_TRACKER || !NO_DMO + +#endif // MPT_OS_WINDOWS + +UUID UUID::Generate() +{ + #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT + #if (_WIN32_WINNT >= 0x0602) + ::GUID guid = ::GUID(); + HRESULT result = CoCreateGuid(&guid); + if(result != S_OK) + { + return mpt::UUID::RFC4122Random(); + } + return mpt::UUIDFromWin32(guid); + #else + return mpt::UUID::RFC4122Random(); + #endif + #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT + ::UUID uuid = ::UUID(); + RPC_STATUS status = ::UuidCreate(&uuid); + if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) + { + return mpt::UUID::RFC4122Random(); + } + status = RPC_S_OK; + if(UuidIsNil(&uuid, &status) != FALSE) + { + return mpt::UUID::RFC4122Random(); + } + if(status != RPC_S_OK) + { + return mpt::UUID::RFC4122Random(); + } + return mpt::UUIDFromWin32(uuid); + #else + return RFC4122Random(); + #endif +} + +UUID UUID::GenerateLocalUseOnly() +{ + #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT + #if (_WIN32_WINNT >= 0x0602) + ::GUID guid = ::GUID(); + HRESULT result = CoCreateGuid(&guid); + if(result != S_OK) + { + return mpt::UUID::RFC4122Random(); + } + return mpt::UUIDFromWin32(guid); + #else + return mpt::UUID::RFC4122Random(); + #endif + #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT + #if _WIN32_WINNT >= 0x0501 + // Available since Win2000, but we check for WinXP in order to not use this + // function in Win32old builds. It is not available on some non-fully + // patched Win98SE installs in the wild. + ::UUID uuid = ::UUID(); + RPC_STATUS status = ::UuidCreateSequential(&uuid); + if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) + { + return Generate(); + } + status = RPC_S_OK; + if(UuidIsNil(&uuid, &status) != FALSE) + { + return mpt::UUID::RFC4122Random(); + } + if(status != RPC_S_OK) + { + return mpt::UUID::RFC4122Random(); + } + return mpt::UUIDFromWin32(uuid); + #else + // Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a + // tiny performance optimization. + return Generate(); + #endif + #else + return RFC4122Random(); + #endif +} + +UUID UUID::RFC4122Random() +{ + UUID result; + mpt::thread_safe_prng & prng = mpt::global_prng(); + result.Data1 = mpt::random(prng); + result.Data2 = mpt::random(prng); + result.Data3 = mpt::random(prng); + result.Data4 = mpt::random(prng); + result.MakeRFC4122(4); + return result; +} + +uint32 UUID::GetData1() const +{ + return Data1; +} + +uint16 UUID::GetData2() const +{ + return Data2; +} + +uint16 UUID::GetData3() const +{ + return Data3; +} + +uint64 UUID::GetData4() const +{ + return Data4; +} + +bool UUID::IsNil() const +{ + return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0); +} + +bool UUID::IsValid() const +{ + return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0); +} + +uint8 UUID::Mm() const +{ + return static_cast((Data3 >> 8) & 0xffu); +} + +uint8 UUID::Nn() const +{ + return static_cast((Data4 >> 56) & 0xffu); +} + +uint8 UUID::Variant() const +{ + return Nn() >> 4u; +} + +uint8 UUID::Version() const +{ + return Mm() >> 4u; +} + +bool UUID::IsRFC4122() const +{ + return (Variant() & 0xcu) == 0x8u; +} + +void UUID::MakeRFC4122(uint8 version) +{ + // variant + uint8 Nn = static_cast((Data4 >> 56) & 0xffu); + Data4 &= 0x00ffffffffffffffull; + Nn &= ~(0xc0u); + Nn |= 0x80u; + Data4 |= static_cast(Nn) << 56; + // version + version &= 0x0fu; + uint8 Mm = static_cast((Data3 >> 8) & 0xffu); + Data3 &= 0x00ffu; + Mm &= ~(0xf0u); + Mm |= (version << 4u); + Data3 |= static_cast(Mm) << 8; +} + +UUID::UUID() +{ + Data1 = 0; + Data2 = 0; + Data3 = 0; + Data4 = 0; +} + +UUID::UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) +{ + this->Data1 = Data1; + this->Data2 = Data2; + this->Data3 = Data3; + this->Data4 = Data4; +} + +bool operator==(const mpt::UUID & a, const mpt::UUID & b) +{ + return (a.Data1 == b.Data1) && (a.Data2 == b.Data2) && (a.Data3 == b.Data3) && (a.Data4 == b.Data4); +} + +bool operator!=(const mpt::UUID & a, const mpt::UUID & b) +{ + return (a.Data1 != b.Data1) || (a.Data2 != b.Data2) || (a.Data3 != b.Data3) || (a.Data4 != b.Data4); +} + +UUID UUID::FromString(const std::string &str) +{ + std::vector segments = mpt::String::Split(str, std::string("-")); + if(segments.size() != 5) + { + return UUID(); + } + if(segments[0].length() != 8) + { + return UUID(); + } + if(segments[1].length() != 4) + { + return UUID(); + } + if(segments[2].length() != 4) + { + return UUID(); + } + if(segments[3].length() != 4) + { + return UUID(); + } + if(segments[4].length() != 12) + { + return UUID(); + } + UUID result; + result.Data1 = mpt::String::Parse::Hex(segments[0]); + result.Data2 = mpt::String::Parse::Hex(segments[1]); + result.Data3 = mpt::String::Parse::Hex(segments[2]); + result.Data4 = mpt::String::Parse::Hex(segments[3] + segments[4]); + return result; +} + +UUID UUID::FromString(const mpt::ustring &str) +{ + std::vector segments = mpt::String::Split(str, MPT_USTRING("-")); + if(segments.size() != 5) + { + return UUID(); + } + if(segments[0].length() != 8) + { + return UUID(); + } + if(segments[1].length() != 4) + { + return UUID(); + } + if(segments[2].length() != 4) + { + return UUID(); + } + if(segments[3].length() != 4) + { + return UUID(); + } + if(segments[4].length() != 12) + { + return UUID(); + } + UUID result; + result.Data1 = mpt::String::Parse::Hex(segments[0]); + result.Data2 = mpt::String::Parse::Hex(segments[1]); + result.Data3 = mpt::String::Parse::Hex(segments[2]); + result.Data4 = mpt::String::Parse::Hex(segments[3] + segments[4]); + return result; +} + +std::string UUID::ToString() const +{ + return std::string() + + mpt::fmt::hex0<8>(GetData1()) + + std::string("-") + + mpt::fmt::hex0<4>(GetData2()) + + std::string("-") + + mpt::fmt::hex0<4>(GetData3()) + + std::string("-") + + mpt::fmt::hex0<4>(static_cast(GetData4() >> 48)) + + std::string("-") + + mpt::fmt::hex0<4>(static_cast(GetData4() >> 32)) + + mpt::fmt::hex0<8>(static_cast(GetData4() >> 0)) + ; +} + +mpt::ustring UUID::ToUString() const +{ + return mpt::ustring() + + mpt::ufmt::hex0<8>(GetData1()) + + MPT_USTRING("-") + + mpt::ufmt::hex0<4>(GetData2()) + + MPT_USTRING("-") + + mpt::ufmt::hex0<4>(GetData3()) + + MPT_USTRING("-") + + mpt::ufmt::hex0<4>(static_cast(GetData4() >> 48)) + + MPT_USTRING("-") + + mpt::ufmt::hex0<4>(static_cast(GetData4() >> 32)) + + mpt::ufmt::hex0<8>(static_cast(GetData4() >> 0)) + ; +} + +UUID::UUID(GUIDms guid) +{ + Data1 = guid.Data1.get(); + Data2 = guid.Data2.get(); + Data3 = guid.Data3.get(); + Data4 = guid.Data4.get(); +} + +UUID::operator GUIDms() const +{ + GUIDms result; + result.Data1 = GetData1(); + result.Data2 = GetData2(); + result.Data3 = GetData3(); + result.Data4 = GetData4(); + return result; +} + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h new file mode 100644 index 000000000..d0b3b3676 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h @@ -0,0 +1,146 @@ +/* + * mptUUID.h + * --------- + * Purpose: UUID utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + +#include "Endianness.h" + +#if MPT_OS_WINDOWS +#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#include +#include +#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_BEGIN + +#if MPT_OS_WINDOWS + +namespace Util +{ + +#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) + +// COM CLSID<->string conversion +// A CLSID string is not necessarily a standard UUID string, +// it might also be a symbolic name for the interface. +// (see CLSIDFromString ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx )) +std::wstring CLSIDToString(CLSID clsid); +CLSID StringToCLSID(const std::wstring &str); +bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid); +bool IsCLSID(const std::wstring &str); + +// COM IID<->string conversion +IID StringToIID(const std::wstring &str); +std::wstring IIDToString(IID iid); + +// General GUID<->string conversion. +// The string must/will be in standard GUID format: {4F9A455D-E7EF-4367-B2F0-0C83A38A5C72} +GUID StringToGUID(const std::wstring &str); +std::wstring GUIDToString(GUID guid); + +// Create a COM GUID +GUID CreateGUID(); + +#if !MPT_OS_WINDOWS_WINRT +// General UUID<->string conversion. +// The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72 +UUID StringToUUID(const mpt::ustring &str); +mpt::ustring UUIDToString(UUID uuid); +#endif // !MPT_OS_WINDOWS_WINRT + +// Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise. +bool IsValid(UUID uuid); + +#endif // MODPLUG_TRACKER || !NO_DMO + +} // namespace Util + +#endif // MPT_OS_WINDOWS + +// Microsoft on-disk layout +struct GUIDms +{ + uint32le Data1; + uint16le Data2; + uint16le Data3; + uint64be Data4; // yes, big endian here +}; +STATIC_ASSERT(sizeof(GUIDms) == 16); + +namespace mpt { + +struct UUID +{ +private: + uint32be Data1; + uint16be Data2; + uint16be Data3; + uint64be Data4; +public: + uint32 GetData1() const; + uint16 GetData2() const; + uint16 GetData3() const; + uint64 GetData4() const; +public: + // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx + // <--32-->-<16>-<16>-<-------64------> + bool IsNil() const; + bool IsValid() const; + uint8 Variant() const; + uint8 Version() const; + bool IsRFC4122() const; +private: + uint8 Mm() const; + uint8 Nn() const; + void MakeRFC4122(uint8 version); +public: +#if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || !defined(NO_DMO)) + explicit UUID(::UUID uuid); + operator ::UUID () const; + static UUID FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5); + #define MPT_UUID_HELPER( prefix , value , suffix ) ( prefix ## value ## suffix ) + #define MPT_UUID(group1, group2, group3, group4, group5) mpt::UUID::FromGroups(MPT_UUID_HELPER(0x,group1,u), MPT_UUID_HELPER(0x,group2,u), MPT_UUID_HELPER(0x,group3,u), MPT_UUID_HELPER(0x,group4,u), MPT_UUID_HELPER(0x,group5,ull)) +#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || !NO_DMO) +public: + UUID(); + explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4); + explicit UUID(GUIDms guid); + operator GUIDms () const; + friend bool operator==(const mpt::UUID & a, const mpt::UUID & b); + friend bool operator!=(const mpt::UUID & a, const mpt::UUID & b); +public: + // Create a UUID + static UUID Generate(); + // Create a UUID that contains local, traceable information. + // Safe for local use. May be faster. + static UUID GenerateLocalUseOnly(); + // Create a RFC4122 Random UUID. + static UUID RFC4122Random(); +public: + // General UUID<->string conversion. + // The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72 + static UUID FromString(const std::string &str); + static UUID FromString(const mpt::ustring &str); + std::string ToString() const; + mpt::ustring ToUString() const; +}; + +STATIC_ASSERT(sizeof(mpt::UUID) == 16); + +bool operator==(const mpt::UUID & a, const mpt::UUID & b); +bool operator!=(const mpt::UUID & a, const mpt::UUID & b); + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp new file mode 100644 index 000000000..747fd7af2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp @@ -0,0 +1,766 @@ +/* + * mptWine.cpp + * ----------- + * Purpose: Wine stuff. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptWine.h" + +#include "mptOS.h" +#include "mptFileIO.h" + +#include +#include + +#if MPT_OS_WINDOWS +#include +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + +namespace mpt +{ +namespace Wine +{ + + + +Context::Context(mpt::Wine::VersionContext versionContext) + : m_VersionContext(versionContext) + , wine_get_dos_file_name(nullptr) + , wine_get_unix_file_name(nullptr) +{ + if(!mpt::Windows::IsWine()) + { + throw mpt::Wine::Exception("Wine not detected."); + } + if(!m_VersionContext.Version().IsValid()) + { + throw mpt::Wine::Exception("Unknown Wine version detected."); + } + m_Kernel32 = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("kernel32.dll"))); + if(!m_Kernel32.IsValid()) + { + throw mpt::Wine::Exception("Could not load Wine kernel32.dll."); + } + if(!m_Kernel32.Bind(wine_get_unix_file_name, "wine_get_unix_file_name")) + { + throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_unix_file_name."); + } + if(!m_Kernel32.Bind(wine_get_dos_file_name, "wine_get_dos_file_name")) + { + throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_dos_file_name."); + } + { + std::string out; + std::string err; + try + { + if(ExecutePosixShellCommand("uname -m", out, err) != 0) + { + throw mpt::Wine::Exception("Wine 'uname -m' failed."); + } + if(!err.empty()) + { + throw mpt::Wine::Exception("Wine 'uname -m' failed."); + } + out = mpt::String::Trim(out, std::string("\r\n")); + m_Uname_m = out; + } catch(const std::exception &) + { + m_Uname_m = std::string(); + } + } + try + { + m_HOME = GetPosixEnvVar("HOME"); + } catch(const std::exception &) + { + m_HOME = std::string(); + } + try + { + m_XDG_DATA_HOME = GetPosixEnvVar("XDG_DATA_HOME"); + if(m_XDG_DATA_HOME.empty()) + { + m_XDG_DATA_HOME = m_HOME + "/.local/share"; + } + } catch(const std::exception &) + { + m_XDG_DATA_HOME = std::string(); + } + try + { + m_XDG_CACHE_HOME = GetPosixEnvVar("XDG_CACHE_HOME"); + if(m_XDG_CACHE_HOME.empty()) + { + m_XDG_CACHE_HOME = m_HOME + "/.cache"; + } + } catch(const std::exception &) + { + m_XDG_CACHE_HOME = std::string(); + } + try + { + m_XDG_CONFIG_HOME = GetPosixEnvVar("XDG_CONFIG_HOME"); + if(m_XDG_CONFIG_HOME.empty()) + { + m_XDG_CONFIG_HOME = m_HOME + "/.config"; + } + } catch(const std::exception &) + { + m_XDG_CONFIG_HOME = std::string(); + } +} + + +std::string Context::PathToPosix(mpt::PathString windowsPath) +{ + std::string result; + if(windowsPath.empty()) + { + return result; + } + if(windowsPath.Length() >= 32000) + { + throw mpt::Wine::Exception("Path too long."); + } + LPSTR tmp = nullptr; + tmp = wine_get_unix_file_name(windowsPath.AsNative().c_str()); + if(!tmp) + { + throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_unix_file_name failed."); + } + result = tmp; + HeapFree(GetProcessHeap(), 0, tmp); + tmp = nullptr; + return result; +} + +mpt::PathString Context::PathToWindows(std::string hostPath) +{ + mpt::PathString result; + if(hostPath.empty()) + { + return result; + } + if(hostPath.length() >= 32000) + { + throw mpt::Wine::Exception("Path too long."); + } + LPWSTR tmp = nullptr; + tmp = wine_get_dos_file_name(hostPath.c_str()); + if(!tmp) + { + throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_dos_file_name failed."); + } + result = mpt::PathString::FromNative(tmp); + HeapFree(GetProcessHeap(), 0, tmp); + tmp = nullptr; + return result; +} + +std::string Context::PathToPosixCanonical(mpt::PathString windowsPath) +{ + std::string result; + std::string hostPath = PathToPosix(windowsPath); + if(hostPath.empty()) + { + return result; + } + std::string output; + std::string error; + int exitcode = ExecutePosixShellCommand(std::string() + "readlink -f " + EscapePosixShell(hostPath), output, error); + if(!error.empty()) + { + throw mpt::Wine::Exception("Wine readlink failed: " + error); + } + if(exitcode != 0 && exitcode != 1) + { + throw mpt::Wine::Exception("Wine readlink failed."); + } + std::string trimmedOutput = mpt::String::Trim(output, std::string("\r\n")); + result = trimmedOutput; + return result; +} + + +static void ExecutePosixCommandProgressDefault(void * /*userdata*/ ) +{ + ::Sleep(10); + return; +} + +static ExecuteProgressResult ExecutePosixShellScriptProgressDefault(void * /*userdata*/ ) +{ + ::Sleep(10); + return ExecuteProgressContinueWaiting; +} + + +std::string Context::EscapePosixShell(std::string line) +{ + const char escape_chars [] = { '|', '&', ';', '<', '>', '(', ')', '$', '`', '"', '\'', ' ', '\t' }; + const char maybe_escape_chars [] = { '*', '?', '[', '#', '~', '=', '%' }; + line = mpt::String::Replace(line, "\\", "\\\\"); + for(std::size_t i = 0; i < mpt::size(escape_chars); ++i) + { + line = mpt::String::Replace(line, std::string(1, escape_chars[i]), "\\" + std::string(1, escape_chars[i])); + } + for(std::size_t i = 0; i < mpt::size(maybe_escape_chars); ++i) + { + line = mpt::String::Replace(line, std::string(1, maybe_escape_chars[i]), "\\" + std::string(1, maybe_escape_chars[i])); + } + return line; +} + + +ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet flags, std::map > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata) +{ + // Relevant documentation: + // https://stackoverflow.com/questions/6004070/execute-shell-commands-from-program-running-in-wine + // https://www.winehq.org/pipermail/wine-bugs/2014-January/374918.html + // https://bugs.winehq.org/show_bug.cgi?id=34730 + + if(!progress) progress = &ExecutePosixCommandProgressDefault; + if(!progressCancel) progressCancel = &ExecutePosixShellScriptProgressDefault; + + if(flags[ExecFlagInteractive]) flags.reset(ExecFlagSilent); + if(flags[ExecFlagSplitOutput]) flags.set(ExecFlagSilent); + + std::vector tempfiles; + + progress(userdata); + + mpt::TempDirGuard dirWindowsTemp(mpt::CreateTempFileName()); + if(dirWindowsTemp.GetDirname().empty()) + { + throw mpt::Wine::Exception("Creating temporary directoy failed."); + } + std::string dirPosix = PathToPosix(dirWindowsTemp.GetDirname()); + if(dirPosix.empty()) + { + throw mpt::Wine::Exception("mpt::Wine::ConvertWindowsPathToHost returned empty path."); + } + const mpt::PathString dirWindows = dirWindowsTemp.GetDirname(); + + progress(userdata); + + // write the script to disk + mpt::PathString scriptFilenameWindows = dirWindows + MPT_PATHSTRING("script.sh"); + { + mpt::ofstream tempfile(scriptFilenameWindows, std::ios::binary); + tempfile << script; + tempfile.flush(); + if(!tempfile) + { + throw mpt::Wine::Exception("Error writing script.sh."); + } + } + std::string scriptFilenamePosix = PathToPosix(scriptFilenameWindows); + if(scriptFilenamePosix.empty()) + { + throw mpt::Wine::Exception("Error converting script.sh path."); + } + + progress(userdata); + + // create a wrapper that will call the script and gather result. + mpt::PathString wrapperstarterFilenameWindows = dirWindows + MPT_PATHSTRING("wrapperstarter.sh"); + { + mpt::ofstream tempfile(wrapperstarterFilenameWindows, std::ios::binary); + std::string wrapperstarterscript; + wrapperstarterscript += std::string() + "#!/usr/bin/env sh" + "\n"; + wrapperstarterscript += std::string() + "exec /usr/bin/env sh " + EscapePosixShell(dirPosix) + "wrapper.sh" + "\n"; + tempfile << wrapperstarterscript; + tempfile.flush(); + if(!tempfile) + { + throw mpt::Wine::Exception("Error writing wrapper.sh."); + } + } + mpt::PathString wrapperFilenameWindows = dirWindows + MPT_PATHSTRING("wrapper.sh"); + std::string cleanupscript; + { + mpt::ofstream tempfile(wrapperFilenameWindows, std::ios::binary); + std::string wrapperscript; + if(!flags[ExecFlagSilent]) + { + wrapperscript += std::string() + "printf \"\\033]0;" + title + "\\a\"" + "\n"; + } + wrapperscript += std::string() + "chmod u+x " + EscapePosixShell(scriptFilenamePosix) + "\n"; + wrapperscript += std::string() + "cd " + EscapePosixShell(dirPosix) + "filetree" + "\n"; + if(flags[ExecFlagInteractive]) + { // no stdout/stderr capturing for interactive scripts + wrapperscript += std::string() + EscapePosixShell(scriptFilenamePosix) + "\n"; + wrapperscript += std::string() + "MPT_RESULT=$?" + "\n"; + wrapperscript += std::string() + "echo ${MPT_RESULT} > " + EscapePosixShell(dirPosix) + "exit" + "\n"; + } else if(flags[ExecFlagSplitOutput]) + { + wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 4>" + EscapePosixShell(dirPosix) + "exit 1>" + EscapePosixShell(dirPosix) + "out 2>" + EscapePosixShell(dirPosix) + "err" + "\n"; + } else + { + wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 2>&1 4>" + EscapePosixShell(dirPosix) + "exit | tee " + EscapePosixShell(dirPosix) + "out" + "\n"; + } + wrapperscript += std::string() + "echo done > " + EscapePosixShell(dirPosix) + "done" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "done" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "exit" + "\n"; + if(flags[ExecFlagInteractive]) + { + // nothing + } else if(flags[ExecFlagSplitOutput]) + { + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "err" + "\n"; + } else + { + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n"; + } + cleanupscript += std::string() + "rm -r " + EscapePosixShell(dirPosix) + "filetree" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "script.sh" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapper.sh" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n"; + cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "terminal.sh" + "\n"; + if(flags[ExecFlagAsync]) + { + wrapperscript += cleanupscript; + cleanupscript.clear(); + } + tempfile << wrapperscript; + tempfile.flush(); + if(!tempfile) + { + throw mpt::Wine::Exception("Error writing wrapper.sh."); + } + } + + progress(userdata); + + ::CreateDirectoryW((dirWindows + MPT_PATHSTRING("filetree")).AsNative().c_str(), NULL); + for(const auto &file : filetree) + { + std::vector path = mpt::String::Split(mpt::ToUnicode(mpt::CharsetUTF8, file.first), MPT_USTRING("/")); + mpt::PathString combinedPath = dirWindows + MPT_PATHSTRING("filetree") + MPT_PATHSTRING("\\"); + if(path.size() > 1) + { + for(std::size_t singlepath = 0; singlepath < path.size() - 1; ++singlepath) + { + if(path[singlepath].empty()) + { + continue; + } + combinedPath += mpt::PathString::FromUnicode(path[singlepath]); + if(!combinedPath.IsDirectory()) + { + if(::CreateDirectoryW(combinedPath.AsNative().c_str(), NULL) == 0) + { + throw mpt::Wine::Exception("Error writing filetree."); + } + } + combinedPath += MPT_PATHSTRING("\\"); + } + } + try + { + mpt::LazyFileRef out(dirWindows + MPT_PATHSTRING("filetree") + MPT_PATHSTRING("\\") + mpt::PathString::FromUTF8(mpt::String::Replace(file.first, "/", "\\"))); + out = file.second; + } catch(std::exception &) + { + throw mpt::Wine::Exception("Error writing filetree."); + } + } + + progress(userdata); + + // create a wrapper that will find a suitable terminal and run the wrapper script in the terminal window. + mpt::PathString terminalWrapperFilenameWindows = dirWindows + MPT_PATHSTRING("terminal.sh"); + { + mpt::ofstream tempfile(terminalWrapperFilenameWindows, std::ios::binary); + // NOTE: + // Modern terminals detach themselves from the invoking shell if another instance is already present. + // This means we cannot rely on terminal invocation being syncronous. + std::vector terminals; + terminals.push_back("x-terminal-emulator"); + terminals.push_back("konsole"); + terminals.push_back("mate-terminal"); + terminals.push_back("xfce4-terminal"); + terminals.push_back("gnome-terminal"); + terminals.push_back("uxterm"); + terminals.push_back("xterm"); + terminals.push_back("rxvt"); + std::map terminalLanchers; + for(std::size_t i = 0; i < terminals.size(); ++i) + { + // mate-terminal on Debian 8 cannot execute commands with arguments, + // thus we use a separate script that requires no arguments to execute. + terminalLanchers[terminals[i]] += std::string() + "if command -v " + terminals[i] + " 2>/dev/null 1>/dev/null ; then" + "\n"; + terminalLanchers[terminals[i]] += std::string() + " chmod u+x " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n"; + terminalLanchers[terminals[i]] += std::string() + " exec `command -v " + terminals[i] + "` -e \"" + EscapePosixShell(dirPosix) + "wrapperstarter.sh\"" + "\n"; + terminalLanchers[terminals[i]] += std::string() + "fi" + "\n"; + } + + std::string terminalscript; + + terminalscript += std::string() + "\n"; + + for(std::size_t i = 0; i < terminals.size(); ++i) + { + terminalscript += terminalLanchers[terminals[i]]; + } + + tempfile << terminalscript; + tempfile.flush(); + if(!tempfile) + { + return ExecResult::Error(); + } + } + + progress(userdata); + + // build unix command line + std::string unixcommand; + bool createProcessSuccess = false; + + if(!createProcessSuccess) + { + + if(flags[ExecFlagSilent]) + { + unixcommand = "/usr/bin/env sh \"" + EscapePosixShell(dirPosix) + "wrapper.sh\""; + } else + { + unixcommand = "/usr/bin/env sh \"" + EscapePosixShell(dirPosix) + "terminal.sh\""; + } + + progress(userdata); + + std::wstring unixcommandW = mpt::ToWide(mpt::CharsetUTF8, unixcommand); + std::vector commandline = std::vector(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1); + std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title); + std::vector titleWv = std::vector(titleW.data(), titleW.data() + titleW.length() + 1); + STARTUPINFOW startupInfo; + MemsetZero(startupInfo); + startupInfo.lpTitle = &titleWv[0]; + startupInfo.cb = sizeof(startupInfo); + PROCESS_INFORMATION processInformation; + MemsetZero(processInformation); + + progress(userdata); + + BOOL success = FALSE; + if(flags[ExecFlagSilent]) + { + success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation); + } else + { + success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation); + } + + progress(userdata); + + createProcessSuccess = (success ? true : false); + + progress(userdata); + + if(success) + { + + if(!flags[ExecFlagAsync]) + { + // note: execution is not syncronous with all Wine versions, + // we additionally explicitly wait for "done" later + while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT) + { // wait + if(progressCancel(userdata) != ExecuteProgressContinueWaiting) + { + CloseHandle(processInformation.hThread); + CloseHandle(processInformation.hProcess); + throw mpt::Wine::Exception("Canceled."); + } + } + } + + progress(userdata); + + CloseHandle(processInformation.hThread); + CloseHandle(processInformation.hProcess); + + } + + } + + progress(userdata); + + // Work around Wine being able to execute PIE binaries on Debian 9. + // Luckily, /bin/bash is still non-PIE on Debian 9. + + if(!createProcessSuccess) + { + + if(flags[ExecFlagSilent]) { + unixcommand = "/bin/bash \"" + EscapePosixShell(dirPosix) + "wrapper.sh\""; + } else + { + unixcommand = "/bin/bash \"" + EscapePosixShell(dirPosix) + "terminal.sh\""; + } + + progress(userdata); + + std::wstring unixcommandW = mpt::ToWide(mpt::CharsetUTF8, unixcommand); + std::vector commandline = std::vector(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1); + std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title); + std::vector titleWv = std::vector(titleW.data(), titleW.data() + titleW.length() + 1); + STARTUPINFOW startupInfo; + MemsetZero(startupInfo); + startupInfo.lpTitle = &titleWv[0]; + startupInfo.cb = sizeof(startupInfo); + PROCESS_INFORMATION processInformation; + MemsetZero(processInformation); + + progress(userdata); + + BOOL success = FALSE; + if(flags[ExecFlagSilent]) + { + success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation); + } else + { + success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation); + } + + progress(userdata); + + createProcessSuccess = (success ? true : false); + + progress(userdata); + + if(success) + { + + if(!flags[ExecFlagAsync]) + { + // note: execution is not syncronous with all Wine versions, + // we additionally explicitly wait for "done" later + while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT) + { // wait + if(progressCancel(userdata) != ExecuteProgressContinueWaiting) + { + CloseHandle(processInformation.hThread); + CloseHandle(processInformation.hProcess); + throw mpt::Wine::Exception("Canceled."); + } + } + } + + progress(userdata); + + CloseHandle(processInformation.hThread); + CloseHandle(processInformation.hProcess); + + } + + } + + progress(userdata); + + if(!createProcessSuccess) + { + throw mpt::Wine::Exception("CreateProcess failed."); + } + + progress(userdata); + + if(flags[ExecFlagAsync]) + { + ExecResult result; + result.exitcode = 0; + return result; + } + + while(!(dirWindows + MPT_PATHSTRING("done")).IsFile()) + { // wait + if(progressCancel(userdata) != ExecuteProgressContinueWaiting) + { + throw mpt::Wine::Exception("Canceled."); + } + } + + progress(userdata); + + int exitCode = 0; + { + mpt::ifstream exitFile(dirWindows + MPT_PATHSTRING("exit"), std::ios::binary); + if(!exitFile) + { + throw mpt::Wine::Exception("Script .exit file not found."); + } + std::string exitString; + exitFile >> exitString; + if(exitString.empty()) + { + throw mpt::Wine::Exception("Script .exit file empty."); + } + exitCode = ConvertStrTo(exitString); + } + + progress(userdata); + + std::string outputString; + if(!flags[ExecFlagInteractive]) + { + mpt::ifstream outputFile(dirWindows + MPT_PATHSTRING("out"), std::ios::binary); + if(outputFile) + { + outputFile.seekg(0, std::ios::end); + std::streampos outputFileSize = outputFile.tellg(); + outputFile.seekg(0, std::ios::beg); + std::vector outputFileBuf(mpt::saturate_cast(static_cast(outputFileSize))); + outputFile.read(&outputFileBuf[0], outputFileBuf.size()); + outputString = std::string(outputFileBuf.begin(), outputFileBuf.end()); + } + } + + progress(userdata); + + std::string errorString; + if(flags[ExecFlagSplitOutput]) + { + mpt::ifstream errorFile(dirWindows + MPT_PATHSTRING("err"), std::ios::binary); + if(errorFile) + { + errorFile.seekg(0, std::ios::end); + std::streampos errorFileSize = errorFile.tellg(); + errorFile.seekg(0, std::ios::beg); + std::vector errorFileBuf(mpt::saturate_cast(static_cast(errorFileSize))); + errorFile.read(&errorFileBuf[0], errorFileBuf.size()); + errorString = std::string(errorFileBuf.begin(), errorFileBuf.end()); + } + } + + progress(userdata); + + ExecResult result; + result.exitcode = exitCode; + result.output = outputString; + result.error = errorString; + + std::deque paths; + paths.push_back(dirWindows + MPT_PATHSTRING("filetree")); + mpt::PathString basePath = (dirWindows + MPT_PATHSTRING("filetree")).EnsureTrailingSlash(); + while(!paths.empty()) + { + mpt::PathString path = paths.front(); + paths.pop_front(); + path.EnsureTrailingSlash(); + HANDLE hFind = NULL; + WIN32_FIND_DATAW wfd; + MemsetZero(wfd); + hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd); + if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) + { + do + { + mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); + if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING("..")) + { + filename = path + filename; + filetree[filename.ToUTF8()] = std::vector(); + if(filename.IsDirectory()) + { + paths.push_back(filename); + } else if(filename.IsFile()) + { + try + { + mpt::LazyFileRef f(filename); + std::vector buf = f; + mpt::PathString treeFilename = mpt::PathString::FromNative(filename.AsNative().substr(basePath.AsNative().length())); + result.filetree[treeFilename.ToUTF8()] = buf; + } catch (std::exception &) + { + // nothing?! + } + } + } + } while(FindNextFileW(hFind, &wfd)); + FindClose(hFind); + } + } + + mpt::DeleteWholeDirectoryTree(dirWindows); + + return result; + +} + + +int Context::ExecutePosixShellCommand(std::string command, std::string & output, std::string & error) +{ + std::string script; + script += std::string() + "#!/usr/bin/env sh" + "\n"; + script += std::string() + "exec " + command + "\n"; + mpt::Wine::ExecResult execResult = ExecutePosixShellScript + ( script + , mpt::Wine::ExecFlagSilent | mpt::Wine::ExecFlagSplitOutput, std::map >() + , std::string() + , nullptr + , nullptr + , nullptr + ); + output = execResult.output; + error = execResult.error; + return execResult.exitcode; +} + + +std::string Context::GetPosixEnvVar(std::string var, std::string def) +{ + // We cannot use std::getenv here because Wine overrides SOME env vars, + // in particular, HOME is unset in the Wine environment. + // Instead, we just spawn a shell that will catch up a sane environment on + // its own. + std::string output; + std::string error; + int exitcode = ExecutePosixShellCommand(std::string() + "echo $" + var, output, error); + if(!error.empty()) + { + throw mpt::Wine::Exception("Wine echo $var failed: " + error); + } + if(exitcode != 0) + { + throw mpt::Wine::Exception("Wine echo $var failed."); + } + std::string result = mpt::String::RTrim(output, std::string("\r\n")); + if(result.empty()) + { + result = def; + } + return result; +} + + +} // namespace Wine +} // namespace mpt + + +#else // !(MODPLUG_TRACKER && MPT_OS_WINDOWS) + + +MPT_MSVC_WORKAROUND_LNK4221(mptWine) + + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.h b/Frameworks/OpenMPT/OpenMPT/common/mptWine.h new file mode 100644 index 000000000..6bec241d8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptWine.h @@ -0,0 +1,127 @@ +/* + * mptWine.h + * --------- + * Purpose: Wine stuff. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "mptOS.h" +#include "FlagSet.h" + +#include +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + +namespace mpt +{ + +namespace Wine +{ + + +class Exception + : public std::runtime_error +{ +public: + Exception(const std::string &text) + : std::runtime_error(text) + { + return; + } +}; + + +typedef void (*ExecutePosixCommandProgress)(void *userdata); + +enum ExecuteProgressResult +{ + ExecuteProgressContinueWaiting = 0, + ExecuteProgressAsyncCancel = -1, +}; + +typedef ExecuteProgressResult (*ExecutePosixShellScriptProgress)(void *userdata); + + +enum ExecFlags +{ + ExecFlagNone = 0, + ExecFlagSilent = 1<<0, // do not show a terminal window + ExecFlagInteractive = 1<<1, // allow interaction (prevents stdout and stderr capturing and implies !silent) + ExecFlagAsync = 1<<2, // do not wait for the script to finish + ExecFlagProgressWindow = 1<<3, // not implemented by mptOS + ExecFlagSplitOutput = 1<<4, // split stdout and stderr (implies silent) + ExecFlagsDefault = ExecFlagNone +}; +MPT_DECLARE_ENUM(ExecFlags) + +struct ExecResult +{ + int exitcode; + std::string output; + std::string error; + std::map > filetree; + static ExecResult Error() + { + ExecResult result; + result.exitcode = -1; + return result; + } +}; + + +class Context +{ +protected: + mpt::Wine::VersionContext m_VersionContext; + mpt::Library m_Kernel32; +private: + LPWSTR (*CDECL wine_get_dos_file_name)(LPCSTR str); + LPSTR (*CDECL wine_get_unix_file_name)(LPCWSTR str); +protected: + std::string m_Uname_m; + std::string m_HOME; + std::string m_XDG_DATA_HOME; + std::string m_XDG_CACHE_HOME; + std::string m_XDG_CONFIG_HOME; +public: + Context(mpt::Wine::VersionContext versionContext); +public: + std::string EscapePosixShell(std::string line); + std::string PathToPosix(mpt::PathString windowsPath); + mpt::PathString PathToWindows(std::string hostPath); + ExecResult ExecutePosixShellScript(std::string script, FlagSet flags, std::map > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata); + int ExecutePosixShellCommand(std::string command, std::string & output, std::string & error); + std::string PathToPosixCanonical(mpt::PathString windowsPath); + std::string GetPosixEnvVar(std::string var, std::string def = std::string()); +public: + mpt::Wine::VersionContext VersionContext() const { return m_VersionContext; } + mpt::Library Kernel32() const { return m_Kernel32; } + std::string Uname_m() const { return m_Uname_m; } + std::string HOME() const { return m_HOME; } + std::string XDG_DATA_HOME() const { return m_XDG_DATA_HOME; } + std::string XDG_CACHE_HOME() const { return m_XDG_CACHE_HOME; } + std::string XDG_CONFIG_HOME() const { return m_XDG_CONFIG_HOME; } +}; + + +} // namespace Wine + +} // namespace mpt + + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp new file mode 100644 index 000000000..91c7d75e4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp @@ -0,0 +1,735 @@ +/* + * serialization_utils.cpp + * ----------------------- + * Purpose: Serializing data to and from MPTM files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "serialization_utils.h" + +#include +#include + +#include "misc_util.h" +#include "mptBufferIO.h" + + +OPENMPT_NAMESPACE_BEGIN + + +namespace srlztn +{ + + +//#define SSB_LOGGING + + +#ifdef SSB_LOGGING +#define SSB_LOG(x) Log(x) +#else +#define SSB_LOG(x) MPT_DO { } MPT_WHILE_0 +#endif + + +static const uint8 HeaderId_FlagByte = 0; + +// Indexing starts from 0. +static inline bool Testbit(uint8 val, uint8 bitindex) {return ((val & (1 << bitindex)) != 0);} + +static inline void Setbit(uint8& val, uint8 bitindex, bool newval) +{ + if(newval) val |= (1 << bitindex); + else val &= ~(1 << bitindex); +} + + +bool ID::IsPrintable() const +{ + for(std::size_t i = 0; i < m_ID.length(); ++i) + { + if(m_ID[i] <= 0 || isprint(static_cast(m_ID[i])) == 0) + { + return false; + } + } + return true; +} + + +//Format: First bit tells whether the size indicator is 1 or 2 bytes. +static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str) +{ + uint16 s = static_cast(str.size()); + LimitMax(s, uint16(uint16_max / 2)); + mpt::IO::WriteAdaptiveInt16LE(oStrm, s); + oStrm.write(str.c_str(), s); +} + + +void WriteItemString(std::ostream& oStrm, const std::string &str) +{ + uint32 id = static_cast(std::min(str.size(), (uint32_max >> 4))) << 4; + id |= 12; // 12 == 1100b + Binarywrite(oStrm, id); + id >>= 4; + if(id > 0) + oStrm.write(str.data(), id); +} + + +void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) +{ + // bits 0,1: Bytes per char type: 1,2,3,4. + // bits 2,3: Bytes in size indicator, 1,2,3,4 + uint32 id = 0; + Binaryread(iStrm, id, 1); + const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b + if (nSizeBytes > 0) + { + uint8 bytes = std::min(3, nSizeBytes); + uint8 v2 = 0; + uint8 v3 = 0; + uint8 v4 = 0; + if(bytes >= 1) Binaryread(iStrm, v2); + if(bytes >= 2) Binaryread(iStrm, v3); + if(bytes >= 3) Binaryread(iStrm, v4); + id &= 0xff; + id |= (v2 << 8) | (v3 << 16) | (v4 << 24); + } + // Limit to 1 MB. + str.resize(std::min(id >> 4, 1000000)); + for(size_t i = 0; i < str.size(); i++) + iStrm.read(&str[i], 1); + + id = (id >> 4) - static_cast(str.size()); + if(id > 0) + iStrm.ignore(id); +} + + +mpt::ustring ID::AsString() const +{ + if(IsPrintable()) + { + return mpt::ToUnicode(mpt::CharsetISO8859_1, m_ID); + } + if(m_ID.length() > 8) + { + return mpt::ustring(); + } + uint64le val; + val.set(0); + std::memcpy(&val, m_ID.data(), m_ID.length()); + return mpt::ufmt::val(val); +} + + +const char Ssb::s_EntryID[3] = {'2','2','8'}; + + +#ifdef SSB_LOGGING +static const MPT_UCHAR_TYPE tstrWriteHeader[] = MPT_ULITERAL("Write header with ID = %1\n"); +static const MPT_UCHAR_TYPE tstrWriteProgress[] = MPT_ULITERAL("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); +static const MPT_UCHAR_TYPE tstrWritingMap[] = MPT_ULITERAL("Writing map to rpos: %1\n"); +static const MPT_UCHAR_TYPE tstrMapEntryWrite[] = MPT_ULITERAL("Writing map entry: id=%1, rpos=%2, size=%3\n"); +static const MPT_UCHAR_TYPE strWriteNote[] = MPT_ULITERAL("Write note: "); +static const MPT_UCHAR_TYPE tstrEndOfStream[] = MPT_ULITERAL("End of stream(rpos): %1\n"); + +static const MPT_UCHAR_TYPE tstrReadingHeader[] = MPT_ULITERAL("Read header with expected ID = %1\n"); +static const MPT_UCHAR_TYPE strNoMapInFile[] = MPT_ULITERAL("No map in the file.\n"); +static const MPT_UCHAR_TYPE strIdMismatch[] = MPT_ULITERAL("ID mismatch, terminating read.\n"); +static const MPT_UCHAR_TYPE strIdMatch[] = MPT_ULITERAL("ID match, continuing reading.\n"); +static const MPT_UCHAR_TYPE tstrReadingMap[] = MPT_ULITERAL("Reading map from rpos: %1\n"); +static const MPT_UCHAR_TYPE tstrEndOfMap[] = MPT_ULITERAL("End of map(rpos): %1\n"); +static const MPT_UCHAR_TYPE tstrReadProgress[] = MPT_ULITERAL("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); +static const MPT_UCHAR_TYPE tstrNoEntryFound[] = MPT_ULITERAL("No entry with id %1 found.\n"); +static const MPT_UCHAR_TYPE strReadNote[] = MPT_ULITERAL("Read note: "); +#endif + + +Ssb::Ssb() + : m_Status(SNT_NONE) + , m_nFixedEntrySize(0) + , m_posStart(0) + , m_nIdbytes(IdSizeVariable) + , m_nCounter(0) + , m_Flags((1 << RwfWMapStartPosEntry) + (1 << RwfWMapSizeEntry) + (1 << RwfWVersionNum)) +{ + return; +} + + +SsbWrite::SsbWrite(std::ostream& oStrm) + : m_pOstrm(&oStrm) + , m_posEntrycount(0) + , m_posMapPosField(0) +{ + return; +} + + +SsbRead::SsbRead(std::istream& iStrm) + : m_pIstrm(&iStrm) + , m_nReadVersion(0) + , m_rposMapBegin(0) + , m_posMapEnd(0) + , m_posDataBegin(0) + , m_rposEndofHdrData(0) + , m_nReadEntrycount(0) + , m_nNextReadHint(0) +{ + return; +} + + +void SsbWrite::AddWriteNote(const SsbStatus s) +{ + m_Status |= s; + SSB_LOG(mpt::format(MPT_USTRING("%1: 0x%2\n"))(strWriteNote, mpt::ufmt::hex(s))); +} + +void SsbRead::AddReadNote(const SsbStatus s) +{ + m_Status |= s; + SSB_LOG(mpt::format(MPT_USTRING("%1: 0x%2\n"))(strReadNote, mpt::ufmt::hex(s))); +} + +void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum) +{ + m_Status |= SNT_PROGRESS; + SSB_LOG(mpt::format(mpt::ustring(tstrReadProgress))( + nNum, + (pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : MPT_USTRING(""), + (pRe) ? pRe->rposStart : 0, + (pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : MPT_USTRING(""), + MPT_USTRING(""))); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(pRe); + MPT_UNREFERENCED_PARAMETER(nNum); +#endif +} + +// Called after writing an entry. +void SsbWrite::AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart) +{ + m_Status |= SNT_PROGRESS; + SSB_LOG(mpt::format(mpt::ustring(tstrWriteProgress))(nEntryNum, id.AsString(), rposStart, nBytecount)); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(id); + MPT_UNREFERENCED_PARAMETER(nEntryNum); + MPT_UNREFERENCED_PARAMETER(nBytecount); + MPT_UNREFERENCED_PARAMETER(rposStart); +#endif +} + + +void SsbRead::ResetReadstatus() +{ + m_Status = SNT_NONE; + m_Idarray.reserve(32); + m_Idarray.push_back(0); +} + + +void SsbWrite::WriteMapItem(const ID &id, + const RposType& rposDataStart, + const DataSize& nDatasize, + const char* pszDesc) +{ + SSB_LOG(mpt::format(mpt::ustring(tstrMapEntryWrite))( + (id.GetSize() > 0) ? id.AsString() : MPT_USTRING(""), + rposDataStart, + nDatasize)); + + mpt::ostringstream mapStream; + + if(m_nIdbytes > 0) + { + if (m_nIdbytes != IdSizeVariable && id.GetSize() != m_nIdbytes) + { AddWriteNote(SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING); return; } + + if (m_nIdbytes == IdSizeVariable) //Variablesize ID? + mpt::IO::WriteAdaptiveInt16LE(mapStream, static_cast(id.GetSize())); + + if(id.GetSize() > 0) + mapStream.write(id.GetBytes(), id.GetSize()); + } + + if (GetFlag(RwfWMapStartPosEntry)) //Startpos + mpt::IO::WriteAdaptiveInt64LE(mapStream, rposDataStart); + if (GetFlag(RwfWMapSizeEntry)) //Entrysize + mpt::IO::WriteAdaptiveInt64LE(mapStream, nDatasize); + if (GetFlag(RwfWMapDescEntry)) //Entry descriptions + WriteAdaptive12String(mapStream, std::string(pszDesc)); + + m_MapStreamString.append(mapStream.str()); + +} + + +void SsbWrite::IncrementWriteCounter() +{ + m_nCounter++; + if (m_nCounter >= (uint16_max >> 2)) + { + FinishWrite(); + AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED); + } +} + + +void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion) +{ + std::ostream& oStrm = *m_pOstrm; + + SSB_LOG(mpt::format(mpt::ustring(tstrWriteHeader))(id.AsString())); + + ResetWritestatus(); + + if(!oStrm.good()) + { AddWriteNote(SNRW_BADGIVEN_STREAM); return; } + + // Start bytes. + oStrm.write(s_EntryID, sizeof(s_EntryID)); + + m_posStart = oStrm.tellp() - Offtype(sizeof(s_EntryID)); + + // Object ID. + { + uint8 idsize = static_cast(id.GetSize()); + Binarywrite(oStrm, idsize); + if(idsize > 0) oStrm.write(id.GetBytes(), id.GetSize()); + } + + // Form header. + uint8 header = 0; + + SetFlag(RwfWMapStartPosEntry, GetFlag(RwfWMapStartPosEntry) && m_nFixedEntrySize == 0); + SetFlag(RwfWMapSizeEntry, GetFlag(RwfWMapSizeEntry) && m_nFixedEntrySize == 0); + + header = (m_nIdbytes != 4) ? (m_nIdbytes & 3) : 3; //0,1 : Bytes per IDtype, 0,1,2,4 + Setbit(header, 2, GetFlag(RwfWMapStartPosEntry)); //2 : Startpos in map? + Setbit(header, 3, GetFlag(RwfWMapSizeEntry)); //3 : Datasize in map? + Setbit(header, 4, GetFlag(RwfWVersionNum)); //4 : Version numeric field? + Setbit(header, 7, GetFlag(RwfWMapDescEntry)); //7 : Entrydescriptions in map? + + // Write header + Binarywrite(oStrm, header); + + // Additional options. + uint8 tempU8 = 0; + Setbit(tempU8, 0, (m_nIdbytes == IdSizeVariable) || (m_nIdbytes == 3) || (m_nIdbytes > 4)); + Setbit(tempU8, 1, m_nFixedEntrySize != 0); + + const uint8 flags = tempU8; + if(flags != s_DefaultFlagbyte) + { + mpt::IO::WriteAdaptiveInt32LE(oStrm, 2); //Headersize - now it is 2. + Binarywrite(oStrm, HeaderId_FlagByte); + Binarywrite(oStrm, flags); + } + else + mpt::IO::WriteAdaptiveInt32LE(oStrm, 0); + + if(Testbit(header, 4)) // Version(numeric)? + mpt::IO::WriteAdaptiveInt64LE(oStrm, nVersion); + + if(Testbit(flags, 0)) // Custom IDbytecount? + { + uint8 n = (m_nIdbytes == IdSizeVariable) ? 1 : static_cast((m_nIdbytes << 1)); + Binarywrite(oStrm, n); + } + + if(Testbit(flags, 1)) // Fixedsize entries? + mpt::IO::WriteAdaptiveInt32LE(oStrm, m_nFixedEntrySize); + + //Entrycount. Reserve two bytes(max uint16_max / 4 entries), actual value is written after writing data. + m_posEntrycount = oStrm.tellp(); + Binarywrite(oStrm, 0); + + SetFlag(RwfRwHasMap, (m_nIdbytes != 0 || GetFlag(RwfWMapStartPosEntry) || GetFlag(RwfWMapSizeEntry) || GetFlag(RwfWMapDescEntry))); + + m_posMapPosField = oStrm.tellp(); + if (GetFlag(RwfRwHasMap)) //Mapping begin pos(reserve space - actual value is written after writing data) + Binarywrite(oStrm, 0); +} + + +SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin) +{ + if (pE != nullptr) + AddReadNote(pE, m_nCounter); + else if (GetFlag(RwfRMapHasId) == false) // Not ID's in map. + { + ReadEntry e; + e.rposStart = static_cast(posReadBegin - m_posStart); + e.nSize = static_cast(m_pIstrm->tellg() - posReadBegin); + AddReadNote(&e, m_nCounter); + } + else // Entry not found. + { + SSB_LOG(mpt::format(mpt::ustring(tstrNoEntryFound))(id.AsString())); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(id); +#endif + return EntryNotFound; + } + m_nCounter++; + return EntryRead; +} + + +void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) +{ + const Offtype nRawEntrySize = m_pOstrm->tellp() - posBeforeWrite; + + if (nRawEntrySize < 0 || static_cast(nRawEntrySize) > std::numeric_limits::max()) + { AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; } + + if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast(nRawEntrySize) > (std::numeric_limits::max() >> 2))) + { AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; } + + DataSize nEntrySize = static_cast(nRawEntrySize); + + // Handle fixed size entries: + if (m_nFixedEntrySize > 0) + { + if(nEntrySize <= m_nFixedEntrySize) + { + for(uint32 i = 0; iput(0); + nEntrySize = m_nFixedEntrySize; + } + else + { AddWriteNote(SNW_INSUFFICIENT_FIXEDSIZE); return; } + } + if (GetFlag(RwfRwHasMap)) + WriteMapItem(id, static_cast(posBeforeWrite - m_posStart), nEntrySize, ""); + + AddWriteNote(id, m_nCounter, nEntrySize, static_cast(posBeforeWrite - m_posStart)); + IncrementWriteCounter(); +} + + +void SsbRead::BeginRead(const ID &id, const uint64& nVersion) +{ + std::istream& iStrm = *m_pIstrm; + + SSB_LOG(mpt::format(mpt::ustring(tstrReadingHeader))(id.AsString())); + + ResetReadstatus(); + + if (!iStrm.good()) + { AddReadNote(SNRW_BADGIVEN_STREAM); return; } + + m_posStart = iStrm.tellg(); + + // Start bytes. + { + char temp[sizeof(s_EntryID)]; + ArrayReader(sizeof(s_EntryID))(iStrm, temp, sizeof(s_EntryID)); + if(std::memcmp(temp, s_EntryID, sizeof(s_EntryID))) + { + AddReadNote(SNR_STARTBYTE_MISMATCH); + return; + } + } + + // Compare IDs. + uint8 storedIdLen = 0; + Binaryread(iStrm, storedIdLen); + char storedIdBuf[256]; + MemsetZero(storedIdBuf); + if(storedIdLen > 0) + { + iStrm.read(storedIdBuf, storedIdLen); + } + if(!(id == ID(storedIdBuf, storedIdLen))) + { + AddReadNote(SNR_OBJECTCLASS_IDMISMATCH); + } + if ((m_Status & SNT_FAILURE) != 0) + { + SSB_LOG(mpt::ustring(strIdMismatch)); + return; + } + + SSB_LOG(mpt::ustring(strIdMatch)); + + // Header + uint8 tempU8; + Binaryread(iStrm, tempU8); + const uint8 header = tempU8; + m_nIdbytes = ((header & 3) == 3) ? 4 : (header & 3); + if (Testbit(header, 6)) + SetFlag(RwfRTwoBytesDescChar, true); + + // Read headerdata size + uint32 tempU32 = 0; + mpt::IO::ReadAdaptiveInt32LE(iStrm, tempU32); + const uint32 headerdatasize = tempU32; + + // If headerdatasize != 0, read known headerdata and ignore rest. + uint8 flagbyte = s_DefaultFlagbyte; + if(headerdatasize >= 2) + { + Binaryread(iStrm, tempU8); + if(tempU8 == HeaderId_FlagByte) + Binaryread(iStrm, flagbyte); + + iStrm.ignore( (tempU8 == HeaderId_FlagByte) ? headerdatasize - 2 : headerdatasize - 1); + } + + uint64 tempU64 = 0; + + // Read version numeric if available. + if (Testbit(header, 4)) + { + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + m_nReadVersion = tempU64; + if(tempU64 > nVersion) + AddReadNote(SNR_LOADING_OBJECT_WITH_LARGER_VERSION); + } + + if (Testbit(header, 5)) + { + Binaryread(iStrm, tempU8); + iStrm.ignore(tempU8); + } + + if(Testbit(flagbyte, 0)) // Custom ID? + { + Binaryread(iStrm, tempU8); + if ((tempU8 & 1) != 0) + m_nIdbytes = IdSizeVariable; + else + m_nIdbytes = (tempU8 >> 1); + if(m_nIdbytes == 0) + AddReadNote(SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED); + } + + m_nFixedEntrySize = 0; + if(Testbit(flagbyte, 1)) // Fixedsize entries? + mpt::IO::ReadAdaptiveInt32LE(iStrm, m_nFixedEntrySize); + + SetFlag(RwfRMapHasStartpos, Testbit(header, 2)); + SetFlag(RwfRMapHasSize, Testbit(header, 3)); + SetFlag(RwfRMapHasId, (m_nIdbytes > 0)); + SetFlag(RwfRMapHasDesc, Testbit(header, 7)); + SetFlag(RwfRwHasMap, GetFlag(RwfRMapHasId) || GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || GetFlag(RwfRMapHasDesc)); + + if (GetFlag(RwfRwHasMap) == false) + { + SSB_LOG(mpt::ustring(strNoMapInFile)); + } + + if (Testbit(flagbyte, 2)) // Object description? + { + uint16 size = 0; + mpt::IO::ReadAdaptiveInt16LE(iStrm, size); + iStrm.ignore(size * (GetFlag(RwfRTwoBytesDescChar) ? 2 : 1)); + } + + if(Testbit(flagbyte, 3)) + iStrm.ignore(5); + + // Read entrycount + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > 16000) + // The current code can only write 16383 entries because it uses a Adaptive64LE with a fixed size=2 + // Additionally, 16000 is an arbitrary limit to avoid an out-of-memory DoS when caching the map. + { AddReadNote(SNR_TOO_MANY_ENTRIES_TO_READ); return; } + + m_nReadEntrycount = static_cast(tempU64); + if(m_nReadEntrycount == 0) + AddReadNote(SNR_ZEROENTRYCOUNT); + + // Read map rpos if map exists. + if (GetFlag(RwfRwHasMap)) + { + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + } + + const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart; + + if (rawEndOfHdrData < 0 || static_cast(rawEndOfHdrData) > std::numeric_limits::max()) + { AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); return; } + + m_rposEndofHdrData = static_cast(rawEndOfHdrData); + m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast(tempU64) : m_rposEndofHdrData; + + if (GetFlag(RwfRwHasMap) == false) + m_posMapEnd = m_posStart + m_rposEndofHdrData; + + SetFlag(RwfRHeaderIsRead, true); +} + + +void SsbRead::CacheMap() +{ + std::istream& iStrm = *m_pIstrm; + if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0) + { + iStrm.seekg(m_posStart + m_rposMapBegin); + + if(iStrm.fail()) + { AddReadNote(SNR_BADSTREAM_AFTER_MAPHEADERSEEK); return; } + + SSB_LOG(mpt::format(mpt::ustring(tstrReadingMap))(m_rposMapBegin)); + + mapData.resize(m_nReadEntrycount); + m_Idarray.reserve(m_nReadEntrycount * 4); + + //Read map + for(NumType i = 0; i 0 && (Util::MaxValueOfType(nOldEnd) - nOldEnd >= nIdsize)) + { + m_Idarray.resize(nOldEnd + nIdsize); + iStrm.read(&m_Idarray[nOldEnd], nIdsize); + } + mapData[i].nIdLength = nIdsize; + mapData[i].nIdpos = nOldEnd; + + // Read position. + if(GetFlag(RwfRMapHasStartpos)) + { + uint64 tempU64; + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + mapData[i].rposStart = static_cast(tempU64); + } + + // Read entry size. + if (m_nFixedEntrySize > 0) + mapData[i].nSize = m_nFixedEntrySize; + else if(GetFlag(RwfRMapHasSize)) // Map has datasize field. + { + uint64 tempU64; + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + mapData[i].nSize = static_cast(tempU64); + } + + // If there's no entry startpos in map, count start pos from datasizes. + // Here readentry.rposStart is set to relative position from databegin. + if (mapData[i].nSize != invalidDatasize && GetFlag(RwfRMapHasStartpos) == false) + mapData[i].rposStart = (i > 0) ? mapData[i-1].rposStart + mapData[i-1].nSize : 0; + + if(GetFlag(RwfRMapHasDesc)) //Map has entrydescriptions? + { + uint16 size = 0; + mpt::IO::ReadAdaptiveInt16LE(iStrm, size); + if(GetFlag(RwfRTwoBytesDescChar)) + iStrm.ignore(size * 2); + else + iStrm.ignore(size); + } + } + m_posMapEnd = iStrm.tellg(); + SSB_LOG(mpt::format(mpt::ustring(tstrEndOfMap))(m_posMapEnd - m_posStart)); + } + + SetFlag(RwfRMapCached, true); + m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData); + m_pIstrm->seekg(m_posDataBegin); + + // If there are no positions in the map but there are entry sizes, rposStart will + // be relative to data start. Now that posDataBegin is known, make them relative to + // startpos. + if (GetFlag(RwfRMapHasStartpos) == false && (GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)) + { + const RposType offset = static_cast(m_posDataBegin - m_posStart); + for(size_t i = 0; i < m_nReadEntrycount; i++) + mapData[i].rposStart += offset; + } +} + + +const ReadEntry* SsbRead::Find(const ID &id) +{ + m_pIstrm->clear(); + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + + if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false) + m_pIstrm->seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter)); + + if (GetFlag(RwfRMapHasId) == true) + { + const size_t nEntries = mapData.size(); + for(size_t i0 = 0; i0 < nEntries; i0++) + { + const size_t i = (i0 + m_nNextReadHint) % nEntries; + if(mapData[i].nIdpos < m_Idarray.size() && id == ID(&m_Idarray[mapData[i].nIdpos], mapData[i].nIdLength)) + { + m_nNextReadHint = (i + 1) % nEntries; + if (mapData[i].rposStart != 0) + m_pIstrm->seekg(m_posStart + Postype(mapData[i].rposStart)); + return &mapData[i]; + } + } + } + return nullptr; +} + + +void SsbWrite::FinishWrite() +{ + std::ostream& oStrm = *m_pOstrm; + const Postype posDataEnd = oStrm.tellp(); + + Postype posMapStart = oStrm.tellp(); + + SSB_LOG(mpt::format(mpt::ustring(tstrWritingMap))(posMapStart - m_posStart)); + + if (GetFlag(RwfRwHasMap)) //Write map + { + oStrm.write(m_MapStreamString.c_str(), m_MapStreamString.length()); + } + + const Postype posMapEnd = oStrm.tellp(); + + // Write entry count. + oStrm.seekp(m_posEntrycount); + + // Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand. + mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2, 2); + + if (GetFlag(RwfRwHasMap)) + { // Write map start position. + oStrm.seekp(m_posMapPosField); + const uint64 rposMap = posMapStart - m_posStart; + + // Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand. + mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8, 8); + + } + + // Seek to end. + oStrm.seekp(std::max(posMapEnd, posDataEnd)); + + SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart)); +} + +} // namespace srlztn + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h new file mode 100644 index 000000000..0d31c68fe --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h @@ -0,0 +1,552 @@ +/* + * serialization_utils.h + * --------------------- + * Purpose: Serializing data to and from MPTM files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "../common/typedefs.h" +#include "../common/mptTypeTraits.h" +#include "../common/mptIO.h" +#include "../common/Endianness.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +OPENMPT_NAMESPACE_BEGIN + +namespace srlztn //SeRiaLiZaTioN +{ + +typedef std::ios::off_type Offtype; +typedef Offtype Postype; + +typedef uintptr_t DataSize; // Data size type. +typedef uintptr_t RposType; // Relative position type. +typedef uintptr_t NumType; // Entry count type. + +const DataSize invalidDatasize = DataSize(-1); + +enum +{ + SNT_PROGRESS = 0x08000000, // = 1 << 27 + SNT_FAILURE = 0x40000000, // = 1 << 30 + SNT_NOTE = 0x20000000, // = 1 << 29 + SNT_WARNING = 0x10000000, // = 1 << 28 + SNT_NONE = 0, + + SNRW_BADGIVEN_STREAM = 1 | SNT_FAILURE, + + // Read failures. + SNR_BADSTREAM_AFTER_MAPHEADERSEEK = 2 | SNT_FAILURE, + SNR_STARTBYTE_MISMATCH = 3 | SNT_FAILURE, + SNR_BADSTREAM_AT_MAP_READ = 4 | SNT_FAILURE, + SNR_INSUFFICIENT_STREAM_OFFTYPE = 5 | SNT_FAILURE, + SNR_OBJECTCLASS_IDMISMATCH = 6 | SNT_FAILURE, + SNR_TOO_MANY_ENTRIES_TO_READ = 7 | SNT_FAILURE, + SNR_INSUFFICIENT_RPOSTYPE = 8 | SNT_FAILURE, + + // Read notes and warnings. + SNR_ZEROENTRYCOUNT = 0x80 | SNT_NOTE, // 0x80 == 1 << 7 + SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED = 0x100 | SNT_NOTE, + SNR_LOADING_OBJECT_WITH_LARGER_VERSION = 0x200 | SNT_NOTE, + + // Write failures. + SNW_INSUFFICIENT_FIXEDSIZE = (0x10) | SNT_FAILURE, + SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING = (0x11) | SNT_FAILURE, + SNW_DATASIZETYPE_OVERFLOW = (0x13) | SNT_FAILURE, + SNW_MAX_WRITE_COUNT_REACHED = (0x14) | SNT_FAILURE, + SNW_INSUFFICIENT_DATASIZETYPE = (0x16) | SNT_FAILURE +}; + + +enum +{ + IdSizeVariable = uint16_max, + IdSizeMaxFixedSize = (uint8_max >> 1) +}; + +typedef int32 SsbStatus; + + +struct ReadEntry +{ + ReadEntry() : nIdpos(0), rposStart(0), nSize(invalidDatasize), nIdLength(0) {} + + uintptr_t nIdpos; // Index of id start in ID array. + RposType rposStart; // Entry start position. + DataSize nSize; // Entry size. + uint16 nIdLength; // Length of id. +}; + + +enum Rwf +{ + RwfWMapStartPosEntry, // Write. True to include data start pos entry to map. + RwfWMapSizeEntry, // Write. True to include data size entry to map. + RwfWMapDescEntry, // Write. True to include description entry to map. + RwfWVersionNum, // Write. True to include version numeric. + RwfRMapCached, // Read. True if map has been cached. + RwfRMapHasId, // Read. True if map has IDs + RwfRMapHasStartpos, // Read. True if map data start pos. + RwfRMapHasSize, // Read. True if map has entry size. + RwfRMapHasDesc, // Read. True if map has entry description. + RwfRTwoBytesDescChar, // Read. True if map description characters are two bytes. + RwfRHeaderIsRead, // Read. True when header is read. + RwfRwHasMap, // Read/write. True if map exists. + RwfNumFlags +}; + + +template +inline void Binarywrite(std::ostream& oStrm, const T& data) +{ + mpt::IO::WriteIntLE(oStrm, data); +} + +template<> +inline void Binarywrite(std::ostream& oStrm, const float& data) +{ + IEEE754binary32LE tmp = IEEE754binary32LE(data); + mpt::IO::Write(oStrm, tmp); +} + +template<> +inline void Binarywrite(std::ostream& oStrm, const double& data) +{ + IEEE754binary64LE tmp = IEEE754binary64LE(data); + mpt::IO::Write(oStrm, tmp); +} + +template +inline void WriteItem(std::ostream& oStrm, const T& data) +{ + static_assert(std::is_trivial::value == true, ""); + Binarywrite(oStrm, data); +} + +void WriteItemString(std::ostream& oStrm, const std::string &str); + +template <> +inline void WriteItem(std::ostream& oStrm, const std::string& str) {WriteItemString(oStrm, str);} + + +template +inline void Binaryread(std::istream& iStrm, T& data) +{ + mpt::IO::ReadIntLE(iStrm, data); +} + +template<> +inline void Binaryread(std::istream& iStrm, float& data) +{ + IEEE754binary32LE tmp = IEEE754binary32LE(0.0f); + mpt::IO::Read(iStrm, tmp); + data = tmp; +} + +template<> +inline void Binaryread(std::istream& iStrm, double& data) +{ + IEEE754binary64LE tmp = IEEE754binary64LE(0.0); + mpt::IO::Read(iStrm, tmp); + data = tmp; +} + +//Read only given number of bytes to the beginning of data; data bytes are memset to 0 before reading. +template +inline void Binaryread(std::istream& iStrm, T& data, const Offtype bytecount) +{ + mpt::IO::ReadBinaryTruncatedLE(iStrm, data, static_cast(bytecount)); +} + +template <> +inline void Binaryread(std::istream& iStrm, float& data, const Offtype bytecount) +{ + typedef IEEE754binary32LE T; + mpt::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); + // There is not much we can sanely do for truncated floats, + // thus we ignore what we just read and return 0. + data = 0.0f; +} + +template <> +inline void Binaryread(std::istream& iStrm, double& data, const Offtype bytecount) +{ + typedef IEEE754binary64LE T; + mpt::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); + // There is not much we can sanely do for truncated floats, + // thus we ignore what we just read and return 0. + data = 0.0; +} + + +template +inline void ReadItem(std::istream& iStrm, T& data, const DataSize nSize) +{ + static_assert(std::is_trivial::value == true, ""); + if (nSize == sizeof(T) || nSize == invalidDatasize) + Binaryread(iStrm, data); + else + Binaryread(iStrm, data, nSize); +} + +void ReadItemString(std::istream& iStrm, std::string& str, const DataSize); + +template <> +inline void ReadItem(std::istream& iStrm, std::string& str, const DataSize nSize) +{ + ReadItemString(iStrm, str, nSize); +} + + + +class ID +{ +private: + std::string m_ID; // NOTE: can contain null characters ('\0') +public: + ID() { } + ID(const std::string &id) : m_ID(id) { } + ID(const char *beg, const char *end) : m_ID(beg, end) { } + ID(const char *id) : m_ID(id?id:"") { } + ID(const char * str, std::size_t len) : m_ID(str, str + len) { } + template + static ID FromInt(const T &val) + { + STATIC_ASSERT(std::numeric_limits::is_integer); + typename mpt::make_le::type valle; + valle = val; + return ID(std::string(mpt::as_raw_memory(valle), mpt::as_raw_memory(valle) + sizeof(valle))); + } + bool IsPrintable() const; + mpt::ustring AsString() const; + const char *GetBytes() const { return m_ID.c_str(); } + std::size_t GetSize() const { return m_ID.length(); } + bool operator == (const ID &other) const { return m_ID == other.m_ID; } + bool operator != (const ID &other) const { return m_ID != other.m_ID; } +}; + + + +class Ssb +{ + +protected: + + Ssb(); + +public: + + SsbStatus GetStatus() const + { + return m_Status; + } + +protected: + + // When writing, returns the number of entries written. + // When reading, returns the number of entries read not including unrecognized entries. + NumType GetCounter() const {return m_nCounter;} + + void SetFlag(Rwf flag, bool val) {m_Flags.set(flag, val);} + bool GetFlag(Rwf flag) const {return m_Flags[flag];} + +protected: + + SsbStatus m_Status; + + uint32 m_nFixedEntrySize; // Read/write: If > 0, data entries have given fixed size. + + Postype m_posStart; // Read/write: Stream position at the beginning of object. + + uint16 m_nIdbytes; // Read/Write: Tells map ID entry size in bytes. If size is variable, value is IdSizeVariable. + NumType m_nCounter; // Read/write: Keeps count of entries written/read. + + std::bitset m_Flags; // Read/write: Various flags. + +protected: + + static const uint8 s_DefaultFlagbyte = 0; + static const char s_EntryID[3]; + +}; + + + +class SsbRead + : public Ssb +{ + +public: + + enum ReadRv // Read return value. + { + EntryRead, + EntryNotFound + }; + enum IdMatchStatus + { + IdMatch, IdMismatch + }; + typedef std::vector::const_iterator ReadIterator; + + SsbRead(std::istream& iStrm); + + // Call this to begin reading: must be called before other read functions. + void BeginRead(const ID &id, const uint64& nVersion); + + // After calling BeginRead(), this returns number of entries in the file. + NumType GetNumEntries() const {return m_nReadEntrycount;} + + // Returns read iterator to the beginning of entries. + // The behaviour of read iterators is undefined if map doesn't + // contain entry ids or data begin positions. + ReadIterator GetReadBegin(); + + // Returns read iterator to the end(one past last) of entries. + ReadIterator GetReadEnd(); + + // Compares given id with read entry id + IdMatchStatus CompareId(const ReadIterator& iter, const ID &id); + + uint64 GetReadVersion() {return m_nReadVersion;} + + // Read item using default read implementation. + template + ReadRv ReadItem(T& obj, const ID &id) {return ReadItem(obj, id, srlztn::ReadItem);} + + // Read item using given function. + template + ReadRv ReadItem(T& obj, const ID &id, FuncObj); + + // Read item using read iterator. + template + ReadRv ReadIterItem(const ReadIterator& iter, T& obj) {return ReadIterItem(iter, obj, srlztn::ReadItem);} + template + ReadRv ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func); + +private: + + // Reads map to cache. + void CacheMap(); + + // Searches for entry with given ID. If found, returns pointer to corresponding entry, else + // returns nullptr. + const ReadEntry* Find(const ID &id); + + // Called after reading an object. + ReadRv OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin); + + void AddReadNote(const SsbStatus s); + + // Called after reading entry. pRe is a pointer to associated map entry if exists. + void AddReadNote(const ReadEntry* const pRe, const NumType nNum); + + void ResetReadstatus(); + +private: + + // mapData is a cache that facilitates faster access to the stored data + // without having to reparse on every access. + // Iterator invalidation in CacheMap() is not a problem because every code + // path that ever returns an iterator into mapData does CacheMap exactly once + // beforehand. Following calls use this already cached map. As the data is + // immutable when reading, there is no need to ever invalidate the cache and + // redo CacheMap(). + + std::istream* m_pIstrm; // Read: Pointer to read stream. + + std::vector m_Idarray; // Read: Holds entry ids. + + std::vector mapData; // Read: Contains map information. + uint64 m_nReadVersion; // Read: Version is placed here when reading. + RposType m_rposMapBegin; // Read: If map exists, rpos of map begin, else m_rposEndofHdrData. + Postype m_posMapEnd; // Read: If map exists, map end position, else pos of end of hdrData. + Postype m_posDataBegin; // Read: Data begin position. + RposType m_rposEndofHdrData; // Read: rpos of end of header data. + NumType m_nReadEntrycount; // Read: Number of entries. + + NumType m_nNextReadHint; // Read: Hint where to start looking for the next read entry. + +}; + + + +class SsbWrite + : public Ssb +{ + +public: + + SsbWrite(std::ostream& oStrm); + + // Write header + void BeginWrite(const ID &id, const uint64& nVersion); + + // Write item using default write implementation. + template + void WriteItem(const T& obj, const ID &id) {WriteItem(obj, id, &srlztn::WriteItem);} + + // Write item using given function. + template + void WriteItem(const T& obj, const ID &id, FuncObj); + + // Writes mapping. + void FinishWrite(); + +private: + + // Called after writing an item. + void OnWroteItem(const ID &id, const Postype& posBeforeWrite); + + void AddWriteNote(const SsbStatus s); + void AddWriteNote(const ID &id, + const NumType nEntryNum, + const DataSize nBytecount, + const RposType rposStart); + + // Writes mapping item to mapstream. + void WriteMapItem(const ID &id, + const RposType& rposDataStart, + const DataSize& nDatasize, + const char* pszDesc); + + void ResetWritestatus() {m_Status = SNT_NONE;} + + void IncrementWriteCounter(); + +private: + + std::ostream* m_pOstrm; // Write: Pointer to write stream. + + Postype m_posEntrycount; // Write: Pos of entrycount field. + Postype m_posMapPosField; // Write: Pos of map position field. + std::string m_MapStreamString; // Write: Map stream string. + +}; + + +template +void SsbWrite::WriteItem(const T& obj, const ID &id, FuncObj Func) +{ + const Postype pos = m_pOstrm->tellp(); + Func(*m_pOstrm, obj); + OnWroteItem(id, pos); +} + +template +SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func) +{ + const ReadEntry* pE = Find(id); + const Postype pos = m_pIstrm->tellg(); + if (pE != nullptr || GetFlag(RwfRMapHasId) == false) + Func(*m_pIstrm, obj, (pE) ? (pE->nSize) : invalidDatasize); + return OnReadEntry(pE, id, pos); +} + + +template +SsbRead::ReadRv SsbRead::ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func) +{ + m_pIstrm->clear(); + if (iter->rposStart != 0) + m_pIstrm->seekg(m_posStart + Postype(iter->rposStart)); + const Postype pos = m_pIstrm->tellg(); + func(*m_pIstrm, obj, iter->nSize); + return OnReadEntry(&(*iter), ID(&m_Idarray[iter->nIdpos], iter->nIdLength), pos); +} + + +inline SsbRead::IdMatchStatus SsbRead::CompareId(const ReadIterator& iter, const ID &id) +{ + if(iter->nIdpos >= m_Idarray.size()) return IdMismatch; + return (id == ID(&m_Idarray[iter->nIdpos], iter->nIdLength)) ? IdMatch : IdMismatch; +} + + +inline SsbRead::ReadIterator SsbRead::GetReadBegin() +{ + MPT_ASSERT(GetFlag(RwfRMapHasId) && (GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)); + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + return mapData.begin(); +} + + +inline SsbRead::ReadIterator SsbRead::GetReadEnd() +{ + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + return mapData.end(); +} + + +template +struct VectorWriter +{ + VectorWriter(size_t nCount) : m_nCount(nCount) {} + void operator()(std::ostream &oStrm, const std::vector &vec) + { + for(size_t i = 0; i < m_nCount; i++) + { + Binarywrite(oStrm, vec[i]); + } + } + size_t m_nCount; +}; + +template +struct VectorReader +{ + VectorReader(size_t nCount) : m_nCount(nCount) {} + void operator()(std::istream& iStrm, std::vector &vec, const size_t) + { + vec.resize(m_nCount); + for(std::size_t i = 0; i < m_nCount; ++i) + { + Binaryread(iStrm, vec[i]); + } + } + size_t m_nCount; +}; + +template +struct ArrayReader +{ + ArrayReader(size_t nCount) : m_nCount(nCount) {} + void operator()(std::istream& iStrm, T* pData, const size_t) + { + for(std::size_t i=0; i // MFC core and standard components +#include // MFC extensions +#include // MFC support for Windows Common Controls +#include +#include +#include + +#endif // !MPT_BUILD_WINESUPPORT + +#include +#include +#include +#include + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + +#if MPT_COMPILER_MSVC +#include +#endif + + +#if MPT_OS_WINDOWS +#if MPT_COMPILER_MSVCCLANGC2 +// windows.h references IUnknown in a template function without having it even forward-declared. +// Clang does not like that. Forward-declaration fixes it. +struct IUnknown; +#endif +#endif + + +// this will be available everywhere + +#include "../common/typedefs.h" +// +// +// +// +// +// + +#include "../common/mptTypeTraits.h" +// + +#include "../common/mptString.h" +// +// +// +// +// + +#include "../common/mptStringFormat.h" + +#include "../common/mptPathString.h" + +#include "../common/Logging.h" + +#include "../common/misc_util.h" +// +// +// +// +// +// +// +// +// + +// for std::abs +#include +#include +#include +#include + +#if defined(MPT_ENABLE_FILEIO_STDIO) +// for FILE* definition (which cannot be forward-declared in a portable way) +#include +#include +#endif + +#ifndef NO_VST +// VST SDK includes these headers after messing with default compiler structure +// packing. No problem in practice as VST SDK sets packing matching the default +// packing and we are compiling with default packing and standard headers should +// be careful about structure packing anyway, but it is very much unclean +// nonetheless. Pre-include the affected headers here as a future-proof +// safe-guard and let their own include guards handle the further including by +// VST SDK. +#include +#include +#include +#include +#endif + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. diff --git a/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp b/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp new file mode 100644 index 000000000..70e9dbb8d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp @@ -0,0 +1,49 @@ +/* + * typedefs.cpp + * ------------ + * Purpose: Basic data type definitions and assorted compiler-related helpers. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "typedefs.h" + +#include "Endianness.h" + + +OPENMPT_NAMESPACE_BEGIN + +#if MPT_PLATFORM_ENDIAN_KNOWN + +MPT_MSVC_WORKAROUND_LNK4221(typedefs) + +#else + +int24::int24(int other) +{ + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { + bytes[0] = (static_cast(other)>>16)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>> 0)&0xff; + } else { + bytes[0] = (static_cast(other)>> 0)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>>16)&0xff; + } +} + +int24::operator int() const +{ + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { + return (static_cast(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2]; + } else { + return (static_cast(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0]; + } +} + +#endif + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/typedefs.h b/Frameworks/OpenMPT/OpenMPT/common/typedefs.h new file mode 100644 index 000000000..88e0e3f99 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/typedefs.h @@ -0,0 +1,544 @@ +/* + * typedefs.h + * ---------- + * Purpose: Basic data type definitions and assorted compiler-related helpers. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + + +OPENMPT_NAMESPACE_BEGIN + + + +// Advanced inline attributes +#if MPT_COMPILER_MSVC +#define MPT_FORCEINLINE __forceinline +#define MPT_NOINLINE __declspec(noinline) +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#define MPT_FORCEINLINE __attribute__((always_inline)) inline +#define MPT_NOINLINE __attribute__((noinline)) +#else +#define MPT_FORCEINLINE inline +#define MPT_NOINLINE +#endif + + + +// constexpr +#define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR11_VAR constexpr +#if MPT_CXX_AT_LEAST(14) +#define MPT_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR14_VAR constexpr +#else +#define MPT_CONSTEXPR14_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR14_VAR const +#endif + + + +// C++17 std::size +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +namespace mpt { +template +MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size()) +{ + return v.size(); +} +template +MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept +{ + return N; +} +} // namespace mpt + + + +// MPT_ARRAY_COUNT macro computes the number of elements in a statically-allocated array. +#if MPT_COMPILER_MSVC +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_ARRAY_COUNT(x) _countof(x) +#else +#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0])) +#endif + + + +// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased. +#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#define MPT_RESTRICT __restrict +#else +#define MPT_RESTRICT +#endif + + + +// Some functions might be deprecated although they are still in use. +// Tag them with "MPT_DEPRECATED". +#if MPT_COMPILER_MSVC +#define MPT_DEPRECATED __declspec(deprecated) +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#define MPT_DEPRECATED __attribute__((deprecated)) +#else +#define MPT_DEPRECATED +#endif +#if defined(MODPLUG_TRACKER) +#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED +#define MPT_DEPRECATED_LIBOPENMPT +#elif defined(LIBOPENMPT_BUILD) +#define MPT_DEPRECATED_TRACKER +#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED +#else +#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED +#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED +#endif + + + +OPENMPT_NAMESPACE_END +#include +#include +OPENMPT_NAMESPACE_BEGIN + + + +#if MPT_CXX_AT_LEAST(14) +namespace mpt { +using std::make_unique; +} // namespace mpt +#else +namespace mpt { +template +std::unique_ptr make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} +} // namespace mpt +#endif + + + +#if MPT_COMPILER_MSVC +#define MPT_CONSTANT_IF(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(x) \ + __pragma(warning(pop)) \ +/**/ +#define MPT_MAYBE_CONSTANT_IF(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(x) \ + __pragma(warning(pop)) \ +/**/ +#endif + +#if MPT_COMPILER_GCC +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ + if(x) \ + _Pragma("GCC diagnostic pop") \ +/**/ +#endif + +#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ + _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \ + _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \ + if(x) \ + _Pragma("clang diagnostic pop") \ +/**/ +#endif + +#if !defined(MPT_CONSTANT_IF) +// MPT_CONSTANT_IF disables compiler warnings for conditions that are either always true or always false for some reason (dependent on template arguments for example) +#define MPT_CONSTANT_IF(x) if(x) +#endif + +#if !defined(MPT_MAYBE_CONSTANT_IF) +// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases). +#define MPT_MAYBE_CONSTANT_IF(x) if(x) +#endif + + + +#if MPT_COMPILER_MSVC +// MSVC warns for the well-known and widespread "do { } while(0)" idiom with warning level 4 ("conditional expression is constant"). +// It does not warn with "while(0,0)". However this again causes warnings with other compilers. +// Solve it with a macro. +#define MPT_DO do +#define MPT_WHILE_0 while(0,0) +#endif + +#ifndef MPT_DO +#define MPT_DO do +#endif +#ifndef MPT_WHILE_0 +#define MPT_WHILE_0 while(0) +#endif + + + +#if MPT_COMPILER_MSVC && defined(UNREFERENCED_PARAMETER) +#define MPT_UNREFERENCED_PARAMETER(x) UNREFERENCED_PARAMETER(x) +#else +#define MPT_UNREFERENCED_PARAMETER(x) (void)(x) +#endif + +#define MPT_UNUSED_VARIABLE(x) MPT_UNREFERENCED_PARAMETER(x) + + + +// Exception handling helpers, because MFC requires explicit deletion of the exception object, +// Thus, always call exactly one of MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() or MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e). + +#if defined(_MFC_VER) + +#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { AfxThrowMemoryException(); } MPT_WHILE_0 +#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( CMemoryException * e ) +#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0 +#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { if(e) { e->Delete(); e = nullptr; } } MPT_WHILE_0 + +#else // !_MFC_VER + +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { throw std::bad_alloc(); } MPT_WHILE_0 +#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( const std::bad_alloc & e ) +#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0 +#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); } MPT_WHILE_0 + +#endif // _MFC_VER + + + +// Static code checkers might need to get the knowledge of our assertions transferred to them. +#define MPT_CHECKER_ASSUME_ASSERTIONS 1 +//#define MPT_CHECKER_ASSUME_ASSERTIONS 0 + +#ifdef MPT_BUILD_ANALYZED + +#if MPT_COMPILER_MSVC + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x)) +#endif + +#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#ifdef NDEBUG +#error "Builds for static analyzers depend on std::assert being enabled, but the current build has #define NDEBUG. This makes no sense." +#endif +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_CHECKER_ASSUME(x) assert(!!(x)) +#endif + +#endif // MPT_COMPILER + +#endif // MPT_BUILD_ANALYZED + +#ifndef MPT_CHECKER_ASSUME +#define MPT_CHECKER_ASSUME(x) MPT_DO { } MPT_WHILE_0 +#endif + + + +#if defined(_MFC_VER) + +#if !defined(ASSERT) +#error "MFC is expected to #define ASSERT" +#endif // !defined(ASERRT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED + +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG + +// let MFC handle our asserts +#define MPT_ASSERT_USE_FRAMEWORK 1 + +#else // !_MFC_VER + +#if defined(ASSERT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG +#endif // !defined(ASERRT) + +// handle assert in our own way without relying on some platform-/framework-specific assert implementation +#define MPT_ASSERT_USE_FRAMEWORK 0 + +#endif // _MFC_VER + + +#if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) + +#define MPT_ASSERT_NOTREACHED() ASSERT(0) +#define MPT_ASSERT(expr) ASSERT((expr)) +#define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg)) +#if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1) +#define MPT_ASSERT_ALWAYS(expr) ASSERT((expr)) +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg)) +#else +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif +#endif + +#elif defined(NO_ASSERTS) + +#define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0) +#define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#else // !NO_ASSERTS + +#define MPT_ASSERT_NOTREACHED() MPT_DO { MPT_CONSTANT_IF(!(0)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 +#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#endif // NO_ASSERTS + + +#if defined(MPT_ASSERT_HANDLER_NEEDED) +// custom assert handler needed +MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function, const char *expr, const char *msg=nullptr); +#endif // MPT_ASSERT_HANDLER_NEEDED + + + +// Compile time assert. +#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr) + + + +// Macro for marking intentional fall-throughs in switch statements - can be used for static analysis if supported. +#if (MPT_CXX >= 17) + #define MPT_FALLTHROUGH [[fallthrough]] +#elif MPT_COMPILER_MSVC + #define MPT_FALLTHROUGH __fallthrough +#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + #define MPT_FALLTHROUGH [[clang::fallthrough]] +#elif MPT_COMPILER_GCC && MPT_GCC_AT_LEAST(7,1,0) + #define MPT_FALLTHROUGH __attribute__((fallthrough)) +#elif defined(__has_cpp_attribute) + #if __has_cpp_attribute(fallthrough) + #define MPT_FALLTHROUGH [[fallthrough]] + #else + #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 + #endif +#else + #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 +#endif + + + +OPENMPT_NAMESPACE_END +#include +#include +#include +OPENMPT_NAMESPACE_BEGIN + +typedef std::int8_t int8; +typedef std::int16_t int16; +typedef std::int32_t int32; +typedef std::int64_t int64; +typedef std::uint8_t uint8; +typedef std::uint16_t uint16; +typedef std::uint32_t uint32; +typedef std::uint64_t uint64; + +const int8 int8_min = INT8_MIN; +const int16 int16_min = INT16_MIN; +const int32 int32_min = INT32_MIN; +const int64 int64_min = INT64_MIN; + +const int8 int8_max = INT8_MAX; +const int16 int16_max = INT16_MAX; +const int32 int32_max = INT32_MAX; +const int64 int64_max = INT64_MAX; + +const uint8 uint8_max = UINT8_MAX; +const uint16 uint16_max = UINT16_MAX; +const uint32 uint32_max = UINT32_MAX; +const uint64 uint64_max = UINT64_MAX; + + +// 24-bit integer wrapper (for 24-bit PCM) +struct int24 +{ + uint8 bytes[3]; + int24() { bytes[0] = bytes[1] = bytes[2] = 0; } +#if MPT_PLATFORM_ENDIAN_KNOWN + explicit int24(int other) + { + #ifdef MPT_PLATFORM_BIG_ENDIAN + bytes[0] = (static_cast(other)>>16)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>> 0)&0xff; + #else + bytes[0] = (static_cast(other)>> 0)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>>16)&0xff; + #endif + } + operator int() const + { + #ifdef MPT_PLATFORM_BIG_ENDIAN + return (static_cast(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2]; + #else + return (static_cast(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0]; + #endif + } +#else + explicit int24(int other); + operator int() const; +#endif +}; +MPT_STATIC_ASSERT(sizeof(int24) == 3); +#define int24_min (0-0x00800000) +#define int24_max (0+0x007fffff) + + +typedef float float32; +MPT_STATIC_ASSERT(sizeof(float32) == 4); + +typedef double float64; +MPT_STATIC_ASSERT(sizeof(float64) == 8); + + +MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); + + +namespace mpt { + +MPT_STATIC_ASSERT(CHAR_BIT == 8); + +MPT_STATIC_ASSERT(sizeof(char) == 1); + +typedef unsigned char byte; +MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1); + +} // namespace mpt + + + +#if MPT_COMPILER_MSVC + + #if defined(_M_X64) + #define MPT_ARCH_BITS 64 + #define MPT_ARCH_BITS_32 0 + #define MPT_ARCH_BITS_64 1 + #elif defined(_M_IX86) + #define MPT_ARCH_BITS 32 + #define MPT_ARCH_BITS_32 1 + #define MPT_ARCH_BITS_64 0 + #endif + +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + + #if defined(__SIZEOF_POINTER__) + #if (__SIZEOF_POINTER__ == 8) + #define MPT_ARCH_BITS 64 + #define MPT_ARCH_BITS_32 0 + #define MPT_ARCH_BITS_64 1 + #elif (__SIZEOF_POINTER__ == 4) + #define MPT_ARCH_BITS 32 + #define MPT_ARCH_BITS_32 1 + #define MPT_ARCH_BITS_64 0 + #endif + #endif + +#endif // MPT_COMPILER + +// fallback + +#if !defined(MPT_ARCH_BITS) +#include +#include +MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); +#if defined(UINTPTR_MAX) + #if (UINTPTR_MAX == 0xffffffffffffffffull) + #define MPT_ARCH_BITS 64 + #define MPT_ARCH_BITS_32 0 + #define MPT_ARCH_BITS_64 1 + #elif (UINTPTR_MAX == 0xffffffffu) + #define MPT_ARCH_BITS 32 + #define MPT_ARCH_BITS_32 1 + #define MPT_ARCH_BITS_64 0 + #endif +#endif // UINTPTR_MAX +#endif // MPT_ARCH_BITS + + + +#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) __attribute__((format(printf, formatstringindex, varargsindex))) +#else +#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) +#endif + + + +#if MPT_COMPILER_MSVC +// warning LNK4221: no public symbols found; archive member will be inaccessible +// There is no way to selectively disable linker warnings. +// #pragma warning does not apply and a command line option does not exist. +// Some options: +// 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro) +// 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now) +// 3. An unused trivial inline function. +// Option 3 does not actually solve the problem though, which leaves us with option 1. +// In any case, for optimized builds, the linker will just remove the useless symbol. +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) +#define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0; +#endif + +#ifndef MPT_MSVC_WORKAROUND_LNK4221 +#define MPT_MSVC_WORKAROUND_LNK4221(x) +#endif + + + +// legacy +#define CountOf(x) MPT_ARRAY_COUNT(x) +#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x) + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp new file mode 100644 index 000000000..a47f2c401 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -0,0 +1,794 @@ +/* + * version.cpp + * ----------- + * Purpose: OpenMPT version handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "version.h" + +#include "mptString.h" +#include "mptStringFormat.h" +#include "mptStringParse.h" + +#include "versionNumber.h" +#include "svn_version.h" + +OPENMPT_NAMESPACE_BEGIN + +namespace MptVersion { + +static_assert((MPT_VERSION_NUMERIC & 0xffff) != 0x0000, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); + +const VersionNum num = MPT_VERSION_NUMERIC; + +const char * const str = MPT_VERSION_STR; + +std::string GetOpenMPTVersionStr() +{ + return std::string("OpenMPT " MPT_VERSION_STR); +} + +VersionNum ToNum(const std::string &s) +{ + VersionNum result = 0; + std::vector numbers = mpt::String::Split(s, std::string(".")); + for(std::size_t i = 0; i < numbers.size() && i < 4; ++i) + { + result |= (mpt::String::Parse::Hex(numbers[i]) & 0xff) << ((3-i)*8); + } + return result; + +} + +std::string ToStr(const VersionNum v) +{ + if(v == 0) + { + // Unknown version + return "Unknown"; + } else if((v & 0xFFFF) == 0) + { + // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) + return mpt::format("%1.%2")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF)); + } else + { + // Full version info available + return mpt::format("%1.%2.%3.%4")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF), mpt::fmt::HEX0<2>((v >> 8) & 0xFF), mpt::fmt::HEX0<2>((v) & 0xFF)); + } +} + +mpt::ustring ToUString(const VersionNum v) +{ + if(v == 0) + { + // Unknown version + return MPT_USTRING("Unknown"); + } else if((v & 0xFFFF) == 0) + { + // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) + return mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF)); + } else + { + // Full version info available + return mpt::format(MPT_USTRING("%1.%2.%3.%4"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF)); + } +} + +VersionNum RemoveBuildNumber(const VersionNum num_) +{ + return (num_ & 0xFFFFFF00); +} + +bool IsTestBuild(const VersionNum num_) +{ + return ( + // Legacy + (num_ > MAKE_VERSION_NUMERIC(1,17,02,54) && num_ < MAKE_VERSION_NUMERIC(1,18,02,00) && num_ != MAKE_VERSION_NUMERIC(1,18,00,00)) + || + // Test builds have non-zero VER_MINORMINOR + (num_ > MAKE_VERSION_NUMERIC(1,18,02,00) && RemoveBuildNumber(num_) != num_) + ); +} + +bool IsDebugBuild() +{ + #ifdef _DEBUG + return true; + #else + return false; + #endif +} + +static std::string GetUrl() +{ + #ifdef OPENMPT_VERSION_URL + return OPENMPT_VERSION_URL; + #else + return ""; + #endif +} + +static int GetRevision() +{ + #if defined(OPENMPT_VERSION_REVISION) + return OPENMPT_VERSION_REVISION; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return 0; + } + if(svnversion.find(":") != std::string::npos) + { + svnversion = svnversion.substr(svnversion.find(":") + 1); + } + if(svnversion.find("-") != std::string::npos) + { + svnversion = svnversion.substr(svnversion.find("-") + 1); + } + if(svnversion.find("M") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("M")); + } + if(svnversion.find("S") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("S")); + } + if(svnversion.find("P") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("P")); + } + return ConvertStrTo(svnversion); + #else + #if MPT_COMPILER_MSVC + #pragma message("SVN revision unknown. Please check your build system.") + #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + #warning "SVN revision unknown. Please check your build system." + #else + // There is no portable way to display a warning. + // Try to provoke a warning with an unused variable. + int SVN_revision_unknown__Please_check_your_build_system; + #endif + return 0; + #endif +} + +static bool IsDirty() +{ + #if defined(OPENMPT_VERSION_DIRTY) + return OPENMPT_VERSION_DIRTY != 0; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return false; + } + if(svnversion.find("M") != std::string::npos) + { + return true; + } + return false; + #else + return false; + #endif +} + +static bool HasMixedRevisions() +{ + #if defined(OPENMPT_VERSION_MIXEDREVISIONS) + return OPENMPT_VERSION_MIXEDREVISIONS != 0; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return false; + } + if(svnversion.find(":") != std::string::npos) + { + return true; + } + if(svnversion.find("-") != std::string::npos) + { + return true; + } + if(svnversion.find("S") != std::string::npos) + { + return true; + } + if(svnversion.find("P") != std::string::npos) + { + return true; + } + return false; + #else + return false; + #endif +} + +static bool IsPackage() +{ + #if defined(OPENMPT_VERSION_IS_PACKAGE) + return OPENMPT_VERSION_IS_PACKAGE != 0; + #else + return false; + #endif +} + +static std::string GetSourceDate() +{ + #if defined(OPENMPT_VERSION_DATE) + return OPENMPT_VERSION_DATE; + #else + return ""; + #endif +} + +SourceInfo GetSourceInfo() +{ + SourceInfo result; + result.Url = GetUrl(); + result.Revision = GetRevision(); + result.IsDirty = IsDirty(); + result.HasMixedRevisions = HasMixedRevisions(); + result.IsPackage = IsPackage(); + result.Date = GetSourceDate(); + return result; +} + +std::string SourceInfo::GetStateString() const +{ + std::string retval; + if(IsDirty) + { + retval += "+dirty"; + } + if(HasMixedRevisions) + { + retval += "+mixed"; + } + if(retval.empty()) + { + retval += "clean"; + } + if(IsPackage) + { + retval += "-pkg"; + } + return retval; +} + +std::string GetBuildDateString() +{ + #ifdef MODPLUG_TRACKER + #if defined(OPENMPT_BUILD_DATE) + return OPENMPT_BUILD_DATE; + #else + return __DATE__ " " __TIME__ ; + #endif + #else // !MODPLUG_TRACKER + return GetSourceInfo().Date; + #endif // MODPLUG_TRACKER +} + +static std::string GetBuildFlagsString() +{ + std::string retval; + #ifdef MODPLUG_TRACKER + if(IsTestBuild()) + { + retval += " TEST"; + } + #endif // MODPLUG_TRACKER + if(IsDebugBuild()) + { + retval += " DEBUG"; + } + return retval; +} + +std::string GetBuildFeaturesString() +{ + std::string retval; + #ifdef LIBOPENMPT_BUILD + #if defined(MPT_CHARSET_WIN32) + retval += " +WINAPI"; + #endif + #if defined(MPT_CHARSET_ICONV) + retval += " +ICONV"; + #endif + #if defined(MPT_CHARSET_CODECVTUTF8) + retval += " +CODECVTUTF8"; + #endif + #if defined(MPT_CHARSET_INTERNAL) + retval += " +INTERNALCHARSETS"; + #endif + #if defined(MPT_WITH_ZLIB) + retval += " +ZLIB"; + #endif + #if defined(MPT_WITH_MINIZ) + retval += " +MINIZ"; + #endif + #if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ) + retval += " -INFLATE"; + #endif + #if defined(MPT_WITH_MPG123) + retval += " +MPG123"; + #endif + #if defined(MPT_WITH_MINIMP3) + retval += " +MINIMP3"; + #endif + #if defined(MPT_WITH_MEDIAFOUNDATION) + retval += " +MF"; + #endif + #if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION) + retval += " -MP3"; + #endif + #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + retval += " +VORBIS"; + #endif + #if defined(MPT_WITH_STBVORBIS) + retval += " +STBVORBIS"; + #endif + #if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS) + retval += " -VORBIS"; + #endif + #if !defined(NO_PLUGINS) + retval += " +PLUGINS"; + #else + retval += " -PLUGINS"; + #endif + #if !defined(NO_DMO) + retval += " +DMO"; + #endif + #endif + #ifdef MODPLUG_TRACKER + #if (MPT_ARCH_BITS == 64) + if (true + && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64) + && (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP64) + ) { + retval += " WIN64OLD"; + } + #elif (MPT_ARCH_BITS == 32) + if (true + && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP) + && (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP) + ) { + retval += " WIN32OLD"; + } + #endif + #if defined(UNICODE) + retval += " UNICODE"; + #else + retval += " ANSI"; + #endif + #ifdef NO_VST + retval += " NO_VST"; + #endif + #ifdef NO_DMO + retval += " NO_DMO"; + #endif + #ifdef NO_PLUGINS + retval += " NO_PLUGINS"; + #endif + #ifndef MPT_WITH_ASIO + retval += " NO_ASIO"; + #endif + #ifndef MPT_WITH_DSOUND + retval += " NO_DSOUND"; + #endif + #endif + return retval; +} + +std::string GetBuildCompilerString() +{ + std::string retval; + #if MPT_COMPILER_GENERIC + retval += "*Generic C++11 Compiler"; + #elif MPT_COMPILER_MSVC + #if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0) + retval += mpt::format("Microsoft Compiler %1.%2.%3.%4") + ( _MSC_FULL_VER / 10000000 + , mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000) + , mpt::fmt::dec0<2>(_MSC_BUILD) + ); + #elif defined(_MSC_FULL_VER) + retval += mpt::format("Microsoft Compiler %1.%2.%3") + ( _MSC_FULL_VER / 10000000 + , mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000) + ); + #else + retval += mpt::format("Microsoft Compiler %1.%2")(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100); + #endif + #elif MPT_COMPILER_GCC + retval += mpt::format("GNU Compiler Collection %1.%2.%3")(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100); + #elif MPT_COMPILER_CLANG + retval += mpt::format("Clang %1.%2.%3")(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100); + #elif MPT_COMPILER_MSVCCLANGC2 + retval += mpt::format("MSVC-Clang/C2 %1")(MPT_COMPILER_MSVCCLANGC2_VERSION); + #else + retval += "*unknown"; + #endif + return retval; +} + +static std::string GetRevisionString() +{ + std::string result; + if(GetRevision() == 0) + { + return result; + } + result = std::string("-r") + mpt::fmt::val(GetRevision()); + if(HasMixedRevisions()) + { + result += "!"; + } + if(IsDirty()) + { + result += "+"; + } + if(IsPackage()) + { + result += "p"; + } + return result; +} + +mpt::ustring GetDownloadURL() +{ + #ifdef MODPLUG_TRACKER + return (MptVersion::IsDebugBuild() || MptVersion::IsTestBuild() || MptVersion::IsDirty() || MptVersion::HasMixedRevisions()) + ? + MPT_USTRING("https://buildbot.openmpt.org/builds/") + : + MPT_USTRING("https://openmpt.org/download") + ; + #else + return MPT_USTRING("https://lib.openmpt.org/"); + #endif +} + +std::string GetVersionString(FlagSet strings) +{ + std::vector result; + if(strings[StringVersion]) + { + result.push_back(MPT_VERSION_STR); + } + if(strings[StringRevision]) + { + if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions()) + { + result.push_back(GetRevisionString()); + } + } + if(strings[StringBitness]) + { + result.push_back(mpt::format(" %1 bit")(sizeof(void*)*8)); + } + if(strings[StringSourceInfo]) + { + const SourceInfo sourceInfo = GetSourceInfo(); + if(!sourceInfo.GetUrlWithRevision().empty()) + { + result.push_back(mpt::format(" %1")(sourceInfo.GetUrlWithRevision())); + } + if(!sourceInfo.Date.empty()) + { + result.push_back(mpt::format(" (%1)")(sourceInfo.Date)); + } + if(!sourceInfo.GetStateString().empty()) + { + result.push_back(mpt::format(" %1")(sourceInfo.GetStateString())); + } + } + if(strings[StringBuildFlags]) + { + if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions()) + { + result.push_back(GetBuildFlagsString()); + } + } + if(strings[StringBuildFeatures]) + { + result.push_back(GetBuildFeaturesString()); + } + return mpt::String::Trim(mpt::String::Combine(result, std::string(""))); +} + +std::string GetVersionStringPure() +{ + FlagSet strings; + strings |= MptVersion::StringVersion; + strings |= MptVersion::StringRevision; + #ifdef MODPLUG_TRACKER + strings |= MptVersion::StringBitness; + #endif + return GetVersionString(strings); +} + +std::string GetVersionStringSimple() +{ + FlagSet strings; + strings |= MptVersion::StringVersion; + strings |= MptVersion::StringRevision; + strings |= MptVersion::StringBuildFlags; + return GetVersionString(strings); +} + +std::string GetVersionStringExtended() +{ + FlagSet strings; + strings |= MptVersion::StringVersion; + strings |= MptVersion::StringRevision; + #ifdef MODPLUG_TRACKER + strings |= MptVersion::StringBitness; + #endif + #ifndef MODPLUG_TRACKER + strings |= MptVersion::StringSourceInfo; + #endif + strings |= MptVersion::StringBuildFlags; + #ifdef MODPLUG_TRACKER + strings |= MptVersion::StringBuildFeatures; + #endif + return GetVersionString(strings); +} + +std::string SourceInfo::GetUrlWithRevision() const +{ + if(Url.empty() || (Revision == 0)) + { + return std::string(); + } + return Url + "@" + mpt::fmt::val(Revision); +} + +mpt::ustring GetURL(std::string key) +{ + mpt::ustring result; + if(key.empty()) + { + result = mpt::ustring(); + } else if(key == "website") + { + #ifdef LIBOPENMPT_BUILD + result = MPT_USTRING("https://lib.openmpt.org/"); + #else + result = MPT_USTRING("https://openmpt.org/"); + #endif + } else if(key == "forum") + { + result = MPT_USTRING("https://forum.openmpt.org/"); + } else if(key == "bugtracker") + { + result = MPT_USTRING("https://bugs.openmpt.org/"); + } else if(key == "updates") + { + result = MPT_USTRING("https://openmpt.org/download"); + } else if(key == "top_picks") + { + result = MPT_USTRING("https://openmpt.org/top_picks"); + } + return result; +} + +mpt::ustring GetFullCreditsString() +{ + return mpt::ToUnicode(mpt::CharsetUTF8, +#ifdef MODPLUG_TRACKER + "OpenMPT / ModPlug Tracker\n" +#else + "libopenmpt (based on OpenMPT / ModPlug Tracker)\n" +#endif + "\n" + "Copyright \xC2\xA9 2004-2018 Contributors\n" + "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" + "\n" + "Contributors:\n" + "Johannes Schultz (2008-2018)\n" + "J\xC3\xB6rn Heusipp (2012-2018)\n" + "Ahti Lepp\xC3\xA4nen (2005-2011)\n" + "Robin Fernandes (2004-2007)\n" + "Sergiy Pylypenko (2007)\n" + "Eric Chavanon (2004-2005)\n" + "Trevor Nunes (2004)\n" + "Olivier Lapicque (1997-2003)\n" + "\n" + "Additional patch submitters:\n" + "coda (http://coda.s3m.us/)\n" + "kode54 (https://kode54.net/)\n" + "Revenant (http://revenant1.net/)\n" + "xaimus (http://xaimus.com/)\n" + "\n" + "Thanks to:\n" + "\n" + "Konstanty for the XMMS-ModPlug resampling implementation\n" + "http://modplug-xmms.sourceforge.net/\n" + "\n" +#ifdef MODPLUG_TRACKER + "Stephan M. Bernsee for pitch shifting source code\n" + "http://www.dspdimension.com/\n" + "\n" + "Aleksey Vaneev of Voxengo for r8brain sample rate converter\n" + "https://github.com/avaneev/r8brain-free-src\n" + "\n" + "Olli Parviainen for SoundTouch Library (time stretching)\n" + "http://www.surina.net/soundtouch/\n" + "\n" +#endif +#ifndef NO_VST + "Hermann Seib for his example VST Host implementation\n" + "http://www.hermannseib.com/english/vsthost.htm\n" + "\n" +#endif + "Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n" + "https://github.com/lclevy/unmo3\n" + "\n" + "Ben \"GreaseMonkey\" Russell for IT sample compression code\n" + "https://github.com/iamgreaser/it2everything/\n" + "\n" + "Antti S. Lankila for Amiga resampler implementation\n" + "https://bel.fi/alankila/modguide/interpolate.txt\n" + "\n" +#ifdef MPT_WITH_ZLIB + "Jean-loup Gailly and Mark Adler for zlib\n" + "http://zlib.net/\n" + "\n" +#endif +#ifdef MPT_WITH_MINIZ + "Rich Geldreich et al. for miniz\n" + "https://github.com/richgel999/miniz\n" + "\n" +#endif +#ifdef MPT_WITH_LHASA + "Simon Howard for lhasa\n" + "https://fragglet.github.io/lhasa/\n" + "\n" +#endif +#ifdef MPT_WITH_UNRAR + "Alexander L. Roshal for UnRAR\n" + "http://rarlab.com/\n" + "\n" +#endif +#ifdef MPT_WITH_PORTAUDIO + "PortAudio contributors\n" + "http://www.portaudio.com/\n" + "\n" +#endif +#ifdef MPT_WITH_FLAC + "Josh Coalson / Xiph.Org Foundation for libFLAC\n" + "https://xiph.org/flac/\n" + "\n" +#endif +#if defined(MPT_WITH_MPG123) + "The mpg123 project for libmpg123\n" + "http://mpg123.de/\n" + "\n" +#endif +#ifdef MPT_WITH_MINIMP3 + "Fabrice Bellard, FFMPEG contributors\n" + "and Martin J. Fiedler (KeyJ/kakiarts) for minimp3\n" + "http://keyj.emphy.de/minimp3/\n" + "\n" +#endif +#ifdef MPT_WITH_STBVORBIS + "Sean Barrett for stb_vorbis\n" + "https://github.com/nothings/stb/\n" + "\n" +#endif +#ifdef MPT_WITH_OGG + "Xiph.Org Foundation for libogg\n" + "https://xiph.org/ogg/\n" + "\n" +#endif +#if defined(MPT_WITH_VORBIS) || defined(MPT_WITH_LIBVORBISFILE) + "Xiph.Org Foundation for libvorbis\n" + "https://xiph.org/vorbis/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUS) + "Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry,\n" + "CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo,\n" + "Xiph.Org Foundation, Microsoft Corporation, Broadcom Corporation for libopus\n" + "https://opus-codec.org/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUSFILE) + "Xiph.Org Foundation and contributors for libopusfile\n" + "https://opus-codec.org/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUSENC) + "Xiph.Org Foundation, Jean-Marc Valin and contributors for libopusenc\n" + "https://git.xiph.org/?p=libopusenc.git;a=summary\n" + "\n" +#endif +#if defined(MPT_WITH_PICOJSON) + "Cybozu Labs Inc. and Kazuho Oku et. al. for picojson\n" + "https://github.com/kazuho/picojson\n" + "\n" +#endif + "Storlek for all the IT compatibility hints and testcases\n" + "as well as the IMF, MDL, OKT and ULT loaders\n" + "http://schismtracker.org/\n" + "\n" +#ifdef MODPLUG_TRACKER + "Lennart Poettering and David Henningsson for RealtimeKit\n" + "http://git.0pointer.net/rtkit.git/\n" + "\n" + "Gary P. Scavone for RtMidi\n" + "https://www.music.mcgill.ca/~gary/rtmidi/\n" + "\n" + "Alexander Uckun for decimal input field\n" + "http://www.codeproject.com/Articles/21257/_\n" + "\n" + "Nobuyuki for application and file icon\n" + "https://twitter.com/nobuyukinyuu\n" + "\n" +#endif + "Daniel Collin (emoon/TBL) for providing test infrastructure\n" + "https://twitter.com/daniel_collin\n" + "\n" + "The people at ModPlug forums for crucial contribution\n" + "in the form of ideas, testing and support;\n" + "thanks particularly to:\n" + "33, 8bitbubsy, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n" + "christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n" + "Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n" + "Skaven, Skilletaudio, Snu, Squirrel Havoc, Waxhead\n" + "\n" +#ifndef NO_VST + "VST PlugIn Technology by Steinberg Media Technologies GmbH\n" + "\n" +#endif +#ifdef MPT_WITH_ASIO + "ASIO Technology by Steinberg Media Technologies GmbH\n" + "\n" +#endif + ); +} + +mpt::ustring GetLicenseString() +{ + return MPT_UTF8( + "The OpenMPT code is licensed under the BSD license." "\n" + "" "\n" + "Copyright (c) 2004-2018, OpenMPT contributors" "\n" + "Copyright (c) 1997-2003, Olivier Lapicque" "\n" + "All rights reserved." "\n" + "" "\n" + "Redistribution and use in source and binary forms, with or without" "\n" + "modification, are permitted provided that the following conditions are met:" "\n" + " * Redistributions of source code must retain the above copyright" "\n" + " notice, this list of conditions and the following disclaimer." "\n" + " * Redistributions in binary form must reproduce the above copyright" "\n" + " notice, this list of conditions and the following disclaimer in the" "\n" + " documentation and/or other materials provided with the distribution." "\n" + " * Neither the name of the OpenMPT project nor the" "\n" + " names of its contributors may be used to endorse or promote products" "\n" + " derived from this software without specific prior written permission." "\n" + "" "\n" + "THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY" "\n" + "EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED" "\n" + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n" + "DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY" "\n" + "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES" "\n" + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;" "\n" + "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND" "\n" + "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" "\n" + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS" "\n" + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n" + ); +} + +} // namespace MptVersion + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.h b/Frameworks/OpenMPT/OpenMPT/common/version.h new file mode 100644 index 000000000..305c02567 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/version.h @@ -0,0 +1,120 @@ +/* + * version.h + * --------- + * Purpose: OpenMPT version handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "FlagSet.h" + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +//Creates version number from version parts that appears in version string. +//For example MAKE_VERSION_NUMERIC(1,17,02,28) gives version number of +//version 1.17.02.28. +#define MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) ((prefix##v0 << 24) | (prefix##v1<<16) | (prefix##v2<<8) | (prefix##v3)) +#define MAKE_VERSION_NUMERIC(v0,v1,v2,v3) (MptVersion::VersionNum(MAKE_VERSION_NUMERIC_HELPER(0x,v0,v1,v2,v3))) + + +namespace MptVersion +{ + + typedef uint32 VersionNum; + + extern const VersionNum num; // e.g. 0x01170208 + extern const char * const str; // e.g "1.17.02.08" + + // Return a OpenMPT version string suitable for file format tags + std::string GetOpenMPTVersionStr(); // e.g. "OpenMPT 1.17.02.08" + + // Returns numerical version value from given version string. + VersionNum ToNum(const std::string &s); + + // Returns version string from given numerical version value. + std::string ToStr(const VersionNum v); + mpt::ustring ToUString(const VersionNum v); + + // Return a version without build number (the last number in the version). + // The current versioning scheme uses this number only for test builds, and it should be 00 for official builds, + // So sometimes it might be wanted to do comparisons without the build number. + VersionNum RemoveBuildNumber(const VersionNum num_); + + // Returns true if a given version number is from a test build, false if it's a release build. + bool IsTestBuild(const VersionNum num_ = MptVersion::num); + + // Return true if this is a debug build with no optimizations + bool IsDebugBuild(); + + struct SourceInfo + { + std::string Url; // svn repository url (or empty string) + int Revision; // svn revision (or 0) + bool IsDirty; // svn working copy is dirty (or false) + bool HasMixedRevisions; // svn working copy has mixed revisions (or false) + bool IsPackage; // source code originates from a packaged version of the source code + std::string Date; // svn date (or empty string) + SourceInfo() : Url(std::string()), Revision(0), IsDirty(false), HasMixedRevisions(false), IsPackage(false) { } + public: + std::string GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string + std::string GetStateString() const; // i.e. "+dirty" or "clean" + }; + SourceInfo GetSourceInfo(); + + // Returns either the URL to download release builds or the URL to download test builds, depending on the current build. + mpt::ustring GetDownloadURL(); + + // Return a string decribing the time of the build process (if built from a svn working copy and tsvn was available during build, otherwise it returns the time version.cpp was last rebuild which could be unreliable as it does not get rebuild every time without tsvn) + std::string GetBuildDateString(); + + // Return a string decribing some of the build features + std::string GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND" + + // Return a string describing the compiler version used for building. + std::string GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01" + + enum Strings + { + StringsNone = 0, + StringVersion = 1<<0, // "1.23.35.45" + StringRevision = 1<<2, // "-r1234+" + StringBitness = 1<<3, // "32 bit" + StringSourceInfo = 1<<4, // "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234 (2016-01-02) +dirty" + StringBuildFlags = 1<<5, // "TEST DEBUG" + StringBuildFeatures = 1<<6, // "NO_VST NO_DSOUND" + }; + MPT_DECLARE_ENUM(Strings) + + // Returns a versions string with the fields selected via @strings. + std::string GetVersionString(FlagSet strings); + + // Returns a pure version string + std::string GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit" + + // Returns a simple version string + std::string GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST" + + // Returns MptVersion::str if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build) + std::string GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG" + + // Returns a URL for the respective keys. Supported keys: "website", "forum", "bugtracker", "updates", "top_picks" + mpt::ustring GetURL(std::string key); + + // Returns a multi-line string containing the full credits for the code base + mpt::ustring GetFullCreditsString(); + + // Returns the OpenMPT license text + mpt::ustring GetLicenseString(); + +} //namespace MptVersion + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h new file mode 100644 index 000000000..58a40b787 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -0,0 +1,30 @@ +/* + * versionNumber.h + * --------------- + * Purpose: OpenMPT version handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +OPENMPT_NAMESPACE_BEGIN + +#define VER_HELPER_STRINGIZE(x) #x +#define VER_STRINGIZE(x) VER_HELPER_STRINGIZE(x) + +//Version definitions. The only thing that needs to be changed when changing version number. +#define VER_MAJORMAJOR 1 +#define VER_MAJOR 27 +#define VER_MINOR 04 +#define VER_MINORMINOR 02 + +//Version string. For example "1.17.02.28" +#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR) + +//Numerical value of the version. +#define MPT_VERSION_NUMERIC MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict new file mode 100644 index 000000000..8761c0956 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict @@ -0,0 +1,304 @@ +669="if" +669="JN" + +amf="ASYLUM Music Format V1.0\x00" +amf="AMF\x0A" + +ams="Extreme" +ams="AMShdr\x1A\x02\x02" + +#dbm="DBM0" +dbm="NAME" +dbm="INFO" +dbm="SONG" +dbm="INST" +dbm="VENV" +dbm="PENV" +dbm="PNAM" +dbm="SMPL" +dbm="DSPE" +#dbm="MPEG" + +digi="DIGI Booster module\x00" + +dmf="DDMF" +dmf="XTRACKER" +dmf="CMSG" +dmf="SEQU" +dmf="SMPI" +dmf="SMPD" +#dmf="SMPJ" +#dmf="ENDE" +#dmf="SETT" + +dsm="RIFF" +dsm="DSMF" + +dtm="D.T." +dtm="S.Q." +#dtm="PATT" +#dtm="INST" +dtm="DAPT" +dtm="DAIT" + +far="FAR\xFE" +far="\x0D\x0A\x1A" + +gdm="GDM\xFE" +gdm="GMFS" + +imf="IM10" +imf="IS10" + +it="IMPM" +it="IMPI" +it="IMPS" +#it="OMPT" +it="PNAM" +it="CNAM" +it="STPM" +it="XTPM" +it="CHBI" +it="FX00" +it="F255" +it="DWRT" +it="PROG" +it="CHFX" + +it="..TD" +it="DTFR" +it=".BPR" +it=".MPR" +it="...C" +it="SnhC" +it="..MT" +it=".MMP" +it=".VWC" +it="VWSL" +it=".APS" +it="VTSV" +it=".VGD" +it="..PR" +it="RSMP" +it="CUES" +it="SWNG" +it=".FSM" +it="AUTH" + +itp=".pti\x03\x01\x00\x00" + +j2b="MUSE" +j2b="\xDE\xAD\xBE\xAF" +j2b="\xDE\xAD\xBA\xBE" +j2b="AMFF" +j2b="AM " +j2b="MAIN" +j2b="INIT" +j2b="ORDR" +j2b="AI " +j2b="AS " + +MDL="DMDL" +# Most chunk IDs are commented out as they are substrings of other dictionary entries +#mdl="IN" +mdl="ME" +#mdl="PA" +#mdl="TR" +mdl="II" +#mdl="VE" +#mdl="PE" +#mdl="FE" +#mdl="IS" +#mdl="SA" + +med="MMD1" + +mo3="MO3\x05" + +# A couple of magic bytes are commented out because they do not modify the loader's behaviour, apart from setting a "made with" string. +mod="M.K." +#mod="M!K!" +mod="M&K!" +mod="N.T." +#mod="FEST" +#mod="NSMS" +#mod="LARD" +mod="OKTA" +#mod="OCTA" +#mod="CD61" +mod="CD81" +#mod="FA08" +mod="FLT8" +mod="EXO8" +# Depending on the byte offset in the file, we generate either a "xCHN" or "xxCH" magic +mod="99CHN" +mod="TDZ8" +ice="MTN\x00" +ice="IT10" +pt36="CMNT" +pt36="PTDT" +sfx="SO31" +# External Startrekker instrument files. +stam="ST1.3 ModuleINFO" +stam="AudioSculpture10" + +mptm="->MPT_ORIGINAL_IT<-" +mptm=".tpm" +mptm="mptm" +mptm="\x89\x08" +mptm="\x8D\x08" +# No structural changes in these format versions +#mptm="\x8E\x08" +#mptm="\x8F\x08" +#mptm="\x90\x08" +mptm="\x91\x08" +mptm="228\x04" + +mt2="MT20" +#mt2="MadTracker 2.0" +mt2="BPM+" +mt2="TFXM" +mt2="TRKS" +mt2="TRKL" +mt2="PATN" +mt2="MSG\x00" +#mt2="PICT" +mt2="SUM\x00" +mt2="VST2" + +mtm="MTM\x10" + +okt="OKTASONG" +okt="CMOD" +okt="SAMP" +okt="SPEE" +okt="SLEN" +okt="PLEN" +okt="PATT" +okt="PBOD" +okt="SBOD" + +plm="PLM\x1A" +plm="PLS\x1A" + +psm="PSM " +psm="FILE" +psm="TITL" +psm="SDFT" +psm="DATE" +psm="OPLH" +psm="PPAN" +psm="DSAM" +psm="DSMP" +psm="MAINSONG" +psm="\x00\xFF\x00\x00\x01\x00" +psm16="PSM\xFE" +psm16="PORD" +psm16="PPAN" +psm16="PSAH" +psm16="PPAT" + +ptm="PTMF" +ptm="\x1A\x03\x02" + +s3m="SCRM" +s3m="SCRS" + +stm="\x1A\x02\x15" + +stp="STP3\x02" + +ult="MAS_UTrack_V004" + +umx="\xC1\x83\x2A\x9E" +umx="music" +umx="sound" + +xm="Extended Module: " +xm="OpenMPT " +#xm="FastTracker v 2.00 " +xm="MilkyTracker " +xm="text" +xm="MIDI" + +it="..OF" +it="LTTP" +it="PTTF" +it="..Fd" +it="..VG" +it="...P" +it="..EV" +it="..EP" +it=".EiP" +it=".SLV" +it=".ELV" +it=".BSV" +it=".ESV" +it=".SLP" +it=".ELP" +it=".BSP" +it=".ESP" +it="SLiP" +it="ELiP" +it="BSiP" +it="ESiP" +it=".ANN" +it=".TCD" +it=".AND" +it="..SP" +it="..SV" +it=".CFI" +it=".RFI" +it="..BM" +it="..PM" +it="..CM" +it=".SPP" +it=".CPP" +it=".[PV" +it=".[PP" +it="[PiP" +it=".[EV" +it=".[EP" +it="[EiP" +it="..[K" +it="..[n" +it=".[MN" +it=".[nf" +it=".PiM" +it="..RV" +it="...R" +it="..SC" +it="..SR" +it="..MF" +it="HEVP" +it="HOVP" +it="NREP" +it="NREA" +it="NREV" +it="GLFP" +it="GLFA" +it="GLFV" +it="DWPM" + +mmcmp="ziRCONia\x0e\x00" + +xpk="XPKF\x00\x10\x00\x00SQSH" + +pp20="PP20" + +plugin_chorus="OMXD\x9C\x62\xE6\xEF" +plugin_compressor="OMXD\x79\x1F\x01\xEF" +plugin_distortion="OMXD\x90\x4C\x11\xEF" +plugin_echo="OMXD\x2C\x93\x3E\xEF" +plugin_flanger="OMXD\x92\x3D\xCA\xEF" +plugin_gargle="OMXD\x10\x82\xFD\xDA" +plugin_i3dl2reverb="OMXD\x71\x5E\x98\xEF" +plugin_parameq="OMXD\x89\xED\x0C\x12" +plugin_wavesreverb="OMDX\x68\x02\xFC\x87" +plugin_lfo="OMPTLFO " +plugin_dbproecho="DBM0Echo" + +midi="MThd\x00\x00\x00\x06\x00\x01\x00\x01\x01\xE0MTrk" + +wave="WAVEfmt " +wave="data" diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/build.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/build.sh new file mode 100755 index 000000000..772aaf5df --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +cd "${0%/*}" +cd ../.. +AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 USE_MINIMP3=1 diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh new file mode 100755 index 000000000..0820ff7a8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +cd "${0%/*}" +. ./fuzz-settings.sh + +# Create tmpfs for storing temporary fuzzing data +mkdir $FUZZING_TEMPDIR +sudo mount -t tmpfs -o size=200M none $FUZZING_TEMPDIR +rm -rf $FUZZING_TEMPDIR/bin +mkdir $FUZZING_TEMPDIR/bin +cp ../../bin/* $FUZZING_TEMPDIR/bin/ +rm $FUZZING_TEMPDIR/bin/libopenmpt.so +cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so +cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so.1 + +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-settings.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-settings.sh new file mode 100755 index 000000000..f8717d45e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-settings.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Input data for fuzzer +# If you run the fuzzer for the first time, specify a directory with some input +# files for the fuzzer, e.g. +# FUZZING_INPUT="-i /home/foo/testcases/" +# If you want to continue fuzzing using the previous findings, use: +# FUZZING_INPUT=-i- +FUZZING_INPUT=-i- + +# Directory to place temporary fuzzing data into +FUZZING_TEMPDIR=~/libopenmpt-fuzzing-temp +# Directory to store permanent fuzzing data (e.g. found crashes) into +FUZZING_FINDINGS_DIR=~/libopenmpt-fuzzing +# Fuzzer timeout in ms, + = don't abort on timeout +FUZZING_TIMEOUT=5000+ +# Path to afl-fuzz binary +AFL_BIN=afl/afl-fuzz \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-slave.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-slave.sh new file mode 100755 index 000000000..b57fe359e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-slave.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +cd "${0%/*}" +. ./fuzz-settings.sh + +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02 diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c new file mode 100644 index 000000000..8fb7ee7aa --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c @@ -0,0 +1,54 @@ +/* + * fuzz.c + * ------ + * Purpose: Tiny libopenmpt user to be used by fuzzing tools + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 512 +#define SAMPLERATE 22050 + +static int16_t buffer[BUFFERSIZE]; + +int main( int argc, char * argv[] ) { + static FILE * file = NULL; + static openmpt_module * mod = NULL; + static size_t count = 0; + static int i = 0; + (void)argc; +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + file = fopen( argv[1], "rb" ); + mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL ); + fclose( file ); + if ( mod == NULL ) return 1; + openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" ); + /* render about a second of the module for fuzzing the actual mix routines */ + for(; i < 50; i++) { + count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + if ( count == 0 ) { + break; + } + } + /* fuzz string-related stuff */ + openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) ); + openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) ); + openmpt_module_destroy( mod ); + return 0; +} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh new file mode 100755 index 000000000..8a83aadf9 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +cd "${0%/*}" +rm afl-latest.tgz +wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz || exit +tar -xzvf afl-latest.tgz +rm afl-latest.tgz +cd afl-* +make || exit +cd llvm_mode +# may need to prepend LLVM_CONFIG=/usr/bin/llvm-config-3.8 or similar, depending on the system +make || exit +cd ../libdislocator +make || exit +cd ../.. +rm -rf afl +mv afl-* afl \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/readme.md b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/readme.md new file mode 100644 index 000000000..f63ef9021 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/readme.md @@ -0,0 +1,46 @@ +libopenmpt fuzz suite +===================== + +In this directory, you can find the necessary tools for fuzzing libopenmpt with +the American Fuzzy Lop fuzzer (afl-fuzz). + +Contents: + +* `all_formats.dict`: A dictionary containing magic bytes from all supported + module formats to make the life of the fuzzer a bit easier. +* `fuzz-master.sh`: Script to launch the main fuzzing process. If you want to + use just one fuzzer instance, run this one. +* `fuzz-slave.sh`: Script to launch the secondary fuzzing process. It is + recommended to run at least two fuzzer instances, as the deterministic and + random fuzz mode have been found to complement each other really well. +* `fuzz-settings.sh`: Set up your preferences and afl settings here before the + first run. +* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt. +* `get-afl.sh`: A simple script to obtain the latest version of the fuzzer. + +Prerequisites +============= +* afl from http://lcamtuf.coredump.cx/afl/ - the makefile expects this to be + installed in `contrib/fuzzing/afl`, as it is automatically done by the + `get-afl.sh` install script. +* Clang with LLVM dev headers (llvm-config needs to be installed). + afl also works with gcc, but our makefile has been set up to make use of afl's + faster LLVM mode. + +How to use +========== +* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use `make` + to build afl-fuzz, `cd llvm_mode`, `make` to build afl-clang-fast. + If building with either option fails because `llvm-config` cannot be found, + try prepending `LLVM_CONFIG=/usr/bin/llvm-config-3.8` or similar, and read the + afl manual. +* Build libopenmpt with the `build.sh` script in this directory. +* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to + specify the input directory for first use. + The default setup mounts a tmpfs folder for all temporary files. You may + change this behaviour if you do not have root privileges. +* Run `fuzz-master.sh` for the first (deterministic) instance of afl-fuzz. +* For a "slave" instance to run on another core, run `fuzz-slave.sh`. +* If you want to make use of even more cores, make a copy of `fuzz-slave.sh` + and adjust "infile02" / "fuzzer02" to "infile03" / "fuzzer03" (they need to be + unique) \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c b/Frameworks/OpenMPT/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c new file mode 100644 index 000000000..71fc3d9dd --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c @@ -0,0 +1,109 @@ +/* + * libopenmpt_bass.c + * ----------------- + * Purpose: Example of how to use libopenmpt with the BASS audio library. + * Notes : BASS from un4seen (http://www.un4seen.com/bass.html) is used for audio output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_bass SOMEMODULE + */ + +#include +#include +#if ( defined( _WIN32 ) || defined( WIN32 ) ) +#include +#define sleep(n) Sleep(n) +#else +#include +#endif + +#include +#include +#include + +#define SAMPLERATE 48000 + +DWORD CALLBACK StreamProc(HSTREAM handle, void *buffer, DWORD length, void *user) +{ + // length is in bytes, but libopenmpt wants samples => divide by number of channels (2) and size of a sample (float = 4) + // same for return value. + size_t count = openmpt_module_read_interleaved_float_stereo( (openmpt_module *)user, SAMPLERATE, length / (2 * sizeof(float)), (float *)buffer ); + count *= (2 * sizeof(float)); + // Reached end of stream? + if(count < length) count |= BASS_STREAMPROC_END; + return (DWORD)count; +} + + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + FILE * file = 0; + openmpt_module * mod = 0; + HSTREAM stream = 0; + int result = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + return 1; + } + + if ( HIWORD( BASS_GetVersion() ) !=BASSVERSION ) { + fprintf( stderr, "Error: Wrong BASS version\n" ); + return 1; + } + + if ( !BASS_Init( -1, SAMPLERATE, 0, NULL, NULL ) ) { + fprintf( stderr, "Error: Cannot initialize BASS (error %d)\n", BASS_ErrorGetCode() ); + return 1; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + result = 1; + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + result = 1; + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + result = 1; + goto fail; + } + + mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL ); + if ( !mod ) { + fprintf( stderr, "Error: %s\n", "openmpt_module_create() failed." ); + goto fail; + } + + // Create a "pull" channel. BASS will call StreamProc whenever the channel needs new data to be decoded. + stream = BASS_StreamCreate(SAMPLERATE, 2, BASS_SAMPLE_FLOAT, StreamProc, mod); + BASS_ChannelPlay(stream, FALSE); + + while ( BASS_ChannelIsActive( stream ) ) { + // Do something useful here + sleep(1); + } + + BASS_StreamFree( stream ); + openmpt_module_destroy( mod ); + +fail: + BASS_Free(); + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/.clang-format b/Frameworks/OpenMPT/OpenMPT/examples/.clang-format new file mode 100644 index 000000000..1f7e170c0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/.clang-format @@ -0,0 +1,63 @@ +AccessModifierOffset: -2 +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IndentCaseLabels: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +Language: Cpp +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 3 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 60 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Middle +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: true +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c new file mode 100644 index 000000000..550f2d030 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c @@ -0,0 +1,203 @@ +/* + * libopenmpt_example_c.c + * ---------------------- + * Purpose: libopenmpt C API example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c SOMEMODULE + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + FILE * file = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + PaError pa_error = paNoError; + int pa_initialized = 0; + PaStream * stream = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + openmpt_module_set_error_func( mod, NULL, NULL ); + + pa_error = Pa_Initialize(); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); + goto fail; + } + pa_initialized = 1; + + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + if ( !stream ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + + pa_error = Pa_StartStream( stream ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + if ( pa_error == paOutputUnderflowed ) { + pa_error = paNoError; + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( stream ) { + if ( Pa_IsStreamActive( stream ) == 1 ) { + Pa_StopStream( stream ); + } + Pa_CloseStream( stream ); + stream = 0; + } + + if ( pa_initialized ) { + Pa_Terminate(); + pa_initialized = 0; + } + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c new file mode 100644 index 000000000..6a4057505 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c @@ -0,0 +1,292 @@ +/* + * libopenmpt_example_c_mem.c + * -------------------------- + * Purpose: libopenmpt C API example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_mem SOMEMODULE + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +typedef struct blob_t { + size_t size; + void * data; +} blob_t; + +static void free_blob( blob_t * blob ) { + if ( blob ) { + if ( blob->data ) { + free( blob->data ); + blob->data = 0; + } + blob->size = 0; + free( blob ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +static blob_t * load_file( const wchar_t * filename ) { +#else +static blob_t * load_file( const char * filename ) { +#endif + blob_t * result = 0; + + blob_t * blob = 0; + FILE * file = 0; + long tell_result = 0; + + blob = malloc( sizeof( blob_t ) ); + if ( !blob ) { + goto fail; + } + memset( blob, 0, sizeof( blob_t ) ); + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( filename, L"rb" ); +#else + file = fopen( filename, "rb" ); +#endif + if ( !file ) { + goto fail; + } + + if ( fseek( file, 0, SEEK_END ) != 0 ) { + goto fail; + } + + tell_result = ftell( file ); + if ( tell_result < 0 ) { + goto fail; + } + if ( (unsigned long)tell_result > SIZE_MAX ) { + goto fail; + } + blob->size = (size_t)tell_result; + + if ( fseek( file, 0, SEEK_SET ) != 0 ) { + goto fail; + } + + blob->data = malloc( blob->size ); + if ( !blob->data ) { + goto fail; + } + memset( blob->data, 0, blob->size ); + + if ( fread( blob->data, 1, blob->size, file ) != blob->size ) { + goto fail; + } + + result = blob; + blob = 0; + goto cleanup; + +fail: + + result = 0; + +cleanup: + + if ( blob ) { + free_blob( blob ); + blob = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + blob_t * blob = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + PaError pa_error = paNoError; + int pa_initialized = 0; + PaStream * stream = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } +#endif + blob = load_file( argv[1] ); + if ( !blob ) { + fprintf( stderr, "Error: %s\n", "load_file() failed." ); + goto fail; + } + + mod = openmpt_module_create_from_memory2( blob->data, blob->size, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create_from_memory2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + pa_error = Pa_Initialize(); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); + goto fail; + } + pa_initialized = 1; + + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + if ( !stream ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + + pa_error = Pa_StartStream( stream ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + if ( pa_error == paOutputUnderflowed ) { + pa_error = paNoError; + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( stream ) { + if ( Pa_IsStreamActive( stream ) == 1 ) { + Pa_StopStream( stream ); + } + Pa_CloseStream( stream ); + stream = 0; + } + + if ( pa_initialized ) { + Pa_Terminate(); + pa_initialized = 0; + } + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( blob ) { + free_blob( blob ); + blob = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_pipe.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_pipe.c new file mode 100644 index 000000000..201a914c9 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_pipe.c @@ -0,0 +1,152 @@ +/* + * libopenmpt_example_c_pipe.c + * --------------------------- + * Purpose: libopenmpt C API simple pipe example + * Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: cat SOMEMODULE | libopenmpt_example_c_pipe | aplay --file-type raw --format=dat + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +static ssize_t xwrite( int fd, const void * buffer, size_t size ) { + size_t written = 0; + ssize_t retval = 0; + while ( written < size ) { + retval = write( fd, (const char *)buffer + written, size - written ); + if ( retval < 0 ) { + if ( errno != EINTR ) { + break; + } + retval = 0; + } + written += retval; + } + return written; +} + +static int16_t buffer[BUFFERSIZE * 2]; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + size_t written = 0; + (void)argv; + + if ( argc != 1 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_pipe' and connect stdin and stdout." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_fd_callbacks(), (void*)(uintptr_t)STDIN_FILENO, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) ); + if ( written == 0 ) { + fprintf( stderr, "Error: %s\n", "write() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_probe.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_probe.c new file mode 100644 index 000000000..e4fbabcd3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_probe.c @@ -0,0 +1,181 @@ +/* + * libopenmpt_example_c_probe.c + * ---------------------------- + * Purpose: libopenmpt C API probing example + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_probe SOMEMODULE ... + * Returns 0 on successful probing for all files. + * Returns 1 on failed probing for 1 or more files. + * Returns 2 on error. + */ + +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY 1 +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT 2 + +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY + +#include +#include +#include +#include +#include + +#include +#include + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + + if ( message ) { + fprintf( stderr, "%s\n", message ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +static int probe_file( const wchar_t * filename ) { +#else +static int probe_file( const char * filename ) { +#endif + + int result = 0; + int mod_err = OPENMPT_ERROR_OK; + FILE * file = NULL; + +#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) + int result_binary = 0; + int probe_file_header_result = OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE; + const char * probe_file_header_result_str = NULL; +#endif +#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) + double probability = 0.0; +#endif + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( filename ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); + goto fail; + } +#else + if ( strlen( filename ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); + goto fail; + } +#endif + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( filename, L"rb" ); +#else + file = fopen( filename, "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + #if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) + probe_file_header_result = openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); + probe_file_header_result_str = NULL; + result_binary = 0; + switch ( probe_file_header_result ) { + case OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS: + probe_file_header_result_str = "Success "; + result_binary = 1; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE: + probe_file_header_result_str = "Failure "; + result_binary = 0; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA: + probe_file_header_result_str = "WantMoreData"; + result_binary = 0; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR: + result_binary = 0; + fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); + goto fail; + break; + default: + result_binary = 0; + fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); + goto fail; + break; + } +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + fprintf( stdout, "%s - %ls\n", probe_file_header_result_str, filename ); +#else + fprintf( stdout, "%s - %s\n", probe_file_header_result_str, filename ); +#endif + if ( result_binary ) { + result = 0; + } else { + result = 1; + } + #elif ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) + probability = openmpt_could_open_probability2( openmpt_stream_get_file_callbacks(), file, 0.25, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + fprintf( stdout, "%s: %f - %ls\n", "Result", probability, filename ); +#else + fprintf( stdout, "%s: %f - %s\n", "Result", probability, filename ); +#endif + if ( probability >= 0.5 ) { + result = 0; + } else { + result = 1; + } + #else + #error "LIBOPENMPT_EXAMPLE_PROBE_RESULT is wrong" + #endif + + goto cleanup; + +fail: + + result = 2; + +cleanup: + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int global_result = 0; + + if ( argc <= 1 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE ...'." ); + goto fail; + } + + for ( int i = 1; i < argc; ++i ) { + int result = probe_file( argv[i] ); + if ( result > global_result ) { + global_result = result; + } + } + + goto cleanup; + +fail: + + global_result = 2; + +cleanup: + + return global_result; + +} + diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_stdout.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_stdout.c new file mode 100644 index 000000000..1488737c3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_stdout.c @@ -0,0 +1,176 @@ +/* + * libopenmpt_example_c_stdout.c + * ----------------------------- + * Purpose: libopenmpt C API simple example + * Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_stdout SOMEMODULE | aplay --file-type raw --format=dat + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +static ssize_t xwrite( int fd, const void * buffer, size_t size ) { + size_t written = 0; + ssize_t retval = 0; + while ( written < size ) { + retval = write( fd, (const char *)buffer + written, size - written ); + if ( retval < 0 ) { + if ( errno != EINTR ) { + break; + } + retval = 0; + } + written += retval; + } + return written; +} + +static int16_t buffer[BUFFERSIZE * 2]; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + FILE * file = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + size_t written = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) ); + if ( written == 0 ) { + fprintf( stderr, "Error: %s\n", "write() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c new file mode 100644 index 000000000..0419ac3b0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c @@ -0,0 +1,65 @@ +/* + * libopenmpt_example_c_unsafe.c + * ----------------------------- + * Purpose: libopenmpt C API simplified example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_unsafe SOMEMODULE + * CAUTION: This simple example does no error cheking at all. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + FILE * file = 0; + openmpt_module * mod = 0; + size_t count = 0; + PaStream * stream = 0; + (void)argc; +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( argv[1], L"rb" ); +#else + file = fopen( argv[1], "rb" ); +#endif + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); + fclose( file ); + Pa_Initialize(); + Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + Pa_StartStream( stream ); + while ( 1 ) { + count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + if ( count == 0 ) { + break; + } + Pa_WriteStream( stream, buffers, (unsigned long)count ); + } + Pa_StopStream( stream ); + Pa_CloseStream( stream ); + Pa_Terminate(); + openmpt_module_destroy( mod ); + return 0; +} diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp new file mode 100644 index 000000000..73c5970fc --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp @@ -0,0 +1,78 @@ +/* + * libopenmpt_example_cxx.cpp + * -------------------------- + * Purpose: libopenmpt C++ API example + * Notes : PortAudio C++ is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_cxx SOMEMODULE + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +#if defined( __GNUC__ ) +// mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see ). +extern "C" int wmain( int argc, wchar_t * argv[] ) { +#else +int wmain( int argc, wchar_t * argv[] ) { +#endif +#else +int main( int argc, char * argv[] ) { +#endif + try { + if ( argc != 2 ) { + throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); + } + const std::size_t buffersize = 480; +#if defined( LIBOPENMPT_QUIRK_NO_CSTDINT ) + const openmpt::std::int32_t samplerate = 48000; +#else + const std::int32_t samplerate = 48000; +#endif + std::vector left( buffersize ); + std::vector right( buffersize ); + const float * const buffers[2] = { left.data(), right.data() }; + std::ifstream file( argv[1], std::ios::binary ); + openmpt::module mod( file ); + portaudio::AutoSystem portaudio_initializer; + portaudio::System & portaudio = portaudio::System::instance(); + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + portaudio::BlockingStream stream( stream_parameters ); + stream.start(); + while ( true ) { + std::size_t count = mod.read( samplerate, buffersize, left.data(), right.data() ); + if ( count == 0 ) { + break; + } + try { + stream.write( buffers, static_cast( count ) ); + } catch ( const portaudio::PaException & pa_exception ) { + if ( pa_exception.paError() != paOutputUnderflowed ) { + throw; + } + } + } + stream.stop(); + } catch ( const std::bad_alloc & ) { + std::cerr << "Error: " << std::string( "out of memory" ) << std::endl; + return 1; + } catch ( const std::exception & e ) { + std::cerr << "Error: " << std::string( e.what() ? e.what() : "unknown error" ) << std::endl; + return 1; + } + return 0; +} diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt new file mode 100644 index 000000000..1e0991447 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt new file mode 100644 index 000000000..4d536900e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt @@ -0,0 +1,10 @@ +minimp3 version as of 2017-04-25 from +http://keyj.emphy.de/files/projects/minimp3.tar.gz . +The following changes have been made: +- mp3_create() declaration has been fixed. +- GET_DATA() has been rewritten to avoid unaligned access warnings. +- Signed/unsigned comparison warnings have been fixed. +- Modifications have been marked with // OpenMPT +- Obviously, unnecessary folders and files have been removed. +- For building, premake is used to generate Visual Studio project files. + See ../build/premake/ for details. diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h new file mode 100644 index 000000000..45fca6092 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h @@ -0,0 +1,184 @@ +// a libc replacement (more or less) for the Microsoft Visual C compiler +// this file is public domain -- do with it whatever you want! +#ifndef __LIBC_H_INCLUDED__ +#define __LIBC_H_INCLUDED__ + +// check if minilibc is required +#ifndef NEED_MINILIBC + #ifndef NOLIBC + #define NEED_MINILIBC 0 + #else + #define NEED_MINILIBC 1 + #endif +#endif + +#ifdef _MSC_VER + #define INLINE __forceinline + #define FASTCALL __fastcall + #ifdef NOLIBC + #ifdef MAIN_PROGRAM + int _fltused=0; + #endif + #endif +#else + #define INLINE inline + #define FASTCALL __attribute__((fastcall)) + #include +#endif + +#ifdef _WIN32 + #ifndef WIN32 + #define WIN32 + #endif +#endif +#ifdef WIN32 + #include +#endif + +#if !NEED_MINILIBC + #include + #include + #include +#endif +#include + +#ifndef __int8_t_defined + #define __int8_t_defined + typedef unsigned char uint8_t; + typedef signed char int8_t; + typedef unsigned short uint16_t; + typedef signed short int16_t; + typedef unsigned int uint32_t; + typedef signed int int32_t; + #ifdef _MSC_VER + typedef unsigned __int64 uint64_t; + typedef signed __int64 int64_t; + #else + typedef unsigned long long uint64_t; + typedef signed long long int64_t; + #endif +#endif + +#ifndef NULL + #define NULL 0 +#endif + +#ifndef M_PI + #define M_PI 3.14159265358979 +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if NEED_MINILIBC + +static INLINE void libc_memset(void *dest, int value, int count) { + if (!count) return; + __asm { + cld + mov edi, dest + mov eax, value + mov ecx, count + rep stosb + } +} + +static INLINE void libc_memcpy(void *dest, const void *src, int count) { + if (!count) return; + __asm { + cld + mov esi, src + mov edi, dest + mov ecx, count + rep movsb + } +} + +#define libc_memmove libc_memcpy + +static INLINE void* libc_malloc(int size) { + return (void*) LocalAlloc(LMEM_FIXED, size); +} + +static INLINE void* libc_calloc(int size, int nmemb) { + return (void*) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, size * nmemb); +} + +static INLINE void* libc_realloc(void* old, int size) { + int oldsize = (int) LocalSize((HLOCAL) old); + void *mem; + if (size <= oldsize) return old; + mem = LocalAlloc(LMEM_FIXED, size); + libc_memcpy(mem, old, oldsize); + LocalFree((HLOCAL) old); + return mem; +} + +static INLINE void libc_free(void *mem) { + LocalFree((HLOCAL) mem); +} + +static INLINE double libc_frexp(double x, int *e) { + double res = -9999.999; + unsigned __int64 i = *(unsigned __int64*)(&x); + if (!(i & 0x7F00000000000000UL)) { + *e = 0; + return x; + } + *e = ((i << 1) >> 53) - 1022; + i &= 0x800FFFFFFFFFFFFFUL; + i |= 0x3FF0000000000000UL; + return *(double*)(&i) * 0.5; +} + +static INLINE double __declspec(naked) libc_exp(double x) { __asm { + fldl2e + fld qword ptr [esp+4] + fmul + fst st(1) + frndint + fxch + fsub st(0), st(1) + f2xm1 + fld1 + fadd + fscale + ret +} } + + +static INLINE double __declspec(naked) libc_pow(double b, double e) { __asm { + fld qword ptr [esp+12] + fld qword ptr [esp+4] + fyl2x +// following is a copy of libc_exp: + fst st(1) + frndint + fxch + fsub st(0), st(1) + f2xm1 + fld1 + fadd + fscale + ret +} } + + + +#else // NEED_MINILIBC == 0 + +#define libc_malloc malloc +#define libc_calloc calloc +#define libc_realloc realloc +#define libc_free free + +#define libc_memset memset +#define libc_memcpy memcpy +#define libc_memmove memmove + +#define libc_frexp frexp +#define libc_exp exp +#define libc_pow pow + +#endif // NEED_MINILIBC + +#endif//__LIBC_H_INCLUDED__ diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c new file mode 100644 index 000000000..4484cb73d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c @@ -0,0 +1,2692 @@ +/* + * MPEG Audio Layer III decoder + * Copyright (c) 2001, 2002 Fabrice Bellard, + * (c) 2007 Martin J. Fiedler + * + * This file is a stripped-down version of the MPEG Audio decoder from + * the FFmpeg libavcodec library. + * + * FFmpeg and minimp3 are free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg and minimp3 are distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libc.h" +#include "minimp3.h" + +#define MP3_FRAME_SIZE 1152 +#define MP3_MAX_CODED_FRAME_SIZE 1792 +#define MP3_MAX_CHANNELS 2 +#define SBLIMIT 32 + +#define MP3_STEREO 0 +#define MP3_JSTEREO 1 +#define MP3_DUAL 2 +#define MP3_MONO 3 + +#define SAME_HEADER_MASK \ + (0xffe00000 | (3 << 17) | (0xf << 12) | (3 << 10) | (3 << 19)) + +#define FRAC_BITS 15 +#define WFRAC_BITS 14 + +#define OUT_MAX (32767) +#define OUT_MIN (-32768) +#define OUT_SHIFT (WFRAC_BITS + FRAC_BITS - 15) + +#define MODE_EXT_MS_STEREO 2 +#define MODE_EXT_I_STEREO 1 + +#define FRAC_ONE (1 << FRAC_BITS) +#define FIX(a) ((int)((a) * FRAC_ONE)) +#define FIXR(a) ((int)((a) * FRAC_ONE + 0.5)) +#define FRAC_RND(a) (((a) + (FRAC_ONE/2)) >> FRAC_BITS) +#define FIXHR(a) ((int)((a) * (1LL<<32) + 0.5)) + +#if !(defined(_MSC_VER) && defined(_M_IX86)) // OpenMPT + #define MULL(a,b) (((int64_t)(a) * (int64_t)(b)) >> FRAC_BITS) + #define MULH(a,b) (((int64_t)(a) * (int64_t)(b)) >> 32) +#else + static INLINE int MULL(int a, int b) { + int res; + __asm { + mov eax, a + imul b + shr eax, 15 + shl edx, 17 + or eax, edx + mov res, eax + } + return res; + } + static INLINE int MULH(int a, int b) { + int res; + __asm { + mov eax, a + imul b + mov res, edx + } + return res; + } +#endif +#define MULS(ra, rb) ((ra) * (rb)) + +#define ISQRT2 FIXR(0.70710678118654752440) + +#define HEADER_SIZE 4 +#define BACKSTEP_SIZE 512 +#define EXTRABYTES 24 + +#define VLC_TYPE int16_t + +//////////////////////////////////////////////////////////////////////////////// + +struct _granule; + +typedef struct _bitstream { + const uint8_t *buffer, *buffer_end; + int index; + int size_in_bits; +} bitstream_t; + +typedef struct _vlc { + int bits; + VLC_TYPE (*table)[2]; ///< code, bits + int table_size, table_allocated; +} vlc_t; + +typedef struct _mp3_context { + uint8_t last_buf[2*BACKSTEP_SIZE + EXTRABYTES]; + int last_buf_size; + int frame_size; + uint32_t free_format_next_header; + int error_protection; + int sample_rate; + int sample_rate_index; + int bit_rate; + bitstream_t gb; + bitstream_t in_gb; + int nb_channels; + int mode; + int mode_ext; + int lsf; + int16_t synth_buf[MP3_MAX_CHANNELS][512 * 2]; + int synth_buf_offset[MP3_MAX_CHANNELS]; + int32_t sb_samples[MP3_MAX_CHANNELS][36][SBLIMIT]; + int32_t mdct_buf[MP3_MAX_CHANNELS][SBLIMIT * 18]; + int dither_state; +} mp3_context_t; + +typedef struct _granule { + uint8_t scfsi; + int part2_3_length; + int big_values; + int global_gain; + int scalefac_compress; + uint8_t block_type; + uint8_t switch_point; + int table_select[3]; + int subblock_gain[3]; + uint8_t scalefac_scale; + uint8_t count1table_select; + int region_size[3]; + int preflag; + int short_start, long_end; + uint8_t scale_factors[40]; + int32_t sb_hybrid[SBLIMIT * 18]; +} granule_t; + +typedef struct _huff_table { + int xsize; + const uint8_t *bits; + const uint16_t *codes; +} huff_table_t; + +static vlc_t huff_vlc[16]; +static vlc_t huff_quad_vlc[2]; +static uint16_t band_index_long[9][23]; +#define TABLE_4_3_SIZE (8191 + 16)*4 +static int8_t *table_4_3_exp; +static uint32_t *table_4_3_value; +static uint32_t exp_table[512]; +static uint32_t expval_table[512][16]; +static int32_t is_table[2][16]; +static int32_t is_table_lsf[2][2][16]; +static int32_t csa_table[8][4]; +static float csa_table_float[8][4]; +static int32_t mdct_win[8][36]; +static int16_t window[512]; + +//////////////////////////////////////////////////////////////////////////////// + +static const uint16_t mp3_bitrate_tab[2][15] = { + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160} +}; + +static const uint16_t mp3_freq_tab[3] = { 44100, 48000, 32000 }; + +static const int32_t mp3_enwindow[257] = { + 0, -1, -1, -1, -1, -1, -1, -2, + -2, -2, -2, -3, -3, -4, -4, -5, + -5, -6, -7, -7, -8, -9, -10, -11, + -13, -14, -16, -17, -19, -21, -24, -26, + -29, -31, -35, -38, -41, -45, -49, -53, + -58, -63, -68, -73, -79, -85, -91, -97, + -104, -111, -117, -125, -132, -139, -147, -154, + -161, -169, -176, -183, -190, -196, -202, -208, + 213, 218, 222, 225, 227, 228, 228, 227, + 224, 221, 215, 208, 200, 189, 177, 163, + 146, 127, 106, 83, 57, 29, -2, -36, + -72, -111, -153, -197, -244, -294, -347, -401, + -459, -519, -581, -645, -711, -779, -848, -919, + -991, -1064, -1137, -1210, -1283, -1356, -1428, -1498, + -1567, -1634, -1698, -1759, -1817, -1870, -1919, -1962, + -2001, -2032, -2057, -2075, -2085, -2087, -2080, -2063, + 2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535, + 1414, 1280, 1131, 970, 794, 605, 402, 185, + -45, -288, -545, -814, -1095, -1388, -1692, -2006, + -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, + -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, + -7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585, + -9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750, + -9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134, + 6574, 5959, 5288, 4561, 3776, 2935, 2037, 1082, + 70, -998, -2122, -3300, -4533, -5818, -7154, -8540, + -9975,-11455,-12980,-14548,-16155,-17799,-19478,-21189, +-22929,-24694,-26482,-28289,-30112,-31947,-33791,-35640, +-37489,-39336,-41176,-43006,-44821,-46617,-48390,-50137, +-51853,-53534,-55178,-56778,-58333,-59838,-61289,-62684, +-64019,-65290,-66494,-67629,-68692,-69679,-70590,-71420, +-72169,-72835,-73415,-73908,-74313,-74630,-74856,-74992, + 75038, +}; + +static const uint8_t slen_table[2][16] = { + { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 }, + { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 }, +}; + +static const uint8_t lsf_nsf_table[6][3][4] = { + { { 6, 5, 5, 5 }, { 9, 9, 9, 9 }, { 6, 9, 9, 9 } }, + { { 6, 5, 7, 3 }, { 9, 9, 12, 6 }, { 6, 9, 12, 6 } }, + { { 11, 10, 0, 0 }, { 18, 18, 0, 0 }, { 15, 18, 0, 0 } }, + { { 7, 7, 7, 0 }, { 12, 12, 12, 0 }, { 6, 15, 12, 0 } }, + { { 6, 6, 6, 3 }, { 12, 9, 9, 6 }, { 6, 12, 9, 6 } }, + { { 8, 8, 5, 0 }, { 15, 12, 9, 0 }, { 6, 18, 9, 0 } }, +}; + +static const uint16_t mp3_huffcodes_1[4] = { + 0x0001, 0x0001, 0x0001, 0x0000, +}; + +static const uint8_t mp3_huffbits_1[4] = { + 1, 3, 2, 3, +}; + +static const uint16_t mp3_huffcodes_2[9] = { + 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, 0x0002, + 0x0000, +}; + +static const uint8_t mp3_huffbits_2[9] = { + 1, 3, 6, 3, 3, 5, 5, 5, + 6, +}; + +static const uint16_t mp3_huffcodes_3[9] = { + 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0002, + 0x0000, +}; + +static const uint8_t mp3_huffbits_3[9] = { + 2, 2, 6, 3, 2, 5, 5, 5, + 6, +}; + +static const uint16_t mp3_huffcodes_5[16] = { + 0x0001, 0x0002, 0x0006, 0x0005, 0x0003, 0x0001, 0x0004, 0x0004, + 0x0007, 0x0005, 0x0007, 0x0001, 0x0006, 0x0001, 0x0001, 0x0000, +}; + +static const uint8_t mp3_huffbits_5[16] = { + 1, 3, 6, 7, 3, 3, 6, 7, + 6, 6, 7, 8, 7, 6, 7, 8, +}; + +static const uint16_t mp3_huffcodes_6[16] = { + 0x0007, 0x0003, 0x0005, 0x0001, 0x0006, 0x0002, 0x0003, 0x0002, + 0x0005, 0x0004, 0x0004, 0x0001, 0x0003, 0x0003, 0x0002, 0x0000, +}; + +static const uint8_t mp3_huffbits_6[16] = { + 3, 3, 5, 7, 3, 2, 4, 5, + 4, 4, 5, 6, 6, 5, 6, 7, +}; + +static const uint16_t mp3_huffcodes_7[36] = { + 0x0001, 0x0002, 0x000a, 0x0013, 0x0010, 0x000a, 0x0003, 0x0003, + 0x0007, 0x000a, 0x0005, 0x0003, 0x000b, 0x0004, 0x000d, 0x0011, + 0x0008, 0x0004, 0x000c, 0x000b, 0x0012, 0x000f, 0x000b, 0x0002, + 0x0007, 0x0006, 0x0009, 0x000e, 0x0003, 0x0001, 0x0006, 0x0004, + 0x0005, 0x0003, 0x0002, 0x0000, +}; + +static const uint8_t mp3_huffbits_7[36] = { + 1, 3, 6, 8, 8, 9, 3, 4, + 6, 7, 7, 8, 6, 5, 7, 8, + 8, 9, 7, 7, 8, 9, 9, 9, + 7, 7, 8, 9, 9, 10, 8, 8, + 9, 10, 10, 10, +}; + +static const uint16_t mp3_huffcodes_8[36] = { + 0x0003, 0x0004, 0x0006, 0x0012, 0x000c, 0x0005, 0x0005, 0x0001, + 0x0002, 0x0010, 0x0009, 0x0003, 0x0007, 0x0003, 0x0005, 0x000e, + 0x0007, 0x0003, 0x0013, 0x0011, 0x000f, 0x000d, 0x000a, 0x0004, + 0x000d, 0x0005, 0x0008, 0x000b, 0x0005, 0x0001, 0x000c, 0x0004, + 0x0004, 0x0001, 0x0001, 0x0000, +}; + +static const uint8_t mp3_huffbits_8[36] = { + 2, 3, 6, 8, 8, 9, 3, 2, + 4, 8, 8, 8, 6, 4, 6, 8, + 8, 9, 8, 8, 8, 9, 9, 10, + 8, 7, 8, 9, 10, 10, 9, 8, + 9, 9, 11, 11, +}; + +static const uint16_t mp3_huffcodes_9[36] = { + 0x0007, 0x0005, 0x0009, 0x000e, 0x000f, 0x0007, 0x0006, 0x0004, + 0x0005, 0x0005, 0x0006, 0x0007, 0x0007, 0x0006, 0x0008, 0x0008, + 0x0008, 0x0005, 0x000f, 0x0006, 0x0009, 0x000a, 0x0005, 0x0001, + 0x000b, 0x0007, 0x0009, 0x0006, 0x0004, 0x0001, 0x000e, 0x0004, + 0x0006, 0x0002, 0x0006, 0x0000, +}; + +static const uint8_t mp3_huffbits_9[36] = { + 3, 3, 5, 6, 8, 9, 3, 3, + 4, 5, 6, 8, 4, 4, 5, 6, + 7, 8, 6, 5, 6, 7, 7, 8, + 7, 6, 7, 7, 8, 9, 8, 7, + 8, 8, 9, 9, +}; + +static const uint16_t mp3_huffcodes_10[64] = { + 0x0001, 0x0002, 0x000a, 0x0017, 0x0023, 0x001e, 0x000c, 0x0011, + 0x0003, 0x0003, 0x0008, 0x000c, 0x0012, 0x0015, 0x000c, 0x0007, + 0x000b, 0x0009, 0x000f, 0x0015, 0x0020, 0x0028, 0x0013, 0x0006, + 0x000e, 0x000d, 0x0016, 0x0022, 0x002e, 0x0017, 0x0012, 0x0007, + 0x0014, 0x0013, 0x0021, 0x002f, 0x001b, 0x0016, 0x0009, 0x0003, + 0x001f, 0x0016, 0x0029, 0x001a, 0x0015, 0x0014, 0x0005, 0x0003, + 0x000e, 0x000d, 0x000a, 0x000b, 0x0010, 0x0006, 0x0005, 0x0001, + 0x0009, 0x0008, 0x0007, 0x0008, 0x0004, 0x0004, 0x0002, 0x0000, +}; + +static const uint8_t mp3_huffbits_10[64] = { + 1, 3, 6, 8, 9, 9, 9, 10, + 3, 4, 6, 7, 8, 9, 8, 8, + 6, 6, 7, 8, 9, 10, 9, 9, + 7, 7, 8, 9, 10, 10, 9, 10, + 8, 8, 9, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 11, 11, 10, 11, + 8, 8, 9, 10, 10, 10, 11, 11, + 9, 8, 9, 10, 10, 11, 11, 11, +}; + +static const uint16_t mp3_huffcodes_11[64] = { + 0x0003, 0x0004, 0x000a, 0x0018, 0x0022, 0x0021, 0x0015, 0x000f, + 0x0005, 0x0003, 0x0004, 0x000a, 0x0020, 0x0011, 0x000b, 0x000a, + 0x000b, 0x0007, 0x000d, 0x0012, 0x001e, 0x001f, 0x0014, 0x0005, + 0x0019, 0x000b, 0x0013, 0x003b, 0x001b, 0x0012, 0x000c, 0x0005, + 0x0023, 0x0021, 0x001f, 0x003a, 0x001e, 0x0010, 0x0007, 0x0005, + 0x001c, 0x001a, 0x0020, 0x0013, 0x0011, 0x000f, 0x0008, 0x000e, + 0x000e, 0x000c, 0x0009, 0x000d, 0x000e, 0x0009, 0x0004, 0x0001, + 0x000b, 0x0004, 0x0006, 0x0006, 0x0006, 0x0003, 0x0002, 0x0000, +}; + +static const uint8_t mp3_huffbits_11[64] = { + 2, 3, 5, 7, 8, 9, 8, 9, + 3, 3, 4, 6, 8, 8, 7, 8, + 5, 5, 6, 7, 8, 9, 8, 8, + 7, 6, 7, 9, 8, 10, 8, 9, + 8, 8, 8, 9, 9, 10, 9, 10, + 8, 8, 9, 10, 10, 11, 10, 11, + 8, 7, 7, 8, 9, 10, 10, 10, + 8, 7, 8, 9, 10, 10, 10, 10, +}; + +static const uint16_t mp3_huffcodes_12[64] = { + 0x0009, 0x0006, 0x0010, 0x0021, 0x0029, 0x0027, 0x0026, 0x001a, + 0x0007, 0x0005, 0x0006, 0x0009, 0x0017, 0x0010, 0x001a, 0x000b, + 0x0011, 0x0007, 0x000b, 0x000e, 0x0015, 0x001e, 0x000a, 0x0007, + 0x0011, 0x000a, 0x000f, 0x000c, 0x0012, 0x001c, 0x000e, 0x0005, + 0x0020, 0x000d, 0x0016, 0x0013, 0x0012, 0x0010, 0x0009, 0x0005, + 0x0028, 0x0011, 0x001f, 0x001d, 0x0011, 0x000d, 0x0004, 0x0002, + 0x001b, 0x000c, 0x000b, 0x000f, 0x000a, 0x0007, 0x0004, 0x0001, + 0x001b, 0x000c, 0x0008, 0x000c, 0x0006, 0x0003, 0x0001, 0x0000, +}; + +static const uint8_t mp3_huffbits_12[64] = { + 4, 3, 5, 7, 8, 9, 9, 9, + 3, 3, 4, 5, 7, 7, 8, 8, + 5, 4, 5, 6, 7, 8, 7, 8, + 6, 5, 6, 6, 7, 8, 8, 8, + 7, 6, 7, 7, 8, 8, 8, 9, + 8, 7, 8, 8, 8, 9, 8, 9, + 8, 7, 7, 8, 8, 9, 9, 10, + 9, 8, 8, 9, 9, 9, 9, 10, +}; + +static const uint16_t mp3_huffcodes_13[256] = { + 0x0001, 0x0005, 0x000e, 0x0015, 0x0022, 0x0033, 0x002e, 0x0047, + 0x002a, 0x0034, 0x0044, 0x0034, 0x0043, 0x002c, 0x002b, 0x0013, + 0x0003, 0x0004, 0x000c, 0x0013, 0x001f, 0x001a, 0x002c, 0x0021, + 0x001f, 0x0018, 0x0020, 0x0018, 0x001f, 0x0023, 0x0016, 0x000e, + 0x000f, 0x000d, 0x0017, 0x0024, 0x003b, 0x0031, 0x004d, 0x0041, + 0x001d, 0x0028, 0x001e, 0x0028, 0x001b, 0x0021, 0x002a, 0x0010, + 0x0016, 0x0014, 0x0025, 0x003d, 0x0038, 0x004f, 0x0049, 0x0040, + 0x002b, 0x004c, 0x0038, 0x0025, 0x001a, 0x001f, 0x0019, 0x000e, + 0x0023, 0x0010, 0x003c, 0x0039, 0x0061, 0x004b, 0x0072, 0x005b, + 0x0036, 0x0049, 0x0037, 0x0029, 0x0030, 0x0035, 0x0017, 0x0018, + 0x003a, 0x001b, 0x0032, 0x0060, 0x004c, 0x0046, 0x005d, 0x0054, + 0x004d, 0x003a, 0x004f, 0x001d, 0x004a, 0x0031, 0x0029, 0x0011, + 0x002f, 0x002d, 0x004e, 0x004a, 0x0073, 0x005e, 0x005a, 0x004f, + 0x0045, 0x0053, 0x0047, 0x0032, 0x003b, 0x0026, 0x0024, 0x000f, + 0x0048, 0x0022, 0x0038, 0x005f, 0x005c, 0x0055, 0x005b, 0x005a, + 0x0056, 0x0049, 0x004d, 0x0041, 0x0033, 0x002c, 0x002b, 0x002a, + 0x002b, 0x0014, 0x001e, 0x002c, 0x0037, 0x004e, 0x0048, 0x0057, + 0x004e, 0x003d, 0x002e, 0x0036, 0x0025, 0x001e, 0x0014, 0x0010, + 0x0035, 0x0019, 0x0029, 0x0025, 0x002c, 0x003b, 0x0036, 0x0051, + 0x0042, 0x004c, 0x0039, 0x0036, 0x0025, 0x0012, 0x0027, 0x000b, + 0x0023, 0x0021, 0x001f, 0x0039, 0x002a, 0x0052, 0x0048, 0x0050, + 0x002f, 0x003a, 0x0037, 0x0015, 0x0016, 0x001a, 0x0026, 0x0016, + 0x0035, 0x0019, 0x0017, 0x0026, 0x0046, 0x003c, 0x0033, 0x0024, + 0x0037, 0x001a, 0x0022, 0x0017, 0x001b, 0x000e, 0x0009, 0x0007, + 0x0022, 0x0020, 0x001c, 0x0027, 0x0031, 0x004b, 0x001e, 0x0034, + 0x0030, 0x0028, 0x0034, 0x001c, 0x0012, 0x0011, 0x0009, 0x0005, + 0x002d, 0x0015, 0x0022, 0x0040, 0x0038, 0x0032, 0x0031, 0x002d, + 0x001f, 0x0013, 0x000c, 0x000f, 0x000a, 0x0007, 0x0006, 0x0003, + 0x0030, 0x0017, 0x0014, 0x0027, 0x0024, 0x0023, 0x0035, 0x0015, + 0x0010, 0x0017, 0x000d, 0x000a, 0x0006, 0x0001, 0x0004, 0x0002, + 0x0010, 0x000f, 0x0011, 0x001b, 0x0019, 0x0014, 0x001d, 0x000b, + 0x0011, 0x000c, 0x0010, 0x0008, 0x0001, 0x0001, 0x0000, 0x0001, +}; + +static const uint8_t mp3_huffbits_13[256] = { + 1, 4, 6, 7, 8, 9, 9, 10, + 9, 10, 11, 11, 12, 12, 13, 13, + 3, 4, 6, 7, 8, 8, 9, 9, + 9, 9, 10, 10, 11, 12, 12, 12, + 6, 6, 7, 8, 9, 9, 10, 10, + 9, 10, 10, 11, 11, 12, 13, 13, + 7, 7, 8, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 12, 13, 13, + 8, 7, 9, 9, 10, 10, 11, 11, + 10, 11, 11, 12, 12, 13, 13, 14, + 9, 8, 9, 10, 10, 10, 11, 11, + 11, 11, 12, 11, 13, 13, 14, 14, + 9, 9, 10, 10, 11, 11, 11, 11, + 11, 12, 12, 12, 13, 13, 14, 14, + 10, 9, 10, 11, 11, 11, 12, 12, + 12, 12, 13, 13, 13, 14, 16, 16, + 9, 8, 9, 10, 10, 11, 11, 12, + 12, 12, 12, 13, 13, 14, 15, 15, + 10, 9, 10, 10, 11, 11, 11, 13, + 12, 13, 13, 14, 14, 14, 16, 15, + 10, 10, 10, 11, 11, 12, 12, 13, + 12, 13, 14, 13, 14, 15, 16, 17, + 11, 10, 10, 11, 12, 12, 12, 12, + 13, 13, 13, 14, 15, 15, 15, 16, + 11, 11, 11, 12, 12, 13, 12, 13, + 14, 14, 15, 15, 15, 16, 16, 16, + 12, 11, 12, 13, 13, 13, 14, 14, + 14, 14, 14, 15, 16, 15, 16, 16, + 13, 12, 12, 13, 13, 13, 15, 14, + 14, 17, 15, 15, 15, 17, 16, 16, + 12, 12, 13, 14, 14, 14, 15, 14, + 15, 15, 16, 16, 19, 18, 19, 16, +}; + +static const uint16_t mp3_huffcodes_15[256] = { + 0x0007, 0x000c, 0x0012, 0x0035, 0x002f, 0x004c, 0x007c, 0x006c, + 0x0059, 0x007b, 0x006c, 0x0077, 0x006b, 0x0051, 0x007a, 0x003f, + 0x000d, 0x0005, 0x0010, 0x001b, 0x002e, 0x0024, 0x003d, 0x0033, + 0x002a, 0x0046, 0x0034, 0x0053, 0x0041, 0x0029, 0x003b, 0x0024, + 0x0013, 0x0011, 0x000f, 0x0018, 0x0029, 0x0022, 0x003b, 0x0030, + 0x0028, 0x0040, 0x0032, 0x004e, 0x003e, 0x0050, 0x0038, 0x0021, + 0x001d, 0x001c, 0x0019, 0x002b, 0x0027, 0x003f, 0x0037, 0x005d, + 0x004c, 0x003b, 0x005d, 0x0048, 0x0036, 0x004b, 0x0032, 0x001d, + 0x0034, 0x0016, 0x002a, 0x0028, 0x0043, 0x0039, 0x005f, 0x004f, + 0x0048, 0x0039, 0x0059, 0x0045, 0x0031, 0x0042, 0x002e, 0x001b, + 0x004d, 0x0025, 0x0023, 0x0042, 0x003a, 0x0034, 0x005b, 0x004a, + 0x003e, 0x0030, 0x004f, 0x003f, 0x005a, 0x003e, 0x0028, 0x0026, + 0x007d, 0x0020, 0x003c, 0x0038, 0x0032, 0x005c, 0x004e, 0x0041, + 0x0037, 0x0057, 0x0047, 0x0033, 0x0049, 0x0033, 0x0046, 0x001e, + 0x006d, 0x0035, 0x0031, 0x005e, 0x0058, 0x004b, 0x0042, 0x007a, + 0x005b, 0x0049, 0x0038, 0x002a, 0x0040, 0x002c, 0x0015, 0x0019, + 0x005a, 0x002b, 0x0029, 0x004d, 0x0049, 0x003f, 0x0038, 0x005c, + 0x004d, 0x0042, 0x002f, 0x0043, 0x0030, 0x0035, 0x0024, 0x0014, + 0x0047, 0x0022, 0x0043, 0x003c, 0x003a, 0x0031, 0x0058, 0x004c, + 0x0043, 0x006a, 0x0047, 0x0036, 0x0026, 0x0027, 0x0017, 0x000f, + 0x006d, 0x0035, 0x0033, 0x002f, 0x005a, 0x0052, 0x003a, 0x0039, + 0x0030, 0x0048, 0x0039, 0x0029, 0x0017, 0x001b, 0x003e, 0x0009, + 0x0056, 0x002a, 0x0028, 0x0025, 0x0046, 0x0040, 0x0034, 0x002b, + 0x0046, 0x0037, 0x002a, 0x0019, 0x001d, 0x0012, 0x000b, 0x000b, + 0x0076, 0x0044, 0x001e, 0x0037, 0x0032, 0x002e, 0x004a, 0x0041, + 0x0031, 0x0027, 0x0018, 0x0010, 0x0016, 0x000d, 0x000e, 0x0007, + 0x005b, 0x002c, 0x0027, 0x0026, 0x0022, 0x003f, 0x0034, 0x002d, + 0x001f, 0x0034, 0x001c, 0x0013, 0x000e, 0x0008, 0x0009, 0x0003, + 0x007b, 0x003c, 0x003a, 0x0035, 0x002f, 0x002b, 0x0020, 0x0016, + 0x0025, 0x0018, 0x0011, 0x000c, 0x000f, 0x000a, 0x0002, 0x0001, + 0x0047, 0x0025, 0x0022, 0x001e, 0x001c, 0x0014, 0x0011, 0x001a, + 0x0015, 0x0010, 0x000a, 0x0006, 0x0008, 0x0006, 0x0002, 0x0000, +}; + +static const uint8_t mp3_huffbits_15[256] = { + 3, 4, 5, 7, 7, 8, 9, 9, + 9, 10, 10, 11, 11, 11, 12, 13, + 4, 3, 5, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 11, 11, + 5, 5, 5, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 11, 11, 11, + 6, 6, 6, 7, 7, 8, 8, 9, + 9, 9, 10, 10, 10, 11, 11, 11, + 7, 6, 7, 7, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 11, 11, 11, + 8, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 11, 11, 11, 12, + 9, 7, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 11, 11, 12, 12, + 9, 8, 8, 9, 9, 9, 9, 10, + 10, 10, 10, 10, 11, 11, 11, 12, + 9, 8, 8, 9, 9, 9, 9, 10, + 10, 10, 10, 11, 11, 12, 12, 12, + 9, 8, 9, 9, 9, 9, 10, 10, + 10, 11, 11, 11, 11, 12, 12, 12, + 10, 9, 9, 9, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 12, 13, 12, + 10, 9, 9, 9, 10, 10, 10, 10, + 11, 11, 11, 11, 12, 12, 12, 13, + 11, 10, 9, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 12, 12, 13, 13, + 11, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 12, 13, 13, + 12, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 12, 13, + 12, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 13, 13, 13, 13, +}; + +static const uint16_t mp3_huffcodes_16[256] = { + 0x0001, 0x0005, 0x000e, 0x002c, 0x004a, 0x003f, 0x006e, 0x005d, + 0x00ac, 0x0095, 0x008a, 0x00f2, 0x00e1, 0x00c3, 0x0178, 0x0011, + 0x0003, 0x0004, 0x000c, 0x0014, 0x0023, 0x003e, 0x0035, 0x002f, + 0x0053, 0x004b, 0x0044, 0x0077, 0x00c9, 0x006b, 0x00cf, 0x0009, + 0x000f, 0x000d, 0x0017, 0x0026, 0x0043, 0x003a, 0x0067, 0x005a, + 0x00a1, 0x0048, 0x007f, 0x0075, 0x006e, 0x00d1, 0x00ce, 0x0010, + 0x002d, 0x0015, 0x0027, 0x0045, 0x0040, 0x0072, 0x0063, 0x0057, + 0x009e, 0x008c, 0x00fc, 0x00d4, 0x00c7, 0x0183, 0x016d, 0x001a, + 0x004b, 0x0024, 0x0044, 0x0041, 0x0073, 0x0065, 0x00b3, 0x00a4, + 0x009b, 0x0108, 0x00f6, 0x00e2, 0x018b, 0x017e, 0x016a, 0x0009, + 0x0042, 0x001e, 0x003b, 0x0038, 0x0066, 0x00b9, 0x00ad, 0x0109, + 0x008e, 0x00fd, 0x00e8, 0x0190, 0x0184, 0x017a, 0x01bd, 0x0010, + 0x006f, 0x0036, 0x0034, 0x0064, 0x00b8, 0x00b2, 0x00a0, 0x0085, + 0x0101, 0x00f4, 0x00e4, 0x00d9, 0x0181, 0x016e, 0x02cb, 0x000a, + 0x0062, 0x0030, 0x005b, 0x0058, 0x00a5, 0x009d, 0x0094, 0x0105, + 0x00f8, 0x0197, 0x018d, 0x0174, 0x017c, 0x0379, 0x0374, 0x0008, + 0x0055, 0x0054, 0x0051, 0x009f, 0x009c, 0x008f, 0x0104, 0x00f9, + 0x01ab, 0x0191, 0x0188, 0x017f, 0x02d7, 0x02c9, 0x02c4, 0x0007, + 0x009a, 0x004c, 0x0049, 0x008d, 0x0083, 0x0100, 0x00f5, 0x01aa, + 0x0196, 0x018a, 0x0180, 0x02df, 0x0167, 0x02c6, 0x0160, 0x000b, + 0x008b, 0x0081, 0x0043, 0x007d, 0x00f7, 0x00e9, 0x00e5, 0x00db, + 0x0189, 0x02e7, 0x02e1, 0x02d0, 0x0375, 0x0372, 0x01b7, 0x0004, + 0x00f3, 0x0078, 0x0076, 0x0073, 0x00e3, 0x00df, 0x018c, 0x02ea, + 0x02e6, 0x02e0, 0x02d1, 0x02c8, 0x02c2, 0x00df, 0x01b4, 0x0006, + 0x00ca, 0x00e0, 0x00de, 0x00da, 0x00d8, 0x0185, 0x0182, 0x017d, + 0x016c, 0x0378, 0x01bb, 0x02c3, 0x01b8, 0x01b5, 0x06c0, 0x0004, + 0x02eb, 0x00d3, 0x00d2, 0x00d0, 0x0172, 0x017b, 0x02de, 0x02d3, + 0x02ca, 0x06c7, 0x0373, 0x036d, 0x036c, 0x0d83, 0x0361, 0x0002, + 0x0179, 0x0171, 0x0066, 0x00bb, 0x02d6, 0x02d2, 0x0166, 0x02c7, + 0x02c5, 0x0362, 0x06c6, 0x0367, 0x0d82, 0x0366, 0x01b2, 0x0000, + 0x000c, 0x000a, 0x0007, 0x000b, 0x000a, 0x0011, 0x000b, 0x0009, + 0x000d, 0x000c, 0x000a, 0x0007, 0x0005, 0x0003, 0x0001, 0x0003, +}; + +static const uint8_t mp3_huffbits_16[256] = { + 1, 4, 6, 8, 9, 9, 10, 10, + 11, 11, 11, 12, 12, 12, 13, 9, + 3, 4, 6, 7, 8, 9, 9, 9, + 10, 10, 10, 11, 12, 11, 12, 8, + 6, 6, 7, 8, 9, 9, 10, 10, + 11, 10, 11, 11, 11, 12, 12, 9, + 8, 7, 8, 9, 9, 10, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 10, + 9, 8, 9, 9, 10, 10, 11, 11, + 11, 12, 12, 12, 13, 13, 13, 9, + 9, 8, 9, 9, 10, 11, 11, 12, + 11, 12, 12, 13, 13, 13, 14, 10, + 10, 9, 9, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 14, 10, + 10, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 13, 13, 15, 15, 10, + 10, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 10, + 11, 10, 10, 11, 11, 12, 12, 13, + 13, 13, 13, 14, 13, 14, 13, 11, + 11, 11, 10, 11, 12, 12, 12, 12, + 13, 14, 14, 14, 15, 15, 14, 10, + 12, 11, 11, 11, 12, 12, 13, 14, + 14, 14, 14, 14, 14, 13, 14, 11, + 12, 12, 12, 12, 12, 13, 13, 13, + 13, 15, 14, 14, 14, 14, 16, 11, + 14, 12, 12, 12, 13, 13, 14, 14, + 14, 16, 15, 15, 15, 17, 15, 11, + 13, 13, 11, 12, 14, 14, 13, 14, + 14, 15, 16, 15, 17, 15, 14, 11, + 9, 8, 8, 9, 9, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 8, +}; + +static const uint16_t mp3_huffcodes_24[256] = { + 0x000f, 0x000d, 0x002e, 0x0050, 0x0092, 0x0106, 0x00f8, 0x01b2, + 0x01aa, 0x029d, 0x028d, 0x0289, 0x026d, 0x0205, 0x0408, 0x0058, + 0x000e, 0x000c, 0x0015, 0x0026, 0x0047, 0x0082, 0x007a, 0x00d8, + 0x00d1, 0x00c6, 0x0147, 0x0159, 0x013f, 0x0129, 0x0117, 0x002a, + 0x002f, 0x0016, 0x0029, 0x004a, 0x0044, 0x0080, 0x0078, 0x00dd, + 0x00cf, 0x00c2, 0x00b6, 0x0154, 0x013b, 0x0127, 0x021d, 0x0012, + 0x0051, 0x0027, 0x004b, 0x0046, 0x0086, 0x007d, 0x0074, 0x00dc, + 0x00cc, 0x00be, 0x00b2, 0x0145, 0x0137, 0x0125, 0x010f, 0x0010, + 0x0093, 0x0048, 0x0045, 0x0087, 0x007f, 0x0076, 0x0070, 0x00d2, + 0x00c8, 0x00bc, 0x0160, 0x0143, 0x0132, 0x011d, 0x021c, 0x000e, + 0x0107, 0x0042, 0x0081, 0x007e, 0x0077, 0x0072, 0x00d6, 0x00ca, + 0x00c0, 0x00b4, 0x0155, 0x013d, 0x012d, 0x0119, 0x0106, 0x000c, + 0x00f9, 0x007b, 0x0079, 0x0075, 0x0071, 0x00d7, 0x00ce, 0x00c3, + 0x00b9, 0x015b, 0x014a, 0x0134, 0x0123, 0x0110, 0x0208, 0x000a, + 0x01b3, 0x0073, 0x006f, 0x006d, 0x00d3, 0x00cb, 0x00c4, 0x00bb, + 0x0161, 0x014c, 0x0139, 0x012a, 0x011b, 0x0213, 0x017d, 0x0011, + 0x01ab, 0x00d4, 0x00d0, 0x00cd, 0x00c9, 0x00c1, 0x00ba, 0x00b1, + 0x00a9, 0x0140, 0x012f, 0x011e, 0x010c, 0x0202, 0x0179, 0x0010, + 0x014f, 0x00c7, 0x00c5, 0x00bf, 0x00bd, 0x00b5, 0x00ae, 0x014d, + 0x0141, 0x0131, 0x0121, 0x0113, 0x0209, 0x017b, 0x0173, 0x000b, + 0x029c, 0x00b8, 0x00b7, 0x00b3, 0x00af, 0x0158, 0x014b, 0x013a, + 0x0130, 0x0122, 0x0115, 0x0212, 0x017f, 0x0175, 0x016e, 0x000a, + 0x028c, 0x015a, 0x00ab, 0x00a8, 0x00a4, 0x013e, 0x0135, 0x012b, + 0x011f, 0x0114, 0x0107, 0x0201, 0x0177, 0x0170, 0x016a, 0x0006, + 0x0288, 0x0142, 0x013c, 0x0138, 0x0133, 0x012e, 0x0124, 0x011c, + 0x010d, 0x0105, 0x0200, 0x0178, 0x0172, 0x016c, 0x0167, 0x0004, + 0x026c, 0x012c, 0x0128, 0x0126, 0x0120, 0x011a, 0x0111, 0x010a, + 0x0203, 0x017c, 0x0176, 0x0171, 0x016d, 0x0169, 0x0165, 0x0002, + 0x0409, 0x0118, 0x0116, 0x0112, 0x010b, 0x0108, 0x0103, 0x017e, + 0x017a, 0x0174, 0x016f, 0x016b, 0x0168, 0x0166, 0x0164, 0x0000, + 0x002b, 0x0014, 0x0013, 0x0011, 0x000f, 0x000d, 0x000b, 0x0009, + 0x0007, 0x0006, 0x0004, 0x0007, 0x0005, 0x0003, 0x0001, 0x0003, +}; + +static const uint8_t mp3_huffbits_24[256] = { + 4, 4, 6, 7, 8, 9, 9, 10, + 10, 11, 11, 11, 11, 11, 12, 9, + 4, 4, 5, 6, 7, 8, 8, 9, + 9, 9, 10, 10, 10, 10, 10, 8, + 6, 5, 6, 7, 7, 8, 8, 9, + 9, 9, 9, 10, 10, 10, 11, 7, + 7, 6, 7, 7, 8, 8, 8, 9, + 9, 9, 9, 10, 10, 10, 10, 7, + 8, 7, 7, 8, 8, 8, 8, 9, + 9, 9, 10, 10, 10, 10, 11, 7, + 9, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 7, + 9, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 11, 7, + 10, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 11, 11, 8, + 10, 9, 9, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 8, + 10, 9, 9, 9, 9, 9, 9, 10, + 10, 10, 10, 10, 11, 11, 11, 8, + 11, 9, 9, 9, 9, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 8, + 11, 10, 9, 9, 9, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 8, + 11, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 8, + 11, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 8, + 12, 10, 10, 10, 10, 10, 10, 11, + 11, 11, 11, 11, 11, 11, 11, 8, + 8, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 4, +}; + +static const huff_table_t mp3_huff_tables[16] = { +{ 1, NULL, NULL }, +{ 2, mp3_huffbits_1, mp3_huffcodes_1 }, +{ 3, mp3_huffbits_2, mp3_huffcodes_2 }, +{ 3, mp3_huffbits_3, mp3_huffcodes_3 }, +{ 4, mp3_huffbits_5, mp3_huffcodes_5 }, +{ 4, mp3_huffbits_6, mp3_huffcodes_6 }, +{ 6, mp3_huffbits_7, mp3_huffcodes_7 }, +{ 6, mp3_huffbits_8, mp3_huffcodes_8 }, +{ 6, mp3_huffbits_9, mp3_huffcodes_9 }, +{ 8, mp3_huffbits_10, mp3_huffcodes_10 }, +{ 8, mp3_huffbits_11, mp3_huffcodes_11 }, +{ 8, mp3_huffbits_12, mp3_huffcodes_12 }, +{ 16, mp3_huffbits_13, mp3_huffcodes_13 }, +{ 16, mp3_huffbits_15, mp3_huffcodes_15 }, +{ 16, mp3_huffbits_16, mp3_huffcodes_16 }, +{ 16, mp3_huffbits_24, mp3_huffcodes_24 }, +}; + +static const uint8_t mp3_huff_data[32][2] = { +{ 0, 0 }, +{ 1, 0 }, +{ 2, 0 }, +{ 3, 0 }, +{ 0, 0 }, +{ 4, 0 }, +{ 5, 0 }, +{ 6, 0 }, +{ 7, 0 }, +{ 8, 0 }, +{ 9, 0 }, +{ 10, 0 }, +{ 11, 0 }, +{ 12, 0 }, +{ 0, 0 }, +{ 13, 0 }, +{ 14, 1 }, +{ 14, 2 }, +{ 14, 3 }, +{ 14, 4 }, +{ 14, 6 }, +{ 14, 8 }, +{ 14, 10 }, +{ 14, 13 }, +{ 15, 4 }, +{ 15, 5 }, +{ 15, 6 }, +{ 15, 7 }, +{ 15, 8 }, +{ 15, 9 }, +{ 15, 11 }, +{ 15, 13 }, +}; + +static const uint8_t mp3_quad_codes[2][16] = { + { 1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1, }, + { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }, +}; + +static const uint8_t mp3_quad_bits[2][16] = { + { 1, 4, 4, 5, 4, 6, 5, 6, 4, 5, 5, 6, 5, 6, 6, 6, }, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, }, +}; + +static const uint8_t band_size_long[9][22] = { +{ 4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, + 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158, }, /* 44100 */ +{ 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, + 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192, }, /* 48000 */ +{ 4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, + 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26, }, /* 32000 */ +{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 22050 */ +{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 18, 22, 26, 32, 38, 46, 52, 64, 70, 76, 36, }, /* 24000 */ +{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 16000 */ +{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 11025 */ +{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, + 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 12000 */ +{ 12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, + 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2, }, /* 8000 */ +}; + +static const uint8_t band_size_short[9][13] = { +{ 4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56, }, /* 44100 */ +{ 4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66, }, /* 48000 */ +{ 4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12, }, /* 32000 */ +{ 4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18, }, /* 22050 */ +{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12, }, /* 24000 */ +{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 16000 */ +{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 11025 */ +{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 12000 */ +{ 8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26, }, /* 8000 */ +}; + +static const uint8_t mp3_pretab[2][22] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 }, +}; + +static const float ci_table[8] = { + -0.6f, -0.535f, -0.33f, -0.185f, -0.095f, -0.041f, -0.0142f, -0.0037f, +}; + +#define C1 FIXHR(0.98480775301220805936/2) +#define C2 FIXHR(0.93969262078590838405/2) +#define C3 FIXHR(0.86602540378443864676/2) +#define C4 FIXHR(0.76604444311897803520/2) +#define C5 FIXHR(0.64278760968653932632/2) +#define C6 FIXHR(0.5/2) +#define C7 FIXHR(0.34202014332566873304/2) +#define C8 FIXHR(0.17364817766693034885/2) + +static const int icos36[9] = { + FIXR(0.50190991877167369479), + FIXR(0.51763809020504152469), //0 + FIXR(0.55168895948124587824), + FIXR(0.61038729438072803416), + FIXR(0.70710678118654752439), //1 + FIXR(0.87172339781054900991), + FIXR(1.18310079157624925896), + FIXR(1.93185165257813657349), //2 + FIXR(5.73685662283492756461), +}; + +static const int icos36h[9] = { + FIXHR(0.50190991877167369479/2), + FIXHR(0.51763809020504152469/2), //0 + FIXHR(0.55168895948124587824/2), + FIXHR(0.61038729438072803416/2), + FIXHR(0.70710678118654752439/2), //1 + FIXHR(0.87172339781054900991/2), + FIXHR(1.18310079157624925896/4), + FIXHR(1.93185165257813657349/4), //2 +// FIXHR(5.73685662283492756461), +}; + +//////////////////////////////////////////////////////////////////////////////// + +static INLINE int unaligned32_be(const uint8_t *p) +{ + return (((p[0]<<8) | p[1])<<16) | (p[2]<<8) | (p[3]); +} + +#define MIN_CACHE_BITS 25 + +#define NEG_SSR32(a,s) ((( int32_t)(a))>>(32-(s))) +#define NEG_USR32(a,s) (((uint32_t)(a))>>(32-(s))) + +#define OPEN_READER(name, gb) \ + int name##_index= (gb)->index;\ + int name##_cache= 0;\ + +#define CLOSE_READER(name, gb)\ + (gb)->index= name##_index;\ + +#define UPDATE_CACHE(name, gb)\ + name##_cache= unaligned32_be(&((gb)->buffer[name##_index>>3])) << (name##_index&0x07); \ + +#define SKIP_CACHE(name, gb, num)\ + name##_cache <<= (num); + +#define SKIP_COUNTER(name, gb, num)\ + name##_index += (num);\ + +#define SKIP_BITS(name, gb, num)\ + {\ + SKIP_CACHE(name, gb, num)\ + SKIP_COUNTER(name, gb, num)\ + }\ + +#define LAST_SKIP_BITS(name, gb, num) SKIP_COUNTER(name, gb, num) +#define LAST_SKIP_CACHE(name, gb, num) ; + +#define SHOW_UBITS(name, gb, num)\ + NEG_USR32(name##_cache, num) + +#define SHOW_SBITS(name, gb, num)\ + NEG_SSR32(name##_cache, num) + +#define GET_CACHE(name, gb)\ + ((uint32_t)name##_cache) + +static INLINE int get_bits_count(bitstream_t *s){ + return s->index; +} + +static INLINE void skip_bits_long(bitstream_t *s, int n){ + s->index += n; +} +#define skip_bits skip_bits_long + +static void init_get_bits(bitstream_t *s, const uint8_t *buffer, int bit_size) { + int buffer_size= (bit_size+7)>>3; + if(buffer_size < 0 || bit_size < 0) { + buffer_size = bit_size = 0; + buffer = NULL; + } + s->buffer= buffer; + s->size_in_bits= bit_size; + s->buffer_end= buffer + buffer_size; + s->index=0; +} + +static INLINE unsigned int get_bits(bitstream_t *s, int n){ + register int tmp; + OPEN_READER(re, s) + UPDATE_CACHE(re, s) + tmp= SHOW_UBITS(re, s, n); + LAST_SKIP_BITS(re, s, n) + CLOSE_READER(re, s) + return tmp; +} + +static INLINE int get_bitsz(bitstream_t *s, int n) +{ + if (n == 0) + return 0; + else + return get_bits(s, n); +} + +static INLINE unsigned int get_bits1(bitstream_t *s){ + int index= s->index; + uint8_t result= s->buffer[ index>>3 ]; + result<<= (index&0x07); + result>>= 8 - 1; + index++; + s->index= index; + return result; +} + +static INLINE void align_get_bits(bitstream_t *s) +{ + int n= (-get_bits_count(s)) & 7; + if(n) skip_bits(s, n); +} + +#if 0 // OpenMPT +#define GET_DATA(v, table, i, wrap, size) \ +{\ + const uint8_t *ptr = (const uint8_t *)table + i * wrap;\ + switch(size) {\ + case 1:\ + v = *(const uint8_t *)ptr;\ + break;\ + case 2:\ + v = *(const uint16_t *)ptr;\ + break;\ + default:\ + v = *(const uint32_t *)ptr;\ + break;\ + }\ +} +#else // OpenMPT +#define GET_DATA(v, table, i, wrap, size) \ +{\ + const uint8_t *ptr = (const uint8_t *)table + i * wrap;\ + switch(size) {\ + case 1: {\ + uint8_t result = 0;\ + memcpy(&result, ptr, 1);\ + v = result;\ + } break;\ + case 2: {\ + uint16_t result = 0;\ + memcpy(&result, ptr, 2);\ + v = result;\ + } break;\ + default: {\ + uint32_t result = 0;\ + memcpy(&result, ptr, 4);\ + v = result;\ + } break;\ + }\ +} // OpenMPT +#endif // OpenMPT + +static INLINE int alloc_table(vlc_t *vlc, int size) { + int index; + index = vlc->table_size; + vlc->table_size += size; + if (vlc->table_size > vlc->table_allocated) { + vlc->table_allocated += (1 << vlc->bits); + vlc->table = libc_realloc(vlc->table, sizeof(VLC_TYPE) * 2 * vlc->table_allocated); + if (!vlc->table) + return -1; + } + return index; +} + +static int build_table( + vlc_t *vlc, int table_nb_bits, + int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + uint32_t code_prefix, int n_prefix +) { + int i, j, k, n, table_size, table_index, nb, n1, index, code_prefix2; + uint32_t code; + VLC_TYPE (*table)[2]; + + table_size = 1 << table_nb_bits; + table_index = alloc_table(vlc, table_size); + if (table_index < 0) + return -1; + table = &vlc->table[table_index]; + + for(i=0;i> n; +#if 0 // OpenMPT + if (n > 0 && code_prefix2 == code_prefix) { +#else // OpenMPT + if (n > 0 && (uint32_t)code_prefix2 == code_prefix) { +#endif // OpenMPT + if (n <= table_nb_bits) { + j = (code << (table_nb_bits - n)) & (table_size - 1); + nb = 1 << (table_nb_bits - n); + for(k=0;k> n) & ((1 << table_nb_bits) - 1); + n1 = -table[j][1]; //bits + if (n > n1) + n1 = n; + table[j][1] = -n1; //bits + } + } + } + for(i=0;i table_nb_bits) { + n = table_nb_bits; + table[i][1] = -n; //bits + } + index = build_table(vlc, n, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + (code_prefix << table_nb_bits) | i, + n_prefix + table_nb_bits); + if (index < 0) + return -1; + table = &vlc->table[table_index]; + table[i][0] = index; //code + } + } + return table_index; +} + +static INLINE int init_vlc( + vlc_t *vlc, int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size +) { + vlc->bits = nb_bits; + if (build_table(vlc, nb_bits, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + 0, 0) < 0) { + libc_free(vlc->table); + return -1; + } + return 0; +} + +#define GET_VLC(code, name, gb, table, bits, max_depth)\ +{\ + int n, index, nb_bits;\ +\ + index= SHOW_UBITS(name, gb, bits);\ + code = table[index][0];\ + n = table[index][1];\ +\ + if(max_depth > 1 && n < 0){\ + LAST_SKIP_BITS(name, gb, bits)\ + UPDATE_CACHE(name, gb)\ +\ + nb_bits = -n;\ +\ + index= SHOW_UBITS(name, gb, nb_bits) + code;\ + code = table[index][0];\ + n = table[index][1];\ + if(max_depth > 2 && n < 0){\ + LAST_SKIP_BITS(name, gb, nb_bits)\ + UPDATE_CACHE(name, gb)\ +\ + nb_bits = -n;\ +\ + index= SHOW_UBITS(name, gb, nb_bits) + code;\ + code = table[index][0];\ + n = table[index][1];\ + }\ + }\ + SKIP_BITS(name, gb, n)\ +} + +static INLINE int get_vlc2(bitstream_t *s, VLC_TYPE (*table)[2], int bits, int max_depth) { + int code; + + OPEN_READER(re, s) + UPDATE_CACHE(re, s) + + GET_VLC(code, re, s, table, bits, max_depth) + + CLOSE_READER(re, s) + return code; +} + +static void switch_buffer(mp3_context_t *s, int *pos, int *end_pos, int *end_pos2) { + if(s->in_gb.buffer && *pos >= s->gb.size_in_bits){ + s->gb= s->in_gb; + s->in_gb.buffer=NULL; + skip_bits_long(&s->gb, *pos - *end_pos); + *end_pos2= + *end_pos= *end_pos2 + get_bits_count(&s->gb) - *pos; + *pos= get_bits_count(&s->gb); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +static INLINE int mp3_check_header(uint32_t header){ + /* header */ + if ((header & 0xffe00000) != 0xffe00000) + return -1; + /* layer check */ + if ((header & (3<<17)) != (1 << 17)) + return -1; + /* bit rate */ + if ((header & (0xf<<12)) == 0xf<<12) + return -1; + /* frequency */ + if ((header & (3<<10)) == 3<<10) + return -1; + return 0; +} + + +static void lsf_sf_expand( + int *slen, int sf, int n1, int n2, int n3 +) { + if (n3) { + slen[3] = sf % n3; + sf /= n3; + } else { + slen[3] = 0; + } + if (n2) { + slen[2] = sf % n2; + sf /= n2; + } else { + slen[2] = 0; + } + slen[1] = sf % n1; + sf /= n1; + slen[0] = sf; +} + +static INLINE int l3_unscale(int value, int exponent) +{ + unsigned int m; + int e; + + e = table_4_3_exp [4*value + (exponent&3)]; + m = table_4_3_value[4*value + (exponent&3)]; + e -= (exponent >> 2); + if (e > 31) + return 0; + m = (m + (1 << (e-1))) >> e; + + return m; +} + +static INLINE int round_sample(int *sum) { + int sum1; + sum1 = (*sum) >> OUT_SHIFT; + *sum &= (1< OUT_MAX) + sum1 = OUT_MAX; + return sum1; +} + +static void exponents_from_scale_factors( + mp3_context_t *s, granule_t *g, int16_t *exponents +) { + const uint8_t *bstab, *pretab; + int len, i, j, k, l, v0, shift, gain, gains[3]; + int16_t *exp_ptr; + + exp_ptr = exponents; + gain = g->global_gain - 210; + shift = g->scalefac_scale + 1; + + bstab = band_size_long[s->sample_rate_index]; + pretab = mp3_pretab[g->preflag]; + for(i=0;ilong_end;i++) { + v0 = gain - ((g->scale_factors[i] + pretab[i]) << shift) + 400; + len = bstab[i]; + for(j=len;j>0;j--) + *exp_ptr++ = v0; + } + + if (g->short_start < 13) { + bstab = band_size_short[s->sample_rate_index]; + gains[0] = gain - (g->subblock_gain[0] << 3); + gains[1] = gain - (g->subblock_gain[1] << 3); + gains[2] = gain - (g->subblock_gain[2] << 3); + k = g->long_end; + for(i=g->short_start;i<13;i++) { + len = bstab[i]; + for(l=0;l<3;l++) { + v0 = gains[l] - (g->scale_factors[k++] << shift) + 400; + for(j=len;j>0;j--) + *exp_ptr++ = v0; + } + } + } +} + +static void reorder_block(mp3_context_t *s, granule_t *g) +{ + int i, j, len; + int32_t *ptr, *dst, *ptr1; + int32_t tmp[576]; + + if (g->block_type != 2) + return; + + if (g->switch_point) { + if (s->sample_rate_index != 8) { + ptr = g->sb_hybrid + 36; + } else { + ptr = g->sb_hybrid + 48; + } + } else { + ptr = g->sb_hybrid; + } + + for(i=g->short_start;i<13;i++) { + len = band_size_short[s->sample_rate_index][i]; + ptr1 = ptr; + dst = tmp; + for(j=len;j>0;j--) { + *dst++ = ptr[0*len]; + *dst++ = ptr[1*len]; + *dst++ = ptr[2*len]; + ptr++; + } + ptr+=2*len; + libc_memcpy(ptr1, tmp, len * 3 * sizeof(*ptr1)); + } +} + +static void compute_antialias(mp3_context_t *s, granule_t *g) { + int32_t *ptr, *csa; + int n, i; + + /* we antialias only "long" bands */ + if (g->block_type == 2) { + if (!g->switch_point) + return; + /* XXX: check this for 8000Hz case */ + n = 1; + } else { + n = SBLIMIT - 1; + } + + ptr = g->sb_hybrid + 18; + for(i = n;i > 0;i--) { + int tmp0, tmp1, tmp2; + csa = &csa_table[0][0]; +#define INT_AA(j) \ + tmp0 = ptr[-1-j];\ + tmp1 = ptr[ j];\ + tmp2= MULH(tmp0 + tmp1, csa[0+4*j]);\ + ptr[-1-j] = 4*(tmp2 - MULH(tmp1, csa[2+4*j]));\ + ptr[ j] = 4*(tmp2 + MULH(tmp0, csa[3+4*j])); + + INT_AA(0) + INT_AA(1) + INT_AA(2) + INT_AA(3) + INT_AA(4) + INT_AA(5) + INT_AA(6) + INT_AA(7) + + ptr += 18; + } +} + +static void compute_stereo( + mp3_context_t *s, granule_t *g0, granule_t *g1 +) { + int i, j, k, l; + int32_t v1, v2; + int sf_max, tmp0, tmp1, sf, len, non_zero_found; + int32_t (*is_tab)[16]; + int32_t *tab0, *tab1; + int non_zero_found_short[3]; + + if (s->mode_ext & MODE_EXT_I_STEREO) { + if (!s->lsf) { + is_tab = is_table; + sf_max = 7; + } else { + is_tab = is_table_lsf[g1->scalefac_compress & 1]; + sf_max = 16; + } + + tab0 = g0->sb_hybrid + 576; + tab1 = g1->sb_hybrid + 576; + + non_zero_found_short[0] = 0; + non_zero_found_short[1] = 0; + non_zero_found_short[2] = 0; + k = (13 - g1->short_start) * 3 + g1->long_end - 3; + for(i = 12;i >= g1->short_start;i--) { + /* for last band, use previous scale factor */ + if (i != 11) + k -= 3; + len = band_size_short[s->sample_rate_index][i]; + for(l=2;l>=0;l--) { + tab0 -= len; + tab1 -= len; + if (!non_zero_found_short[l]) { + /* test if non zero band. if so, stop doing i-stereo */ + for(j=0;jscale_factors[k + l]; + if (sf >= sf_max) + goto found1; + + v1 = is_tab[0][sf]; + v2 = is_tab[1][sf]; + for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { + /* lower part of the spectrum : do ms stereo + if enabled */ + for(j=0;jlong_end - 1;i >= 0;i--) { + len = band_size_long[s->sample_rate_index][i]; + tab0 -= len; + tab1 -= len; + /* test if non zero band. if so, stop doing i-stereo */ + if (!non_zero_found) { + for(j=0;jscale_factors[k]; + if (sf >= sf_max) + goto found2; + v1 = is_tab[0][sf]; + v2 = is_tab[1][sf]; + for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { + /* lower part of the spectrum : do ms stereo + if enabled */ + for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { + /* ms stereo ONLY */ + /* NOTE: the 1/sqrt(2) normalization factor is included in the + global gain */ + tab0 = g0->sb_hybrid; + tab1 = g1->sb_hybrid; + for(i=0;i<576;i++) { + tmp0 = tab0[i]; + tmp1 = tab1[i]; + tab0[i] = tmp0 + tmp1; + tab1[i] = tmp0 - tmp1; + } + } +} + +static int huffman_decode( + mp3_context_t *s, granule_t *g, int16_t *exponents, int end_pos2 +) { + int s_index; + int i; + int last_pos, bits_left; + vlc_t *vlc; + int end_pos= s->gb.size_in_bits; + if (end_pos2 < end_pos) end_pos = end_pos2; + + /* low frequencies (called big values) */ + s_index = 0; + for(i=0;i<3;i++) { + int j, k, l, linbits; + j = g->region_size[i]; + if (j == 0) + continue; + /* select vlc table */ + k = g->table_select[i]; + l = mp3_huff_data[k][0]; + linbits = mp3_huff_data[k][1]; + vlc = &huff_vlc[l]; + + if(!l){ + libc_memset(&g->sb_hybrid[s_index], 0, sizeof(*g->sb_hybrid)*2*j); + s_index += 2*j; + continue; + } + + /* read huffcode and compute each couple */ + for(;j>0;j--) { + int exponent, x, y, v; + int pos= get_bits_count(&s->gb); + + if (pos >= end_pos){ + switch_buffer(s, &pos, &end_pos, &end_pos2); + if(pos >= end_pos) + break; + } + y = get_vlc2(&s->gb, vlc->table, 7, 3); + + if(!y){ + g->sb_hybrid[s_index ] = + g->sb_hybrid[s_index+1] = 0; + s_index += 2; + continue; + } + + exponent= exponents[s_index]; + + if(y&16){ + x = y >> 5; + y = y & 0x0f; + if (x < 15){ + v = expval_table[ exponent ][ x ]; + }else{ + x += get_bitsz(&s->gb, linbits); + v = l3_unscale(x, exponent); + } + if (get_bits1(&s->gb)) + v = -v; + g->sb_hybrid[s_index] = v; + if (y < 15){ + v = expval_table[ exponent ][ y ]; + }else{ + y += get_bitsz(&s->gb, linbits); + v = l3_unscale(y, exponent); + } + if (get_bits1(&s->gb)) + v = -v; + g->sb_hybrid[s_index+1] = v; + }else{ + x = y >> 5; + y = y & 0x0f; + x += y; + if (x < 15){ + v = expval_table[ exponent ][ x ]; + }else{ + x += get_bitsz(&s->gb, linbits); + v = l3_unscale(x, exponent); + } + if (get_bits1(&s->gb)) + v = -v; + g->sb_hybrid[s_index+!!y] = v; + g->sb_hybrid[s_index+ !y] = 0; + } + s_index+=2; + } + } + + /* high frequencies */ + vlc = &huff_quad_vlc[g->count1table_select]; + last_pos=0; + while (s_index <= 572) { + int pos, code; + pos = get_bits_count(&s->gb); + if (pos >= end_pos) { + if (pos > end_pos2 && last_pos){ + /* some encoders generate an incorrect size for this + part. We must go back into the data */ + s_index -= 4; + skip_bits_long(&s->gb, last_pos - pos); + break; + } + switch_buffer(s, &pos, &end_pos, &end_pos2); + if(pos >= end_pos) + break; + } + last_pos= pos; + + code = get_vlc2(&s->gb, vlc->table, vlc->bits, 1); + g->sb_hybrid[s_index+0]= + g->sb_hybrid[s_index+1]= + g->sb_hybrid[s_index+2]= + g->sb_hybrid[s_index+3]= 0; + while(code){ + const static int idxtab[16]={3,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0}; + int v; + int pos= s_index+idxtab[code]; + code ^= 8>>idxtab[code]; + v = exp_table[ exponents[pos] ]; + if(get_bits1(&s->gb)) + v = -v; + g->sb_hybrid[pos] = v; + } + s_index+=4; + } + libc_memset(&g->sb_hybrid[s_index], 0, sizeof(*g->sb_hybrid)*(576 - s_index)); + + /* skip extension bits */ + bits_left = end_pos2 - get_bits_count(&s->gb); + if (bits_left < 0) { + return -1; + } + skip_bits_long(&s->gb, bits_left); + + i= get_bits_count(&s->gb); + switch_buffer(s, &i, &end_pos, &end_pos2); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// + +static void imdct12(int *out, int *in) +{ + int in0, in1, in2, in3, in4, in5, t1, t2; + + in0= in[0*3]; + in1= in[1*3] + in[0*3]; + in2= in[2*3] + in[1*3]; + in3= in[3*3] + in[2*3]; + in4= in[4*3] + in[3*3]; + in5= in[5*3] + in[4*3]; + in5 += in3; + in3 += in1; + + in2= MULH(2*in2, C3); + in3= MULH(4*in3, C3); + + t1 = in0 - in4; + t2 = MULH(2*(in1 - in5), icos36h[4]); + + out[ 7]= + out[10]= t1 + t2; + out[ 1]= + out[ 4]= t1 - t2; + + in0 += in4>>1; + in4 = in0 + in2; + in5 += 2*in1; + in1 = MULH(in5 + in3, icos36h[1]); + out[ 8]= + out[ 9]= in4 + in1; + out[ 2]= + out[ 3]= in4 - in1; + + in0 -= in2; + in5 = MULH(2*(in5 - in3), icos36h[7]); + out[ 0]= + out[ 5]= in0 - in5; + out[ 6]= + out[11]= in0 + in5; +} + +static void imdct36(int *out, int *buf, int *in, int *win) +{ + int i, j, t0, t1, t2, t3, s0, s1, s2, s3; + int tmp[18], *tmp1, *in1; + + for(i=17;i>=1;i--) + in[i] += in[i-1]; + for(i=17;i>=3;i-=2) + in[i] += in[i-2]; + + for(j=0;j<2;j++) { + tmp1 = tmp + j; + in1 = in + j; + t2 = in1[2*4] + in1[2*8] - in1[2*2]; + + t3 = in1[2*0] + (in1[2*6]>>1); + t1 = in1[2*0] - in1[2*6]; + tmp1[ 6] = t1 - (t2>>1); + tmp1[16] = t1 + t2; + + t0 = MULH(2*(in1[2*2] + in1[2*4]), C2); + t1 = MULH( in1[2*4] - in1[2*8] , -2*C8); + t2 = MULH(2*(in1[2*2] + in1[2*8]), -C4); + + tmp1[10] = t3 - t0 - t2; + tmp1[ 2] = t3 + t0 + t1; + tmp1[14] = t3 + t2 - t1; + + tmp1[ 4] = MULH(2*(in1[2*5] + in1[2*7] - in1[2*1]), -C3); + t2 = MULH(2*(in1[2*1] + in1[2*5]), C1); + t3 = MULH( in1[2*5] - in1[2*7] , -2*C7); + t0 = MULH(2*in1[2*3], C3); + + t1 = MULH(2*(in1[2*1] + in1[2*7]), -C5); + + tmp1[ 0] = t2 + t3 + t0; + tmp1[12] = t2 + t1 - t0; + tmp1[ 8] = t3 - t1 - t0; + } + + i = 0; + for(j=0;j<4;j++) { + t0 = tmp[i]; + t1 = tmp[i + 2]; + s0 = t1 + t0; + s2 = t1 - t0; + + t2 = tmp[i + 1]; + t3 = tmp[i + 3]; + s1 = MULH(2*(t3 + t2), icos36h[j]); + s3 = MULL(t3 - t2, icos36[8 - j]); + + t0 = s0 + s1; + t1 = s0 - s1; + out[(9 + j)*SBLIMIT] = MULH(t1, win[9 + j]) + buf[9 + j]; + out[(8 - j)*SBLIMIT] = MULH(t1, win[8 - j]) + buf[8 - j]; + buf[9 + j] = MULH(t0, win[18 + 9 + j]); + buf[8 - j] = MULH(t0, win[18 + 8 - j]); + + t0 = s2 + s3; + t1 = s2 - s3; + out[(9 + 8 - j)*SBLIMIT] = MULH(t1, win[9 + 8 - j]) + buf[9 + 8 - j]; + out[( j)*SBLIMIT] = MULH(t1, win[ j]) + buf[ j]; + buf[9 + 8 - j] = MULH(t0, win[18 + 9 + 8 - j]); + buf[ + j] = MULH(t0, win[18 + j]); + i += 4; + } + + s0 = tmp[16]; + s1 = MULH(2*tmp[17], icos36h[4]); + t0 = s0 + s1; + t1 = s0 - s1; + out[(9 + 4)*SBLIMIT] = MULH(t1, win[9 + 4]) + buf[9 + 4]; + out[(8 - 4)*SBLIMIT] = MULH(t1, win[8 - 4]) + buf[8 - 4]; + buf[9 + 4] = MULH(t0, win[18 + 9 + 4]); + buf[8 - 4] = MULH(t0, win[18 + 8 - 4]); +} + +static void compute_imdct( + mp3_context_t *s, granule_t *g, int32_t *sb_samples, int32_t *mdct_buf +) { + int32_t *ptr, *win, *win1, *buf, *out_ptr, *ptr1; + int32_t out2[12]; + int i, j, mdct_long_end, v, sblimit; + + /* find last non zero block */ + ptr = g->sb_hybrid + 576; + ptr1 = g->sb_hybrid + 2 * 18; + while (ptr >= ptr1) { + ptr -= 6; + v = ptr[0] | ptr[1] | ptr[2] | ptr[3] | ptr[4] | ptr[5]; + if (v != 0) + break; + } + sblimit = ((ptr - g->sb_hybrid) / 18) + 1; + + if (g->block_type == 2) { + /* XXX: check for 8000 Hz */ + if (g->switch_point) + mdct_long_end = 2; + else + mdct_long_end = 0; + } else { + mdct_long_end = sblimit; + } + + buf = mdct_buf; + ptr = g->sb_hybrid; + for(j=0;jswitch_point && j < 2) + win1 = mdct_win[0]; + else + win1 = mdct_win[g->block_type]; + /* select frequency inversion */ + win = win1 + ((4 * 36) & -(j & 1)); + imdct36(out_ptr, buf, ptr, win); + out_ptr += 18*SBLIMIT; + ptr += 18; + buf += 18; + } + for(j=mdct_long_end;j 32767) + v = 32767; + else if (v < -32768) + v = -32768; + synth_buf[j] = v; + } + /* copy to avoid wrap */ + libc_memcpy(synth_buf + 512, synth_buf, 32 * sizeof(int16_t)); + + samples2 = samples + 31 * incr; + w = window; + w2 = window + 31; + + sum = *dither_state; + p = synth_buf + 16; + SUM8(sum, +=, w, p); + p = synth_buf + 48; + SUM8(sum, -=, w + 32, p); + *samples = round_sample(&sum); + samples += incr; + w++; + + /* we calculate two samples at the same time to avoid one memory + access per two sample */ + for(j=1;j<16;j++) { + sum2 = 0; + p = synth_buf + 16 + j; + SUM8P2(sum, +=, sum2, -=, w, w2, p); + p = synth_buf + 48 - j; + SUM8P2(sum, -=, sum2, -=, w + 32, w2 + 32, p); + + *samples = round_sample(&sum); + samples += incr; + sum += sum2; + *samples2 = round_sample(&sum); + samples2 -= incr; + w++; + w2--; + } + + p = synth_buf + 32; + SUM8(sum, -=, w + 32, p); + *samples = round_sample(&sum); + *dither_state= sum; + + offset = (offset - 32) & 511; + *synth_buf_offset = offset; +} + +//////////////////////////////////////////////////////////////////////////////// + +static int decode_header(mp3_context_t *s, uint32_t header) { + int sample_rate, frame_size, mpeg25, padding; + int sample_rate_index, bitrate_index; + if (header & (1<<20)) { + s->lsf = (header & (1<<19)) ? 0 : 1; + mpeg25 = 0; + } else { + s->lsf = 1; + mpeg25 = 1; + } + + sample_rate_index = (header >> 10) & 3; + sample_rate = mp3_freq_tab[sample_rate_index] >> (s->lsf + mpeg25); + sample_rate_index += 3 * (s->lsf + mpeg25); + s->sample_rate_index = sample_rate_index; + s->error_protection = ((header >> 16) & 1) ^ 1; + s->sample_rate = sample_rate; + + bitrate_index = (header >> 12) & 0xf; + padding = (header >> 9) & 1; + s->mode = (header >> 6) & 3; + s->mode_ext = (header >> 4) & 3; + s->nb_channels = (s->mode == MP3_MONO) ? 1 : 2; + + if (bitrate_index != 0) { + frame_size = mp3_bitrate_tab[s->lsf][bitrate_index]; + s->bit_rate = frame_size * 1000; + s->frame_size = (frame_size * 144000) / (sample_rate << s->lsf) + padding; + } else { + /* if no frame size computed, signal it */ + return 1; + } + return 0; +} + +static int mp_decode_layer3(mp3_context_t *s) { + int nb_granules, main_data_begin, private_bits; + int gr, ch, blocksplit_flag, i, j, k, n, bits_pos; + granule_t *g; + static granule_t granules[2][2]; + static int16_t exponents[576]; + const uint8_t *ptr; + + if (s->lsf) { + main_data_begin = get_bits(&s->gb, 8); + private_bits = get_bits(&s->gb, s->nb_channels); + nb_granules = 1; + } else { + main_data_begin = get_bits(&s->gb, 9); + if (s->nb_channels == 2) + private_bits = get_bits(&s->gb, 3); + else + private_bits = get_bits(&s->gb, 5); + nb_granules = 2; + for(ch=0;chnb_channels;ch++) { + granules[ch][0].scfsi = 0; /* all scale factors are transmitted */ + granules[ch][1].scfsi = get_bits(&s->gb, 4); + } + } + + for(gr=0;grnb_channels;ch++) { + g = &granules[ch][gr]; + g->part2_3_length = get_bits(&s->gb, 12); + g->big_values = get_bits(&s->gb, 9); + g->global_gain = get_bits(&s->gb, 8); + /* if MS stereo only is selected, we precompute the + 1/sqrt(2) renormalization factor */ + if ((s->mode_ext & (MODE_EXT_MS_STEREO | MODE_EXT_I_STEREO)) == + MODE_EXT_MS_STEREO) + g->global_gain -= 2; + if (s->lsf) + g->scalefac_compress = get_bits(&s->gb, 9); + else + g->scalefac_compress = get_bits(&s->gb, 4); + blocksplit_flag = get_bits(&s->gb, 1); + if (blocksplit_flag) { + g->block_type = get_bits(&s->gb, 2); + if (g->block_type == 0) + return -1; + g->switch_point = get_bits(&s->gb, 1); + for(i=0;i<2;i++) + g->table_select[i] = get_bits(&s->gb, 5); + for(i=0;i<3;i++) + g->subblock_gain[i] = get_bits(&s->gb, 3); + /* compute huffman coded region sizes */ + if (g->block_type == 2) + g->region_size[0] = (36 / 2); + else { + if (s->sample_rate_index <= 2) + g->region_size[0] = (36 / 2); + else if (s->sample_rate_index != 8) + g->region_size[0] = (54 / 2); + else + g->region_size[0] = (108 / 2); + } + g->region_size[1] = (576 / 2); + } else { + int region_address1, region_address2, l; + g->block_type = 0; + g->switch_point = 0; + for(i=0;i<3;i++) + g->table_select[i] = get_bits(&s->gb, 5); + /* compute huffman coded region sizes */ + region_address1 = get_bits(&s->gb, 4); + region_address2 = get_bits(&s->gb, 3); + g->region_size[0] = + band_index_long[s->sample_rate_index][region_address1 + 1] >> 1; + l = region_address1 + region_address2 + 2; + /* should not overflow */ + if (l > 22) + l = 22; + g->region_size[1] = + band_index_long[s->sample_rate_index][l] >> 1; + } + /* convert region offsets to region sizes and truncate + size to big_values */ + g->region_size[2] = (576 / 2); + j = 0; + for(i=0;i<3;i++) { + k = g->region_size[i]; + if (g->big_values < k) k = g->big_values; + g->region_size[i] = k - j; + j = k; + } + + /* compute band indexes */ + if (g->block_type == 2) { + if (g->switch_point) { + /* if switched mode, we handle the 36 first samples as + long blocks. For 8000Hz, we handle the 48 first + exponents as long blocks (XXX: check this!) */ + if (s->sample_rate_index <= 2) + g->long_end = 8; + else if (s->sample_rate_index != 8) + g->long_end = 6; + else + g->long_end = 4; /* 8000 Hz */ + + g->short_start = 2 + (s->sample_rate_index != 8); + } else { + g->long_end = 0; + g->short_start = 0; + } + } else { + g->short_start = 13; + g->long_end = 22; + } + + g->preflag = 0; + if (!s->lsf) + g->preflag = get_bits(&s->gb, 1); + g->scalefac_scale = get_bits(&s->gb, 1); + g->count1table_select = get_bits(&s->gb, 1); + } + } + + ptr = s->gb.buffer + (get_bits_count(&s->gb)>>3); + /* now we get bits from the main_data_begin offset */ + if(main_data_begin > s->last_buf_size){ + s->last_buf_size= main_data_begin; + } + + memcpy(s->last_buf + s->last_buf_size, ptr, EXTRABYTES); + s->in_gb= s->gb; + init_get_bits(&s->gb, s->last_buf + s->last_buf_size - main_data_begin, main_data_begin*8); + + for(gr=0;grnb_channels;ch++) { + g = &granules[ch][gr]; + + bits_pos = get_bits_count(&s->gb); + + if (!s->lsf) { + uint8_t *sc; + int slen, slen1, slen2; + + /* MPEG1 scale factors */ + slen1 = slen_table[0][g->scalefac_compress]; + slen2 = slen_table[1][g->scalefac_compress]; + if (g->block_type == 2) { + n = g->switch_point ? 17 : 18; + j = 0; + if(slen1){ + for(i=0;iscale_factors[j++] = get_bits(&s->gb, slen1); + }else{ + libc_memset((void*) &g->scale_factors[j], 0, n); + j += n; +// for(i=0;iscale_factors[j++] = 0; + } + if(slen2){ + for(i=0;i<18;i++) + g->scale_factors[j++] = get_bits(&s->gb, slen2); + for(i=0;i<3;i++) + g->scale_factors[j++] = 0; + }else{ + for(i=0;i<21;i++) + g->scale_factors[j++] = 0; + } + } else { + sc = granules[ch][0].scale_factors; + j = 0; + for(k=0;k<4;k++) { + n = (k == 0 ? 6 : 5); + if ((g->scfsi & (0x8 >> k)) == 0) { + slen = (k < 2) ? slen1 : slen2; + if(slen){ + for(i=0;iscale_factors[j++] = get_bits(&s->gb, slen); + }else{ + libc_memset((void*) &g->scale_factors[j], 0, n); + j += n; +// for(i=0;iscale_factors[j++] = 0; + } + } else { + /* simply copy from last granule */ + for(i=0;iscale_factors[j] = sc[j]; + j++; + } + } + } + g->scale_factors[j++] = 0; + } + } else { + int tindex, tindex2, slen[4], sl, sf; + + /* LSF scale factors */ + if (g->block_type == 2) { + tindex = g->switch_point ? 2 : 1; + } else { + tindex = 0; + } + sf = g->scalefac_compress; + if ((s->mode_ext & MODE_EXT_I_STEREO) && ch == 1) { + /* intensity stereo case */ + sf >>= 1; + if (sf < 180) { + lsf_sf_expand(slen, sf, 6, 6, 0); + tindex2 = 3; + } else if (sf < 244) { + lsf_sf_expand(slen, sf - 180, 4, 4, 0); + tindex2 = 4; + } else { + lsf_sf_expand(slen, sf - 244, 3, 0, 0); + tindex2 = 5; + } + } else { + /* normal case */ + if (sf < 400) { + lsf_sf_expand(slen, sf, 5, 4, 4); + tindex2 = 0; + } else if (sf < 500) { + lsf_sf_expand(slen, sf - 400, 5, 4, 0); + tindex2 = 1; + } else { + lsf_sf_expand(slen, sf - 500, 3, 0, 0); + tindex2 = 2; + g->preflag = 1; + } + } + + j = 0; + for(k=0;k<4;k++) { + n = lsf_nsf_table[tindex2][tindex][k]; + sl = slen[k]; + if(sl){ + for(i=0;iscale_factors[j++] = get_bits(&s->gb, sl); + }else{ + libc_memset((void*) &g->scale_factors[j], 0, n); + j += n; +// for(i=0;iscale_factors[j++] = 0; + } + } + /* XXX: should compute exact size */ + libc_memset((void*) &g->scale_factors[j], 0, 40 - j); +// for(;j<40;j++) +// g->scale_factors[j] = 0; + } + + exponents_from_scale_factors(s, g, exponents); + + /* read Huffman coded residue */ + if (huffman_decode(s, g, exponents, + bits_pos + g->part2_3_length) < 0) + return -1; + } /* ch */ + + if (s->nb_channels == 2) + compute_stereo(s, &granules[0][gr], &granules[1][gr]); + + for(ch=0;chnb_channels;ch++) { + g = &granules[ch][gr]; + reorder_block(s, g); + compute_antialias(s, g); + compute_imdct(s, g, &s->sb_samples[ch][18 * gr][0], s->mdct_buf[ch]); + } + } /* gr */ + return nb_granules * 18; +} + +static int mp3_decode_main( + mp3_context_t *s, + int16_t *samples, const uint8_t *buf, int buf_size +) { + int i, nb_frames, ch; + int16_t *samples_ptr; + + init_get_bits(&s->gb, buf + HEADER_SIZE, (buf_size - HEADER_SIZE)*8); + + if (s->error_protection) + get_bits(&s->gb, 16); + + nb_frames = mp_decode_layer3(s); + + s->last_buf_size=0; + if(s->in_gb.buffer){ + align_get_bits(&s->gb); + i= (s->gb.size_in_bits - get_bits_count(&s->gb))>>3; + if(i >= 0 && i <= BACKSTEP_SIZE){ + libc_memmove(s->last_buf, s->gb.buffer + (get_bits_count(&s->gb)>>3), i); + s->last_buf_size=i; + } + s->gb= s->in_gb; + } + + align_get_bits(&s->gb); + i= (s->gb.size_in_bits - get_bits_count(&s->gb))>>3; + + if(i<0 || i > BACKSTEP_SIZE || nb_frames<0){ + i = buf_size - HEADER_SIZE; + if (BACKSTEP_SIZE < i) i = BACKSTEP_SIZE; + } + libc_memcpy(s->last_buf + s->last_buf_size, s->gb.buffer + buf_size - HEADER_SIZE - i, i); + s->last_buf_size += i; + + /* apply the synthesis filter */ + for(ch=0;chnb_channels;ch++) { + samples_ptr = samples + ch; + for(i=0;isynth_buf[ch], &(s->synth_buf_offset[ch]), + window, &s->dither_state, + samples_ptr, s->nb_channels, + s->sb_samples[ch][i] + ); + samples_ptr += 32 * s->nb_channels; + } + } + return nb_frames * 32 * sizeof(uint16_t) * s->nb_channels; +} + +//////////////////////////////////////////////////////////////////////////////// + +static int mp3_decode_init(mp3_context_t *s) { + static int init=0; + int i, j, k; + + if (!init) { + /* synth init */ + for(i=0;i<257;i++) { + int v; + v = mp3_enwindow[i]; + #if WFRAC_BITS < 16 + v = (v + (1 << (16 - WFRAC_BITS - 1))) >> (16 - WFRAC_BITS); + #endif + window[i] = v; + if ((i & 63) != 0) + v = -v; + if (i != 0) + window[512 - i] = v; + } + + /* huffman decode tables */ + for(i=1;i<16;i++) { + const huff_table_t *h = &mp3_huff_tables[i]; + int xsize, x, y; + unsigned int n; + uint8_t tmp_bits [512]; + uint16_t tmp_codes[512]; + + libc_memset(tmp_bits , 0, sizeof(tmp_bits )); + libc_memset(tmp_codes, 0, sizeof(tmp_codes)); + + xsize = h->xsize; + n = xsize * xsize; + + j = 0; + for(x=0;xbits [j ]; + tmp_codes[(x << 5) | y | ((x&&y)<<4)]= h->codes[j++]; + } + } + + init_vlc(&huff_vlc[i], 7, 512, + tmp_bits, 1, 1, tmp_codes, 2, 2); + } + for(i=0;i<2;i++) { + init_vlc(&huff_quad_vlc[i], i == 0 ? 7 : 4, 16, + mp3_quad_bits[i], 1, 1, mp3_quad_codes[i], 1, 1); + } + + for(i=0;i<9;i++) { + k = 0; + for(j=0;j<22;j++) { + band_index_long[i][j] = k; + k += band_size_long[i][j]; + } + band_index_long[i][22] = k; + } + + /* compute n ^ (4/3) and store it in mantissa/exp format */ + table_4_3_exp= libc_malloc(TABLE_4_3_SIZE * sizeof(table_4_3_exp[0])); + if(!table_4_3_exp) + return -1; + table_4_3_value= libc_malloc(TABLE_4_3_SIZE * sizeof(table_4_3_value[0])); + if(!table_4_3_value) + return -1; + + for(i=1;i>4); + double f= libc_pow(i&15, 4.0 / 3.0) * libc_pow(2, (exponent-400)*0.25 + FRAC_BITS + 5); + expval_table[exponent][i&15]= f; + if((i&15)==1) + exp_table[exponent]= f; + } + + for(i=0;i<7;i++) { + float f; + int v; + if (i != 6) { + f = tan((double)i * M_PI / 12.0); + v = FIXR(f / (1.0 + f)); + } else { + v = FIXR(1.0); + } + is_table[0][i] = v; + is_table[1][6 - i] = v; + } + for(i=7;i<16;i++) + is_table[0][i] = is_table[1][i] = 0.0; + + for(i=0;i<16;i++) { + double f; + int e, k; + + for(j=0;j<2;j++) { + e = -(j + 1) * ((i + 1) >> 1); + f = libc_pow(2.0, e / 4.0); + k = i & 1; + is_table_lsf[j][k ^ 1][i] = FIXR(f); + is_table_lsf[j][k][i] = FIXR(1.0); + } + } + + for(i=0;i<8;i++) { + float ci, cs, ca; + ci = ci_table[i]; + cs = 1.0 / sqrt(1.0 + ci * ci); + ca = cs * ci; + csa_table[i][0] = FIXHR(cs/4); + csa_table[i][1] = FIXHR(ca/4); + csa_table[i][2] = FIXHR(ca/4) + FIXHR(cs/4); + csa_table[i][3] = FIXHR(ca/4) - FIXHR(cs/4); + csa_table_float[i][0] = cs; + csa_table_float[i][1] = ca; + csa_table_float[i][2] = ca + cs; + csa_table_float[i][3] = ca - cs; + } + + /* compute mdct windows */ + for(i=0;i<36;i++) { + for(j=0; j<4; j++){ + double d; + + if(j==2 && i%3 != 1) + continue; + + d= sin(M_PI * (i + 0.5) / 36.0); + if(j==1){ + if (i>=30) d= 0; + else if(i>=24) d= sin(M_PI * (i - 18 + 0.5) / 12.0); + else if(i>=18) d= 1; + }else if(j==3){ + if (i< 6) d= 0; + else if(i< 12) d= sin(M_PI * (i - 6 + 0.5) / 12.0); + else if(i< 18) d= 1; + } + d*= 0.5 / cos(M_PI*(2*i + 19)/72); + if(j==2) + mdct_win[j][i/3] = FIXHR((d / (1<<5))); + else + mdct_win[j][i ] = FIXHR((d / (1<<5))); + } + } + for(j=0;j<4;j++) { + for(i=0;i<36;i+=2) { + mdct_win[j + 4][i] = mdct_win[j][i]; + mdct_win[j + 4][i + 1] = -mdct_win[j][i + 1]; + } + } + init = 1; + } + return 0; +} + +static int mp3_decode_frame( + mp3_context_t *s, + int16_t *out_samples, int *data_size, + uint8_t *buf, int buf_size +) { + uint32_t header; + int out_size; + int extra_bytes = 0; + +retry: + if(buf_size < HEADER_SIZE) + return -1; + + header = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + if(mp3_check_header(header) < 0){ + buf++; + buf_size--; + extra_bytes++; + goto retry; + } + + if (decode_header(s, header) == 1) { + s->frame_size = -1; + return -1; + } + + if(s->frame_size<=0 || s->frame_size > buf_size){ + return -1; // incomplete frame + } + if(s->frame_size < buf_size) { + buf_size = s->frame_size; + } + + out_size = mp3_decode_main(s, out_samples, buf, buf_size); + if(out_size>=0) + *data_size = out_size; + // else: Error while decoding MPEG audio frame. + s->frame_size += extra_bytes; + return buf_size; +} + +//////////////////////////////////////////////////////////////////////////////// + +#if 1 // OpenMPT +mp3_decoder_t *mp3_create(void) { // OpenMPT +#else // OpenMPT +mp3_decoder_t mp3_create(void) { +#endif // OpenMPT + void *dec = libc_calloc(sizeof(mp3_context_t), 1); + if (dec) mp3_decode_init((mp3_context_t*) dec); +#if 1 // OpenMPT + return (mp3_decoder_t*) dec; // OpenMPT +#else // OpenMPT + return (mp3_decoder_t) dec; +#endif // OpenMPT +} + +void mp3_done(mp3_decoder_t *dec) { + if (dec) libc_free(dec); +} + +int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info) { + int res, size = -1; + mp3_context_t *s = (mp3_context_t*) dec; + if (!s) return 0; + res = mp3_decode_frame(s, (int16_t*) out, &size, buf, bytes); + if (res < 0) return 0; + if (info) { + info->sample_rate = s->sample_rate; + info->channels = s->nb_channels; + info->audio_bytes = size; + } + return s->frame_size; +} diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h new file mode 100644 index 000000000..06f49ebb6 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h @@ -0,0 +1,23 @@ +#ifndef __MINIMP3_H_INCLUDED__ +#define __MINIMP3_H_INCLUDED__ + +#define MP3_MAX_SAMPLES_PER_FRAME (1152*2) + +typedef struct _mp3_info { + int sample_rate; + int channels; + int audio_bytes; // generated amount of audio per frame +} mp3_info_t; + +typedef void* mp3_decoder_t; + +#if 1 // OpenMPT +extern mp3_decoder_t *mp3_create(void); // OpenMPT +#else // OpenMPT +extern mp3_decoder_t mp3_create(void); +#endif // OpenMPT +extern int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info); +extern void mp3_done(mp3_decoder_t *dec); +#define mp3_free(dec) do { mp3_done(dec); dec = NULL; } while(0) + +#endif//__MINIMP3_H_INCLUDED__ diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt new file mode 100644 index 000000000..db64ae76f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt @@ -0,0 +1,17 @@ +miniz DEFLATE implementation. +https://github.com/richgel999/miniz +2.0.6 beta +Modifications for OpenMPT: + * #define MINIZ_NO_STDIO has been set because OpenMPT does not need stdio + functionality and miniz relies on secure-CRT file i/o functions in windows + builds which are not available on all mingw64 versions. + * #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 is used unconditionally, + because unaligned access is undefined behaviour. + * Various warnings in platform detection logic using undefined macros have + been fixed. + * Warning `warning: cast from 'const mz_uint8 *' (aka 'const unsigned char *') + to 'const mz_uint32 *' (aka 'const unsigned int *') increases required + alignment from 1 to 4 [-Wcast-align]` has been fixed. + * Prototypes of `tdefl_compressor_alloc` and `tinfl_decompressor_alloc` + have beeen fixed +No further changes have been made. diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c new file mode 100644 index 000000000..65e18d8b5 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c @@ -0,0 +1,7579 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + +#include "miniz.h" + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + 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 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. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +#if 0 // OpenMPT +tdefl_compressor *tdefl_compressor_alloc() +#else // OpenMPT +tdefl_compressor *tdefl_compressor_alloc(void) // OpenMPT +#endif // OpenMPT +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#if 0 // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc() +#else // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc(void) // OpenMPT +#endif // OpenMPT +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + + pArchive_name++; + } + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (uncomp_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +static mz_uint32 read_32ne(const mz_uint8 *p) // OpenMPT +{ // OpenMPT + mz_uint32 result = 0; // OpenMPT + memcpy(&result, p, sizeof(mz_uint32)); // OpenMPT + return result; // OpenMPT +} // OpenMPT + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ +#if 0 // OpenMPT + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; +#else // OpenMPT + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); // OpenMPT + const mz_uint32 src_crc32 = read_32ne(pSrc_descriptor + (0 * sizeof(mz_uint32))); // OpenMPT + const mz_uint64 src_comp_size = read_32ne(pSrc_descriptor + (1 * sizeof(mz_uint32)));; // OpenMPT + const mz_uint64 src_uncomp_size = read_32ne(pSrc_descriptor + (2 * sizeof(mz_uint32))); // OpenMPT +#endif // OpenMPT + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h new file mode 100644 index 000000000..66aa727ce --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h @@ -0,0 +1,1349 @@ +/* miniz.c 2.0.6 beta - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ +#define MINIZ_NO_STDIO // OpenMPT + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) // OpenMPT +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif +#else // OpenMPT +#if MINIZ_X86_OR_X64_CPU // OpenMPT +#define MINIZ_LITTLE_ENDIAN 1 // OpenMPT +#else // OpenMPT +#define MINIZ_LITTLE_ENDIAN 0 // OpenMPT +#endif // OpenMPT +#endif // OpenMPT + +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#if 0 // OpenMPT +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#else // OpenMPT +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 // OpenMPT +#endif // OpenMPT +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.0.1" +#define MZ_VERNUM 0xA010 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 0 +#define MZ_VER_REVISION 1 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif +#pragma once +#include +#include +#include +#include + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern void miniz_def_free_func(void *opaque, void *address); +extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif +#pragma once + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +#if 0 // OpenMPT +tdefl_compressor *tdefl_compressor_alloc(); +#else // OpenMPT +tdefl_compressor *tdefl_compressor_alloc(void); // OpenMPT +#endif // OpenMPT +void tdefl_compressor_free(tdefl_compressor *pComp); + +#ifdef __cplusplus +} +#endif +#pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ + +#if 0 // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc(); +#else // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc(void); // OpenMPT +#endif // OpenMPT +void tinfl_decompressor_free(tinfl_decompressor *pDecomp); + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +void mz_zip_zero_struct(mz_zip_archive *pZip); + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +/* Returns MZ_FALSE if the file cannot be found. */ +mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in new file mode 100644 index 000000000..dc35085b2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libmodplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: 0.8.8.5 +Requires.private: libopenmpt +Libs: -L${libdir} -lmodplug +Libs.private: +Cflags: -I${includedir} + diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h new file mode 100644 index 000000000..46ea02c16 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h @@ -0,0 +1,171 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#ifndef MODPLUG_H__INCLUDED +#define MODPLUG_H__INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +struct _ModPlugFile; +typedef struct _ModPlugFile ModPlugFile; + +struct _ModPlugNote { + unsigned char Note; + unsigned char Instrument; + unsigned char VolumeEffect; + unsigned char Effect; + unsigned char Volume; + unsigned char Parameter; +}; +typedef struct _ModPlugNote ModPlugNote; + +typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); + +/* Load a mod file. [data] should point to a block of memory containing the complete + * file, and [size] should be the size of that block. + * Return the loaded mod file on success, or NULL on failure. */ +ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +void ModPlug_Unload(ModPlugFile* file); + +/* Read sample data into the buffer. Returns the number of bytes read. If the end + * of the mod has been reached, zero is returned. */ +int ModPlug_Read(ModPlugFile* file, void* buffer, int size); + +/* Get the name of the mod. The returned buffer is stored within the ModPlugFile + * structure and will remain valid until you unload the file. */ +const char* ModPlug_GetName(ModPlugFile* file); + +/* Get the length of the mod, in milliseconds. Note that this result is not always + * accurate, especially in the case of mods with loops. */ +int ModPlug_GetLength(ModPlugFile* file); + +/* Seek to a particular position in the song. Note that seeking and MODs don't mix very + * well. Some mods will be missing instruments for a short time after a seek, as ModPlug + * does not scan the sequence backwards to find out which instruments were supposed to be + * playing at that time. (Doing so would be difficult and not very reliable.) Also, + * note that seeking is not very exact in some mods -- especially those for which + * ModPlug_GetLength() does not report the full length. */ +void ModPlug_Seek(ModPlugFile* file, int millisecond); + +enum _ModPlug_Flags +{ + MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ + MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ + MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ + MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ + MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ +}; + +enum _ModPlug_ResamplingMode +{ + MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ + MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ + MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ + MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ +}; + +typedef struct _ModPlug_Settings +{ + int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ + + /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then + * down-mixes to the settings you choose. */ + int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ + int mBits; /* Bits per sample - 8, 16, or 32 */ + int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ + int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ + + int mStereoSeparation; /* Stereo separation, 1 - 256 */ + int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */ + + int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ + int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ + int mBassAmount; /* XBass level 0(quiet)-100(loud) */ + int mBassRange; /* XBass cutoff in Hz 10-100 */ + int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ + int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ + int mLoopCount; /* Number of times to loop. Zero prevents looping. + -1 loops forever. */ +} ModPlug_Settings; + +/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, + * sampling rate, and loop count, will take effect immediately. Those options which don't + * take effect immediately will take effect the next time you load a mod. */ +void ModPlug_GetSettings(ModPlug_Settings* settings); +void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +int ModPlug_GetCurrentSpeed(ModPlugFile* file); +int ModPlug_GetCurrentTempo(ModPlugFile* file); +int ModPlug_GetCurrentOrder(ModPlugFile* file); +int ModPlug_GetCurrentPattern(ModPlugFile* file); +int ModPlug_GetCurrentRow(ModPlugFile* file); +int ModPlug_GetPlayingChannels(ModPlugFile* file); + +void ModPlug_SeekOrder(ModPlugFile* file,int order); +int ModPlug_GetModuleType(ModPlugFile* file); +char* ModPlug_GetMessage(ModPlugFile* file); + + +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif // MODPLUG_NO_FILESAVE + +unsigned int ModPlug_NumInstruments(ModPlugFile* file); +unsigned int ModPlug_NumSamples(ModPlugFile* file); +unsigned int ModPlug_NumPatterns(ModPlugFile* file); +unsigned int ModPlug_NumChannels(ModPlugFile* file); +unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); + +/* + * ================= + * Mixer callback + * ================= + * + * Use this callback if you want to 'modify' the mixed data of LibModPlug. + * + * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; + * + * 'buffer': A buffer of mixed samples + * 'channels': N. of channels in the buffer + * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) + * + * (Samples are signed 32-bit integers) + */ +void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h new file mode 100644 index 000000000..8e02ad8a8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h @@ -0,0 +1,168 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#ifndef MODPLUG_H__INCLUDED +#define MODPLUG_H__INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +struct _ModPlugFile; +typedef struct _ModPlugFile ModPlugFile; + +struct _ModPlugNote { + unsigned char Note; + unsigned char Instrument; + unsigned char VolumeEffect; + unsigned char Effect; + unsigned char Volume; + unsigned char Parameter; +}; +typedef struct _ModPlugNote ModPlugNote; + +typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); + +/* Load a mod file. [data] should point to a block of memory containing the complete + * file, and [size] should be the size of that block. + * Return the loaded mod file on success, or NULL on failure. */ +ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +void ModPlug_Unload(ModPlugFile* file); + +/* Read sample data into the buffer. Returns the number of bytes read. If the end + * of the mod has been reached, zero is returned. */ +int ModPlug_Read(ModPlugFile* file, void* buffer, int size); + +/* Get the name of the mod. The returned buffer is stored within the ModPlugFile + * structure and will remain valid until you unload the file. */ +const char* ModPlug_GetName(ModPlugFile* file); + +/* Get the length of the mod, in milliseconds. Note that this result is not always + * accurate, especially in the case of mods with loops. */ +int ModPlug_GetLength(ModPlugFile* file); + +/* Seek to a particular position in the song. Note that seeking and MODs don't mix very + * well. Some mods will be missing instruments for a short time after a seek, as ModPlug + * does not scan the sequence backwards to find out which instruments were supposed to be + * playing at that time. (Doing so would be difficult and not very reliable.) Also, + * note that seeking is not very exact in some mods -- especially those for which + * ModPlug_GetLength() does not report the full length. */ +void ModPlug_Seek(ModPlugFile* file, int millisecond); + +enum _ModPlug_Flags +{ + MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ + MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ + MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ + MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ + MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ +}; + +enum _ModPlug_ResamplingMode +{ + MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ + MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ + MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ + MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ +}; + +typedef struct _ModPlug_Settings +{ + int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ + + /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then + * down-mixes to the settings you choose. */ + int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ + int mBits; /* Bits per sample - 8, 16, or 32 */ + int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ + int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ + + int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ + int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ + int mBassAmount; /* XBass level 0(quiet)-100(loud) */ + int mBassRange; /* XBass cutoff in Hz 10-100 */ + int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ + int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ + int mLoopCount; /* Number of times to loop. Zero prevents looping. + -1 loops forever. */ +} ModPlug_Settings; + +/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, + * sampling rate, and loop count, will take effect immediately. Those options which don't + * take effect immediately will take effect the next time you load a mod. */ +void ModPlug_GetSettings(ModPlug_Settings* settings); +void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +int ModPlug_GetCurrentSpeed(ModPlugFile* file); +int ModPlug_GetCurrentTempo(ModPlugFile* file); +int ModPlug_GetCurrentOrder(ModPlugFile* file); +int ModPlug_GetCurrentPattern(ModPlugFile* file); +int ModPlug_GetCurrentRow(ModPlugFile* file); +int ModPlug_GetPlayingChannels(ModPlugFile* file); + +void ModPlug_SeekOrder(ModPlugFile* file,int order); +int ModPlug_GetModuleType(ModPlugFile* file); +char* ModPlug_GetMessage(ModPlugFile* file); + + +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif // MODPLUG_NO_FILESAVE + +unsigned int ModPlug_NumInstruments(ModPlugFile* file); +unsigned int ModPlug_NumSamples(ModPlugFile* file); +unsigned int ModPlug_NumPatterns(ModPlugFile* file); +unsigned int ModPlug_NumChannels(ModPlugFile* file); +unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); + +/* + * ================= + * Mixer callback + * ================= + * + * Use this callback if you want to 'modify' the mixed data of LibModPlug. + * + * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; + * + * 'buffer': A buffer of mixed samples + * 'channels': N. of channels in the buffer + * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) + * + * (Samples are signed 32-bit integers) + */ +void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h new file mode 100644 index 000000000..ed7d47e55 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h @@ -0,0 +1,1017 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) +*/ + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +#include "config.h" +#define CONFIG_H_INCLUDED 1 +#endif + +#ifndef __SNDFILE_H +#define __SNDFILE_H + +#ifdef UNDER_CE +int _strnicmp(const char *str1,const char *str2, int n); +#endif + +#ifndef LPCBYTE +typedef const BYTE * LPCBYTE; +#endif + +#define MOD_AMIGAC2 0x1AB +#define MAX_SAMPLE_LENGTH 16000000 +#define MAX_SAMPLE_RATE 192000 +#define MAX_ORDERS 256 +#define MAX_PATTERNS 240 +#define MAX_SAMPLES 240 +#define MAX_INSTRUMENTS MAX_SAMPLES +#ifdef MODPLUG_FASTSOUNDLIB +#define MAX_CHANNELS 80 +#else +#define MAX_CHANNELS 128 +#endif +#define MAX_BASECHANNELS 64 +#define MAX_ENVPOINTS 32 +#define MIN_PERIOD 0x0020 +#define MAX_PERIOD 0xFFFF +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 +#define MAX_INFONAME 80 +#define MAX_EQ_BANDS 6 +#define MAX_MIXPLUGINS 8 + + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + + + +// Channel flags: +// Bits 0-7: Sample Flags +#define CHN_16BIT 0x01 +#define CHN_LOOP 0x02 +#define CHN_PINGPONGLOOP 0x04 +#define CHN_SUSTAINLOOP 0x08 +#define CHN_PINGPONGSUSTAIN 0x10 +#define CHN_PANNING 0x20 +#define CHN_STEREO 0x40 +#define CHN_PINGPONGFLAG 0x80 +// Bits 8-31: Channel Flags +#define CHN_MUTE 0x100 +#define CHN_KEYOFF 0x200 +#define CHN_NOTEFADE 0x400 +#define CHN_SURROUND 0x800 +#define CHN_NOIDO 0x1000 +#define CHN_HQSRC 0x2000 +#define CHN_FILTER 0x4000 +#define CHN_VOLUMERAMP 0x8000 +#define CHN_VIBRATO 0x10000 +#define CHN_TREMOLO 0x20000 +#define CHN_PANBRELLO 0x40000 +#define CHN_PORTAMENTO 0x80000 +#define CHN_GLISSANDO 0x100000 +#define CHN_VOLENV 0x200000 +#define CHN_PANENV 0x400000 +#define CHN_PITCHENV 0x800000 +#define CHN_FASTVOLRAMP 0x1000000 +#define CHN_EXTRALOUD 0x2000000 +#define CHN_REVERB 0x4000000 +#define CHN_NOREVERB 0x8000000 + + +#define ENV_VOLUME 0x0001 +#define ENV_VOLSUSTAIN 0x0002 +#define ENV_VOLLOOP 0x0004 +#define ENV_PANNING 0x0008 +#define ENV_PANSUSTAIN 0x0010 +#define ENV_PANLOOP 0x0020 +#define ENV_PITCH 0x0040 +#define ENV_PITCHSUSTAIN 0x0080 +#define ENV_PITCHLOOP 0x0100 +#define ENV_SETPANNING 0x0200 +#define ENV_FILTER 0x0400 +#define ENV_VOLCARRY 0x0800 +#define ENV_PANCARRY 0x1000 +#define ENV_PITCHCARRY 0x2000 + +#define CMD_NONE 0 +#define CMD_ARPEGGIO 1 +#define CMD_PORTAMENTOUP 2 +#define CMD_PORTAMENTODOWN 3 +#define CMD_TONEPORTAMENTO 4 +#define CMD_VIBRATO 5 +#define CMD_TONEPORTAVOL 6 +#define CMD_VIBRATOVOL 7 +#define CMD_TREMOLO 8 +#define CMD_PANNING8 9 +#define CMD_OFFSET 10 +#define CMD_VOLUMESLIDE 11 +#define CMD_POSITIONJUMP 12 +#define CMD_VOLUME 13 +#define CMD_PATTERNBREAK 14 +#define CMD_RETRIG 15 +#define CMD_SPEED 16 +#define CMD_TEMPO 17 +#define CMD_TREMOR 18 +#define CMD_MODCMDEX 19 +#define CMD_S3MCMDEX 20 +#define CMD_CHANNELVOLUME 21 +#define CMD_CHANNELVOLSLIDE 22 +#define CMD_GLOBALVOLUME 23 +#define CMD_GLOBALVOLSLIDE 24 +#define CMD_KEYOFF 25 +#define CMD_FINEVIBRATO 26 +#define CMD_PANBRELLO 27 +#define CMD_XFINEPORTAUPDOWN 28 +#define CMD_PANNINGSLIDE 29 +#define CMD_SETENVPOSITION 30 +#define CMD_MIDI 31 + + +// Volume Column commands +#define VOLCMD_VOLUME 1 +#define VOLCMD_PANNING 2 +#define VOLCMD_VOLSLIDEUP 3 +#define VOLCMD_VOLSLIDEDOWN 4 +#define VOLCMD_FINEVOLUP 5 +#define VOLCMD_FINEVOLDOWN 6 +#define VOLCMD_VIBRATOSPEED 7 +#define VOLCMD_VIBRATO 8 +#define VOLCMD_PANSLIDELEFT 9 +#define VOLCMD_PANSLIDERIGHT 10 +#define VOLCMD_TONEPORTAMENTO 11 +#define VOLCMD_PORTAUP 12 +#define VOLCMD_PORTADOWN 13 + +#define RSF_16BIT 0x04 +#define RSF_STEREO 0x08 + +#define RS_PCM8S 0 // 8-bit signed +#define RS_PCM8U 1 // 8-bit unsigned +#define RS_PCM8D 2 // 8-bit delta values +#define RS_ADPCM4 3 // 4-bit ADPCM-packed +#define RS_PCM16D 4 // 16-bit delta values +#define RS_PCM16S 5 // 16-bit signed +#define RS_PCM16U 6 // 16-bit unsigned +#define RS_PCM16M 7 // 16-bit motorola order +#define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed +#define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values +#define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed +#define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values +#define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian +// IT 2.14 compressed samples +#define RS_IT2148 0x10 +#define RS_IT21416 0x14 +#define RS_IT2158 0x12 +#define RS_IT21516 0x16 +// AMS Packed Samples +#define RS_AMS8 0x11 +#define RS_AMS16 0x15 +// DMF Huffman compression +#define RS_DMF8 0x13 +#define RS_DMF16 0x17 +// MDL Huffman compression +#define RS_MDL8 0x20 +#define RS_MDL16 0x24 +#define RS_PTM8DTO16 0x25 +// Stereo Interleaved Samples +#define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed +#define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed +#define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian +// 24-bit signed +#define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed +#define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed +#define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed +#define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed + +// NNA types +#define NNA_NOTECUT 0 +#define NNA_CONTINUE 1 +#define NNA_NOTEOFF 2 +#define NNA_NOTEFADE 3 + +// DCT types +#define DCT_NONE 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +// DNA types +#define DNA_NOTECUT 0 +#define DNA_NOTEOFF 1 +#define DNA_NOTEFADE 2 + +// Mixer Hardware-Dependent features +#define SYSMIX_ENABLEMMX 0x01 +#define SYSMIX_WINDOWSNT 0x02 +#define SYSMIX_SLOWCPU 0x04 +#define SYSMIX_FASTCPU 0x08 + +// Module flags +#define SONG_EMBEDMIDICFG 0x0001 +#define SONG_FASTVOLSLIDES 0x0002 +#define SONG_ITOLDEFFECTS 0x0004 +#define SONG_ITCOMPATMODE 0x0008 +#define SONG_LINEARSLIDES 0x0010 +#define SONG_PATTERNLOOP 0x0020 +#define SONG_STEP 0x0040 +#define SONG_PAUSED 0x0080 +#define SONG_FADINGSONG 0x0100 +#define SONG_ENDREACHED 0x0200 +#define SONG_GLOBALFADE 0x0400 +#define SONG_CPUVERYHIGH 0x0800 +#define SONG_FIRSTTICK 0x1000 +#define SONG_MPTFILTERMODE 0x2000 +#define SONG_SURROUNDPAN 0x4000 +#define SONG_EXFILTERRANGE 0x8000 +#define SONG_AMIGALIMITS 0x10000 + +// Global Options (Renderer) +#define SNDMIX_REVERSESTEREO 0x0001 +#define SNDMIX_NOISEREDUCTION 0x0002 +#define SNDMIX_AGC 0x0004 +#define SNDMIX_NORESAMPLING 0x0008 +#define SNDMIX_HQRESAMPLER 0x0010 +#define SNDMIX_MEGABASS 0x0020 +#define SNDMIX_SURROUND 0x0040 +#define SNDMIX_REVERB 0x0080 +#define SNDMIX_EQ 0x0100 +#define SNDMIX_SOFTPANNING 0x0200 +#define SNDMIX_ULTRAHQSRCMODE 0x0400 +// Misc Flags (can safely be turned on or off) +#define SNDMIX_DIRECTTODISK 0x10000 +#define SNDMIX_ENABLEMMX 0x20000 +#define SNDMIX_NOBACKWARDJUMPS 0x40000 +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader + + +// Reverb Types (GM2 Presets) +enum { + REVERBTYPE_SMALLROOM, + REVERBTYPE_MEDIUMROOM, + REVERBTYPE_LARGEROOM, + REVERBTYPE_SMALLHALL, + REVERBTYPE_MEDIUMHALL, + REVERBTYPE_LARGEHALL, + NUM_REVERBTYPES +}; + + +enum { + SRCMODE_NEAREST, + SRCMODE_LINEAR, + SRCMODE_SPLINE, + SRCMODE_POLYPHASE, + NUM_SRC_MODES +}; + + +// Sample Struct +typedef struct _MODINSTRUMENT +{ + UINT nLength,nLoopStart,nLoopEnd; + UINT nSustainStart, nSustainEnd; + signed char *pSample; + UINT nC4Speed; + WORD nPan; + WORD nVolume; + WORD nGlobalVol; + WORD uFlags; + signed char RelativeTone; + signed char nFineTune; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; + CHAR name[22]; +} MODINSTRUMENT; + + +// Instrument Struct +typedef struct _INSTRUMENTHEADER +{ + UINT nFadeOut; + DWORD dwFlags; + WORD nGlobalVol; + WORD nPan; + WORD VolPoints[MAX_ENVPOINTS]; + WORD PanPoints[MAX_ENVPOINTS]; + WORD PitchPoints[MAX_ENVPOINTS]; + BYTE VolEnv[MAX_ENVPOINTS]; + BYTE PanEnv[MAX_ENVPOINTS]; + BYTE PitchEnv[MAX_ENVPOINTS]; + BYTE Keyboard[128]; + BYTE NoteMap[128]; + + BYTE nVolEnv; + BYTE nPanEnv; + BYTE nPitchEnv; + BYTE nVolLoopStart; + BYTE nVolLoopEnd; + BYTE nVolSustainBegin; + BYTE nVolSustainEnd; + BYTE nPanLoopStart; + BYTE nPanLoopEnd; + BYTE nPanSustainBegin; + BYTE nPanSustainEnd; + BYTE nPitchLoopStart; + BYTE nPitchLoopEnd; + BYTE nPitchSustainBegin; + BYTE nPitchSustainEnd; + BYTE nNNA; + BYTE nDCT; + BYTE nDNA; + BYTE nPanSwing; + BYTE nVolSwing; + BYTE nIFC; + BYTE nIFR; + WORD wMidiBank; + BYTE nMidiProgram; + BYTE nMidiChannel; + BYTE nMidiDrumKey; + signed char nPPS; + unsigned char nPPC; + CHAR name[32]; + CHAR filename[12]; +} INSTRUMENTHEADER; + + +// Channel Struct +typedef struct _MODCHANNEL +{ + // First 32-bytes: Most used mixing information: don't change it + signed char * pCurrentSample; + DWORD nPos; + DWORD nPosLo; // actually 16-bit + LONG nInc; // 16.16 + LONG nRightVol; + LONG nLeftVol; + LONG nRightRamp; + LONG nLeftRamp; + // 2nd cache line + DWORD nLength; + DWORD dwFlags; + DWORD nLoopStart; + DWORD nLoopEnd; + LONG nRampRightVol; + LONG nRampLeftVol; + LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; + LONG nFilter_A0, nFilter_B0, nFilter_B1; + LONG nROfs, nLOfs; + LONG nRampLength; + // Information not used in the mixer + signed char * pSample; + LONG nNewRightVol, nNewLeftVol; + LONG nRealVolume, nRealPan; + LONG nVolume, nPan, nFadeOutVol; + LONG nPeriod, nC4Speed, nPortamentoDest; + INSTRUMENTHEADER *pHeader; + MODINSTRUMENT *pInstrument; + DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; + DWORD nMasterChn, nVUMeter; + LONG nGlobalVol, nInsVol; + LONG nFineTune, nTranspose; + LONG nPortamentoSlide, nAutoVibDepth; + UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; + // 16-bit members + signed short nVolSwing, nPanSwing; + // 8-bit members + BYTE nNote, nNNA; + BYTE nNewNote, nNewIns, nCommand, nArpeggio; + BYTE nOldVolumeSlide, nOldFineVolUpDown; + BYTE nOldPortaUpDown, nOldFinePortaUpDown; + BYTE nOldPanSlide, nOldChnVolSlide; + BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; + BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; + BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; + BYTE nOldCmdEx, nOldVolParam, nOldTempo; + BYTE nOldOffset, nOldHiOffset; + BYTE nCutOff, nResonance; + BYTE nRetrigCount, nRetrigParam; + BYTE nTremorCount, nTremorParam; + BYTE nPatternLoop, nPatternLoopCount; + BYTE nRowNote, nRowInstr; + BYTE nRowVolCmd, nRowVolume; + BYTE nRowCommand, nRowParam; + BYTE nLeftVU, nRightVU; + BYTE nActiveMacro, nPadding; +} MODCHANNEL; + + +typedef struct _MODCHANNELSETTINGS +{ + UINT nPan; + UINT nVolume; + DWORD dwFlags; + UINT nMixPlugin; + char szName[MAX_CHANNELNAME]; // changed from CHAR +} MODCHANNELSETTINGS; + + +typedef struct _MODCOMMAND +{ + BYTE note; + BYTE instr; + BYTE volcmd; + BYTE command; + BYTE vol; + BYTE param; +} MODCOMMAND, *LPMODCOMMAND; + +//////////////////////////////////////////////////////////////////// +// Mix Plugins +#define MIXPLUG_MIXREADY 0x01 // Set when cleared + +class IMixPlugin +{ +public: + virtual ~IMixPlugin(); + virtual int AddRef() = 0; + virtual int Release() = 0; + virtual void SaveAllParameters() = 0; + virtual void RestoreAllParameters() = 0; + virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; + virtual void Init(unsigned long nFreq, int bReset) = 0; + virtual void MidiSend(DWORD dwMidiCode) = 0; + virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; +}; + + +#define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix +#define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect +#define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) + +typedef struct _SNDMIXPLUGINSTATE +{ + DWORD dwFlags; // MIXPLUG_XXXX + LONG nVolDecayL, nVolDecayR; // Buffer click removal + int *pMixBuffer; // Stereo effect send buffer + float *pOutBufferL; // Temp storage for int -> float conversion + float *pOutBufferR; +} SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; + +typedef struct _SNDMIXPLUGININFO +{ + DWORD dwPluginId1; + DWORD dwPluginId2; + DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX + DWORD dwOutputRouting; // 0=mix 0x80+=fx + DWORD dwReserved[4]; // Reserved for routing info + CHAR szName[32]; + CHAR szLibraryName[64]; // original DLL name +} SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + +typedef struct _SNDMIXPLUGIN +{ + IMixPlugin *pMixPlugin; + PSNDMIXPLUGINSTATE pMixState; + ULONG nPluginDataSize; + PVOID pPluginData; + SNDMIXPLUGININFO Info; +} SNDMIXPLUGIN, *PSNDMIXPLUGIN; + +typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); + +//////////////////////////////////////////////////////////////////// + +enum { + MIDIOUT_START=0, + MIDIOUT_STOP, + MIDIOUT_TICK, + MIDIOUT_NOTEON, + MIDIOUT_NOTEOFF, + MIDIOUT_VOLUME, + MIDIOUT_PAN, + MIDIOUT_BANKSEL, + MIDIOUT_PROGRAM, +}; + + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} MODMIDICFG, *LPMODMIDICFG; + +#define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. + +typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels + + + +//============== +class CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth, m_nXBassRange; + static UINT m_nReverbDepth, m_nReverbDelay, gnReverbType; + static UINT m_nProLogicDepth, m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo, gdwSoundSetup, gdwMixingFreq, gnBitsPerSample, gnChannels; + static UINT gnAGC, gnVolumeRampSamples, gnVUMeter, gnCPUUsage; + static LPSNDMIXHOOKPROC gpSndMixHook; + static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; + +public: // for Editing + MODCHANNEL Chn[MAX_CHANNELS]; // Channels + UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed + MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments + INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers + MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings + MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns + WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths + BYTE Order[MAX_ORDERS]; // Pattern Orders + MODMIDICFG m_MidiCfg; // Midi macro config table + SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins + UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; + DWORD m_dwSongFlags; // Song flags SONG_XXXX + UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; + UINT m_nType, m_nSamples, m_nInstruments; + UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; + UINT m_nMusicSpeed, m_nMusicTempo; + UINT m_nNextRow, m_nRow; + UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; + UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; + UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; + LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; + DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; + UINT m_nMaxOrderPosition; + UINT m_nPatternNames; + LPSTR m_lpszSongComments, m_lpszPatternNames; + char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR + CHAR CompressionTable[16]; + +public: + CSoundFile(); + ~CSoundFile(); + +public: + BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); + BOOL Destroy(); + UINT GetType() const { return m_nType; } + UINT GetNumChannels() const; + UINT GetLogicalChannels() const { return m_nChannels; } + BOOL SetMasterVolume(UINT vol, BOOL bAdjustAGC=FALSE); + UINT GetMasterVolume() const { return m_nMasterVolume; } + UINT GetNumPatterns() const; + UINT GetNumInstruments() const; + UINT GetNumSamples() const { return m_nSamples; } + UINT GetCurrentPos() const; + UINT GetCurrentPattern() const { return m_nPattern; } + UINT GetCurrentOrder() const { return m_nCurrentPattern; } + UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetMaxPosition() const; + void SetCurrentPos(UINT nPos); + void SetCurrentOrder(UINT nOrder); + void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } + LPCSTR GetTitle() const { return m_szNames[0]; } + UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; + UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; + UINT GetMusicSpeed() const { return m_nMusicSpeed; } + UINT GetMusicTempo() const { return m_nMusicTempo; } + DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); + DWORD GetSongTime() { return GetLength(FALSE, TRUE); } + void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } + int GetRepeatCount() const { return m_nRepeatCount; } + BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } + void LoopPattern(int nPat, int nRow=0); + void CheckCPUUsage(UINT nCPU); + BOOL SetPatternName(UINT nPat, LPCSTR lpszName); + BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; + // Module Loaders + BOOL ReadXM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadS3M(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMod(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMed(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadSTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadIT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL Read669(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUlt(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadWav(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadFAR(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMDL(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadOKT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDBM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPAT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestPAT(LPCBYTE lpStream, DWORD dwMemLength); + // Save Functions +#ifndef MODPLUG_NO_FILESAVE + UINT WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen=0); + BOOL SaveXM(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveS3M(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveMod(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveIT(LPCSTR lpszFileName, UINT nPacking=0); +#endif // MODPLUG_NO_FILESAVE + // MOD Convert function + UINT GetBestSaveFormat() const; + UINT GetSaveFormats() const; + void ConvertModCommand(MODCOMMAND *) const; + void S3MConvert(MODCOMMAND *m, BOOL bIT) const; + void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; + WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; + +public: + // Real-time sound functions + VOID ResetChannels(); + + UINT Read(LPVOID lpBuffer, UINT cbBuffer); + UINT CreateStereoMix(int count); + BOOL FadeSong(UINT msec); + BOOL GlobalFadeSong(UINT msec); + UINT GetTotalTickCount() const { return m_nTotalCount; } + VOID ResetTotalTickCount() { m_nTotalCount = 0; } + +public: + // Mixer Config + static BOOL InitPlayer(BOOL bReset=FALSE); + static BOOL SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels); + static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); + static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX + static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } + static DWORD GetSampleRate() { return gdwMixingFreq; } + static DWORD GetBitsPerSample() { return gnBitsPerSample; } + static DWORD InitSysInfo(); + static DWORD GetSysInfo() { return gdwSysInfo; } + // AGC + static BOOL GetAGC() { return (gdwSoundSetup & SNDMIX_AGC) ? TRUE : FALSE; } + static void SetAGC(BOOL b); + static void ResetAGC(); + static void ProcessAGC(int count); + + //GCCFIX -- added these functions back in! + static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); + // DSP Effects + static void InitializeDSP(BOOL bReset); + static void ProcessStereoDSP(int count); + static void ProcessMonoDSP(int count); + // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] + static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); + // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] + static BOOL SetXBassParameters(UINT nDepth, UINT nRange); + // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] + static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); +public: + BOOL ReadNote(); + BOOL ProcessRow(); + BOOL ProcessEffects(); + UINT GetNNAChannel(UINT nChn) const; + void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); + void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); + void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); + // Channel Effects + void PortamentoUp(MODCHANNEL *pChn, UINT param); + void PortamentoDown(MODCHANNEL *pChn, UINT param); + void FinePortamentoUp(MODCHANNEL *pChn, UINT param); + void FinePortamentoDown(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); + void TonePortamento(MODCHANNEL *pChn, UINT param); + void Vibrato(MODCHANNEL *pChn, UINT param); + void FineVibrato(MODCHANNEL *pChn, UINT param); + void VolumeSlide(MODCHANNEL *pChn, UINT param); + void PanningSlide(MODCHANNEL *pChn, UINT param); + void ChannelVolSlide(MODCHANNEL *pChn, UINT param); + void FineVolumeUp(MODCHANNEL *pChn, UINT param); + void FineVolumeDown(MODCHANNEL *pChn, UINT param); + void Tremolo(MODCHANNEL *pChn, UINT param); + void Panbrello(MODCHANNEL *pChn, UINT param); + void RetrigNote(UINT nChn, UINT param); + void NoteCut(UINT nChn, UINT nTick); + void KeyOff(UINT nChn); + int PatternLoop(MODCHANNEL *, UINT param); + void ExtendedMODCommands(UINT nChn, UINT param); + void ExtendedS3MCommands(UINT nChn, UINT param); + void ExtendedChannelEffect(MODCHANNEL *, UINT param); + void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); + void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; + // Low-Level effect processing + void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); + // Global Effects + void SetTempo(UINT param); + void SetSpeed(UINT param); + void GlobalVolSlide(UINT param); + DWORD IsSongFinished(UINT nOrder, UINT nRow) const; + BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; + // Read/Write sample functions + signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } + UINT PackSample(int &sample, int next); + BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); + UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); + BOOL DestroySample(UINT nSample); + BOOL DestroyInstrument(UINT nInstr); + BOOL IsSampleUsed(UINT nSample); + BOOL IsInstrumentUsed(UINT nInstr); + BOOL RemoveInstrumentSamples(UINT nInstr); + UINT DetectUnusedSamples(BOOL *); + BOOL RemoveSelectedSamples(BOOL *); + void AdjustSampleLoop(MODINSTRUMENT *pIns); + // I/O from another sound file + BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); + BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); + // Period/Note functions + UINT GetNoteFromPeriod(UINT period) const; + UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; + UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; + // Misc functions + MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } + void ResetMidiCfg(); + UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); + BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); + UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); + UINT LoadMixPlugins(const void *pData, UINT nLen); +#ifndef NO_FILTER + DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] +#endif + + // Static helper functions +public: + static DWORD TransposeToFrequency(int transp, int ftune=0); + static int FrequencyToTranspose(DWORD freq); + static void FrequencyToTranspose(MODINSTRUMENT *psmp); + + // System-Dependant functions +public: + static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); + static signed char* AllocateSample(UINT nbytes); + static void FreePattern(LPVOID pat); + static void FreeSample(LPVOID p); + static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); +}; + + +// inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } +// inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } + + +////////////////////////////////////////////////////////// +// WAVE format information + +#pragma pack(1) + +// Standard IFF chunks IDs +#define IFFID_FORM 0x4d524f46 +#define IFFID_RIFF 0x46464952 +#define IFFID_WAVE 0x45564157 +#define IFFID_LIST 0x5453494C +#define IFFID_INFO 0x4F464E49 + +// IFF Info fields +#define IFFID_ICOP 0x504F4349 +#define IFFID_IART 0x54524149 +#define IFFID_IPRD 0x44525049 +#define IFFID_INAM 0x4D414E49 +#define IFFID_ICMT 0x544D4349 +#define IFFID_IENG 0x474E4549 +#define IFFID_ISFT 0x54465349 +#define IFFID_ISBJ 0x4A425349 +#define IFFID_IGNR 0x524E4749 +#define IFFID_ICRD 0x44524349 + +// Wave IFF chunks IDs +#define IFFID_wave 0x65766177 +#define IFFID_fmt 0x20746D66 +#define IFFID_wsmp 0x706D7377 +#define IFFID_pcm 0x206d6370 +#define IFFID_data 0x61746164 +#define IFFID_smpl 0x6C706D73 +#define IFFID_xtra 0x61727478 + +typedef struct WAVEFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD filesize; // file length-8 + DWORD id_WAVE; +} WAVEFILEHEADER; + + +typedef struct WAVEFORMATHEADER +{ + DWORD id_fmt; // "fmt " + DWORD hdrlen; // 16 + WORD format; // 1 + WORD channels; // 1:mono, 2:stereo + DWORD freqHz; // sampling freq + DWORD bytessec; // bytes/sec=freqHz*samplesize + WORD samplesize; // sizeof(sample) + WORD bitspersample; // bits per sample (8/16) +} WAVEFORMATHEADER; + + +typedef struct WAVEDATAHEADER +{ + DWORD id_data; // "data" + DWORD length; // length of data +} WAVEDATAHEADER; + + +typedef struct WAVESMPLHEADER +{ + // SMPL + DWORD smpl_id; // "smpl" -> 0x6C706D73 + DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) + DWORD dwManufacturer; + DWORD dwProduct; + DWORD dwSamplePeriod; // 1000000000/freqHz + DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone + DWORD dwPitchFraction; + DWORD dwSMPTEFormat; + DWORD dwSMPTEOffset; + DWORD dwSampleLoops; // number of loops + DWORD cbSamplerData; +} WAVESMPLHEADER; + + +typedef struct SAMPLELOOPSTRUCT +{ + DWORD dwIdentifier; + DWORD dwLoopType; // 0=normal, 1=bidi + DWORD dwLoopStart; + DWORD dwLoopEnd; // Byte offset ? + DWORD dwFraction; + DWORD dwPlayCount; // Loop Count, 0=infinite +} SAMPLELOOPSTRUCT; + + +typedef struct WAVESAMPLERINFO +{ + WAVESMPLHEADER wsiHdr; + SAMPLELOOPSTRUCT wsiLoops[2]; +} WAVESAMPLERINFO; + + +typedef struct WAVELISTHEADER +{ + DWORD list_id; // "LIST" -> 0x5453494C + DWORD list_len; + DWORD info; // "INFO" +} WAVELISTHEADER; + + +typedef struct WAVEEXTRAHEADER +{ + DWORD xtra_id; // "xtra" -> 0x61727478 + DWORD xtra_len; + DWORD dwFlags; + WORD wPan; + WORD wVolume; + WORD wGlobalVol; + WORD wReserved; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; +} WAVEEXTRAHEADER; + +#pragma pack() + +/////////////////////////////////////////////////////////// +// Low-level Mixing functions + +#define MIXBUFFERSIZE 512 +#define MIXING_ATTENUATION 4 +#define MIXING_CLIPMIN (-0x08000000) +#define MIXING_CLIPMAX (0x07FFFFFF) +#define VOLUMERAMPPRECISION 12 +#define FADESONGDELAY 100 +#define EQ_BUFFERSIZE (MIXBUFFERSIZE) +#define AGC_PRECISION 9 +#define AGC_UNITY (1 << AGC_PRECISION) + +// Calling conventions +#ifdef MSC_VER +#define MPPASMCALL __cdecl +#define MPPFASTCALL __fastcall +#else +#define MPPASMCALL +#define MPPFASTCALL +#endif + +#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) +#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) + +int _muldiv(long a, long b, long c); +int _muldivr(long a, long b, long c); + + +// Byte swapping functions from the GNU C Library and libsdl + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static __inline unsigned short int +bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static __inline unsigned int +bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if (defined ARM) && (defined _WIN32_WCE) +static __inline unsigned short int +ARM_get16(const void *data) +{ + unsigned short int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +static __inline unsigned int +ARM_get32(const void *data) +{ + unsigned int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +#define bswapLE16(X) ARM_get16(&X) +#define bswapLE32(X) ARM_get32(&X) +#define bswapBE16(X) bswap_16(ARM_get16(&X)) +#define bswapBE32(X) bswap_32(ARM_get32(&X)) + +// From libsdl +#elif defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN +#define bswapLE16(X) bswap_16(X) +#define bswapLE32(X) bswap_32(X) +#define bswapBE16(X) (X) +#define bswapBE32(X) (X) +#else +#define bswapLE16(X) (X) +#define bswapLE32(X) (X) +#define bswapBE16(X) bswap_16(X) +#define bswapBE32(X) bswap_32(X) +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h new file mode 100644 index 000000000..b1b69e772 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h @@ -0,0 +1,127 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf , + * Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) + */ + +#ifndef _STDAFX_H_ +#define _STDAFX_H_ + +/* Autoconf detection of stdint/inttypes */ +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +# include "config.h" +# define CONFIG_H_INCLUDED 1 +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + + +#ifdef _WIN32 + +#ifdef MSC_VER +#pragma warning (disable:4201) +#pragma warning (disable:4514) +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#define srandom(_seed) srand(_seed) +#define random() rand() +#define sleep(_ms) Sleep(_ms) + +inline void ProcessPlugins(int n) {} + +#define strncasecmp(a,b,c) strncmp(a,b,c) +#define strcasecmp(a,b) strcmp(a,b) +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define HAVE_SINF 1 + +#else + +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif + +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t* PUCHAR; +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef int64_t LONGLONG; +typedef int32_t* LPLONG; +typedef uint32_t* LPDWORD; +typedef uint16_t WORD; +typedef uint8_t BYTE; +typedef uint8_t* LPBYTE; +typedef bool BOOL; +typedef char* LPSTR; +typedef void* LPVOID; +typedef uint16_t* LPWORD; +typedef const char* LPCSTR; +typedef void* PVOID; +typedef void VOID; + +inline LONG MulDiv (long a, long b, long c) +{ + // if (!c) return 0; + return ((uint64_t) a * (uint64_t) b ) / c; +} + +#define MODPLUG_NO_FILESAVE +#define NO_AGC +#define LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define WAVE_FORMAT_PCM 1 +//#define ENABLE_EQ + +#define GHND 0 + +inline int8_t * GlobalAllocPtr(unsigned int, size_t size) +{ + int8_t * p = (int8_t *) malloc(size); + + if (p != NULL) memset(p, 0, size); + return p; +} + +inline void ProcessPlugins(int /* n */ ) {} + +#define GlobalFreePtr(p) free((void *)(p)) + +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define wsprintf sprintf + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#endif // _WIN32 + +#endif + + + diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt new file mode 100644 index 000000000..d8319f644 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -0,0 +1,17 @@ +This folder contains the stb_vorbis library from +https://github.com/nothings/stb/blob/master/stb_vorbis.c +(commit 9d9f75eb682dd98b34de08bb5c489c6c561c9fa6) + +Modifications: + * Use of alloca has been replaced with malloc, as alloca is not in C99 and + fails to compile. + * Macro redefinition of alloca with mingw-w64 has been fixed. + * Macro redefinition of STB_VORBIS_NO_STDIO has been fixed. + * The following warnings have been silenced: + include/stb_vorbis/stb_vorbis.c:3928:32: warning: ‘hi’ may be used uninitialized in this function [-Wmaybe-uninitialized] + include/stb_vorbis/stb_vorbis.c:3927:32: warning: ‘low’ may be used uninitialized in this function [-Wmaybe-uninitialized] +Modifications are always additions and have been marked with // OpenMPT. + +For building, premake is used to generate Visual Studio project files. +See ../build/premake/ for details. + diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c new file mode 100644 index 000000000..43f343b5b --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -0,0 +1,5463 @@ +// Ogg Vorbis audio decoder - v1.11 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking sponsored +// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, +// Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix github:infatum +// +// Partial history: +// 1.11 - 2017/07/23 - fix MinGW compilation +// 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #ifndef STB_VORBIS_NO_STDIO // OpenMPT + #define STB_VORBIS_NO_STDIO + #endif // OpenMPT +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline +#if 0 // OpenMPT + #define alloca __builtin_alloca +#endif // OpenMPT +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#if 0 // OpenMPT +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) 0 +#endif +#else // OpenMPT +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : malloc(size)) // OpenMPT +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : free(p)) // OpenMPT +#endif // OpenMPT +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { +#if 0 // OpenMPT + int low,hi; +#else // OpenMPT + int low=0; // OpenMPT + int hi=0; // OpenMPT +#endif // OpenMPT + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) + return 1; + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015/04/19 - don't define __forceinline if it's redundant + 1.04 - 2014/08/27 - fix missing const-correct case in API + 1.03 - 2014/08/07 - Warning fixes + 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float + 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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/Frameworks/OpenMPT/OpenMPT/libopenmpt/Doxyfile b/Frameworks/OpenMPT/OpenMPT/libopenmpt/Doxyfile new file mode 100644 index 000000000..e8542c1b6 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/Doxyfile @@ -0,0 +1,2466 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libopenmpt" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "unknown" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "cross-platform C++ and C library to decode tracked music files" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = bin/docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = . + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = libopenmpt/dox/index.dox \ + libopenmpt/dox/quickstart.md \ + README.md \ + libopenmpt/dox/dependencies.md \ + libopenmpt/dox/packaging.md \ + libopenmpt/dox/tests.md \ + libopenmpt/dox/changelog.md \ + libopenmpt/dox/todo.md \ + libopenmpt/libopenmpt.hpp \ + libopenmpt/libopenmpt.h \ + libopenmpt/libopenmpt_stream_callbacks_buffer.h \ + libopenmpt/libopenmpt_stream_callbacks_fd.h \ + libopenmpt/libopenmpt_stream_callbacks_file.h \ + libopenmpt/libopenmpt_config.h \ + libopenmpt/libopenmpt_version.h \ + libopenmpt/libopenmpt_ext.hpp \ + libopenmpt/libopenmpt_ext.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = LIBOPENMPT_API \ + LIBOPENMPT_CXX_API \ + LIBOPENMPT_API_HELPER_EXPORT \ + LIBOPENMPT_API_HELPER_IMPORT \ + LIBOPENMPT_API_HELPER_PUBLIC \ + LIBOPENMPT_API_HELPER_LOCAL \ + OPENMPT_API_VERSION_HELPER_STRINGIZE \ + OPENMPT_API_VERSION_STRINGIZE \ + openmpt::detail + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = examples/ \ + LICENSE + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /